This guide explains the full lifecycle of an identity verification session — from creation to result retrieval.
- An API credential with the
deepcredentials.verifyscope. See Authentication. - A credential definition configured in your organization.
| Status | Description |
|---|---|
pending | Session created, waiting for user to scan QR and present credentials. |
success | Credentials verified successfully. credential_subject_data contains the disclosed claims. Check credential_status too — a suspended or revoked credential still reports success here. |
failed | Verification failed. Check error_code and error_description for details. |
expired | Session exceeded its TTL without completion. |
- Developmenthttps://api.dev.deepcredentials.swiss/b2b/v1/verification-sessions
- Integrationhttps://api.int.deepcredentials.swiss/b2b/v1/verification-sessions
curl -i -X POST \
https://api.dev.deepcredentials.swiss/b2b/v1/verification-sessions \
-H 'Authorization: Bearer <YOUR_JWT_HERE>' \
-H 'Content-Type: application/json' \
-H 'X-Environment: beta' \
-H 'X-Org-Id: 497f6eca-6276-4993-bfeb-53cbbbba6f08' \
-d '{
"allowed_issuer_dids": [
"string"
],
"case_id": "c74269e5-1f97-4e20-9164-ffbe3494d8d6",
"claims": [
"string"
],
"credential_definition_id": "631a1b36-dbe6-4899-b427-3167b0844238",
"purpose": "string",
"reference_id": "string"
}'UUID v4 identifier.
Base64-encoded PNG; QR payload is the same URI as deeplink.
Wallet app link, e.g. openid4vp://?client_id=...&request_uri=...
Lifecycle state of a verification session.
Session TTL deadline (RFC3339).
You have two options to present the verification request to the user:
The qr_content_base64 field is a standard Base64-encoded PNG image. Display it directly:
<img src="data:image/png;base64,iVBORw0KGgo..." alt="Scan to verify your identity" />Or decode it to a file:
import base64
png_bytes = base64.b64decode(qr_content_base64)
with open("qr.png", "wb") as f:
f.write(png_bytes)The deeplink field contains a URI that opens the wallet app directly:
swiyu-verify://?client_id=...&request_uri=...Use this when the user is on a mobile device — redirect them to the deeplink instead of showing a QR code.
Option A: Polling
Poll GET /b2b/v1/verification-sessions/{sessionId} until the status is no longer pending:
- Developmenthttps://api.dev.deepcredentials.swiss/b2b/v1/verification-sessions/{sessionId}
- Integrationhttps://api.int.deepcredentials.swiss/b2b/v1/verification-sessions/{sessionId}
curl -i -X GET \
'https://api.dev.deepcredentials.swiss/b2b/v1/verification-sessions/{sessionId}' \
-H 'Authorization: Bearer <YOUR_JWT_HERE>' \
-H 'X-Environment: beta' \
-H 'X-Org-Id: 497f6eca-6276-4993-bfeb-53cbbbba6f08'Session id (matches the value returned at creation time). UUID v4 identifier.
Current lifecycle state of the session.
When a pending session expires if not completed (RFC3339).
Swiyu environment: "beta" or "production" (matches the credential binding).
Credential definition reference.
Claim keys requested in the presentation definition.
For sessions created through the deprecated credential_definition_id plus claims request shape, this maps requested claim names to the types configured on the credential definition. It is currently an empty object for sessions created with case_id. Partners using case-based verification must not rely on this field to infer disclosed-value types.
When the session was created (RFC3339).
Effective issuer allow-list for this session (may come from credential definition locks).
Set when the session leaves pending (success, failed, or expired).
On success: the token-status-list state of the credential the holder presented — "valid", "suspended" (shown as "Locked" in the portal), or "revoked". Resolved when the holder presented and never recomputed.
A "suspended" or "revoked" value still accompanies a successful verification with its disclosed claims: the wallet can present such a credential and the holder can consent to sharing it, so the verifier is given the state and applies its own policy. Branch on this in addition to `status` if a locked credential should not be accepted.
On success: disclosed subject claims (structure depends on credential definition and wallet).
Only when status is pending: wallet deep link (e.g. openid4vp://...).
Machine-readable failure reason when status is failed or expired.
Human-readable error detail when status is failed or expired.
On success: the vct of the credential the holder actually presented, relevant when the session accepts more than one credential type.
Purpose string from the create request, if any.
Only when status is pending: Base64 PNG QR code for the wallet.
Your reference_id from the create request, if any.
While status is pending, only session_id, qr_content_base64, deeplink, status, and expires_at are populated. On success, credential_subject_data contains the disclosed claims and qr_content_base64/deeplink are omitted. Sessions created with the deprecated credential_definition_id plus claims shape populate credential_subject_types from the definition. Case-based sessions currently return an empty object instead, so do not use that field to infer types in the preferred flow.
On success, credential_status reports the token-status-list state of the credential the holder actually presented. Values: valid, suspended, or revoked.
suspended and revoked arrive with status: "success" and the full credential_subject_data. This is not a bug: a wallet can present a locked or revoked credential and the holder can consent to sharing it, so the presentation genuinely verified — the issuer has simply since withdrawn the credential's validity.
Checking status == "success" alone will accept a withdrawn credential. If your integration must not accept one, branch on credential_status in addition to status.
When status is no longer pending, qr_content_base64 and deeplink are omitted from the response — the wallet flow is complete.
Polling recommendations:
- Poll every 2–3 seconds.
- Stop when
statusissuccess,failed, orexpired. - Respect the
expires_attimestamp — stop polling after expiration.
For production integrations, opt in to the Verification Completed webhook event. Its data field is a slim terminal hint containing session_id, status, and, on successful status resolution, credential_status. Follow it with authenticated GET /b2b/v1/verification-sessions/{sessionId} to reconcile the authoritative session and read disclosed fields. The callback is not a copy of the GET response.
See the Webhooks guide for setup, signature verification, and secret rotation.
On failure: the same response object is returned with status: failed and error_code/error_description populated. See the full schema above.
On a status-check failure: if the credential's token-status-list entry can't be fetched or its signature can't be verified, the session fails with error_code: status_check_failed rather than being reported with an unknown or stale state. credential_status is absent on such a session.
A status_check_failed rejection may result from problems with the issuer's status-list infrastructure, not from a cryptographically invalid credential. For example, the issuer rotates its signing key and republishes the status list under the new key — credentials issued before the rotation reference the old key and can no longer have their list verified — or the issuer issues credentials with no status reference at all. In both cases, a valid credential is rejected and your session returns no claims. This is especially relevant when verifying credentials from issuers you do not operate.
On expiration:
Sessions that remain pending past their expires_at transition to expired automatically.
See Error Handling for a complete list of error codes.
- Webhooks — Receive verification results in real time.
- Error Handling — Handle all possible error scenarios.