Sessions
Mint hosted-flow sessions with your secret key, read and revoke them, and poll for a result — including the cross-device handoff.
A session scopes one enrollment or authentication flow. Your backend mints it with your secret key; the browser only ever sees the single-use flowCode (inside hostedUrl), which the hosted flow redeems for an in-memory session token.
POST /v1/sessions
Create a session. This is a server-to-server call from your backend, and usually the first API call in every flow.
Auth: provider secret key (x-api-key)
Request
| Field | Type | Required | Description |
|---|---|---|---|
scope | "enroll" | "authenticate" | "full" | Yes | What the session can do. full allows both enrollment and authentication. |
externalUserId | string | No | Your user ID. Binds the session to that account and is echoed on the auth result. |
providerSubject | string | No | Your stable alias for the persona, from a previous auth result. Must already be known for your provider; rejected otherwise. |
callbackUrl | string (URL) | No | Where the hosted flow returns the result. The origin must be on your provider's allowed-callback-origins list — an empty list rejects every callback URL. |
enrollMode | "new_persona" | No | Force a fresh-persona enrollment (skips the "log in with your existing persona" branch). Requires enroll or full scope. Rejected if the external user is already enrolled. |
ttl | number | No | Session lifetime in seconds, 1–86,400. Default 3,600. |
Response
| Field | Type | Description |
|---|---|---|
sessionId | string (UUID) | The session's ID — use it for correlation and for GET /v1/sessions/:id |
sessionToken | string | sess_-prefixed token. For direct session-token calls only — never expose it to the browser. |
flowCode | string | Single-use flow_-prefixed code the hosted flow redeems for the token |
hostedUrl | string | Ready-to-use hosted-flow URL (https://auth.authensee.com/flow/{flowCode}) — hand this to the browser |
scope | string | Echoed scope |
expiresAt | string | ISO 8601 expiration timestamp |
Send the user the hostedUrl, never the token
The flowCode inside hostedUrl is the only credential that should ever reach a browser, and it can be redeemed exactly once. If you use @rebellion-systems/authensee-sdk, its createSession() deliberately omits sessionToken from the return value.
Errors
| Code | Status | When |
|---|---|---|
VALIDATION_ERROR | 400 | callbackUrl origin not on your allowlist, unknown providerSubject, enrollMode with authenticate scope, or TTL out of range |
UNAUTHORIZED | 401 | Missing or invalid secret key |
NOT_FOUND | 404 | scope: "authenticate" with an externalUserId that has no AuthenSee enrollment — enroll first, then authenticate |
CONFLICT | 409 | scope: "authenticate" for an account whose enrollment is incomplete, or enrollMode: "new_persona" for an account that is already enrolled |
GET /v1/sessions/current
Read the current session using its own token. Unlike GET /v1/sessions/:id, this is authenticated by the session token itself — it's how the hosted flow validates a session for any provider without holding that provider's secret key.
Auth: session token (Authorization: Bearer sess_...)
Request
Response
The response carries everything the hosted flow needs to run the right ceremony: the provider's current factorCombination, the co-brand theme (when configured — see theming), and a ceremony descriptor stating whether the flow needs a cross-device handoff and which device capabilities it requires (for example, passkey_and_behavior requires an accelerometer and sets requiresHandoff: true). Branding is read live, so a mid-flight brand change shows up on the next refresh.
GET /v1/sessions/result
Long-poll for the session's outcome. The hosted flow uses this on the device that initiated a cross-device handoff: the desktop polls its own session while the user completes the ceremony on their phone.
Auth: session token (Authorization: Bearer sess_...)
Request
| Query parameter | Type | Required | Description |
|---|---|---|---|
waitMs | number | No | How long the server may hold the request while the result is pending, 0–25,000 ms. Default 25,000. |
The server holds the request only while the status is pending. Every other status returns immediately, so the polling device reflects state changes without waiting out the timer.
Response
One of four statuses:
| Status | Meaning |
|---|---|
pending | Nothing has happened yet (for a handoff: the phone hasn't redeemed the code) |
in_progress | The second device connected but hasn't finished the ceremony |
failed | The second device reported a terminal failure; code and message are present when it supplied them |
complete | The ceremony finished — for a login, authResultCode is present for the server-side exchange |
The linked completion. When the flow completed by linking an existing persona instead of a fresh login, the complete response carries linked: true and no authResultCode:
Distinguish the two by the presence of linked — a link marker is never exchangeable for a token. An enroll-only completion returns complete with an empty factorsVerified and no code.
Cross-device handoff
When the ceremony must run on a different device than the one that opened the flow — for example, a motion-gesture enrollment started on a desktop — the hosted flow mints a single-use handoff code, shows it as a QR code, and long-polls GET /v1/sessions/result while the phone completes the ceremony. These endpoints are driven by the hosted flow itself; a typical integration never calls them directly.
POST /v1/sessions/handoff
Mint a single-use handoff code for the current session. Does not invalidate the initiating device's token.
Auth: session token (Authorization: Bearer sess_...)
The code expires after 2 minutes (or at session expiry, whichever comes first); the initiating device can mint a fresh one.
POST /v1/sessions/handoff/redeem
Anonymous: the second device redeems the handoff code for its own device-scoped session token. The response is the minimal session shape (sessionId, sessionToken, scope, expiresAt) — no flowCode or hostedUrl is exposed here.
POST /v1/sessions/handoff/fail
The second device reports a terminal ceremony failure (with optional code and message, up to 120 and 500 characters), so the initiating device's poll resolves to failed instead of spinning. Idempotent.
Auth: the second device's session token (Authorization: Bearer)
GET /v1/sessions/:id
Read a session you minted. Returns the same shape as GET /v1/sessions/current.
Auth: provider secret key (x-api-key)
| Code | Status | When |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid secret key |
FORBIDDEN | 403 | The session belongs to a different provider |
NOT_FOUND | 404 | No session with this ID |
DELETE /v1/sessions/:id
Revoke a session before it expires. Subsequent session-token calls fail with UNAUTHORIZED.
Auth: provider secret key (x-api-key)
Returns 204 No Content on success. Errors match GET /v1/sessions/:id.
The flow-code redemption
For completeness: the hosted flow turns flowCode into a session token via POST /v1/hosted/flow-code/redeem. The code is single-use — a mid-flow full-page refresh restarts the flow because the code can't be redeemed twice — and the endpoint serves the AuthenSee hosted origin, so it's not part of your integration surface. See hosted flow surface security for why the token lives only in memory.
Next
- Enrollment & verification — what the hosted flow does with the session
- Auth results — exchanging the
authResultCodeyour callback receives - Embed guide — minting sessions from a real backend and opening the popup