---
title: "Callback and SSE"
description: "The verifier service can be configured to use a callback or Server Sent Events (SSE) to be notified when the verification session is completed."
stack: "Community Stack (open source) — 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/community-stack/verifier2/callback-and-sse
generated: 2026-07-20
---
# Callback and SSE

The verifier service can be configured to use a callback or Server Sent Events (SSE) to be notified when the verification session is completed.

## Callback

The callback is a URL that will be called when the verification session is completed. The callback will be called with the verification session ID and the verification session object. 

**Note:**

In previous versions of the verifier service, the callback information was passed through headers. This is no longer supported and the required configuration is now passed in the body of the request.

Example Request:
```json
"core_flow": {
  "dcql_query": {
    "credentials": [
      {
        "id": "example_openbadge_jwt_vc",
        "format": "jwt_vc_json",
        "meta":  {
          "type_values": [
            [
              "VerifiableCredential",
              "OpenBadgeCredential"
            ]
          ]
        },
        "claims": [
          {
            "path": [
              "name"
            ]
          }
        ]
      }
    ]
  },
  "webhook": {
    "url": "https://example.com/callback",
    "basicAuthUser": "1234567890",
    "basicAuthPass": "1234567890",
    "bearerToken": "1234567890"
  }
}
```

**Body Parameters**

- **dcql_query**: The DCQL query to be used for the verification.
- **webhook**: The webhook configuration to be used for the verification.
  - **url**: The URL to be called when the verification session is completed.
  For authentication, you can use basic authentication or bearer token authentication.
  - **basicAuthUser**: The username for basic authentication.
  - **basicAuthPass**: The password for basic authentication.
  OR
  - **bearerToken**: The bearer token for bearer authentication.

## Server Sent Events (SSE)

The SSE is a stream of events that will be sent to the client when the verification session is completed. The events will be sent to the client via a WebSocket connection.

From the desired client, you can subscribe to the SSE stream by using the following endpoint:
```bash
curl -X 'GET' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{verificationSessionID}/verifier2-service-api/verification-session/events' \
  -H 'Authorization: Bearer {yourToken}'
```

**Path Parameters**
- **orgID**: The organization ID.
- **verificationSessionID**: The target indicates the verification session to target, generally given as {target}.{sessionID} (e.g. org.tenant.verifier.61566cec-23ea-4fca-9049-5c8b316e3271) which is returned in the `creationTarget` parameter when creating the verification session.

## Verification Session Info

As is the case with polling the verification session info endpoint, both the callback andthe SSE stream will return the verficiation session object containing the following useful fields:

- `status`: The status of the verification session.
- `attempted`: Whether the verification session has been attempted.
- `reattemptable`: Whether the verification session can be retried.
- `policyResults`: The results of the policies applied to the verification session.
- `presentedRawData`: The raw data verifiable presentation token.
- `presentedCredentials`: The parsed credentials presented by the user.
- `failure`: Structured detail of *why* a session ended in `FAILED` (see below). `null` for successful or in-progress sessions.

