Skip to main content
Crivacy Docs

Sessions

Last updated 2026-05-24

What is a session?

A session is a single end-to-end run of the KYC verification pipeline for one user. It is the unit of work the firm creates against Crivacy: one session per user-per-verification-attempt.

Internally a session is a row in kyc_sessions and is backed by one (or two, for enhanced KYC) verification flows at Crivacy's identity vendor. The firm only sees the Crivacy-side handles, the vendor wire is encapsulated.

Sessions are the only object the firm controls directly through the REST API. Credentials are derived from sessions automatically after the verification reaches a terminal approved state.

Lifecycle

A session progresses through one or more of the following states. The status is exposed on every read response.

StateMeaning
pendingSession created. The user has not opened the verification URL yet.
in_progressThe user has opened the verification URL and is actively submitting documents / selfie.
in_reviewThe vendor has received the submission and is running checks (manual or automated).
identity_approved(Enhanced only) Identity phase passed. The user must now complete the address phase.
address_in_progress(Enhanced only) User is submitting proof-of-address documents.
approvedTerminal, success. The credential is minted to Sepolia; a credential.created webhook follows.
rejectedTerminal, failure. Vendor declined the submission. The decline reason is exposed via the session detail endpoint.
resubmission_pendingVendor requested the user re-submit one or more documents. The session remains active.
expiredTerminal. The user did not complete the flow before the 24-hour expiresAt deadline.
kyc_expiredTerminal. The vendor marked the underlying KYC run as expired (regulatory hold or stale documents).
revokedTerminal. Cancelled by the firm via DELETE /api/v1/sessions/:id, or revoked by Crivacy as part of an upstream credential revocation.

The progression is monotonic: a session never goes backwards from a terminal state. The four terminal states (approved / rejected / expired / revoked) are the only ones for which the firm should not expect further updates.

Create a session

curl -X POST https://app.crivacy.io/api/v1/sessions \
  -H "x-api-key: crv_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "userRef": "user_8x7k2m",
    "level": "basic",
    "redirectUrl": "https://yourapp.com/kyc/return",
    "language": "en",
    "metadata": { "tier": "premium" }
  }'

Request fields:

FieldTypeRequiredDescription
userRefstringFirm-side opaque identifier for the user. Anything stable that the firm can resolve back to its own user.
levelbasic | enhancedVerification depth. Defaults to basic (identity only). enhanced runs identity + proof-of-address.
redirectUrlhttps URLWhere the vendor returns the user after they finish. Defaults to the firm's dashboard-configured return URL.
languageBCP-47UI language hint (en, tr, de-CH, …). Falls back to the vendor default.
metadataobjectFree-form key-value bag the firm can stash on the session. Echoed back on read.

One active session per (userRef, workflow) pair. Creating a second session for the same user while one is still non-terminal returns HTTP 409 with code: "conflict". Cancel the existing session first (DELETE /api/v1/sessions/:id) or wait for it to reach a terminal state.

Response (HTTP 201):

{
  "id": "ses_01j8r3y...",
  "firmId": "firm_...",
  "userRef": "user_8x7k2m",
  "status": "pending",
  "level": "basic",
  "workflow": "identity",
  "verificationUrl": "https://verify.didit.me/session/qb5cM0c7uEdX",
  "expiresAt": "2026-05-25T01:23:45.000Z",
  "createdAt": "2026-05-24T01:23:45.000Z"
}

Redirect the user to verificationUrl, that is the entry point for the vendor-hosted flow.

Get a session

curl https://app.crivacy.io/api/v1/sessions/ses_01j8r3y... \
  -H "x-api-key: crv_live_..."

Returns the session in detail form, including any phases (identity / address) currently in progress for enhanced sessions, the current status, and timestamps. Use this when polling, or when reacting to a kyc.session.* webhook that only carries the session id.

List sessions

curl 'https://app.crivacy.io/api/v1/sessions?status=approved&limit=50' \
  -H "x-api-key: crv_live_..."

Query parameters:

ParamDescription
statusFilter by status (e.g. pending, in_review, approved).
userRefRestrict to one user.
createdAfter / createdBeforeISO 8601 timestamps.
limitPage size, max 100. Defaults to 25.
cursorOpaque pagination cursor from a previous response.

Returns { data: SessionSummary[], pagination: { nextCursor } }. Summary form (id / status / level / timestamps), not detail, use GET /sessions/:id for the full record.

Cancel a session

curl -X DELETE https://app.crivacy.io/api/v1/sessions/ses_01j8r3y... \
  -H "x-api-key: crv_live_..."

Marks a non-terminal session as revoked. Idempotent, calling DELETE on a session that is already terminal (approved / rejected / expired / revoked) returns HTTP 204 without state change.

Use this to free up the one active session per user slot if the user abandoned the flow, or to invalidate a session that was created in error.

Webhook events

Sessions emit the following events to your registered webhook endpoints (configure them from the dashboard → Webhooks):

EventTriggers on
kyc.session.createdSession insert (the firm just got an HTTP 201).
kyc.session.approvedStatus reached approved; a credential.created event follows once the on-chain mint settles.
kyc.session.rejectedStatus reached rejected. Decline reason on the session detail.
kyc.session.kyc_expiredVendor declared the underlying KYC run expired.
kyc.status_changedAny other intermediate transition the firm subscribed to.

Webhook payloads carry the KycSessionWebhookPayload shape: sessionId, userRef, workflow, level, verificationUrl, expiresAt, createdAt. See Webhooks for delivery semantics (signatures, retry policy, idempotency).

Identity-only (basic) vs identity + address (enhanced)

Aspectbasicenhanced
Phasesidentityidentity → address
Vendor flows backing the session12 (chained, the second auto-starts after identity is approved)
Resulting credential level on SepoliaBasicEnhanced
KycNFT eligibilityNoYes (Crivacy policy, Enhanced-only soulbound showcase NFT; the KycNFT template itself has no level gate in its ensure clause, the per-level mint decision lives in the Crivacy backend)
Status pathpending → in_progress → in_review → approvedadds → identity_approved → address_in_progress before approved

The phase split is internal, the firm always sees a single session id with a single status field that reflects the currently-running phase.

Scopes & rate limits

EndpointRequired scope
POST /api/v1/sessionskyc:create
GET /api/v1/sessionskyc:read
GET /api/v1/sessions/:idkyc:read
DELETE /api/v1/sessions/:idkyc:create

See Authentication → Scopes for the full scope catalog and Rate limits for the per-tier request budget that applies to session endpoints.

  • Credentials, what a session produces on successful completion.
  • Webhooks, how to subscribe to session-lifecycle events.
  • Error codes, the full code catalog including conflict, not_found, and the vendor-availability errors a session create can surface.
Sessions -- Crivacy Docs