ZKAuth
Get started

Reference

API reference

The engine is REST over JSON and enforces HTTPS. Most endpoints are plain requests, but register and login carry zero-knowledge material that the client computes from the password, which is why the SDK generates it for you.

Base URL

text
https://api.zkauth.dev

Authentication

Send your project key in x-api-key on every request. Routes that act on a signed-in user also need the session token as a bearer token. Bearer-only requests and invalid keys are rejected.

http
x-api-key: zka_live_<project_key>       # every requestAuthorization: Bearer <session-token>   # user routes only

Validate a key

GET/api/v1/client/me

Returns the client that owns a key: a fully self-contained request, so it’s the best first call to confirm your setup in any language.

curl https://api.zkauth.dev/api/v1/client/me \  -H "x-api-key: zka_test_<project_key>"

Machine token introspection

POST/api/v1/m2m/introspect

Introspects the authenticated project API key as an opaque machine token. Send the key only in x-api-key; the body accepts optional exact issuer and audience assertions and rejects raw token or API-key fields.

json
POST /api/v1/m2m/introspect{  "issuer": "https://api.zkauth.dev",  "audience": "zkauth:client:<client-id>"}
json
{  "success": true,  "data": {    "active": true,    "tokenType": "opaque",    "tokenFormat": "zkauth_project_api_key",    "subject": "client:<client-id>:key:<key-id>",    "issuer": "https://api.zkauth.dev",    "audience": "zkauth:client:<client-id>",    "client": { "id": "<client-id>", "name": "Production", "status": "active" },    "key": {      "id": "<key-id>",      "prefix": "zka_live_abcd",      "type": "live",      "scopes": ["auth.login"],      "expiresAt": null,      "lastUsedAt": "2026-06-21T00:00:00.000Z",      "rateLimitPerMinute": 100    }  }}

M2M boundary

This is project API-key self-introspection for trusted server code. It is not OAuth client credentials, JWT bearer-token issuance, local JWT verification, or user-created API keys.

Register

POST/api/v1/auth/register

The body carries a client-derived salt and commitment, never the password. The SDK computes these; if you implement them yourself you must reproduce the Argon2id + Poseidon derivation.

