Notifications & Session Events

The Issuer2 Service offers two ways to observe issuance in real time:

  • Webhooks (notifications) — the issuer pushes events to your server. Best for server-to-server integration and production workflows.
  • Server-Sent Events (SSE) — your client streams events from the issuer. Best for live UI, such as showing issuance progress in a browser.

Both surface the same underlying issuance-session events.

WebhooksSSE
Best forServer-to-server, productionBrowser/client apps
DirectionIssuer pushes to your serverClient streams from issuer
ReliabilityMore reliable for long-running processesConnection may drop
SetupRequires a public endpointNo server needed

Webhooks

Webhook notifications are configured in the notifications object of a credential profile or as a runtime override when creating an offer.

Configuration

{
  "notifications": {
    "webhook": {
      "url": "https://your-server.com/webhook/issuance",
      "bearer_token": "your-secret-token"
    }
  }
}

Configuration Properties

  • webhook.url: String (required) - The URL to receive webhook notifications.
  • webhook.bearer_token: String (optional) - Bearer token sent with the webhook (as Authorization: Bearer <token>) for authentication.
  • webhook.basic_auth_username / webhook.basic_auth_password: String (optional) - Basic-auth credentials sent with the webhook instead of a bearer token.

Configure notifications at the profile level to apply to every offer from that profile, or as a per-offer runtime override to change them for a single offer.

Webhook Events

Your endpoint receives POST requests for the issuance-session events below. The event names are the same across webhooks and SSE:

StageEvents
Offercredential_offer_created, credential_offer_retrieved
Pushed authorizationpushed_authorization_request_succeeded, pushed_authorization_request_failed
Authorizationauthorization_request_succeeded, authorization_request_failed
Tokentoken_request_authorization_code_succeeded, token_request_authorization_code_failed, token_request_pre_authorized_code_succeeded, token_request_pre_authorized_code_failed, token_request_refresh_token_succeeded, token_request_refresh_token_failed, token_request_failed
Noncenonce_request_succeeded, nonce_request_failed
Credentialcredential_request_sd_jwt_vc_succeeded, credential_request_sd_jwt_vc_failed, credential_request_w3c_vc_succeeded, credential_request_w3c_vc_failed, credential_request_mso_mdoc_succeeded, credential_request_mso_mdoc_failed, credential_request_failed
Session lifecycleissuance_status_changed

Each protocol endpoint publishes one outcome event. token_request_failed and credential_request_failed are used when the grant or credential format cannot be resolved. Events include the session only after trusted correlation. Uncorrelated failures and nonce events are published only on GET /v2/{target}/issuer-service-api/events. Only terminal credential endpoint failures conclude a session; earlier failures and retryable invalid_proof / invalid_nonce leave the grant usable so the wallet can retry.

Failure events carry error and error_description on the envelope. Nested session.failure is not published. Terminal credential failures are also persisted on the session.

Each webhook is a POST whose JSON body is the same envelope used for SSE: target, event, session, requestId, and optional error / error_description. target is the issuance-session ID (the offerId) when the request is correlated, otherwise the requestId.

{
  "target": "9cf9e1f6-dc53-48f2-8124-b160433d7728",
  "event": "credential_offer_retrieved",
  "requestId": "3f1c2a8e-9b44-4c1d-8e2f-1a2b3c4d5e6f",
  "session": { "id": "9cf9e1f6-dc53-48f2-8124-b160433d7728", "status": "ACTIVE", "...": "..." }
}

Example Endpoint (Node.js/Express)

const express = require('express');
const app = express();
app.use(express.json());

app.post('/webhook/issuance', (req, res) => {
  if (req.headers.authorization !== 'Bearer your-secret-token') {
    return res.status(401).send('Unauthorized');
  }
  const { event, target, session } = req.body;
  console.log(`Issuance event '${event}' for session ${target}, status: ${session?.status}`);
  res.status(200).send('OK');
});

app.listen(3000);

Best Practices

  1. Secure your endpoint – Use HTTPS and verify the bearer token.
  2. Respond quickly – Return 200 promptly; process events asynchronously if needed.
  3. Handle retries – Implement idempotency in case of duplicate deliveries.
  4. Log events – Keep records of received events for debugging and auditing

Session Events (SSE)

The SSE endpoint streams issuance-session events to a connected client in real time. Use it to monitor the progress of a credential offer as it is claimed.

CURL

Endpoint: GET /v2/{target}/issuer-service-api/issuance-session/{issuance-session}/events | API Reference

Example Request
curl -N \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{target}/issuer-service-api/issuance-session/{offerId}/events' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Accept: text/event-stream'

Path Parameters

  • orgID: String (required) - Your organization ID, e.g. test.enterprise-sandbox.waltid.dev.
  • target: String (required) - The resource identifier of the issuer service, {organizationID}.{tenantID}.{issuerServiceID}.
  • issuance-session: String (required) - The offerId returned when creating the credential offer.

Header Parameters

  • Authorization: String (required) - Bearer token. Format: Bearer {token}.

Example Response

Each event is delivered as a single SSE data: line. The event name is carried inside the JSON payload's event property (the same envelope as the webhook). The first line is an empty priming frame sent when the connection opens:

data: {}

data: {"target":"9cf9e1f6-dc53-48f2-8124-b160433d7728","event":"credential_offer_retrieved","requestId":"...","session":{ ... }}

data: {"target":"9cf9e1f6-dc53-48f2-8124-b160433d7728","event":"token_request_pre_authorized_code_succeeded","requestId":"...","session":{ ... }}

Event Types

The session SSE stream emits the same correlated issuance-session events listed under Webhook Events. Uncorrelated failures and nonce events are available on GET /v2/{target}/issuer-service-api/events.

Event Format

Every event is a single data: line whose value is the JSON envelope; there is no SSE event: line:

data: {"target":"{offerId}","event":"{eventType}","requestId":"{requestId}","session":{ ... }}

Best Practices

  1. Handle reconnection – SSE connections may drop; implement reconnection logic.
  2. Set timeouts – Close connections after the expected session duration.
  3. Prefer webhooks for production – For server-to-server integration, webhooks are more reliable than SSE.
  4. Monitor Multiple Sessions – Open separate SSE connections for each session you want to monitor

Next Steps

Last updated on August 21, 2026