AuthenSeeDocs

API reference

The AuthenSee auth server REST API — sessions, enrollment and verification, auth results, and personas.

The AuthenSee auth server exposes a REST API for session management, persona identity, enrollment, challenge generation, and proof verification. All endpoints are prefixed with /v1/ and accept and return JSON.

https://api.authensee.com/v1

Authentication

The API uses two credentials, and every endpoint accepts exactly one of them:

CredentialHeaderWho holds itUsed for
Provider secret key (sk_live_... / sk_test_...)x-api-keyYour backend onlyServer-to-server calls: minting sessions, exchanging auth results
Session token (sess_...)Authorization: BearerThe hosted flow, in memoryCeremony calls scoped to one session: identify, enroll, challenge, verify
x-api-key: sk_live_9aB3xK7mQ2wE5rT8yU1iO4pL6s     # server-to-server
Authorization: Bearer sess_aG25OA7mEnbOSX2J8Uwn...  # session-scoped

Session tokens are minted by POST /v1/sessions (a secret-key call on your backend) and scoped by scope (enroll, authenticate, or full) and a TTL. The hosted flow obtains the token by redeeming a single-use flowCode; the token itself never travels in a URL or a cookie.

Which calls do I make?

A typical integration makes exactly two API calls from its own code: POST /v1/sessions to start a flow and POST /v1/auth-results/exchange to finish one. The session-token endpoints are called by the AuthenSee hosted flow on the user's behalf — they're documented here so you can see precisely what crosses the wire.

Endpoints

AreaEndpoints
SessionsPOST /v1/sessions · GET /v1/sessions/current · GET /v1/sessions/result · GET /v1/sessions/:id · DELETE /v1/sessions/:id · cross-device handoff
Enrollment & verificationPOST /v1/enrollments · POST /v1/enrollments/add · POST /v1/challenges · POST /v1/verify
Auth resultsPOST /v1/auth-results/exchange · GET /v1/auth-results/:id · JWT claims
PersonasPOST /v1/personas/identify · GET /v1/personas/:personaId · enrollment status · provider links · unlink

The scheme model

Every enrollment is registered against one scheme — a fixed factor combination backed by a dedicated circuit. The server stores one aggregate commitment per enrolled scheme per persona, and derives all factor and circuit semantics from the schemeId — never from client input:

SchemeCircuitFactors verifiedSelected by provider policy
passkey_question_v1passkey_question_authImage points + passkeypasskey_and_image_points (default)
behavior_passkey_v1behavior_auth_passkeyMotion gesture + passkeypasskey_and_behavior
passkey_only_v1passkey_only_authPasskey onlypasskey_only, or opportunistically alongside any other scheme
agent_keypair_v1agent_keypair_authRaw ECDSA-P256 keypair (no WebAuthn)Agent personas only

Clients never send factorType or circuitType on /v1/verify — the server rejects those keys to prevent verifier downgrade or factor-claim swap.

The ceremony pipeline is always enroll → challenge → verify:

POST /v1/enrollments   →  { enrollmentId }              register the aggregate commitment
POST /v1/challenges    →  { nonce, challengeBytes, … }  bind a single-use challenge
POST /v1/verify        →  { authResultCode, … }         verify the ZK proof

See Enrollment & verification for each step.

Legacy /v1/factors* endpoints are deliberately disabled and return an error pointing at /v1/enrollments. There is no per-factor record on the server side, and no endpoint lists a persona's enrolled schemes — the on-device SDK is the only authoritative source for "what's enrolled on this device".

Errors

Every non-2xx response carries a machine-readable code and a human-readable message:

{
  "code": "VALIDATION_ERROR",
  "message": "callbackUrl origin https://evil.example.com is not on this provider's allowlist"
}

Classify failures by code, not by message text. Rate-limit responses add a retryAfter field (seconds).

CodeStatusCondition
VALIDATION_ERROR400Malformed request, failed input validation, or a value outside your provider's configuration (for example a callbackUrl origin not on your allowlist)
INVALID_PROOF400ZK proof failed verification — distinct from VALIDATION_ERROR so clients can tell "your proof didn't verify" from "your request was bad"
UNAUTHORIZED401Missing or invalid session token or API key
FORBIDDEN403Scope, ownership, or session-binding mismatch — including agent personas when your policy blocks agents
NOT_FOUND404Provider, persona, enrollment, or challenge not found
CONFLICT409Conflicting state, such as a nullifier that has already been spent
PERSONA_ALREADY_ENROLLED409Bootstrap enrollment attempted for a persona that already has a current enrollment — route the user to authenticate or link instead
policy_upgrade_required409Login proved a scheme your current factor combination no longer accepts — see the upgrade ceremony
CHALLENGE_EXPIRED410The challenge existed but its TTL elapsed — prompt the user to start again (a genuinely absent challenge returns NOT_FOUND)
RATE_LIMITED429IP or provider quota exceeded, or an agent persona exceeding your per-agent budget (retryAfter seconds)
INTERNAL_ERROR5xxUnhandled server error

One error deviates from the flat shape: policy_upgrade_required nests its payload under error so its two extra machine-readable fields (factorCombination, allowedSchemes) don't collide with the top-level code/message:

{
  "error": {
    "code": "policy_upgrade_required",
    "message": "Proven scheme does not satisfy this provider's current policy (passkey_only); allowed: passkey_only_v1",
    "factorCombination": "passkey_only",
    "allowedSchemes": ["passkey_only_v1"]
  }
}

Signing keys

Auth-result JWTs are signed with EdDSA (Ed25519). The public key is published at:

https://api.authensee.com/.well-known/jwks.json

See Auth results for the token's claims and how to verify it.

Next

On this page