Credential Offers¶
Credential offers start OID4VCI issuance. Your backend creates an offer via the API, EUDIPLO returns an offer URI, and you present that URI to the wallet (for example as a QR code or deep link).
This page covers request shape and offer behavior. For flow selection guidance, see the Issuance Overview.
Before You Create an Offer¶
Usually you create these resources first:
- A Credential Configuration
- An Issuance Configuration
- Optionally an Attribute Provider if claims should be fetched dynamically
Creating Credential Offers¶
Use the credential offer endpoint to create the offer.
When creating an offer, you can:
- Define the flow with
flow - Select the credentials with
credentialConfigurationIds - Optionally select an external authorization server with
authorization_server - Optionally override claims with
credentialClaims - Optionally enable a transaction code with
tx_codeandtx_code_description - Optionally configure notifications with
webhookEndpointId
Common Request Fields¶
response_type- how the offer is returned to the callerflow-pre_authorized_codeorauthorization_codecredentialConfigurationIds- list of credential configuration IDs to include in the offerauthorization_server- optional authorization server identifier for flows using an external AScredentialClaims- optional per-credential claims source overridetx_code- optional transaction code for pre-authorized flowstx_code_description- optional prompt shown with the transaction codewebhookEndpointId- optional notification webhook endpoint ID
Notification webhook endpoint references
webhookEndpointId references a standalone Webhook Endpoint resource. Create the endpoint first, then reference it by ID.
If both a credential configuration and an offer specify webhookEndpointId, the offer-level value is used for that session.
Example: Pre-authorized Offer with Inline Claims¶
{
"response_type": "uri",
"flow": "pre_authorized_code",
"credentialConfigurationIds": ["citizen"],
"credentialClaims": {
"citizen": {
"type": "inline",
"claims": {
"given_name": "John",
"family_name": "Doe"
}
}
}
}
Example: Authorization Code Offer with External AS¶
{
"response_type": "uri",
"flow": "authorization_code",
"authorization_server": "https://keycloak.example.com/realms/myrealm",
"credentialConfigurationIds": ["employee_badge"]
}
Single-Use Offers¶
Credential offers are single-use and non-replayable. Once a wallet completes issuance with an offer:
- Token replay with the same authorization or pre-authorized code is rejected with an
invalid_granterror - The offer is marked as consumed at the credential endpoint and cannot be used again after successful credential processing
- The
consumedAttimestamp records when the offer was first used
Important considerations:
- Create a new offer for each issuance request
- Combine single-use enforcement with TTL-based cleanup so expired offers do not accumulate
- Refresh tokens remain valid for follow-up operations on the issued credentials
This prevents credential offer replay attacks where an intercepted offer could otherwise be reused.
Passing Claims¶
EUDIPLO provides multiple methods to pass claims during issuance. Claims are resolved in the following priority order:
- Offer-level claims via
credentialClaims - Configuration-level Attribute Provider via
attributeProviderId - Configuration-level static claims on the credential configuration
Claims are not merged
Higher priority sources completely override lower priority sources. If an offer-level webhook or Attribute Provider is used, lower-priority sources are not merged in.
Request Shape for credentialClaims¶
credentialClaims must be an object keyed by credential configuration ID. Each
key must also appear in credentialConfigurationIds.
Each value selects the claims source for that credential:
type: "inline"- pass the claims directly in the requesttype: "attributeProvider"- fetch claims from an existing Attribute Providertype: "webhook"- fetch claims from an inline webhook definition for this offer only
The most common way to override claims is the inline variant:
{
"response_type": "uri",
"flow": "pre_authorized_code",
"credentialConfigurationIds": ["citizen"],
"credentialClaims": {
"citizen": {
"type": "inline",
"claims": {
"given_name": "John",
"family_name": "Doe"
}
}
}
}
In this example:
citizenis the credential configuration IDclaimscontains the actual credential claim values- the inline claims override any configuration-level Attribute Provider or static claims for
citizen
You can also override the source instead of embedding claims directly:
{
"response_type": "uri",
"flow": "pre_authorized_code",
"credentialConfigurationIds": ["citizen", "employee_badge"],
"credentialClaims": {
"citizen": {
"type": "attributeProvider",
"attributeProviderId": "citizen-claims-provider"
},
"employee_badge": {
"type": "webhook",
"webhook": {
"url": "https://issuer.example.com/api/claims/employee-badge"
}
}
}
}
Notes:
credentialClaimskeys must be a subset ofcredentialConfigurationIds- values are resolved per credential configuration, not globally for the whole offer
- if you want to override only one credential in a multi-credential offer, include only that credential in
credentialClaims - for the full webhook shape, see Attribute Providers, Webhooks, and the API documentation
When to Use Each Method¶
- Configuration-level static claims for fixed metadata used on every issuance
- Configuration-level Attribute Providers for dynamic claims based on authentication context
- Offer-level inline claims when claim values are already known at offer creation time
- Offer-level webhook or Attribute Provider overrides when claim resolution should vary per offer
For the broader claims model, see Fetching Claims.