Docs
Issuance

Pre-Authorized Code Flow

Server-initiated credential issuance with transaction codes

The pre-authorized code flow is the simplest issuance flow. The server generates a one-time code that the wallet exchanges for an access token. No user interaction with an authorization endpoint is required.

Flow overview

Token exchange

The wallet exchanges the pre-authorized code (from the offer) for an access token:

POST /oid4vci/token
Content-Type: application/x-www-form-urlencoded
DPoP: <dpop-proof-jwt>

grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code
&pre-authorized_code=code_from_offer
&tx_code=847291
ParameterRequiredDescription
grant_typeYesurn:ietf:params:oauth:grant-type:pre-authorized_code
pre-authorized_codeYesCode from the credential offer
tx_codeConditionalTransaction code shown to the user (if offer includes tx_code)

Response

{
  "access_token": "eyJ...",
  "token_type": "DPoP",
  "expires_in": 3600
}

token_type is DPoP when the request carries a DPoP proof header (as above), otherwise Bearer.

The token response does not contain a nonce (OID4VCI 1.0). The wallet fetches the proof nonce with a separate call:

POST /oid4vci/nonce
{
  "c_nonce": "nonce_123"
}

Credential request

With the access token, the wallet requests the credential:

POST /oid4vci/credential
Authorization: DPoP eyJ...
DPoP: <dpop-proof-jwt>
Content-Type: application/json

{
  "credential_configuration_id": "eu.europa.ec.eudi.pid.1",
  "proof": {
    "proof_type": "jwt",
    "jwt": "eyJ..."
  }
}

The proof JWT binds the credential to the wallet's key. It includes the c_nonce obtained from POST /oid4vci/nonce. To issue multiple credentials bound to different keys in one request, send proofs (plural) instead: { "proofs": { "jwt": ["eyJ...", "eyJ..."] } }.

Response

{
  "credentials": [
    { "credential": "..." }
  ],
  "c_nonce": "new_nonce_456",
  "c_nonce_expires_in": 300
}

The primary shape is the credentials array (one entry per proof); read credentials[0].credential. A deprecated top-level credential field is also present for backward compatibility when exactly one credential is issued; do not rely on it. Each credential value is the signed credential in the requested format (CBOR for mDoc, compact serialization for SD-JWT).

Unlike the token response, the credential response still includes c_nonce and c_nonce_expires_in for the next proof.

Transaction codes

When an offer includes a tx_code, the user must enter it in the wallet during the token exchange. This provides out-of-band verification that the person claiming the credential is the intended recipient.

The transaction code is returned when you create the offer:

{
  "offer_id": "abc123",
  "credential_offer": { "credential_issuer": "https://api.verifiables.com", "...": "..." },
  "credential_offer_uri": "openid-credential-offer://...",
  "tx_code": "847291"
}

Display or send this code to the user through a secure channel (SMS, email, in-person).