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
| Field | Type | Required | Description |
|---|---|---|---|
authResultCode | string | Yes | The one-time ar_-prefixed code from your callback or from /v1/verify |
Response
| Field | Type | Description |
|---|---|---|
id | string | The auth-result ID (ar_ prefix) — use with GET /v1/auth-results/:id |
status | string | "authenticated" for a login |
providerId | string (UUID) | Your provider ID |
providerSubject | string | Your stable, provider-scoped alias for the persona — the identity you should key on |
externalUserId | string | null | Your own user ID, when the session was bound to one |
sessionId | string | null | The session that produced the proof |
challengeId | string (UUID) | The challenge the proof was bound to |
personaType | "human" | "agent" | The persona's type |
factorsVerified | string[] | The factors the proof attested to, derived from the scheme |
schemeId | string | null | The proven scheme |
authTime | string | When the underlying authentication happened |
expiresAt | string | When the result (and its code) expires |
token | string | The signed auth-result JWT — claims below |
tokenType | "auth_result" | Token type discriminator |
Errors
| Code | Status | When |
|---|---|---|
VALIDATION_ERROR | 400 | Code already consumed, expired, or not in an exchangeable state |
UNAUTHORIZED | 401 | Missing or invalid secret key |
FORBIDDEN | 403 | The result belongs to a different provider |
NOT_FOUND | 404 | No 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)
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.
Standard claims (RFC 7519)
| Claim | Carries | Example |
|---|---|---|
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 timestamp | 1783076534 |
exp (expires at) | Unix timestamp, 10 minutes after iat | 1783077134 |
jti (JWT ID) | Unique token identifier — art_ prefix | "art_Kj8mN2..." |
Verifying is one call in any JWT library:
Custom claims
| Claim | Type | Description |
|---|---|---|
auth_result_id | string | Server-side result ID (ar_ prefix). Use with GET /v1/auth-results/:id to retrieve the authoritative record. |
challenge_id | string (UUID) | The challenge this proof was bound to |
session_id | string (UUID) | The session that produced the proof (omitted in test contexts) |
external_user_id | string | Your own user ID for the persona (omitted if not bound) |
persona_type | "human" | "agent" | Same data as the persona record |
scheme_id | string | Primary key for what the proof attested to. Map this to a factor list via the scheme table. |
auth_time | number | Unix 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
authResultCodeon your callback page