Webhooks¶
EUDIPLO supports webhook endpoints that let external services actively participate in issuance and presentation flows.
While webhooks are optional, they make the overall process more dynamic—for example:
- requesting credential data from your backend service only when needed,
- notifying your system when a wallet has completed a flow, or
- deferring credential issuance when your backend needs time to process (e.g., KYC verification, approval workflows).
Supported Webhook Scenarios¶
| Flow | Purpose |
|---|---|
| Credential Issuance Webhook | Dynamically provides claims during the credential request process. |
| Deferred Issuance Webhook | Signals that credential issuance should be deferred for later retrieval. |
| Presentation Webhook | Receives verified claims from the wallet. |
| Notification Webhook | Receives status updates (e.g., accepted or denied) about issuance flows. |
Interactive Authorization (IAE)
For user interactions during issuance (e.g., verifiable presentations, web-based verification), see Interactive Authorization Endpoint.
Example Webhook Service¶
A simple webhook simulator is available in the test/webhook
directory. It can be run locally or deployed to a Cloudflare Worker, and is a good starting point for testing webhook functionality.
Webhook Configuration¶
A webhook configuration object defines how EUDIPLO interacts with your service.
It must include:
url: The endpoint URL. EUDIPLO sends an HTTPPOSTrequest with JSON data.auth: (Optional) Authentication configuration.type: Authentication type. Currently supported:apiKey– sends a key in a request header.
Example¶
{
"url": "http://localhost:8787/consume",
"auth": {
"type": "apiKey",
"config": {
"headerName": "x-api-key",
"value": "your-api-key"
}
}
}
Claims Webhook¶
The claims webhook allows EUDIPLO to fetch attributes dynamically instead of embedding them in the credential offer. This is useful if:
- claims are not known in advance, or
-
you want to avoid including sensitive data in the offer for privacy reasons.
-
Pre-authenticated / Authenticated flows:
Called during the credential request.
If no webhook is configured, EUDIPLO falls back to claims provided in the credential offer or defined in the credential configuration. -
Interactive Authorization (IAE) with presentation:
When using IAE with anopenid4vp_presentationaction, the webhook is called after the wallet completes the presentation.
EUDIPLO sends verified claims to your service, which must respond with the claims to persist in the credential.
This avoids your service needing to manage state between the authentication and issuance phases.
See Interactive Authorization Endpoint for details.
{
"claimsWebhook": {
"url": "http://localhost:8787/process",
"auth": {
"type": "apiKey",
"config": {
"headerName": "x-api-key",
"value": "your-api-key"
}
}
}
}
Claims Webhook Request Format¶
EUDIPLO sends an HTTP POST request with the following structure:
{
"session": "a6318799-dff4-4b60-9d1d-58703611bd23",
"credential_configuration_id": "EmployeeBadge",
"identity": {
"iss": "https://idp.example.com/realms/myrealm",
"sub": "user-uuid-from-idp",
"token_claims": {
"email": "user@example.com",
"preferred_username": "jdoe",
"given_name": "John",
"family_name": "Doe"
}
},
"credentials": []
}
| Field | Type | Description |
|---|---|---|
session |
string | The session ID identifying the issuance request |
credential_configuration_id |
string | The ID of the credential configuration being requested |
identity |
object | Identity context from the authorization flow (see below) |
credentials |
array | Presented credentials (only for IAE with presentation flows) |
Identity Object¶
The identity object contains information about the authenticated user. Its contents depend on the authorization flow used:
| Field | Type | Description |
|---|---|---|
iss |
string | The issuer URL of the authorization server |
sub |
string | The subject identifier (user ID) from the authorization server |
token_claims |
object | All available claims from the access token (and ID token for Chained AS) |
Identity Sources by Flow¶
| Flow | Identity Source |
|---|---|
| External AS | Claims from the external authorization server's access token |
| Chained AS | Claims from the upstream OIDC provider (merged ID token + access token) |
| Pre-authenticated | Not available (no user authentication) |
| IAE | Identity from the IAE interaction (presentation or web redirect) |
Token Claims
The token_claims field contains all available claims from the token—EUDIPLO does not filter them. This gives your webhook full flexibility to use any claim the authorization server provides. For Chained AS flows, ID token claims take precedence over access token claims when both contain the same field.
Deferred Credential Issuance¶
Deferred issuance allows your backend to signal that the credential cannot be issued immediately. This is useful when:
- Background verification is required (e.g., KYC, identity proofing)
- An approval workflow must be completed
- External data sources need time to respond
- The credential requires asynchronous processing
How It Works¶
When the claims webhook returns a deferred response, EUDIPLO:
- Stores the pending request with a
transaction_id - Returns HTTP 202 (Accepted) to the wallet with the
transaction_id - The wallet polls the deferred credential endpoint until the credential is ready
sequenceDiagram
autonumber
actor Wallet as EUDI Wallet
participant EUDIPLO as Middleware
participant Service as Your Backend
Wallet->>EUDIPLO: Credential Request
EUDIPLO->>Service: Claims Webhook
Service-->>EUDIPLO: { "deferred": true, "interval": 5 }
EUDIPLO-->>Wallet: HTTP 202 + transaction_id
loop Polling (every interval seconds)
Wallet->>EUDIPLO: GET /deferred_credential
alt Credential not ready
EUDIPLO-->>Wallet: { "error": "issuance_pending", "interval": 5 }
else Credential ready
EUDIPLO-->>Wallet: { "credential": "..." }
end
end
Webhook Response for Deferred Issuance¶
To trigger deferred issuance, your webhook should return:
| Field | Type | Description |
|---|---|---|
deferred |
boolean | Set to true to defer the credential issuance |
interval |
number | Recommended polling interval in seconds (default: 5) |
Completing Deferred Issuance¶
Once your backend has completed processing, you need to call EUDIPLO's API to provide the credential:
# Complete the deferred transaction with the credential
POST /issuer/deferred/{transactionId}/complete
Content-Type: application/json
Authorization: Bearer <your-token>
{
"credential": "<the-issued-credential-string>"
}
Or, if the issuance failed:
# Mark the deferred transaction as failed
POST /issuer/deferred/{transactionId}/fail
Content-Type: application/json
Authorization: Bearer <your-token>
{
"errorMessage": "KYC verification failed"
}
Deferred Credential Errors¶
When the wallet polls the deferred credential endpoint, it may receive:
| Error Code | HTTP Status | Description |
|---|---|---|
issuance_pending |
400 | Credential is still being processed. Retry later. |
invalid_transaction_id |
400 | Transaction not found, expired, or already retrieved. |
The issuance_pending error includes an interval field indicating when to retry:
{
"error": "issuance_pending",
"error_description": "The credential issuance is still pending",
"interval": 5
}
Transaction Lifecycle¶
Deferred transactions have the following states:
| Status | Description |
|---|---|
pending |
Waiting for your backend to complete processing |
ready |
Credential is ready for wallet retrieval |
retrieved |
Wallet has successfully retrieved the credential |
expired |
Transaction expired (default: 24 hours) |
failed |
Issuance failed due to an error |
Transaction Expiry
Deferred transactions expire after 24 hours by default. Expired transactions are automatically cleaned up hourly.
Notification Webhook¶
The notification webhook receives the outcome of the issuance process (e.g., accepted or denied).
This confirms that the wallet has received and accepted the credential.
{
"notifyWebhook": {
"url": "http://localhost:8787/notify",
"auth": {
"type": "apiKey",
"config": {
"headerName": "x-api-key",
"value": "your-api-key"
}
}
}
}
If no notification webhook is configured, you can fetch the session result by querying the /session endpoint with the sessionId.
Presentation Webhook¶
The presentation webhook receives verified claims from the wallet after a presentation flow completes.
{
"webhook": {
"url": "http://localhost:8787/notify",
"auth": {
"type": "apiKey",
"config": {
"headerName": "x-api-key",
"value": "your-api-key"
}
}
}
}
Webhook Request Format¶
EUDIPLO sends an HTTP POST request with the following structure:
credentials: Array of credential objects. Each includes:id: The ID of the DCQL query.values: The claims presented by the wallet.- SD-JWT VC–specific fields (e.g.,
cnf,status) are removed for simplicity.
- SD-JWT VC–specific fields (e.g.,
error: Present instead ofvaluesif verification failed.
session: The session ID identifying the request.
{
"credentials": [
{
"id": "pid",
"values": {
"iss": "https://service.eudi-wallet.dev",
"iat": 1751884150,
"vct": "https://service.eudi-wallet.dev/credentials/vct/pid",
"address": {
"locality": "KÖLN",
"postal_code": "51147",
"street_address": "HEIDESTRAẞE 17"
}
}
},
{
"id": "citizen",
"error": "Credential verification failed: invalid signature"
}
],
"session": "a6318799-dff4-4b60-9d1d-58703611bd23"
}
Info
Requests always use Content-Type: application/json. A retry mechanism is not yet implemented—if a webhook fails, the process halts. Retry support may be added in the future.
Webhook Response Format¶
A response is required for:
- IAE with presentation webhooks, and
- issuance webhooks.
The response must be a JSON object keyed by the credential configuration ID. Each entry contains the claims to issue.
Example Response¶
Issuing a credential with ID citizen:
This response is injected into the issuance flow to create the final credential.