Practitioner credentials
When you pass the SDC Certified Practitioner exam, SDCStudio issues you a numbered credential: a small signed JSON document, a seal carrying your number, and a public verify page. This guide covers what you receive, how to put it on LinkedIn, and how anyone can check it without an account.
What you receive
On Settings → Practitioner once you are certified:
- Seal No. NNNN as SVG and PNG, for your own site, signature block, or a LinkedIn post. The number is yours; numbers are never reused.
- Your verify link,
https://sdcstudio.axius-sdc.com/practitioners/verify/<credential id>. Anyone who opens it sees your name, the dates, the status, and the signed document. - Add to LinkedIn profile. Opens LinkedIn's Licenses & certifications form prefilled with the program name, Axius SDC, Inc. as the issuer, the issue date, your credential ID and the verify link. LinkedIn shows the entry with the issuer's logo; it does not display badge images in that section, so the seal is for your Featured section or a post.
- A one-time program credit of 100,000 credits ($100 of SDCStudio use) in your wallet, recorded as "SDC Certified Practitioner exam pass" in the transaction history.
- Two opt-ins: list me in the public directory, and a LinkedIn profile URL shown on your verify page. Both are off until you turn them on.
What is in the credential
{
"version": "1.0",
"type": "SDC Practitioner Credential",
"program": "SDC Certified Practitioner",
"number": 7,
"issuer": {"name": "Axius SDC, Inc.", "url": "https://axius-sdc.com"},
"subject": {"name": "Your Name", "organization": "Your Org", "ct_id": "your SDCStudio id"},
"exam_passed_on": "2026-09-15",
"issued_on": "2026-09-21",
"credential_id": "…64 hex characters…",
"signature": {"key_id": "sdcstudio-signing-key-v1", "alg": "ES256", "value": "…"}
}
A credential issued once the issuer also signs post-quantum says "version": "1.1" and carries signatures instead, a list of two over the same digest, which must both verify:
"signatures": [
{"key_id": "sdcstudio-signing-key-v1", "alg": "ES256", "value": "…"},
{"key_id": "sdcstudio-pq-signing-key-v1", "alg": "ML-DSA-65", "value": "…"}
]
Your email address is never in it. ct_id is the identifier already on every component you publish, so a reader can join the credential to your published work.
How to verify one
The construction is the one a Settlement Receipt uses, and the key is the same key.
credential_idis the SHA-256 of the document canonicalized with RFC 8785, with thecredential_id,signatureandsignaturesmembers removed.- An
ES256valueis an ECDSA P-256 signature over that digest, 64 bytesr||s, base64url without padding. AnML-DSA-65value(version 1.1) is a pure ML-DSA-65 signature with the same 32 digest bytes as its message, 3309 raw bytes, base64url without padding. - Each
key_idnames an entry inhttps://sdcstudio.axius-sdc.com/.well-known/sdcstudio-signing-keys.json. Every listed signature must verify, and a 1.1 credential must carry both.
With pip install sdcreceipt (which brings the canonicalizer and cryptography):
import base64, hashlib, json, sys, urllib.request
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, utils
from sdcgovernance.jcs import canonicalize_bytes
doc = json.load(open(sys.argv[1])) # credential.json
keys = json.load(urllib.request.urlopen("https://sdcstudio.axius-sdc.com/.well-known/sdcstudio-signing-keys.json"))
content = {k: v for k, v in doc.items() if k not in ("credential_id", "signature", "signatures")}
digest = hashlib.sha256(canonicalize_bytes(content)).digest()
assert digest.hex() == doc["credential_id"], "content does not match credential_id"
entries = doc.get("signatures") or [doc["signature"]]
if doc["version"] == "1.1":
assert {e["alg"] for e in entries} == {"ES256", "ML-DSA-65"}, "a 1.1 credential carries both signatures"
for entry in entries:
pem = next(k["public_key_pem"] for k in keys["keys"] if k["key_id"] == entry["key_id"])
public = serialization.load_pem_public_key(pem.encode())
raw = base64.urlsafe_b64decode(entry["value"] + "==")
if entry["alg"] == "ES256":
public.verify(utils.encode_dss_signature(int.from_bytes(raw[:32], "big"), int.from_bytes(raw[32:], "big")),
digest, ec.ECDSA(utils.Prehashed(hashes.SHA256())))
else: # ML-DSA-65: the digest bytes are the message (cryptography 48 or later)
public.verify(raw, digest)
print("VERIFIED")
Then check the status: GET https://sdcstudio.axius-sdc.com/api/v1/practitioners/credentials/<credential id> returns active, revoked (with the date) or withdrawn. The full status list is at /api/v1/practitioners/credentials/index.json.
Statuses
| Status | Meaning | What is served |
|---|---|---|
| active | in good standing | document, seal, verify page |
| revoked | the certification was withdrawn by Axius SDC, Inc. on the date shown | document and seal stay served, marked revoked, so a copy can be checked against the record |
| withdrawn | the holder closed their account | number, dates and status only; the document and seal are no longer served |
Public endpoints
| Route | Returns |
|---|---|
GET /api/v1/practitioners/credentials/{credential_id} |
status and, unless withdrawn, the signed document and seal URL |
GET /api/v1/practitioners/credentials/{credential_id}/seal.svg |
the seal |
GET /api/v1/practitioners/credentials/index.json |
every credential id with its status |
GET /api/v1/practitioners/directory |
practitioners who opted in |
/practitioners/verify/{credential_id} |
the verify page |
/practitioners/directory |
the directory page |
All are unauthenticated, cacheable for five minutes, and rate-limited per client address.