How to Accept SD-JWT VC Credentials via OID4VCI in a Wallet with walt.id

TL;DR

Learn how to accept SD-JWT VC credentials (IETF) via OID4VCI in a wallet using walt.id's Wallet API, including the credential offer format and how selective disclosures are represented in the response.

What you'll learn

  • Recognise the SD-JWT VC credential offer format
  • Accept SD-JWT VC credential offers via the Wallet API
  • Understand how selective disclosures appear in the API response

The API call to accept an SD-JWT VC credential is identical to accepting a W3C VC — the same /wallet-api/wallet/{walletId}/exchange/useOfferRequest endpoint is used. The differences lie in the credential offer format and the structure of the response.

Credential Offer URL

As with all OID4VCI issuance, the issuer communicates the credential offer via a URL beginning with openid-credential-offer://.

Example Offer URL

openid-credential-offer://issuer.portal.walt.id/?credential_offer=<credential_offer>

Example Credential Offer Object

For SD-JWT VCs, the offer uses a vct field (Verifiable Credential Type) instead of the types array used by W3C credentials. This URI uniquely identifies the credential type defined by the issuer.

{
  "credential_issuer": "https://issuer.demo.walt.id",
  "credential_configuration_ids": [
    "identity_credential"
  ],
  "grants": {
    "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
      "pre-authorized_code": "<pre-authorized_code>",
      "user_pin_required": false
    }
  }
}

Accepting Credential Offers

CURL

Endpoint: /wallet-api/wallet/{walletId}/exchange/useOfferRequest | API Reference

Example Request

curl -X 'POST' \
  'http://0.0.0.0:7001/wallet-api/wallet/{walletId}/exchange/useOfferRequest?did={did}' \
  -H 'accept: application/json' \
  -H 'Content-Type: text/plain' \
  -H 'authorization: Bearer {token}' \
  -d 'openid-credential-offer://issuer.portal.walt.id/?credential_offer=<credential_offer>'

Path Parameters

  • walletId: String - The ID of the wallet to receive the credential into. See Accounts & Wallets for how to retrieve it.

Query Parameters

  • did (optional): String - The DID of the wallet holder to use when accepting the credential. If omitted, the wallet's default DID is used.

Header Parameters

  • authorization: String - Bearer token obtained from the login endpoint. Format: Bearer {token}.

Body

The credential offer URL received from the issuer, provided as plain text.

openid-credential-offer://issuer.portal.walt.id/?credential_offer=<credential_offer>

When using the terminal, the credential offer URL returned from the issuer may have a % at the end. Make sure to remove this before passing it to the wallet.


Example Response

On success, the API returns a list of the credentials received and stored in the wallet.

