Issue a credential
This guide shows you how to issue a credential of any type using the Truvity EUDIW Connector management API. You create a credential offer, display it to the user, and process the issuance callback in your backend.
The examples use generic claim names. Substitute your own credential_configuration_id and claims for your use case.
- A running connector instance with its public base URL configured and a callback URL pointing to your backend endpoint (for example,
http://localhost:3000/callback/issuance). See connector architecture for the deployment model and how callbacks are delivered. - Access to the internal management API (port 8081)
- A callback endpoint reachable from the connector over internal networking
- An X.509 access certificate configured in the connector
- An Issuer Signing Certificate configured in the connector. See Manage certificates to generate one.
- Type Metadata configured for the credential type you want to issue
Overview
Create a credential offer via the management API, display the offer URI to the user as a QR code or deep link, and handle the issuance callback when the flow completes.
Time to implement: 1–2 hours.
Step 1: Create a credential offer
Call POST /offers on the management API with the credential configuration ID and claims for the credential you want to issue. The connector returns 201 Created on success.
The credential_configuration_id, claim names, and claim values in this guide are generic examples. In production, use the credential configuration ID and claims defined in your Type Metadata.
- cURL
curl -X POST http://connector:8081/offers \
-H "Content-Type: application/json" \
-d '{
"credential_configuration_id": "IdentityCredential",
"claims": {
"given_name": "Erika",
"family_name": "Mustermann",
"document_number": "T22000129",
"issuing_country": "DE"
}
}'
Example response (201 Created):
{
"offer_id": "abc123def456",
"credential_offer_uri": "openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fissuer.example.com%2Foidc4vci%2Foffers%2Fabc123def456"
}
The request body contains:
credential_configuration_id—references a credential configuration in the connector's Credential Issuer Metadata. Must match a configured type in your Type Metadata.claims—the credential claims as key-value pairs. Use the claim names defined in your Type Metadata.tx_code(optional)—adds transaction code authorization. Containsinput_mode("numeric"or"text"),length(number of characters), and an optionaldescription(human-readable purpose displayed by wallets). See Use transaction codes for details.
The response contains:
offer_id—a correlation token for matching callbacks to this offer. Store this value in your session or database to identify which offer a callback belongs to.credential_offer_uri—anopenid-credential-offer://URI for the wallet. Display this to the user as a QR code or deep link.tx_code_value(conditional)—present only whentx_codeis included in the request. Deliver this to the user via a separate channel (for example, SMS or email).
Step 2: Display the credential offer to the user
Use credential_offer_uri to generate a QR code when the user is on a desktop. Use it as a deep link when the user is on a mobile device.
This step is client-side. Use any QR code library to encode the credential_offer_uri into a QR code image. For same-device flows, redirect the user's browser to the credential_offer_uri deep link.
- Shell
# Cross-device: generate a QR code PNG from the URI (requires qrencode)
qrencode -o qrcode.png "$credential_offer_uri"
# Same-device: open the deep link directly (macOS)
open "$credential_offer_uri"
Step 3: Handle the issuance callback
Implement a callback endpoint that receives the issuance event from the connector. The offerId field correlates the event with your original offer. Use it to look up the session you stored in Step 1.
- cURL
# Simulate an ISSUED callback for testing
curl -X POST http://backend.example.com:3000/callback/issuance \
-H "Content-Type: application/json" \
-d '{
"eventId": "abc123def456",
"status": "ISSUED",
"offerId": "abc123def456"
}'
The callback payload contains:
| Field | Description |
|---|---|
eventId | Correlation key tied to the offer lifecycle. Set to the same value as offerId. To deduplicate retries, combine eventId + status. |
status | One of OFFER_CREATED, ISSUED, FAILED, or EXPIRED. |
offerId | Correlation token matching the offer_id from Step 1. |
errorDetails | Present for FAILED status. Describes the failure reason. |
Handle each status:
- OFFER_CREATED—the offer was created and the session is active. Use for audit logging or to start a timeout timer.
- ISSUED—the credential was successfully issued to the wallet. Update your records accordingly.
- FAILED—issuance failed. Check
errorDetailsfor the reason, log the error, and consider offering the user a retry. - EXPIRED—the session time-to-live (TTL) expired before the wallet completed the flow. Create a new offer if the user wants to try again.
For the complete list of payload fields and status descriptions, see the callback events reference.
Testing
Test checklist
- Credential offer creates successfully and returns HTTP
201 Created - Response contains
offer_idandcredential_offer_uri - QR code renders from
credential_offer_uri - Deep link opens the wallet on mobile
- Callback receives
OFFER_CREATEDstatus immediately after offer creation - Callback receives the issuance event with
ISSUEDstatus after wallet completes - Callback correctly correlates
offerIdwith the original session - Callback handles all four statuses (
OFFER_CREATED,ISSUED,FAILED,EXPIRED) -
FAILEDcallback includeserrorDetails - Expired sessions are handled gracefully
-
eventIdmatchesofferIdin all callbacks
Troubleshooting
unknown_credential_configuration error
The credential_configuration_id in your offer request doesn't match any configured credential type. Verify that your Type Metadata is configured and that the credential_configuration_id matches a key in the connector's Credential Issuer Metadata.
Callback not receiving events
The connector delivers callbacks asynchronously. Verify that your callback endpoint is reachable from the connector over internal networking.
Offer expired before wallet completes
The default session time-to-live (TTL) is five minutes. If users consistently time out, consider adjusting the TTL or guiding users to scan the QR code promptly.
Next steps
- Implement AOC issuance—production-ready Account Ownership Credential issuance in a banking app
- Use transaction codes—add transaction code authorization to issuance flows
Further reading
- OID4VCI protocol—how the issuance protocol works
- Callback events—issuance event statuses and payload fields
- Error codes—wallet-facing HTTP error responses
- Configure credential types—set up Type Metadata for your credential types