Receiving a Credential
Overview
When your wallet receives a credential offer from an issuer, the process varies depending on the authorization flow used. This guide covers the three supported flows:
Pre-Authorized Code Flow: The issuer has already authorized the credential issuance
Authorization Code Flow (issuer-initiated): The issuer starts the flow with an invitation
Authorization Code Flow (wallet-initiated): Your wallet initiates the credential request
Handle Invitation
Most credential issuance flows begin when an issuer shares an invitation URL (often as a QR code). Use the "Handle invitation" endpoints to parse the invitation and determine the next steps.
Your system must support the issuance protocol used by the issuer to parse the invitation correctly.
Related guide: Configure OID4VCI for wallets
- API
- React Native
curl -L -X POST '/api/interaction/v1/handle-invitation' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"url": "openid-credential-offer-final1://?credential_offer_uri=https...", // Get invitation URL from QR code or deep link
"redirectUri": "myapp://callback" // Your registered deep link
}'
const invitationUrl = 'openid-credential-offer-final1://?credential_offer_uri=https...' // Get invitation URL from QR code or deep link
const redirectUri = 'myapp://callback' // Your registered deep link
const invitation = await core.handleInvitation({ url, organisationId, redirectUri })
Request parameters
url(required): The invitation URL from the issuerredirectUri: Your wallet's registered deep link where the authorization server should redirect after authentication. This URI must be registered as a deep link in your wallet application. This value is required for Authorization Code Flows and ignored in Pre-Authorized Code Flows.transport: For wallets with multiple transport protocols enabled, use this array to specify which transport protocol(s) should be used.
Transport protocol selection
This is typically only applicable to mobile devices, which could have,
for example, BLE and MQTT transport protocols.
The system determines which transport protocol to use based on both the
transport array and your transport order configuration:
transport array value | System behavior |
|---|---|
| Not provided or [] | Uses the transport protocol with the highest order value from your configuration |
| [one value] | Uses the specified transport protocol |
| [multiple values] | Uses the first supported transport found, following the order in your configuration |
See the supportedTransports capability of your verification protocol
for supported options.
Related guide: Transport order configuration
Response
This endpoint returns information that determines which flow to follow:
interactionId: A unique reference for the interactioninteractionType: Will beISSUANCEfor credential offersauthorizationCodeFlowUrl(conditional): If present, you must follow the Authorization Code Flow (issuer-initiated). Redirect the user to this URL to complete authentication.keyStorageSecurity: An enum providing guidance on the security required for issuance of the credential.txCode(conditional): If present, the issuer requires a transaction code. The user must provide this code to complete issuance.
Determining the next steps
Based on the response from "Handle invitation":
| Response includes | Next action |
|---|---|
authorizationCodeFlowUrl | Follow the Authorization Code Flow (issuer-initiated) |
txCode object | Prompt user for transaction code, then [accept with transaction code] |
| Neither | Continue directly to [accept the credential] |
Pre-Authorized Code Flow
In this flow, the issuer has already authorized the credential issuance. After handling the invitation, you can immediately accept the credential.
Accept credential issuance
Call the "Accept issuance" endpoint to accept the credential offer. There are two options when accepting an offer:
- Let the system auto-generate a new key pair (recommended)
- Specify an existing identifier
When letting the system auto-generate a new key pair, you only need to
reference the interactionId. The key pair will be generated according
to your configuration. See
Auto-Generate Keys for Credential Issuance
for a configuration guide.
Transaction codes
Some issuers require a transaction code to complete credential issuance. The issuer provides this code through a separate channel – for example, via email, SMS, or displayed on their website.
When "Handle invitation" returns a txCode object, you must prompt the
user to enter this code.
Transaction code object
The txCode object contains:
length: The expected length of the codeinputMode: The type of input expected (for examplenumeric,text)description: Instructions for the user on where to find or how to use the code
Accepting with a transaction code
Include the user-provided transaction code in the "Accept issuance" request:
- API
- React Native
curl -L -X POST '/api/interaction/v1/issuance-accept' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"identifierId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // Choose an identifier
"interactionId": "27755a19-f29f-434a-bf65-fcaed99a3642", // From handle invitation response
"txCode": "123456" // User-provided transaction code
}'
const interactionId = invitation.interactionId // From handle invitation response
const identifierId = 'your-identifier-id'
const txCode = '123456' // User-provided transaction code
const credentialId = await core.holderAcceptCredential({interactionId, identifierId, txCode});
The endpoint returns the credential ID directly.
Retrieve the credential
Accepting an issuance returns the credential ID. Retrieve it for the user to review.
Authorization Code Flow (issuer-initiated)
To enable this flow, the issuer and client ID must be specified in the configuration.
This flow supports PKCE (Proof Key for Code Exchange) and PAR (Pushed Authorization Requests) for enhanced security. These are handled automatically by the system.
When "Handle invitation" returns an authorizationCodeFlowUrl, the user
must complete authentication before the credential can be issued.
Step 1: Redirect to authorization server
When "Handle invitation" returns an authorizationCodeFlowUrl, redirect
the user to this URL. The user will complete authentication with the
authorization server outside your wallet system.
Step 2: Continue issuance
Once authentication is complete, the authorization server redirects the
user back to your wallet using the redirectUri you provided. Pass the
redirect URL to the "Continue Issuance" endpoint to exchange the
authorization code for an access token.
The response returns the same information as "Handle invitation":
interactionId: A unique reference for the interactioninteractionType: Will beISSUANCEfor credential offers
Step 3: Accept and retrieve
After continuing issuance, follow the same process as the Pre-Authorized Code Flow to accept and retrieve the credential.
Authorization Code Flow (wallet-initiated)
Use this flow when your wallet initiates the credential request rather than responding to an issuer's invitation.
This flow supports PKCE (Proof Key for Code Exchange) and PAR (Pushed Authorization Requests) for enhanced security. These are handled automatically by the system.
Step 1: Initiate issuance
For wallet-initiated flows, use this endpoint to make the initial request for credential issuance. This endpoint requires authorization request parameters.
The following is an example call:
- API
- React Native
curl -L -X POST '/api/interaction/v1/initiate-issuance' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"protocol": "OpenID4VCI", // From your configuration
"issuer": "https://example.com", // Issuer URL
"clientId": "openid-client", // Wallet's client ID
"redirectUri": "https://example.com", // Optional
"scope": [ // Either provide the scope, OR
"bli",
"blu"
],
"authorizationDetails": [ // Provide the authorizationDetails
{
"type": "openid_credential",
"credentialConfigurationId": "UniversityDegreeCredential"
}
]
}'
const request = {
organisationId: 'your-org-id',
protocol: 'OpenID4VCI', // From your configuration
issuer: 'https://example.com', // Issuer URL
clientId: 'openid-client', // Wallet's client ID
redirectUri: 'https://example.com', // Optional
scope: [ // Either provide the scope, OR
'bli',
'blu'
],
authorizationDetails: [ // Provide the authorizationDetails
{
type: 'openid_credential',
credentialConfigurationId: 'UniversityDegreeCredential'
}
]
};
const response = await core.initiateIssuance(request);
The issuer responds with the authorization endpoint URL; the end user can follow this to complete the authorization process with the authorization server.
Step 2: Continue issuance
Once authentication is complete, the authorization server redirects the
user back to your wallet using the redirectUri you provided. Pass the
redirect URL to the "Continue Issuance" endpoint to exchange the
authorization code for an access token.
The response returns the same information as "Handle invitation":
interactionId: A unique reference for the interactioninteractionType: Will beISSUANCEfor credential offers
Step 3: Accept and retrieve
After continuing issuance, follow the same process as the Pre-Authorized Code Flow to accept and retrieve the credential.
Optional: Reject an Offer
If you are within the token expiration time, you can optionally reject a credential offer. This action:
- Sends a
"credential_deleted"notification to the issuer (if supported) - Sets the credential status to
REJECTEDin your wallet system
Some issuers using OpenID4VCI include an optional notification_id during
credential issuance. When present, this enables your wallet to send status
updates back to the issuer about what happened with the offer.