AuthenSeeDocs

Auth results

Exchange one-time result codes for signed auth-result JWTs, re-read past results, and verify the EdDSA token.

A successful login produces a one-time authResultCode — delivered to your callback URL by the hosted flow, or returned directly by /v1/verify. Your backend exchanges it, once, for the authoritative auth result and its signed JWT. Both endpoints here are server-to-server calls with your secret key; they're also wrapped by the provider SDK's exchangeAuthResult() and getAuthResult().

POST /v1/auth-results/exchange

Exchange a one-time authResultCode for the signed auth-result JWT and its provider-scoped claims.

Auth: provider secret key (x-api-key)

Request

curl -X POST https://api.authensee.com/v1/auth-results/exchange \
  -H "x-api-key: sk_live_9aB3xK7mQ2wE5rT8yU1iO4pL6s" \
  -H "Content-Type: application/json" \
  -d '{ "authResultCode": "ar_bxvS6dFBzoJKW3BQxYtDm4qGh8N2LcVp" }'
FieldTypeRequiredDescription
authResultCodestringYesThe one-time ar_-prefixed code from your callback or from /v1/verify

Response

{
  "id": "ar_bxvS6dFBzoJKW3BQxYtDm4qGh8N2LcVp",
  "status": "authenticated",
  "providerId": "01906dd2-4c8a-7bb3-b7f0-5e12c9a4d833",
  "providerSubject": "sub_9f3kQzWx7RmT2yLpB8vN4cD6",
  "externalUserId": "user_12345",
  "sessionId": "9182e25e-0b7b-4d92-ae98-a3c710c905c1",
  "challengeId": "01917f8a-2e5b-7d4c-9a1f-6b3e8c4d6f0a",
  "personaType": "human",
  "factorsVerified": ["image_points", "passkey"],
  "schemeId": "passkey_question_v1",
  "authTime": "2026-07-09T11:02:14.000Z",
  "expiresAt": "2026-07-09T11:12:14.000Z",
  "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOi...",
  "tokenType": "auth_result"
}
FieldTypeDescription
idstringThe auth-result ID (ar_ prefix) — use with GET /v1/auth-results/:id
statusstring"authenticated" for a login
providerIdstring (UUID)Your provider ID
providerSubjectstringYour stable, provider-scoped alias for the persona — the identity you should key on
externalUserIdstring | nullYour own user ID, when the session was bound to one
sessionIdstring | nullThe session that produced the proof
challengeIdstring (UUID)The challenge the proof was bound to
personaType"human" | "agent"The persona's type
factorsVerifiedstring[]The factors the proof attested to, derived from the scheme
schemeIdstring | nullThe proven scheme
authTimestringWhen the underlying authentication happened
expiresAtstringWhen the result (and its code) expires
tokenstringThe signed auth-result JWT — claims below
tokenType"auth_result"Token type discriminator

Errors

CodeStatusWhen
VALIDATION_ERROR400Code already consumed, expired, or not in an exchangeable state
UNAUTHORIZED401Missing or invalid secret key
FORBIDDEN403The result belongs to a different provider
NOT_FOUND404No result with this code

authResultCode is single-use: exchanging it twice fails on the second call. A persona.link completion mints a different, non-exchangeable "linked" marker that only ever surfaces through the callback or the session result poll, never through this endpoint.


GET /v1/auth-results/:id

Re-read a result you've already exchanged, without minting a new token. Returns the same fields as the exchange response, minus token and tokenType.

Auth: provider secret key (x-api-key)

curl https://api.authensee.com/v1/auth-results/ar_bxvS6dFBzoJKW3BQxYtDm4qGh8N2LcVp \
  -H "x-api-key: sk_live_9aB3xK7mQ2wE5rT8yU1iO4pL6s"

Errors match the exchange endpoint: 401 for a bad key, 403 for another provider's result, 404 for an unknown ID.


JWT claims

The auth-result JWT is signed with EdDSA (Ed25519); the public key is published at https://api.authensee.com/.well-known/jwks.json. Every claim appears exactly once and uses snake_case, matching OIDC convention.

{
  "iss": "https://auth.authensee.com",
  "sub": "sub_9f3kQzWx7RmT2yLpB8vN4cD6",
  "aud": "01906dd2-4c8a-7bb3-b7f0-5e12c9a4d833",
  "iat": 1783076534,
  "exp": 1783077134,
  "jti": "art_Kj8mN2pQ4rS6tV9wX1yZ3aB5cD7eF0gH",
  "auth_result_id": "ar_bxvS6dFBzoJKW3BQxYtDm4qGh8N2LcVp",
  "challenge_id": "01917f8a-2e5b-7d4c-9a1f-6b3e8c4d6f0a",
  "session_id": "9182e25e-0b7b-4d92-ae98-a3c710c905c1",
  "external_user_id": "user_12345",
  "persona_type": "human",
  "scheme_id": "passkey_question_v1",
  "auth_time": 1783076534
}

Standard claims (RFC 7519)

ClaimCarriesExample
iss (issuer)The auth server origin that signed the token"https://auth.authensee.com"
sub (subject)The provider-scoped providerSubject — a stable per-(persona, provider) alias, not the raw persona ID"sub_9f3kQzWx..."
aud (audience)The provider the token is intended for — your provider ID"01906dd2-..."
iat (issued at)Unix timestamp1783076534
exp (expires at)Unix timestamp, 10 minutes after iat1783077134
jti (JWT ID)Unique token identifier — art_ prefix"art_Kj8mN2..."

Verifying is one call in any JWT library:

import { jwtVerify, createRemoteJWKSet } from 'jose';
 
const jwks = createRemoteJWKSet(
  new URL('https://api.authensee.com/.well-known/jwks.json'),
);
 
const { payload } = await jwtVerify(token, jwks, {
  issuer: 'https://auth.authensee.com',
  audience: yourProviderId,
});
 
console.log(payload.sub);          // providerSubject
console.log(payload.scheme_id);    // e.g. 'passkey_question_v1'
console.log(payload.persona_type); // 'human' | 'agent'

Custom claims

ClaimTypeDescription
auth_result_idstringServer-side result ID (ar_ prefix). Use with GET /v1/auth-results/:id to retrieve the authoritative record.
challenge_idstring (UUID)The challenge this proof was bound to
session_idstring (UUID)The session that produced the proof (omitted in test contexts)
external_user_idstringYour own user ID for the persona (omitted if not bound)
persona_type"human" | "agent"Same data as the persona record
scheme_idstringPrimary key for what the proof attested to. Map this to a factor list via the scheme table.
auth_timenumberUnix timestamp of the underlying authentication (mirrors iat, since auth and token issuance are atomic)

No factors_verified claim in the JWT

The factor list is a plain field on the exchange response, but not inside the signed token. Every scheme has a fixed factor set known to the server, so consumers derive it from scheme_id — the token stays smaller, and adding new schemes never requires re-issuing JWTs with different factor shapes.

No personaId, providerId, or agent_id body claims

sub (providerSubject) and aud (your provider ID) are the canonical identity claims; there is no raw persona ID on this token. Distinguish agent personas via persona_type: "agent".

Next

  • Personas — the identity behind providerSubject
  • Sessions — where the flow that produced this result started
  • Embed guide — receiving the authResultCode on your callback page

On this page