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.

  1. credential_id is the SHA-256 of the document canonicalized with RFC 8785, with the credential_id, signature and signatures members removed.
  2. An ES256 value is an ECDSA P-256 signature over that digest, 64 bytes r||s, base64url without padding. An ML-DSA-65 value (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.
  3. Each key_id names an entry in https://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.