Base URL
All API requests are made to one of the following base URLs. Use the production URL for live integrations and the playground proxy for interactive testing from the dashboard.
https://api.crivacy.ioProduction API.https://app.crivacy.io/api/internal/playgroundDashboard playground proxy. Uses the session cookie instead of an API key.
Authentication
Most endpoints require an API key sent via the x-api-key header. Dashboard endpoints use a session cookie (JWT). Admin endpoints require JWT plus TOTP verification. See the Authentication guide for details.
1.0.0OpenAPI3.1.0Sessions
Create and read KYC verification sessions. A session represents an end user going through Didit identity capture before a credential is minted on Sepolia.
Create a KYC session
Starts a new verification session. Returns the Didit redirect URLs the end user must visit. The firm key must hold the kyc:create scope.
Request body
Schema: SessionCreateRequest
Responses
redirectUrl in the response points at Didit phase 1.validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).List KYC sessions
Cursor-paginated list of sessions, newest first.
Parameters
cursorqueryPaginationCursoroptional- Opaque base64url pagination cursor issued by the API.
limitqueryPaginationLimitoptional- Page size. Must be in
[1, 100], defaults to 25. statusqueryobjectoptional- Filter by lifecycle state.
userRefqueryobjectoptional- Exact match on
userRef. createdAfterqueryDateTimeIsooptional- ISO 8601 datetime in UTC (
YYYY-MM-DDTHH:MM:SS.sssZ). createdBeforequeryDateTimeIsooptional- ISO 8601 datetime in UTC (
YYYY-MM-DDTHH:MM:SS.sssZ).
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Read a KYC session
Returns the full session state including both Didit phases. Safe to poll; rate limit is the firm standard (60 req/s default).
Parameters
idpathKycSessionIdrequired- Unique KYC session identifier.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Cancel a KYC session
Marks a session as expired server-side. Has no effect if the session is already in a terminal state (approved, rejected, expired, revoked).
Parameters
idpathKycSessionIdrequired- Unique KYC session identifier.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Credentials
Read and verify on-chain-issued KYC credentials. A credential is the immutable attestation that an end user passed the verification flow.
Read a credential
Returns the active credential for userRef, including the on-chain pointer for independent verification. Responds with 404 (not_found) if no credential exists, 410 (credential_revoked) if the credential was revoked, or 200 for the active happy path. Requires kyc:read scope.
Parameters
userRefpathUserRefrequired- Opaque identifier the firm uses for the end user. Any non-empty string up to 128 chars. Stored verbatim and returned verbatim; Crivacy does not interpret the value.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Verify a credential on chain
Reads the credential straight from the CrivacyKYC contract on Sepolia by the subject’s EVM address and returns { valid, reason, credential, verifiedAt }. Crivacy performs the read on the caller’s behalf; the plaintext lifecycle is public on chain. Requires kyc:verify scope.
Request body
Schema: CredentialVerifyRequest
Responses
valid is false for any failure mode.validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Credential history
Returns the append-only history of lifecycle events for userRef. Includes all credentials ever issued, not just the current one. Requires kyc:read scope.
Parameters
userRefpathUserRefrequired- Opaque identifier the firm uses for the end user. Any non-empty string up to 128 chars. Stored verbatim and returned verbatim; Crivacy does not interpret the value.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Webhooks
Manage webhook subscriptions for asynchronous events (credential lifecycle, session state changes).
List webhook subscriptions
Returns every webhook subscription owned by the firm. Requires webhooks:manage scope.
Parameters
cursorqueryPaginationCursoroptional- Opaque base64url pagination cursor issued by the API.
limitqueryPaginationLimitoptional- Page size. Must be in
[1, 100], defaults to 25.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Create a webhook subscription
Creates a new webhook subscription and returns the full signing secret exactly once. Store the secret immediately — every subsequent response masks it. Requires webhooks:manage scope.
Request body
Schema: WebhookCreateRequest
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Read a webhook subscription
Parameters
idpathWebhookSubscriptionIdrequired- Unique webhook subscription identifier.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Update a webhook subscription
Partially updates a subscription. Supports toggling active state, updating the URL, adjusting the event filter, or setting a new description. Requires webhooks:manage scope.
Parameters
idpathWebhookSubscriptionIdrequired- Unique webhook subscription identifier.
Request body
Schema: WebhookUpdateRequest
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Delete a webhook subscription
Hard-deletes the subscription. Pending deliveries for this subscription are dropped. Requires webhooks:manage scope.
Parameters
idpathWebhookSubscriptionIdrequired- Unique webhook subscription identifier.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Send a test event
Enqueues a synthetic delivery to the subscription URL. Returns 202 Accepted with the delivery id so the caller can poll GET /api/v1/webhooks/{id}/deliveries for the outcome.
Parameters
idpathWebhookSubscriptionIdrequired- Unique webhook subscription identifier.
Request body
Schema: WebhookTestRequest
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).List recent delivery attempts
Returns the most recent delivery attempts for a subscription, newest first.
Parameters
idpathWebhookSubscriptionIdrequired- Unique webhook subscription identifier.
cursorqueryPaginationCursoroptional- Opaque base64url pagination cursor issued by the API.
limitqueryPaginationLimitoptional- Page size. Must be in
[1, 100], defaults to 25.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Usage
Inspect API usage for the current billing period and historical months.
Current period usage
Returns the aggregate usage for the current calendar month. Requires usage:read scope.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Historical usage
Returns monthly usage aggregates for up to the past 24 months, newest first. Requires usage:read scope.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Limits
Read the tier configuration and remaining rate-limit / quota capacity for the authenticating API key.
Rate limit and quota state
Returns the firm tier, the current token-bucket state, and the remaining monthly quota for the authenticating API key.
Responses
validation_failed, invalid_request, malformed_json).unauthenticated, invalid_api_key, expired_api_key).scope_forbidden, tier_forbidden, ip_blocked).not_found).conflict, idempotency_mismatch).rate_limited, quota_exceeded).internal_error).upstream_unavailable, chain_unavailable, didit_unavailable).maintenance).Health
Liveness probes and public component status. Safe to hit without authentication.
Liveness probe
Aggregated liveness of the API. Returns 200 when every critical check passes, 503 (maintenance) otherwise. Safe to hit from unauthenticated monitoring tools.
Responses
internal_error).maintenance).Component status
Public component status view — identical data shown on the /status page. Returns 200 regardless of the underlying component states; the states themselves describe the outcome.
Responses
internal_error).maintenance).Internal — Auth
Dashboard session management — login, logout, token refresh, TOTP enrollment.
Dashboard login
Exchanges email + password (+ optional TOTP code) for a session cookie. Returns 401 (invalid_credentials) for any failure mode — email enumeration is intentionally impossible. When the firm requires TOTP but the user has not enrolled, the call still succeeds and requireTotpSetup is set to true so the UI can redirect into the enrollment flow.
Request body
Schema: LoginRequest
Responses
Set-Cookie: __Host-crv_session=... is attached.validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Dashboard logout
Invalidates the current dashboard session server-side and clears the cookie via Set-Cookie: __Host-crv_session=; Max-Age=0. Idempotent — calling logout without an active session still returns 204.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Refresh the dashboard session
Extends the current session expiration by the configured dashboard session length (default 1 hour). Called by the dashboard opportunistically while the user is active.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Begin TOTP enrollment
Generates a new TOTP secret for the current user and returns it as an otpauth URL plus ten one-time recovery codes. The secret is not yet active — the user must confirm by calling POST /api/internal/auth/totp/verify with a valid code. Calling setup again before verifying replaces the staged secret.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Confirm TOTP enrollment or supply a step-up code
Verifies a 6-digit TOTP code. When a staged secret from totp/setup is pending, this call activates it. When TOTP is already enabled, this call performs a step-up for privileged operations and refreshes the session with a short-lived step-up flag.
Request body
Schema: TotpVerifyRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Internal — Firm
Firm profile and settings for dashboard users.
Read firm profile
Returns the firm profile — branding, contact email, IP allowlist, data retention window. Any dashboard user can read.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Update firm profile
Partially updates the firm profile. Each field in the request body is optional; at least one must be present. Requires the admin or owner firm-user role; the payload is validated against the same schema the public dashboard form uses.
Request body
Schema: FirmUpdateRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Internal — API Keys
Dashboard-side API key issuance, rotation, revocation.
List API keys
Returns every API key issued to the firm, regardless of mode or revocation state. Cursor paginated.
Parameters
cursorqueryPaginationCursoroptional- Opaque base64url pagination cursor issued by the API.
limitqueryPaginationLimitoptional- Page size. Must be in
[1, 100], defaults to 25.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Create an API key
Mints a new API key and returns the full plaintext secret exactly once. Store the secret immediately — every subsequent response masks it. Requires the admin or owner firm-user role.
Request body
Schema: ApiKeyCreateRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Revoke an API key
Marks the key as revoked. Subsequent requests signed with the key will fail with 401. Revocation is irreversible; use POST /api/internal/api-keys/{id}/rotate when a rolling replacement is desired instead.
Parameters
idpathApiKeyIdrequired- Unique API key identifier.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Rotate an API key
Issues a new plaintext secret for the same key record, revoking the previous plaintext. The key id, prefix display name, scopes, and mode are unchanged. The new plaintext is returned exactly once.
Parameters
idpathApiKeyIdrequired- Unique API key identifier.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Internal — Usage
Dashboard usage charts fed by the aggregated usage rollups.
Hourly usage samples for dashboard charts
Returns dense hourly usage samples for the requested window. The endpoint is read-only and caches internally for 60 seconds, so multiple dashboard tabs rendering the same window share the same response body.
Parameters
fromqueryobjectrequired- Inclusive start of the chart window. Must be within the last 31 days; earlier dates are clamped.
toqueryobjectrequired- Exclusive end of the chart window. Must be greater than
fromand must not be in the future.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Internal — Webhooks
Dashboard webhook delivery inspection and manual replay.
List webhook deliveries across all firm subscriptions
Returns webhook delivery attempts across every subscription owned by the firm, newest first. The dashboard uses this surface for the unified delivery log view. Cursor paginated.
Parameters
cursorqueryPaginationCursoroptional- Opaque base64url pagination cursor issued by the API.
limitqueryPaginationLimitoptional- Page size. Must be in
[1, 100], defaults to 25. subscriptionIdquerystringoptional- Filter by webhook subscription id.
statusqueryobjectoptional- Filter by delivery status.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Replay a webhook delivery
Requeues a previously-attempted delivery for another send. Safe to call on any delivery in a terminal state (succeeded, failed, dead); the worker will deduplicate at the HTTP level via the envelope id. Requires the admin or owner firm-user role.
Parameters
idpathWebhookDeliveryIdrequired- Unique webhook delivery attempt identifier.
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Internal — Audit
Audit log readback for privileged firm operations.
Read the firm audit log
Returns audit-log rows scoped to the authenticated firm, filtered by the query parameters, newest first. Rows are never edited or deleted; retention is controlled by the firm dataRetentionDays setting. Requires the admin or owner firm-user role.
Parameters
cursorqueryPaginationCursoroptional- Opaque base64url pagination cursor issued by the API.
limitqueryPaginationLimitoptional- Page size. Must be in
[1, 100], defaults to 25. actionquerystringoptionalactorKindqueryAuditActorKindoptional- Class of actor that authored an audit-log entry.
targetKindqueryAuditTargetKindoptional- Class of resource an audit-log entry refers to.
sincequeryDateTimeIsooptional- ISO 8601 datetime in UTC (
YYYY-MM-DDTHH:MM:SS.sssZ). untilqueryDateTimeIsooptional- ISO 8601 datetime in UTC (
YYYY-MM-DDTHH:MM:SS.sssZ).
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Internal — Playground
Interactive playground that proxies a request to the real public API using a test-mode API key.
Proxy a request through a test-mode API key
Executes a public API request on behalf of the dashboard user using one of their test-mode API keys. The request and response are recorded in a dedicated playground log separate from the production usage table so playground traffic never contaminates billing or charts. Live-mode keys are rejected with 400 (invalid_argument).
Request body
Schema: PlaygroundExecuteRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session, totp_required, totp_invalid).role_forbidden, permission_denied, scope_forbidden).not_found).conflict).rate_limited).internal_error).Admin — Firms
Crivacy admin — firm lifecycle, tier overrides, quota overrides, soft-delete.
List firms
Returns every firm in the system, including soft-deleted records. Cursor paginated. Sort order is createdAt DESC.
Parameters
cursorqueryPaginationCursoroptional- Opaque base64url pagination cursor issued by the API.
limitqueryPaginationLimitoptional- Page size. Must be in
[1, 100], defaults to 25.
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Create a firm
Provisions a new firm record and its initial owner user. The response carries a one-shot password-reset URL valid for 24 hours — hand it to the firm operator out-of-band. No plaintext password is ever generated or stored.
Request body
Schema: AdminFirmCreateRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Read a firm
Returns the admin view of a firm, including limit overrides and soft-delete state.
Parameters
idpathFirmIdrequired- Unique firm identifier.
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Update a firm
Partially updates a firm — tier, per-firm rate-limit override, per-firm monthly quota override, data retention window. Any field not present in the request body is left untouched. Tier changes take effect immediately for subsequent requests.
Parameters
idpathFirmIdrequired- Unique firm identifier.
Request body
Schema: AdminFirmUpdateRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Soft-delete a firm
Marks the firm as soft-deleted. All firm users are immediately logged out, every API key is revoked, and outbound webhooks are disabled. The firm record is retained for audit and billing; hard deletion happens through a separate retention job, not this endpoint.
Parameters
idpathFirmIdrequired- Unique firm identifier.
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Admin — System
Crivacy admin — internal system metrics, queue depths, operational dashboards.
Live system metrics
Returns live internal metrics — db pool state, chain reachability, didit reachability, recent HTTP error rate. Not cached; every call hits the in-process metric registry.
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).pg-boss queue depth
Returns the pending/active/completed/failed count for every background queue, plus the oldest pending job timestamp. Used by the admin dashboard to detect stuck workers.
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Admin — Status
Crivacy admin — publish incidents and component state updates to the public status page.
Publish an incident
Creates a new status-page incident. When publish is true (the default) the incident is immediately visible to unauthenticated callers hitting GET /api/v1/status; otherwise the incident is staged as a draft and can be published later via PATCH. Linked components are switched to degraded_performance or worse depending on the incident severity.
Request body
Schema: AdminIncidentCreateRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Update a component state manually
Overrides the automated state of a status-page component. When manualOverride is true (the default), subsequent probe results will not overwrite the state until the override is cleared by another manual update with state: operational. Used to force a component into maintenance during planned work.
Request body
Schema: AdminComponentUpdateRequest
Responses
validation_failed, invalid_request, malformed_json).invalid_session).role_forbidden, permission_denied).not_found).conflict).internal_error).Incoming Webhooks
Endpoints Crivacy hosts to receive events from upstream providers (currently Didit).
Didit webhook receiver
Receives verification decisions from Didit. The request must carry a valid X-Signature-V2 HMAC header computed over the raw request body using the shared webhook secret. The response is 200 received even when the embedded decision is a rejection — the HTTP status reports whether Crivacy accepted the delivery, not the decision outcome.
Request body
Schema: DiditWebhookPayload
Responses
validation_failed, malformed_json).webhook_signature_invalid).not_found).payload_too_large).internal_error).