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.

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:

"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:

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.

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.

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

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

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

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

Last updated on May 13, 2026