---
title: "Notifications & Session Events"
description: "Track credential issuance events in real time via webhooks or Server-Sent Events in the walt.id Enterprise Issuer2 Service."
stack: "Enterprise Stack (commercial) — version 0.21.0"
stack_version: "0.21.0"
stack_comparison: https://docs.walt.id/community-vs-enterprise.md
canonical_url: https://docs.walt.id/enterprise-stack/services/issuer2-service/notifications
generated: 2026-07-20
---
# 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.

| | Webhooks | SSE |
|---|----------|-----|
| **Best for** | Server-to-server, production | Browser/client apps |
| **Direction** | Issuer pushes to your server | Client streams from issuer |
| **Reliability** | More reliable for long-running processes | Connection may drop |
| **Setup** | Requires a public endpoint | No server needed |

---

## Webhooks

Webhook notifications are configured in the `notifications` object of a [credential profile](https://docs.walt.id/enterprise-stack/services/issuer2-service/credential-profiles/create-profile.md) or as a [runtime override](https://docs.walt.id/enterprise-stack/services/issuer2-service/credential-offers/create-offer.md#runtime-overrides) when creating an offer.

### Configuration

```json
{
  "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.

**Note:**

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:

| Event | When triggered |
|-------|----------------|
| `resolved_credential_offer` | A wallet resolves the credential offer. |
| `requested_token` | A wallet requests an access token. |
| `sdjwt_issue` | An SD-JWT VC credential is issued. |
| `jwt_issue` | A W3C JWT credential is issued. |
| `generated_mdoc` | An mDoc credential is generated. |
| `issuance_status` | The session status changes (e.g. `SUCCESSFUL`, `EXPIRED`). |

Each webhook is a POST whose JSON body is the same envelope used for [SSE](#session-events-sse): `target`, `event`, and the full `session` object. `target` is the **issuance-session ID** (the `offerId`), not the issuer service path, and `event` holds one of the event names above.

```json
{
  "target": "9cf9e1f6-dc53-48f2-8124-b160433d7728",
  "event": "resolved_credential_offer",
  "session": { "id": "9cf9e1f6-dc53-48f2-8124-b160433d7728", "status": "ACTIVE", "...": "..." }
}
```

### Example Endpoint (Node.js/Express)

```javascript
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
5. **Monitor failures** – If your endpoint is unreachable, issuance still continues, but the event may be lost. Fall back to SSE or polling if you must not miss events.

---

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

**Option: CURL**

**Endpoint:** `GET /v2/{target}/issuer-service-api/issuance-session/{issuance-session}/events` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

##### Example Request

```bash
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 `{target, event, session}` envelope as the webhook). The first line is an empty priming frame sent when the connection opens:

```text
data: {}

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

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

### Event Types

The SSE stream emits the same issuance-session events listed under [Webhook Events](#webhook-events): `resolved_credential_offer`, `requested_token`, `sdjwt_issue`, `jwt_issue`, `generated_mdoc`, and `issuance_status`.

### 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}","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

- [Create a Profile](https://docs.walt.id/enterprise-stack/services/issuer2-service/credential-profiles/create-profile.md) – Add notifications to your profiles.
- [Create an Offer](https://docs.walt.id/enterprise-stack/services/issuer2-service/credential-offers/create-offer.md) – Override notifications per offer.
