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| Parameter | Required | Description |
|---|---|---|
grant_type | Yes | urn:ietf:params:oauth:grant-type:pre-authorized_code |
pre-authorized_code | Yes | Code from the credential offer |
tx_code | Conditional | Transaction 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).