AuthenSeeDocs

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

curl -X POST https://api.authensee.com/v1/personas/identify \
  -H "Authorization: Bearer sess_aG25OA7mEnbOSX2J8UwnEE-GhoRdTLxN2hVeFxhWons" \
  -H "Content-Type: application/json" \
  -d '{
    "externalUserId": "user_12345",
    "isHuman": true
  }'
FieldTypeRequiredDescription
externalUserIdstringYesYour application's user ID. Must match the session's bound externalUserId when the session carries one.
isHumanbooleanNoDefault 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:

{
  "id": "01917f8a-6b3e-7d4c-9a1f-2e5b8c4d6f0a",
  "personaType": "human",
  "email": null,
  "status": "active",
  "createdAt": "2026-07-09T10:30:00.000Z",
  "updatedAt": "2026-07-09T10:30:00.000Z"
}
FieldTypeDescription
idstring (UUID)Stable AuthenSee persona ID — this is personaId everywhere else in the API
personaType"human" | "agent"The persona's type
emailstring | nullOptional recovery email, if set
statusstringPersona status (e.g. "active")
createdAt / updatedAtstringISO 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_...)

curl https://api.authensee.com/v1/personas/01917f8a-6b3e-7d4c-9a1f-2e5b8c4d6f0a \
  -H "Authorization: Bearer sess_aG25OA7mEnbOSX2J8UwnEE-GhoRdTLxN2hVeFxhWons"

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

curl https://api.authensee.com/v1/personas/01917f8a-6b3e-7d4c-9a1f-2e5b8c4d6f0a/enrollment-status \
  -H "Authorization: Bearer sess_aG25OA7mEnbOSX2J8UwnEE-GhoRdTLxN2hVeFxhWons"
{ "enrolled": true }

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)

curl https://api.authensee.com/v1/personas/provider-binding \
  -H "Authorization: Bearer sess_aG25OA7mEnbOSX2J8UwnEE-GhoRdTLxN2hVeFxhWons"
{ "bound": true, "enrolled": true }

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

curl -X POST https://api.authensee.com/v1/personas/01917f8a-6b3e-7d4c-9a1f-2e5b8c4d6f0a/provider-links \
  -H "Authorization: Bearer sess_aG25OA7mEnbOSX2J8UwnEE-GhoRdTLxN2hVeFxhWons" \
  -H "x-action-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY3Rpb24iOi..." \
  -H "Content-Type: application/json"

Response

201 when the link was created, 200 when it already existed:

{
  "linked": true,
  "providerSubject": "sub_9f3kQzWx7RmT2yLpB8vN4cD6"
}

How the completion reaches your app depends on the surface:

  • Same-device flow — the hosted page redirects to your callbackUrl with linked=1&providerSubject=... in place of authResultCode (see the hosted pages guide).
  • Cross-device QR handoff — the initiating device's GET /v1/sessions/result poll 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

curl -X DELETE https://api.authensee.com/v1/personas/01917f8a-6b3e-7d4c-9a1f-2e5b8c4d6f0a \
  -H "Authorization: Bearer sess_aG25OA7mEnbOSX2J8UwnEE-GhoRdTLxN2hVeFxhWons" \
  -H "x-action-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY3Rpb24iOi..."

Response

{
  "persona": {
    "id": "01917f8a-6b3e-7d4c-9a1f-2e5b8c4d6f0a",
    "personaType": "human",
    "email": null,
    "status": "active",
    "createdAt": "2026-07-09T10:30:00.000Z",
    "updatedAt": "2026-07-09T11:20:00.000Z"
  },
  "remainingMappings": 1,
  "personaDeactivated": false
}
FieldTypeDescription
personaobjectThe persona after the unlink
remainingMappingsnumberProvider mappings still attached to the persona
personaDeactivatedbooleantrue only when this unlink removed the last mapping

Errors

CodeStatusWhen
UNAUTHORIZED401Missing or invalid session token
FORBIDDEN403The session's provider isn't linked to this persona, or the action token is missing/invalid while factors exist
NOT_FOUND404No persona with this ID

Next