Policy results will be split into vcPolicy and vpPolicy results. More information about the policy results can be found [here](https://docs.walt.id/community-stack/verifier2/policies/overview.md).

## Structured failure detail

When `status` is `FAILED`, the session carries a `failure` object describing the cause. The `type` discriminator distinguishes the failure modes, and each subtype carries detail specific to that mode. `statusReason` may contain a human-readable summary for quick display; use `failure.reason` for the canonical failure explanation.

All existing webhook fields keep their shape — `failure` is a new optional field. Consumers that only read `status` / `statusReason` / `policyResults` keep working unchanged.

### VC policy violation (per-credential attribution)

When a `vc_policies` check fails, each affected policy result carries `query_id` + `credential_index` so the failure can be tied back to the specific credential that failed. The same attributed policy result entries are exposed in `failure.violations`.

```json
{
  "event": "credential_policy_results_available",
  "session": {
    "status": "FAILED",
    "policy_results": {
      "vc_policies": [
        {
          "query_id": "pid",
          "credential_index": 1,
          "policy": {"id": "signature"},
          "success": false,
          "error": "Signature verification failed"
        }
      ]
    },
    "failure": {
      "type": "vc_policy_violations",
      "reason": "1 credential policy violation(s)",
      "violations": [
        {
          "query_id": "pid",
          "credential_index": 1,
          "policy": {"id": "signature"},
          "success": false,
          "error": "Signature verification failed"
        }
      ]
    }
  }
}
```

### DCQL fulfillment failure

When the presented credentials do not satisfy the DCQL query (missing required IDs or unsatisfied required `credential_set` options), `failure.failure` lists exactly which queries or sets were unmet.

```json
{
  "event": "dcql_fulfillment_check_failed",
  "session": {
    "status": "FAILED",
    "failure": {
      "type": "dcql_fulfillment",
      "reason": "DCQL Fulfillment Failed: 1 required CredentialSet(s) not satisfied. Unsatisfied sets: [[[pid], [mdl]]], Successfully validated query IDs: [email]",
      "failure": {
        "missing_query_ids": [],
        "unsatisfied_sets": [
          { "options": [["pid"], ["mdl"]] }
        ],
        "successfully_validated_query_ids": ["email"]
      }
    }
  }
}
```

### Presentation validation failure

When one or more VP policies fail (e.g. bad signature, wrong audience), `failure.failed_policies` mirrors `presentation_validation_results` but filtered to only the failed entries.

```json
{
  "event": "presentation_validation_failed",
  "session": {
    "status": "FAILED",
    "failure": {
      "type": "presentation_validation",
      "reason": "Presentation validation failed: ...",
      "failed_policies": {
        "pid": {
          "jwt_vc_json/envelope_signature": {
            "success": false,
            "errors": [ { "error": "InvalidSignature", "message": "..." } ]
          }
        }
      }
    }
  }
}
```

### Wallet-initiated rejection (OpenID4VP §8.5)

Wallets can POST an OpenID4VP [§8.5 error response](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-8.5) to the `direct_post` endpoint instead of a `vp_token` — for example when the user refuses to share credentials. `WalletPresentFunctionality2.walletRejectHandling` can create these rejection responses, and the verifier accepts both url-encoded (`error=access_denied&error_description=…&state=…`) and JSON bodies. When the original request included `state`, the error response must echo the same `state`.

Session transitions to `FAILED` with `failure.type = "wallet_error_response"`, and the webhook emits the new `wallet_error_response_received` event. If the verifier session was configured with `redirects.errorRedirectUri`, the `direct_post` response contains that URL as `redirect_uri` so the wallet can send the user back to the verifier's error page.

```json
{
  "event": "wallet_error_response_received",
  "session": {
    "status": "FAILED",
    "statusReason": "Wallet returned OID4VP error: access_denied",
    "failure": {
      "type": "wallet_error_response",
      "reason": "Wallet returned OID4VP error response per §8.5",
      "error": "access_denied",
      "error_description": "User denied",
      "state": "abc123"
    }
  }
}
```

**State echo**: when the original Authorization Request included a `state`, the wallet error response **must** echo the same `state`. Missing or mismatched state is rejected with `invalid_request` (HTTP 400) — matching the OAuth 2.0 / §8.5 contract.

**Idempotency**: once the session has reached a terminal state (`SUCCESSFUL` or `FAILED`), a duplicate or late wallet error POST is acknowledged with HTTP 200 but does **not** overwrite the existing `status` / `statusReason` / `failure`. This prevents a replayed or malicious error POST from flipping a successful verification to failed.

**Wallet helper**: use `WalletPresentFunctionality2.walletRejectHandling(...)` to produce the rejection response on the wallet side. The helper supports `fragment`, `query`, `form_post`, `direct_post`, and `direct_post.jwt` response modes, and accepts either the typed `OID4VPErrorCode` enum (recommended) or a raw `String` error code for forward compatibility with codes not yet in the enum. For `direct_post.jwt`, the helper sends the unencrypted form-encoded error response to `response_uri`, which OpenID4VP 1.0 permits when the Wallet cannot generate an encrypted response.
