Skip to main content

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.

Prerequisites
  • 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.

note

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 -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. Contains input_mode ("numeric" or "text"), length (number of characters), and an optional description (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—an openid-credential-offer:// URI for the wallet. Display this to the user as a QR code or deep link.
  • tx_code_value (conditional)—present only when tx_code is 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.

# 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.

# 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:

FieldDescription
eventIdCorrelation key tied to the offer lifecycle. Set to the same value as offerId. To deduplicate retries, combine eventId + status.
statusOne of OFFER_CREATED, ISSUED, FAILED, or EXPIRED.
offerIdCorrelation token matching the offer_id from Step 1.
errorDetailsPresent 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 errorDetails for 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_id and credential_offer_uri
  • QR code renders from credential_offer_uri
  • Deep link opens the wallet on mobile
  • Callback receives OFFER_CREATED status immediately after offer creation
  • Callback receives the issuance event with ISSUED status after wallet completes
  • Callback correctly correlates offerId with the original session
  • Callback handles all four statuses (OFFER_CREATED, ISSUED, FAILED, EXPIRED)
  • FAILED callback includes errorDetails
  • Expired sessions are handled gracefully
  • eventId matches offerId in 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

Further reading