Personas
Map your user IDs to AuthenSee personas, check enrollment state, link an existing persona to your provider, and unlink.
A persona is a user's AuthenSee identity — one set of enrolled factors, reusable across every provider. These endpoints map your externalUserId to a persona, report whether it has enrolled anything, and manage the persona ↔ provider link. All of them are session-token calls made by the hosted flow during a ceremony.
POST /v1/personas/identify
Associate your user ID with an AuthenSee persona. Creates a new persona if none exists for this provider + external-ID combination; returns the existing one otherwise.
Auth: session token (Authorization: Bearer sess_...)
Request
| Field | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | Your application's user ID. Must match the session's bound externalUserId when the session carries one. |
isHuman | boolean | No | Default true. Set false for agent personas — which your provider policy can block outright or rate-limit per persona. |
Response
201 when a persona was created, 200 when an existing one was returned:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Stable AuthenSee persona ID — this is personaId everywhere else in the API |
personaType | "human" | "agent" | The persona's type |
email | string | null | Optional recovery email, if set |
status | string | Persona status (e.g. "active") |
createdAt / updatedAt | string | ISO 8601 timestamps |
There is no enrolledFactors field — the server doesn't expose per-factor listings. Use GET /v1/personas/:personaId/enrollment-status for a boolean.
GET /v1/personas/:personaId
Fetch a persona by ID. Returns the same shape as identify. The session's provider must be linked to the persona; anything else fails with FORBIDDEN.
Auth: session token (Authorization: Bearer sess_...)
GET /v1/personas/:personaId/enrollment-status
Answer one question: has this persona enrolled anything?
Auth: session token (Authorization: Bearer sess_...); the session's provider must be linked to the persona
Which schemes are enrolled is deliberately not queryable — the on-device SDK is the only authoritative source for that.
GET /v1/personas/provider-binding
Check whether the session's bound externalUserId maps to a persona at all, and whether that persona is enrolled — without needing a persona ID first. The hosted flow uses this to decide between "enroll", "authenticate", and "link an existing persona" branches.
Auth: session token (Authorization: Bearer sess_...), user-bound (the session must carry externalUserId)
When no mapping exists the response is just { "bound": false }.
GET /v1/personas/:personaId/providers
List the persona's mappings for the calling provider — each entry is { providerId, externalUserId }. The session's provider must be linked to the persona and only sees its own mappings, never other tenants'.
Auth: session token (Authorization: Bearer sess_...)
POST /v1/personas/:personaId/provider-links
Link a persona the user just proved control of to the current provider session, instead of enrolling a fresh persona. This is the server side of "log in with your existing persona".
Like every enrollment mutation, it's action-gated: the caller first mints a challenge with action.type: "persona.link" and payload { providerId, externalUserId } matching the current session, proves any scheme the persona already holds via /v1/verify, and receives a short-lived action-scoped JWT — which this endpoint requires in the x-action-token header.
Auth: session token (Authorization: Bearer sess_...), user-bound, plus the persona.link action token
Request
Response
201 when the link was created, 200 when it already existed:
How the completion reaches your app depends on the surface:
- Same-device flow — the hosted page redirects to your
callbackUrlwithlinked=1&providerSubject=...in place ofauthResultCode(see the hosted pages guide). - Cross-device QR handoff — the initiating device's
GET /v1/sessions/resultpoll resolves to{ status: "complete", linked: true, providerSubject }once the phone's proof lands. The initiating device never sees the phone's proof.
A link never mints an exchangeable authResultCode — it establishes the mapping; the user's next login produces one.
DELETE /v1/personas/:personaId
Unlink the calling provider from a persona. This removes only your (provider, persona) binding — it never deactivates the persona for other providers. The persona itself is deactivated only when the unlink removes its last remaining provider mapping.
When the persona has any current enrollment, the call must carry a persona.unlink action token (minted the same way as persona.link, with payload { personaId, providerId }) in the x-action-token header — so an unlink can't be driven by a stolen session alone. If the persona has no enrolled factors, the session token suffices.
Auth: session token (Authorization: Bearer sess_...), plus the persona.unlink action token when factors exist
Request
Response
| Field | Type | Description |
|---|---|---|
persona | object | The persona after the unlink |
remainingMappings | number | Provider mappings still attached to the persona |
personaDeactivated | boolean | true only when this unlink removed the last mapping |
Errors
| Code | Status | When |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid session token |
FORBIDDEN | 403 | The session's provider isn't linked to this persona, or the action token is missing/invalid while factors exist |
NOT_FOUND | 404 | No persona with this ID |
Next
- Enrollment & verification — how the action tokens above are minted
- Linking an existing persona — the concept behind provider links
- Hosted pages guide — where these calls happen in a real ceremony