json
POST /api/v1/auth/register{  "email": "ada@example.com",  "salt": "...",          // derived from the password on the client  "commitment": "...",    // Poseidon commitment, computed on the client  "deviceInfo": { "deviceName": "Chrome on Mac", "deviceType": "desktop" }}

You'll want the SDK here

Generating a valid commitment (register) and Groth16 proof (login) requires the circuits and crypto bundled in zkauth-client. Raw HTTP is best for the read, session, password-reset, OPAQUE, and WebAuthn endpoints below.

Log in

GET/api/v1/auth/salt/:email

Returns the public salt for a user (the SDK fetches this for you).

POST/api/v1/auth/login

Verifies a Groth16 proof and applies replay protection.

  • If user MFA is enabled, the response is a short-lived MFA challenge instead of a normal session.
  • If project-required MFA is enabled and the user has no usable factor, login returns an enrollment-required response.
  • The enrollment-only token is short-lived and can be used only for TOTP setup and verification.
json
POST /api/v1/auth/login{  "email": "ada@example.com",  "proof": { "pi_a": [...], "pi_b": [...], "pi_c": [...], "protocol": "groth16", "curve": "bn128" },  "publicSignals": ["..."],  "deviceInfo": { "deviceName": "Chrome on Mac", "deviceType": "desktop" },  "proofNonce": "...",  "proofTimestamp": 1730000000000}

Success response

json
{  "success": true,  "data": {    "token": "eyJ...",    "user": { "id": "usr_...", "email": "ada@example.com", "emailVerified": true },    "session": { "sessionId": "...", "expiresAt": "..." }  }}

MFA challenge response

json
{  "success": false,  "code": "MFA_REQUIRED",  "data": {    "mfaRequired": true,    "token": "eyJ...",    "availableMethods": ["totp", "backup"],    "session": { "sessionId": "...", "type": "mfa_pending" }  }}

MFA enrollment-required response

json
{  "success": false,  "code": "MFA_ENROLLMENT_REQUIRED",  "data": {    "mfaRequired": true,    "enrollmentRequired": true,    "token": "eyJ...",    "availableMethods": [],    "session": { "sessionId": "...", "type": "mfa_enrollment" }  }}
POST/api/v1/auth/verify-mfa

Exchanges the pending MFA token plus a TOTP or backup code for the authenticated session.

Hosted sign-in handoff

POST/api/v1/auth/hosted-handoff

Bearer-authenticated hosted UI endpoint that issues a short-lived, single-use handoff code for an allowlisted application callback. The response contains the redirect URL with code, not the bearer session token.

POST/api/v1/auth/hosted-handoff/redeem

Server-side endpoint for application callbacks. Send the project API key and the handoff code to redeem the engine session token, then store it in your own secure app session cookie.

json
POST /api/v1/auth/hosted-handoff/redeem{  "code": "single_use_handoff_code"}

Session identity and rotation

These require the session token as a bearer token.

GET/api/v1/auth/me

Return the authenticated user and current session metadata, including device, expiry, last activity, risk, MFA, and sensitive-session fields.

json
{  "success": true,  "data": {    "user": { "id": "usr_...", "email": "ada@example.com" },    "session": {      "id": "sess_...",      "type": "mfa_verified",      "deviceId": "dev_...",      "expiresAt": "2026-06-19T11:00:00.000Z",      "lastActivityAt": "2026-06-18T11:50:00.000Z",      "riskLevel": "low",      "trustScore": 90,      "mfaVerified": true,      "sensitiveVerifiedAt": "2026-06-18T11:45:00.000Z",      "sensitiveVerifiedFactor": "totp"    }  }}
POST/api/v1/auth/logout

Invalidate the current session and revoke the active bearer token.

POST/api/v1/auth/refresh

Rotate the active session token and revoke the previous token.

  • The default engine session lifetime is 24 hours, and project security policy can set a shorter or longer bounded lifetime.
  • High-risk sessions may be shorter.
  • Refresh preserves the original sensitive-session timestamp; it does not create a fresh MFA verification.
GET/api/v1/dashboard/statsGET/api/v1/dashboard/activity

Return user-scoped account statistics and recent login activity for the authenticated bearer session.

Hosted helper pages route these through the project-bound hosted proxy and bound displayed network/browser metadata before rendering.

GET/api/v1/auth/verify-email/:token

Confirm an email with the token from registration.

Password reset

POST/api/v1/password/forgot-passwordGET/api/v1/password/verify-reset-tokenPOST/api/v1/password/reset-password

Request a reset, verify the token, then set a new commitment. Resets may require approval depending on your project policy. Password reset emails redirect to the project callback URL with zkauth_action=password_reset and token.

Devices

GET/api/v1/devicesPOST/api/v1/devices/registerPOST/api/v1/devices/verifyDELETE/api/v1/devices/:deviceIdGET/api/v1/device-approvals/approveGET/api/v1/device-approvals/denyGET/api/v1/device-approvals/pending

List, trust, verify, and remove a user’s devices. Device approval and denial links are separate single-use decisions; successful approval grants trust only inside the authenticated project.

OPAQUE

GET/api/v1/opaque/statusPOST/api/v1/opaque/register/responsePOST/api/v1/opaque/register/finishPOST/api/v1/opaque/login/startPOST/api/v1/opaque/login/finishPOST/api/v1/opaque/migration/startPOST/api/v1/opaque/migration/finish

Migration endpoints support user-mediated movement from legacy password proof credentials to persisted OPAQUE records.

WebAuthn

POST/api/v1/webauthn/register/optionsPOST/api/v1/webauthn/register/verifyPOST/api/v1/webauthn/authenticate/optionsPOST/api/v1/webauthn/authenticate/verify

Route these through your backend proxy so the key stays server-side. See Authentication.

Sessions & account security

User-scoped controls under /api/v1/security, all bearer-authenticated.

GET/api/v1/security/sessionsDELETE/api/v1/security/sessions/:sessionIdPOST/api/v1/security/sessions/terminate-othersGET/api/v1/security/devicesPATCH/api/v1/security/devices/:deviceId/trustDELETE/api/v1/security/devices/:deviceId

List active sessions with device/activity metadata, revoke a specific non-current session, or terminate every active session except the current one.

  • Trusted-device listing, trust changes, and removals are scoped to the authenticated project.
  • Removing a trusted device also invalidates matching sessions for that project.
  • Terminating all other sessions requires a fresh first-factor session.
  • Use /auth/logout to revoke the current session.
json
{  "success": true,  "data": {    "sessions": [      {        "id": "sess_...",        "deviceName": "Chrome on Windows",        "deviceType": "desktop",        "trustLevel": "trusted",        "lastActivity": "2026-06-18T11:50:00.000Z",        "expiresAt": "2026-06-19T11:00:00.000Z",        "sessionType": "mfa_verified",        "mfaVerified": true,        "sensitiveVerifiedAt": "2026-06-18T11:45:00.000Z",        "isCurrent": true      }    ],    "total": 1  }}
GET/api/v1/security/mfa/statusPOST/api/v1/security/mfa/totp/setupPOST/api/v1/security/mfa/totp/verifyPOST/api/v1/security/mfa/backup-codes/generatePOST/api/v1/security/mfa/disableGET/api/v1/security/recovery-codes/statusPOST/api/v1/security/recovery-codes/generatePOST/api/v1/security/recovery-codes/regeneratePOST/api/v1/security/recovery-codes/download

TOTP setup and verification require a fresh first-factor session.

  • When a project-required unenrolled login uses the enrollment-only token, successful TOTP verification returns a fresh MFA-backed engine session.
  • Backup-code generation, recovery-code export, and MFA disable require a fresh MFA-backed session.
POST/api/v1/security/emergency-reset/requestPOST/api/v1/security/emergency-reset/confirmGET/api/v1/security/emergency-reset/statusPOST/api/v1/security/emergency-reset/cancel

Start, confirm, inspect, or cancel account recovery. Confirming is gated by the configured delay.

Webhooks

Project-scoped webhook management endpoints.

GET/api/v1/client/webhooksPOST/api/v1/client/webhooksGET/api/v1/client/webhooks/eventsGET/api/v1/client/webhooks/:webhookIdPUT/api/v1/client/webhooks/:webhookIdDELETE/api/v1/client/webhooks/:webhookIdPOST/api/v1/client/webhooks/:webhookId/testGET/api/v1/client/webhooks/:webhookId/deliveriesPUT/api/v1/client/webhooks/bulk

Use signed test deliveries before enabling production event delivery.

Transparency & evidence

Read-only endpoints that expose how the engine is actually configured, so you can verify our security claims instead of taking them on faith.

GET/api/v1/security/crypto-policy

The active cryptographic parameters (curve, hash, Argon2id settings).

GET/api/v1/security/standards

Standards mappings, published without claiming certification.

GET/api/v1/security/pq-readiness

Post-quantum readiness posture.

GET/api/v1/security/evidenceGET/api/v1/security/assurance/policy

Assurance and evidence records for operator and auditor review.

Key formats & limits

PrefixUse
zka_live_...Production traffic.
zka_test_...Development and CI.

Rate limits apply per client. Failed requests return a non-2xx status with a JSON error envelope:

error envelopejson
{  "success": false,  "error": {    "code": "VALIDATION_ERROR",    "message": "One or more fields are invalid.",    "request_id": "req_...",    "docs_url": "https://zkauth.dev/docs/errors#VALIDATION_ERROR"  },  "code": "VALIDATION_ERROR",  "message": "One or more fields are invalid.",  "request_id": "req_..."}

Prefer error.code and error.request_id in new integrations. The top-level fields are compatibility fields for older clients.