Skip to main content
Version: v0.25.0 (Latest)

Issue credentials

This guide covers OID4VCI issuance only. Complete these linked prerequisites before starting:

  1. Platform foundations and authentication boundaries for the tenant service token and the wallet-token boundary.
  2. Tenant provisioning and confidential-client access for the tenant and hosted authorization server.
  3. Credential designs and claims for the design, format, and claims.
  4. Status-list creation and lifecycle for any status reference; create the list before the issuer binds it.
  5. Authorization-server configuration for the AS, clients, grants, and issuer binding.
  6. Issuer configuration for the issuer identity, signing selection, and credential configuration.

For presentation requests and verification results, continue with Verifier and DCQL configuration and Trust-domain creation and verifier binding.

Full pre-authorized-code flow​

Loading example...
Loading example...

This is the smallest complete issuance run. It starts with an already configured issuer and ends with a credential plus the status reference that a verifier must evaluate.

  1. Read issuer metadata anonymously. Resolve credential_issuer, credential_endpoint, token_endpoint (or the AS metadata URL), the chosen credential_configuration_id, supported proof algorithms, and the advertised format.
  2. Create an offer with the tenant token. Select exactly one configuration and the pre-authorized_code grant. Store the returned credential_offer URI and offer/session id.
  3. Resolve the offer anonymously. The wallet fetches the URI and confirms that the offer's configuration id and grant match the issuer metadata. Do not use the service client's token here.
  4. Exchange the pre-authorized code. POST to the token endpoint with the code. Store the response access_token as walletAccessToken and its expiry. The token response is not the nonce contract: do not expect or cache a generic c_nonce from it. This token belongs only to this issuance session.
  5. Resolve the issuer nonce contract. If issuer metadata advertises nonce_endpoint, POST to that exact endpoint and read the top-level c_nonce from its response. The endpoint is a single issuer capability, not a format-specific or flow-specific URL. If nonce_endpoint is absent, the issuer has not advertised a c_nonce requirement and the proof must omit the nonce claim.
  6. Build the wallet proof. Create a holder key, sign the proof JWT with the algorithm advertised by the configuration, set aud to the credential issuer, and, only when the nonce endpoint returned one, set nonce to that c_nonce. A tenant service token is not a proof of possession.
  7. Request the credential. POST the selected credential_configuration_id, requested subject claims, and proof to credential_endpoint, with Bearer walletAccessToken.
  8. Validate the response locally. Confirm the response format, issuer, configuration/type identifier, claims, validity, signature, and credentialStatus URI/index. Save the credential in the wallet only after those checks pass.
  9. Verify separately. The offer and credential response do not prove presentation. Use the verifier/DCQL guide to request a presentation, then evaluate signature, holder binding, trust, validity and current status.

The response-derived chain is:

issuer metadata -> credential_offer -> pre-authorized_code
-> walletAccessToken -> (POST nonce_endpoint -> c_nonce, when advertised)
-> credential request
-> credential + credentialStatus(status_list, status_list_index)

The canonical Postman collection follows this chain in 3. Tenant APIs > Issue credentials and its Receive it in a test wallet folder. The service client's token is used for offer administration; the wallet token is used only for token and credential protocol calls. Each token request stores its own response and clears incompatible stale session variables.

Postman correlation: Under 3. Tenant APIs, the canonical EDK Enterprise collection reads the issuer in Credential issuer, designs in Credential designs, standalone status publication in Status lists, and adds a credential configuration of your own in Define your own credential. Issue credentials runs the wallet protocol: 2. Create an offer from a template, 3. Create an EuPid offer, or 4. Create an EmployeeBadge offer with a transaction code, then 5. Get the offer status. Receive it in a test wallet plays the wallet: 1. Resolve the credential offer, 2. Read issuer metadata, 3. Read authorization server metadata, 4. Redeem the pre-authorized code, 5. Get a nonce, and 6. Request the credential. The folder script generates a P-256 holder key in the Postman sandbox and signs the proof with it. The wallet token is stored as walletAccessToken and is never reused for administration. 6. Find the status list of the credential and 7. Revoke the issued credential then revoke it.

Follow the issuance path​

Treat issuance as a chain of identifiers, not as one form submission. The design supplies the credential_configuration_id; the issuer metadata publishes that configuration; the offer binds the wallet to it; the token response authorizes the credential request; and the final response contains the format-specific credential plus any status reference. Carry IDs and URIs forward from responses. Do not derive them from a display name or by incrementing an index.

The Console Test surface is useful for an operator walkthrough. The REST protocol is the integration boundary for a wallet or issuer service. The tabs below show the same material action through each surface; a Console success is not evidence that an external wallet completed the protocol.

Choose the credential format​

Use the same format from the credential design through the credential request.

CredentialOID4VCI formatType identifierStatus publication
SD-JWT VCdc+sd-jwtvctJWT Token Status List, application/statuslist+jwt
ISO mdocmso_mdocdocTypeCWT Token Status List, application/statuslist+cwt
W3C VCDM 1.1 JWTjwt_vc_jsonVCDM 1.1 context and typesBitstring Status List credential, application/vc+jwt
W3C VCDM 2.0 JWTjwt_vc_json-ldVCDM 2.0 context and typesBitstring Status List credential, application/vc+jwt

For mdoc, each requested claim includes its namespace and element name. For example, org.iso.18013.5.1/family_name is different from the JSON claim family_name.

Confirm the issuer configuration​

Issuer setup is intentionally outside this issuance guide. Follow Issuer configuration, read the issuer metadata, and confirm the selected configuration and grant are advertised before creating an offer.

Issue with a pre-authorized code​

  1. Open the issuer Test page.
  2. Select the credential configuration and a pre-authorized-code request template.
  3. Enter the claims required by the design.
  4. Create the offer and give its URI to the wallet.
  5. Open the issuance session to inspect its current state.

Issue with an authorization code​

Use this grant when the wallet must authenticate the holder or obtain consent before issuance.

Select the authorization server in the issuer settings or request template. Confirm the client, scopes, redirect URI, and claim mappings used by the credential configuration, then create the offer from the issuer Test page.

Check the issued credential​

After the credential request succeeds:

  1. Confirm that the response format matches the requested credential_configuration_id.
  2. Decode the credential with a library that supports that format.
  3. Check the issuer, type identifier, validity period, claims, and status URI and index.
  4. Store the credential in the wallet.

For mdoc, check the docType, namespace-qualified elements, DSC chain, and x5chain. For W3C credentials, check the selected VCDM context and the expected credential structure.

For SD-JWT VC, check the vct, issuer, disclosure digests, and JWT Token Status List URI/index. For ISO mdoc, check the docType, namespace-qualified elements, issuer-signed items, DSC chain, and CWT Token Status List URI/index. For W3C VCDM 1.1, check the JSON-LD context and VC/credential subject shape; for VCDM 2.0, check the VCDM 2.0 context and data-model terms. A verifier must reject an unknown format, wrong type identifier, invalid signature, expired credential, or unresolvable status URI; it must not silently reinterpret one format as another.

Next​