[
  {
    "wallet": "6006b6f4-e651-46db-b6ae-e7bd6e9c40f2",
    "id": "urn:uuid:48841c75-36cb-416b-af2e-6d4e606c8ca6",
    "document": "eyJraWQiOiJkaWQ6a2V5Ono2TWtqb1JocTFqU05KZExpcnVTWHJGRnhhZ3FyenRaYVhIcUhHVVRLSmJjTnl3cCN6Nk1ram9SaHExalNOSmRMaXJ1U1hyRkZ4YWdxcnp0WmFYSHFIR1VUS0piY055d3AiLCJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJnaXZlbl9uYW1lIjoiSm9obiIsImZhbWlseV9uYW1lIjoiRG9lIiwiZW1haWwiOiJqb2huZG9lQGV4YW1wbGUuY29tIiwicGhvbmVfbnVtYmVyIjoiKzEtMjAyLTU1NS0wMTAxIiwiYWRkcmVzcyI6eyJzdHJlZXRfYWRkcmVzcyI6IjEyMyBNYWluIFN0IiwibG9jYWxpdHkiOiJBbnl0b3duIiwicmVnaW9uIjoiQW55c3RhdGUiLCJjb3VudHJ5IjoiVVMifSwiaXNfb3Zlcl8xOCI6dHJ1ZSwiaXNfb3Zlcl8yMSI6dHJ1ZSwiaXNfb3Zlcl82NSI6dHJ1ZSwiaWQiOiJ1cm46dXVpZDo0ODg0MWM3NS0zNmNiLTQxNmItYWYyZS02ZDRlNjA2YzhjYTYiLCJpYXQiOjE3NzYzNTIzNjEsIm5iZiI6MTc3NjM1MjM2MSwiZXhwIjoxODA3ODg4MzYxLCJfc2RfYWxnIjoic2hhLTI1NiIsImlzcyI6ImRpZDprZXk6ejZNa2pvUmhxMWpTTkpkTGlydVNYckZGeGFncXJ6dFphWEhxSEdVVEtKYmNOeXdwIiwiY25mIjp7Imp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2Iiwia2lkIjoialo0WlFHampnLXpYRmZGNFpmbldfZDhXYkljZ0VYSW9uNGhJbXhhYzRiZyIsIngiOiJuMUNoQTNJSU1SdkVNSm51V055djU0bTVrZURhay1leHN0N3F6eXdPTmZFIiwieSI6Il9qWVN6U19Db3RQX2dMMGxiVUNyZWVOTmU4MXIweHEzMlllSjJ0NG1xblkifX0sInZjdCI6Imh0dHBzOi8vaXNzdWVyLmRlbW8ud2FsdC5pZC9kcmFmdDEzL2lkZW50aXR5X2NyZWRlbnRpYWwiLCJfc2QiOlsid2NXMkJXU2g5MXhneHh1RjhFckJJMkkxOVpoUEFST2pBTEtoZGRrX0JXUSJdfQ.u198RgZC1pv3VbZy7DbMHh6g432qnT4AIHhploWyyITtW5PCbUp_6ewJBvNjwf1tfNu8cDhcdP8E4UyG0djZAQ",
    "disclosures": "WyJVd3hscTJaX2ZCdlpZNGF2Y2JDTWpRIiwiYmlydGhkYXRlIiwiMTk0MC0wMS0wMSJd",
    "addedOn": "2026-04-16T15:12:41.345828048Z",
    "pending": false,
    "format": "vc+sd-jwt",
    "parsedDocument": {
      "given_name": "John",
      "family_name": "Doe",
      "email": "johndoe@example.com",
      "phone_number": "+1-202-555-0101",
      "address": {
        "street_address": "123 Main St",
        "locality": "Anytown",
        "region": "Anystate",
        "country": "US"
      },
      "is_over_18": true,
      "is_over_21": true,
      "is_over_65": true,
      "id": "urn:uuid:48841c75-36cb-416b-af2e-6d4e606c8ca6",
      "iat": 1776352361,
      "nbf": 1776352361,
      "exp": 1807888361,
      "_sd_alg": "sha-256",
      "iss": "did:key:z6MkjoRhq1jSNJdLiruSXrFFxagqrztZaXHqHGUTKJbcNywp",
      "cnf": {
        "jwk": {
          "kty": "EC",
          "crv": "P-256",
          "kid": "jZ4ZQGjjg-zXFfF4ZfnW_d8WbIcgEXIon4hImxac4bg",
          "x": "n1ChA3IIMRvEMJnuWNyv54m5keDak-exst7qzywONfE",
          "y": "_jYSzS_CotP_gL0lbUCreeNNe81r0xq32YeJ2t4mqnY"
        }
      },
      "vct": "https://issuer.demo.walt.id/draft13/identity_credential",
      "_sd": ["wcW2BWSh91xgxxuF8ErBI2I19ZhPAROjALKhddk_BWQ"]
    }
  }
]

Response Fields

  • wallet: String - The ID of the wallet the credential was stored in.
  • id: String - The unique identifier of the stored credential.
  • document: String - The raw signed SD-JWT token.
  • disclosures: String - Base64url-encoded disclosure(s) included with the credential. Each disclosure encodes a salt, claim name, and claim value — representing a selectively disclosable attribute the holder can choose to reveal when presenting. Decoding the value from the example above gives:
    ["Uwxlq2Z_fBvZY4avcbCMjQ", "birthdate", "1940-01-01"]
    
  • addedOn: String - ISO 8601 timestamp of when the credential was stored.
  • pending: Boolean - Whether the credential is pending acceptance.
  • format: String - The credential format. vc+sd-jwt identifies this as an IETF SD-JWT VC.
  • parsedDocument: Object - The decoded credential claims as a flat JSON object. Key fields include:
    • vct: String - The Verifiable Credential Type URI identifying the credential type.
    • iss: String - The DID of the issuer.
    • cnf.jwk: Object - The holder's public key, binding the credential to the wallet.
    • _sd_alg: String - The hash algorithm used for selective disclosure (typically sha-256).
    • _sd: Array - Hashes of claims that are selectively disclosable but withheld in this issuance.
    • iat, nbf, exp: Number - Issued at, not before, and expiration timestamps (Unix).
Last updated on April 16, 2026