Developer reference
11FaceID API
A small REST surface for enrollment, 1:1 identity verification, secure QR token lifecycle, and audit history. Public fields use verificationImage, spoofCheck, matchConfidence, and verificationThreshold. Integrations must migrate to these names; legacy names are not available on this versioned API. Requests and responses are JSON. No facial images are stored — only encrypted feature templates.
/api/v1
Every endpoint below is versioned under /api/v1. Send your key as Authorization: Bearer <key>; the organization is derived from the key, so there is never a tenant id to pass.
Machine-readable spec: /api/v1/openapi.json
Tokens & the browser
There is no token exchange in this API â your key is the credential, sent directly on every call. But your key must never reach a browser, and for the cases where a browser has to talk to 11FaceID, the API hands you a second, deliberately weaker credential to use instead.
Never put an API key in client-side code.
A key in browser JavaScript, a mobile bundle, or a public repo is readable by anyone who looks, and it carries every scope you granted it for your whole organization. Keys belong on your server.
Your backend → 11FaceID
Your API key, sent as a Bearer header. It never leaves your infrastructure.
Authorization: Bearer sk_live_…An end user's browser → 11FaceID
A signed token in the URL, minted by your backend with your key. It unlocks one person's verification and nothing else, so it is safe to put in a page.
/verify/<signed-token>How you get one
Enrolling already returns the first token, as verifyUrl. Render it as a QR code, email it, or put it behind your own domain. When the person opens it, verification runs with the token and no key at all.
Token lifetime
24 hours from issue. Re-issuing increments the version and kills every earlier token for that person; revoking takes effect immediately, checked live on every verification. Session and project links follow the same rule — the live state in the database is the real gate, not the token.
Scopes
A key only reaches the endpoints its scopes cover. Create each key with the narrowest set that does the job — a check-in kiosk rarely needs more than identities:write and verify. A call outside a key's scopes returns 403 with code insufficient_scope.
identities:readList and read enrolled identities and their QR status
identities:writeEnroll, update, delete identities; revoke/reissue QR tokens; send invites
verifyRun 1:1 verification and 1:N search
sessions:readList Session Assurance sessions and read their state
sessions:writeStart, tick, re-verify and end Session Assurance sessions
datasets:readList face datasets and their members
datasets:writeCreate, edit and delete face datasets and their members
settings:readRead use cases, face config, limits, policies and project links
settings:writeChange use cases, face config, policies, project links and calibration
audit:readRead the audit trail, verification history and dashboard stats
Getting started
/api/v1/whoamiConfirm a key is live and discover what it can do — the organization it belongs to, the current plan, and the expanded list of scopes. Has no side effects, so it is the safe first call from a new integration.
Requires Authorization: Bearer <api key> · Scope: Any valid key
Response
{
"tenantId": "tnt_event_northwind",
"tenantName": "Northwind Events",
"plan": "starter",
"authKind": "api_key",
"actor": "key:Kiosk – lobby",
"role": "editor",
"scopes": ["identities:read", "identities:write", "verify"]
}Identities & enrollment
/api/v1/enrollEnroll an identity for your organization. The captured face is processed in memory, a feature template is extracted and encrypted, and only the encrypted template is stored — the image is discarded. Issues the first secure QR token.
Requires Authorization: Bearer <api key> · Scope: identities:write
Request
{
"useCase": "event",
"fullName": "Amelia Stone",
"phone": "+1 555 0100",
"email": "amelia@example.com",
"fields": { "ticketId": "TKT-4821", "eventName": "Northwind Summit" },
"faceImage": "data:image/jpeg;base64,…" // processed then discarded
}Response
{
"id": "FID-A1B2C3",
"status": "pending",
"verifyUrl": "/verify/<signed-token>",
"sessionUrl": "/session-join/<signed-token>", // Session use cases only
"qr": { "version": 1, "status": "active", "expiresAt": "…" },
"registration": { "id": "FID-A1B2C3", "faceTemplate": { "dim": 512, "alg": "AES-256-GCM" } }
}/api/v1/registrationsList enrolled identities, optionally narrowed to one use case, status, or verified state. Returns profiles only — never template material.
Requires Authorization: Bearer <api key> · Scope: identities:read
Request
?useCase=event&status=pending&verified=false // all optional
Response
{
"count": 42,
"registrations": [ { "id": "FID-A1B2C3", "fullName": "Amelia Stone", "status": "checked_in" } ],
"emailStatus": { "FID-A1B2C3": "email_sent" }
}/api/v1/registrations/{id}Fetch one enrolled identity together with its QR token history and audit trail. Returns template metadata only (dimensionality and algorithm), never the raw template or any image.
Requires Authorization: Bearer <api key> · Scope: identities:read
Response
{
"registration": { "id": "FID-A1B2C3", "fullName": "Amelia Stone",
"faceTemplate": { "dim": 512, "alg": "AES-256-GCM" } },
"qrTokens": [ { "version": 2, "status": "active", "expiresAt": "…" } ],
"logs": [ { "type": "verify", "decision": "approved", "timestamp": "…" } ]
}/api/v1/registrations/{id}Edit contact details and/or re-enroll the face. Organization and use case are immutable. Re-enrolling supersedes the outstanding QR token, because it points at a template that no longer exists, and emails the new one.
Requires Authorization: Bearer <api key> · Scope: identities:write · Write access
Request
{
"fullName": "Amelia Stone-Reid",
"fields": { "ticketId": "TKT-5000" },
"faceImage": "data:image/jpeg;base64,…" // optional — re-enrolls the face
}Response
{
"registration": { "id": "FID-A1B2C3", "fullName": "Amelia Stone-Reid" },
"qr": { "version": 3, "status": "active" }, // present only if re-enrolled
"reenrolled": true
}/api/v1/registrations/{id}Destroy the registration, its encrypted template, and its QR tokens. Irreversible; the audit trail is retained.
Requires Authorization: Bearer <api key> · Scope: identities:write · Write access
Response
{ "id": "FID-A1B2C3", "deleted": true }/api/v1/registrations/{id}/qr-linkGet a shareable verification link for an identity, issuing a fresh token only if none is currently usable.
Requires Authorization: Bearer <api key> · Scope: identities:read
Response
{ "ok": true, "verifyUrl": "/verify/<signed-token>", "expiresAt": "…", "issued": false }/api/v1/registrations/{id}/send-emailRe-send the person's email with their QR code, use case and its open period — for the common case where the first one never arrived. For a Session use case the email carries the use case's Session link instead of the verification link (`link` in the response says which was sent).
Requires Authorization: Bearer <api key> · Scope: identities:write · Write access
Response
{ "ok": true, "sent": true, "link": "verify" } // "session" for a Session use caseVerification
/api/v1/verifyRun a 1:1 verification. Supply the secure QR token (preferred, and no credential needed) or a registration id with a key holding the verify scope. The live face is processed in memory, checked for spoofing, and the live template is compared against the stored encrypted template. Expired, revoked and cross-organization attempts are blocked and audit-logged.
Requires Authorization: Bearer <api key> · Scope: verify
Request
{
"token": "<signed-qr-token>", // no credential needed with a token
"verificationImage": "data:image/jpeg;base64,…" // processed then discarded
}
// or, with an API key holding `verify`:
{ "id": "FID-A1B2C3", "verificationImage": "data:image/jpeg;base64,…" }Response
{
"spoofCheck": "pass",
"face_match": "pass",
"matchConfidence": 0.91,
"verificationThreshold": 0.75,
"decision": "approved",
"reason": "Live face matched the enrolled template.",
"timestamp": "…",
"tenant_id": "tnt_event_northwind",
"registration_id": "FID-A1B2C3"
}Secure QR tokens
/api/v1/qr/resolveResolve a secure QR token to its current state (active, expired, revoked, reissued, invalid) and the profile behind it. Powers the hosted verification page; no credential is needed because the token's signature is the proof.
No credential required — the signed QR token is the proof
Request
?token=<signed-qr-token>
Response
{
"status": "active", // active | expired | revoked | reissued | invalid
"usable": true,
"qr": { "version": 2, "status": "active", "expiresAt": "…" },
"registration": { "id": "FID-A1B2C3", "fullName": "Amelia Stone" },
"tenant": { "id": "tnt_event_northwind", "name": "Northwind Events", "type": "event" }
}/api/v1/qr/revokeRevoke the active secure QR token for a registration. Subsequent verification attempts with that token are blocked and logged.
Requires Authorization: Bearer <api key> · Scope: identities:write · Write access
Request
{ "registrationId": "FID-A1B2C3" }Response
{ "ok": true, "registrationId": "FID-A1B2C3", "status": "revoked" }/api/v1/qr/reissueIssue a fresh secure QR token, superseding any prior active token (its version increments). Returns the new verify URL.
Requires Authorization: Bearer <api key> · Scope: identities:write · Write access
Request
{ "registrationId": "FID-A1B2C3" }Response
{
"ok": true,
"registrationId": "FID-A1B2C3",
"verifyUrl": "/verify/<new-signed-token>",
"qr": { "version": 2, "status": "active" }
}Face datasets
/api/v1/face-datasetsList your face datasets and how many identities each one holds.
Requires Authorization: Bearer <api key> · Scope: datasets:read
Response
{ "datasets": [ { "id": "fds_…", "name": "Staff", "memberCount": 24 } ] }/api/v1/face-datasetsCreate a face dataset — a named group of enrolled identities.
Requires Authorization: Bearer <api key> · Scope: datasets:write · Write access
Request
{ "name": "Staff" }Response
{ "ok": true, "dataset": { "id": "<uuid>", "tenantId": "tnt_event_northwind",
"name": "Staff", "createdAt": "…", "createdBy": null, "memberCount": 0 } }/api/v1/face-datasets/{id}Fetch one dataset together with its members.
Requires Authorization: Bearer <api key> · Scope: datasets:read
Response
{ "dataset": { "id": "fds_…", "name": "Staff" },
"members": [ { "registrationId": "FID-A1B2C3", "fullName": "Amelia Stone" } ] }/api/v1/face-datasets/{id}Rename a dataset.
Requires Authorization: Bearer <api key> · Scope: datasets:write · Write access
Request
{ "name": "Staff — 2026" }Response
{ "ok": true }/api/v1/face-datasets/{id}Delete a dataset. The identities in it are untouched; only the grouping goes away.
Requires Authorization: Bearer <api key> · Scope: datasets:write · Write access
Response
{ "ok": true, "id": "fds_…" }/api/v1/face-datasets/{id}/membersAdd people to a dataset — link identities that are already enrolled (e.g. a subset of a bigger dataset), or enroll a new face.
Requires Authorization: Bearer <api key> · Scope: datasets:write · Write access
Request
// Link people who are already enrolled (no new photo, no extra quota):
{ "registrationIds": ["FID-A1B2C3", "FID-D4E5F6"] } // or { "registrationId": "FID-A1B2C3" }
// …or enroll a new face straight into the dataset:
{ "fullName": "Amelia Stone", "phone": "+66812345678", "email": "amelia@example.com",
"faceImage": "data:image/jpeg;base64,…" }Response
// link: { "ok": true, "added": 2, "alreadyMembers": 0 }
// enroll: { "ok": true, "registrationId": "FID-A1B2C3" }/api/v1/face-datasets/{id}/members/{registrationId}Remove an identity from a dataset without deleting the registration.
Requires Authorization: Bearer <api key> · Scope: datasets:write · Write access
Response
{ "ok": true }/api/v1/face-datasets/{id}/use-casesRequires Authorization: Bearer <api key> · Scope: datasets:read
Response
{ "useCases": [ { "id": "<uuid>", "key": "math_10", "label": "Math — Grade 10",
"closedAt": null, "linked": true } ] }/api/v1/face-datasets/{id}/use-casesRequires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
// The full set of use cases that should verify against this dataset.
// Other datasets' links on those use cases are untouched. [] unlinks all.
{ "useCaseIds": ["<uuid>", "<uuid>"] }Response
{ "ok": true, "linked": 2, "added": 1, "removed": 0 }Session Assurance
Continuous, policy-driven assurance throughout a session. These endpoints are available only when the module is enabled; otherwise they return 403. Organizations can define multiple Verification Policies and assign different policies to different workflows without changing the verification engine.
/api/v1/session-policiesList Verification Policies and the available presets (Online Auction, Online Examination, Online Training, Remote Attendance). A preset is a recommended starting configuration only.
Requires Authorization: Bearer <api key> · Scope: settings:read
Response
{
"policies": [
{ "id": "pol_online_auction", "name": "Online Auction / Bidder Monitoring", "custom": false },
{ "id": "pol_…", "name": "Exam — Room 1", "custom": true, "libraryVisible": false, "linkedUseCases": [ { "key": "exam", "label": "Exam" } ] }
],
"presets": [ { "key": "online_auction", "name": "Online Auction / Bidder Monitoring" } ]
}/api/v1/session-policiesCreate a Verification Policy (Save as Custom Policy / Duplicate).
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
{
"name": "Exam — Final",
"initialVerification": { "qrRequired": true, "spoofCheckRequired": true, "retryLimit": 3, "retryIntervalSecs": 5, "faceQualityRequired": true },
"verification": { "intervalSecs": 45, "randomized": true, "maxConsecutiveMismatches": 3, "maxMissedVerifications": 2 },
"camera": { "disconnectedTimeoutSecs": 10, "blockedDetection": true, "unavailableAction": "require_reverify" },
"response": { "onVerificationFailure": "warn", "onMaxConsecutiveMismatches": "terminate", "onMaxMissedVerifications": "require_reverify" },
"auditLevel": "full_history"
}Response
{ "ok": true, "policy": { "id": "pol_…", "custom": true } }/api/v1/session-policies/{id}Edit an existing Verification Policy.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
{ "verification": { "intervalSecs": 60 } }Response
{ "ok": true, "policy": { "id": "pol_…", "updatedAt": "…" } }/api/v1/session-policies/{id}Delete a Verification Policy. Blocked (409) while referenced by a live session.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Response
{ "ok": true, "id": "pol_…", "status": "deleted" }/api/v1/sessionsList assurance sessions for your organization and their current state.
Requires Authorization: Bearer <api key> · Scope: sessions:read
Response
{ "count": 1, "sessions": [ { "id": "ses_…", "policyName": "Online Auction / Bidder Monitoring", "status": "active" } ] }/api/v1/sessions/startStart a Session Assurance session for an enrolled identity under a selected policy. Records an initial 1:1 verification and returns a hosted session URL you can send the person to; pass returnUrl to bring them back to your own app afterwards.
Requires Authorization: Bearer <api key> · Scope: sessions:write
Request
{ "registrationId": "FID-D4E5F6", "policyId": "pol_online_auction", "returnUrl": "https://your.app/done" }Response
{ "ok": true, "session": { "id": "ses_…", "status": "active", "totalChecks": 1 },
"sessionUrl": "https://…/session/<signed-token>" }/api/v1/sessions/checkRun one periodic identity verification. Frames are processed in memory only. Optional flags simulate a missed check or a camera problem; the policy decides the response.
Requires Authorization: Bearer <api key> · Scope: sessions:read
Request
{ "sessionId": "ses_…", "kind": "scheduled" } // or { "missed": true } / { "camera": "lost" }Response
{ "ok": true, "session": { "status": "warning", "failedChecks": 1, "consecutiveMismatches": 1 } }/api/v1/sessions/reverifyPerform an immediate re-verification. On success the session returns to active; on failure it escalates per the policy.
Requires Authorization: Bearer <api key> · Scope: sessions:write
Request
{ "sessionId": "ses_…", "verificationImage": "data:image/jpeg;base64,…" }Response
{ "ok": true, "session": { "status": "active" } }/api/v1/sessions/endEnd a session and write the closing audit event. A session ended before its policy's time limit is recorded as expired; one that ran its full length as completed.
Requires Authorization: Bearer <api key> · Scope: sessions:write
Request
{ "sessionId": "ses_…" }Response
{ "ok": true, "session": { "status": "expired", "endReason": "ended_early_by_admin", "endedAt": "…" } }/api/v1/sessions/tickAdvance every live session by one scheduler step. Use this when you drive the clock yourself — from your own cron, say — instead of relying on the browser session page.
Requires Authorization: Bearer <api key> · Scope: sessions:write
Response
{ "ok": true, "advanced": 3, "sessions": [ { "id": "ses_…", "status": "active" } ] }/api/v1/sessions/{id}/linkRe-issue the hosted session URL for a session that is still live, for when the original link was lost.
Requires Authorization: Bearer <api key> · Scope: sessions:write
Response
{ "ok": true, "sessionUrl": "https://…/session/<signed-token>" }Configuration
/api/v1/use-casesList your use cases and the custom fields each one collects at enrollment.
Requires Authorization: Bearer <api key> · Scope: settings:read
Response
{ "useCases": [ { "key": "event", "label": "Event check-in", "fields": [ { "key": "ticketId", "label": "Ticket ID" } ] } ] }/api/v1/use-casesCreate a use case with its own set of enrollment fields.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
{ "key": "exam", "label": "Exam check-in", "fields": [ { "key": "seatNo", "label": "Seat number" } ] }Response
{ "ok": true, "useCase": { "key": "exam" }, "policyId": null }/api/v1/use-cases/{id}Edit a use case — its label, fields, schedule, capacity, or the return URL its hosted links redirect to.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
{ "label": "Exam check-in (2026)", "returnUrl": "https://your.app/done" }Response
{ "ok": true, "useCase": { "key": "exam" } }/api/v1/use-cases/{id}Delete a use case. Blocked while identities are still enrolled under it.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Response
{ "ok": true }/api/v1/settings/face-configRead the organization's detector-confidence floor for enrollment. Null means the engine default applies.
Requires Authorization: Bearer <api key> · Scope: settings:read
Response
{ "enrollMinDetScore": 0.8, "isCustom": false,
"platformDefault": 0.8, "range": { "min": 0.5, "max": 0.9 } }/api/v1/settings/face-configRaise or lower the detector-confidence floor required to accept an enrollment photo. Send null to go back to the engine default.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
{ "minDetScore": 0.7 } // null resets to the platform defaultResponse
{ "enrollMinDetScore": 0.7, "isCustom": true,
"platformDefault": 0.8, "range": { "min": 0.5, "max": 0.9 } }/api/v1/calibrationCompare two images through the live engine and get the raw similarity back, without enrolling anything. Use it to pick a match threshold against your own photos rather than guessing.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
{ "image": "data:image/jpeg;base64,…" }Response
{
"detect": { "accepted": true, "reason": "OK", "numFaces": 1, "score": 0.9, "bbox": [278, 188, 811, 927] },
"spoofCheck": { "probability": 0.94, "isLive": true },
"enrollMinDetScore": 0.8,
"wouldEnroll": true
}/api/v1/limitsCurrent plan limits and usage — enrolled faces, team seats, verifications this month.
Requires Authorization: Bearer <api key> · Scope: settings:read
Response
{
"limits": { "maxVerificationsPerMonth": 1000, "maxEnrolledFaces": null, "maxUsers": 5 },
"usage": { "verificationsThisMonth": 2, "enrolledFaces": 9, "users": 1 },
"plan": "starter",
"trialEndsAt": null
}/api/v1/tenantsYour organization's profile as the API sees it.
Requires Authorization: Bearer <api key> · Scope: settings:read
Response
{ "count": 1, "tenants": [
{ "id": "tnt_event_northwind", "name": "Northwind Events", "type": "event", "defaultUseCase": "event" }
] }/api/v1/project-linksGet the shareable self-enrollment and self-verification links for a use case, plus whether they are currently open, closed, or full.
Requires Authorization: Bearer <api key> · Scope: settings:read
Request
?useCase=event
Response
{
"enrollPath": "/enroll-link/<signed-token>",
"verifyPath": "/verify-link/<signed-token>",
"status": { "open": true, "full": false }
}/api/v1/project-linksEmail someone the self-enrollment link for a use case.
Requires Authorization: Bearer <api key> · Scope: settings:write · Write access
Request
{ "useCase": "event", "email": "amelia@example.com" }Response
{ "ok": true }Audit & reporting
/api/v1/logsThe append-only audit trail for your organization, newest first and capped per request.
Requires Authorization: Bearer <api key> · Scope: audit:read
Request
?registrationId=FID-A1B2C3&limit=200 // both optional
Response
{ "count": 200, "logs": [ { "type": "enroll", "reason": "…", "timestamp": "…" } ] }/api/v1/verificationsVerification history — successful, failed, and blocked attempts, with the reason each one was blocked.
Requires Authorization: Bearer <api key> · Scope: audit:read
Request
?registrationId=FID-A1B2C3&limit=200 // both optional
Response
{
"count": 42,
"verifications": [
{ "type": "verify", "decision": "approved", "matchConfidence": 0.91, "timestamp": "…" },
{ "type": "verify_blocked", "block": "qr_expired", "timestamp": "…" }
]
}/api/v1/dashboardThe numbers behind the portal dashboard: enrollment and verification totals, approval rate, session stats, and recent activity.
Requires Authorization: Bearer <api key> · Scope: audit:read
Response
{
"stats": { "registrations": 812, "verifications": 4310, "approvalRate": 0.97 },
"sessionStats": { "active": 3, "flagged": 1 },
"recent": [ { "type": "verify", "decision": "approved", "timestamp": "…" } ],
"activity": [ … ], "activityTotal": 512
}