HomeGuidesRecipesAPI Endpoint Reference
Log In
Guides

Credential Presentation (SD-JWT)

The vp_token for an SD-JWT VC presentation is a single string with three types of components, delimited by tildes (~):

<Issuer-Signed-JWT>~<Disclosure-1>~<Disclosure-2>~...~<Key-Binding-JWT>
ComponentDescription
Issuer-Signed JWTThe base SD-JWT signed by Proof. Contains hashed claim digests in the _sd array, the holder's public key in the cnf claim, and the credential type in vct.
DisclosuresBase64url-encoded arrays of [salt, claim_name, claim_value]. Only claims requested by the provided scope value will be returned.
Key Binding JWTSigned by the holder's private key (in Proof's cloud wallet). Contains nonce, aud, and iat. Proves holder possession, prevents replay, and optionally binds the presentation to a specific set of transaction data.

Sample Payloads: Identity Credential

The following shows a complete, decoded VP Token for a https://credentials.notarize.com/ProofCredentialV1 credential presentation where the verifier requested given_name, family_name, and birthdate. Each component is shown separately so you can see exactly what to expect.

Issuer-Signed JWT Header

{
  "alg": "ES256",
  "typ": "dc+sd-jwt",
  "kid": "d96f1ca3-864f-49c8-92c7-17d341ae1234"
}
ℹ️

The kid value references a key in Proof's JWKs endpoint. Always fetch the key dynamically — never hardcode it, as Proof rotates signing keys periodically.

Issuer-Signed JWT Payload

{
  "iss": "https://api.proof.com",
  "sub": "user_8f3a92b1-47d6-4e2c-b5a0-1c9d3e7f6a84",
  "vct": "https://credentials.notarize.com/ProofCredentialV1",
  "iat": 1739280000,
  "cnf": {
    "jwk": {
      "kty": "EC",
      "crv": "P-256",
      "x": "b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ",
      "y": "Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8"
    }
  },
  "_sd": [
    "--NrmJe6y2u_S2dC8RKHHv3rElWsqpY8r6xslJuwaJI",
    "JzYjH4svliH0R3PyEMfeZu6Jt69u5qehZo7F7EPYlSE",
    "UgEtkU6-DwPQXQM-UrsaWMd7MOWMB_3iZjDeyp6gvUY",
    "S4sER_VP45OHkmC7-BVsj2NqlA_0jhsJE7P0rTkkUFQ",
    "bViw5OlxKJBrlXkjSBkz2MERPl1gUHnKxHmkR6Zf6Mk",
    "h4JMnGjFOh9HfOB-r09jqeMxEpGELe3BDLn_fuY34vs",
    "qF42T_IHyzGvTIGLH1gPMFGVLW92-Ol9HwFBaFDuGB4",
    "UkVeSoup2-mIUXvkbsm2aQr6BdwyW34Tydo6PtLINQ0"
  ]
}

Payload — What to validate

FieldValidation
issMust exactly match https://api.proof.com (or sandbox equivalent). Reject if different.
vctMust match the vct_values you specified in your DCQL query. For this example: https://credentials.notarize.com/ProofCredentialV1.
iatIssuance timestamp. Use for audit logging. Optionally reject credentials older than your policy allows.
cnf.jwkThe holder's public key. You'll use this to verify the Key Binding JWT signature. Do not use this for anything else.
_sdArray of base64url-encoded SHA-256 digests. Each disclosure you receive must hash to one of these values.

Disclosures (3 of 8 disclosed)

Only the three claims requested via the provided scope parameter are disclosed. The remaining five claims in the credential (address, document_number, etc.) are not included — the verifier never sees them.

Disclosure 1 – given_name (base64url decoded)

["2GLC42sKQveCfGfryNRN9w", "given_name", "Darren"]

Disclosure 2 – family_name (base64url decoded)

["eluV5Og3gSNII8EYnsxA_A", "family_name", "Louie"]

Disclosure 3 – date_of_birth (base64url decoded)

["6Ij7tM-a5iVPGboS5tmvVA", "birthdate", "1990-07-15"]

Disclosure format

ElementTypeDescription
[0]stringSalt — Random value that prevents rainbow-table attacks on claim digests. You don't use this directly, but it's part of the hash input.
[1]stringClaim name — The name of the disclosed attribute (e.g. given_name). This is the key you'll use when extracting claims.
[2]anyClaim value — The actual value. Type depends on the claim: strings for names, ISO 8601 date strings for dates, objects for structured data like addresses.
⚠️

Disclosure integrity check: For each disclosure, compute SHA-256(base64url_encoded_disclosure_string) and verify the resulting digest appears in the _sd array. Hash the raw base64url string as received in the VP Token — not the decoded JSON.

Key Binding JWT

Header:

{
  "alg": "ES256",
  "typ": "kb+jwt"
}

Payload:

{
  "nonce": "n-0S6_WzA2Mj_6a8bRs2TU",
  "aud": "camdrbpxd",
  "iat": 1739280120,
  "sd_hash": "fUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo"
}

Key Binding JWT — What to validate

FieldValidation
nonceMust exactly match the nonce you sent in the authorization request. This proves the presentation was created for your session.
audMust match your client_id value`.
iatPresentation creation time. Reject if too far in the past (recommended: no more than 5 minutes). This limits the replay window.
sd_hashSHA-256 hash over the Issuer-Signed JWT concatenated with all disclosures (with ~ delimiters). Binds the KB-JWT to this specific presentation.
SignatureVerify using the public key from the cnf.jwk claim in the Issuer-Signed JWT. This proves the holder (Proof's cloud wallet) authorized this presentation.

Transaction Data

The key binding JWT will, if requested, contain data from the transaction_data object passed in the authorization request. Depending on the transaction data type, data will be included in a top-level claim in the key binding JWT which will include the same claims as the payload claim in the transaction data request object. Those top-level claims are as follows: