Proof Developer Reference
Integrate identity verification into your product in minutes. Proof is a multi-tenant REST API — bring your own camera, we handle the biometrics.
API Key Auth
x-api-key: prf_live_…Used for /api/verify and /api/enroll
Service Token Auth
Authorization: Bearer …Used for /api/presence/* endpoints
Authentication
Proof uses two token types depending on the endpoint:
| Token Type | Header | Used for |
|---|---|---|
| API Key | x-api-key: prf_live_… | Verify, Enroll |
| Service Token | Authorization: Bearer … | Presence sessions, MCP server |
Create API keys and service tokens in Admin → API Keys.
Admin Authentication & 2FA
Admin accounts support two-factor authentication via passkey (WebAuthn) or Proof 2FA (biometric verification with consent). New admins must enroll at least one 2FA method within 7 days of account creation. After the deadline, login is blocked until enrollment is complete.
Passkey (WebAuthn)
- Hardware security keys (YubiKey, Titan) and platform authenticators (Touch ID, Windows Hello)
- Phishing-resistant — origin-bound credentials
- Multiple passkeys per admin account
- Managed in Admin → Security → Passkeys
Proof 2FA
- Live identity scan with blink detection
- Requires explicit biometric consent (BIPA/GDPR compliant)
- Uses the same liveness pipeline as user verification
- Can be used alongside or as an alternative to passkeys
Login API response changes
When 2FA is enabled, the POST /api/auth/login response includes a requires_2fa field with available methods:
// POST /api/auth/login — 2FA required response
{
"requires_2fa": true,
"methods": {
"passkey": true,
"face": true
},
"sessionToken": "tmp_sess_...",
"message": "Two-factor authentication required"
}The client should present the user with their available 2FA options. Complete the challenge via POST /api/auth/2fa/passkey/authenticate or POST /api/auth/2fa/face, passing the sessionToken from the login response.
Enrollment deadline: Admins have 7 days from account creation to enroll in at least one 2FA method (passkey or face). After the deadline, POST /api/auth/login returns 403 with "error": "2FA enrollment required" until enrollment is completed.
Identity Verification
Match a probe image against your org's enrolled users. Returns a verified flag, matched identity, and per-check pipeline breakdown.
/api/verifyRequest
{
"image": "data:image/jpeg;base64,/9j/...",
"profile": "strict-finance", // optional: profile ID or slug
"deviceInfo": { // optional: improves device check score
"userAgent": "Mozilla/5.0...",
"cameraWidth": 1280,
"cameraHeight": 720,
"screenWidth": 1920,
"screenHeight": 1080
}
}Response — verified
{
"verified": true,
"userId": "clx1a2b3c4d5e6f",
"email": "alice@example.com",
"confidence": 0.94,
"compositeScore": 0.87,
"checkResults": [
{ "check": "liveness", "pass": true, "score": 0.92, "weight": 1.5 },
{ "check": "frameConsistency", "pass": true, "score": 1.00, "weight": 1.0 },
{ "check": "impossibleTravel", "pass": true, "score": 1.00, "weight": 1.2 },
{ "check": "deviceConsistency","pass": true, "score": 0.75, "weight": 0.8 }
]
}Response — not verified
{ "verified": false, "reason": "Identity not recognised" }User Enrollment
Register an identity template for a user. The user will be matchable by subsequent verify calls. Typically triggered from your registration flow or via the admin portal.
/api/enrollRequest
{
"image": "data:image/jpeg;base64,/9j/...",
"email": "alice@example.com"
}Response
{ "enrolled": true, "userId": "clx1a2b3c4d5e6f" }Passkey API (WebAuthn)
Manage passkey credentials for admin 2FA. Passkeys provide phishing-resistant authentication using platform authenticators (Touch ID, Windows Hello) or hardware security keys (YubiKey, Titan).
/api/auth/2fa/passkey/register-optionsGenerate WebAuthn registration options for the current admin session.
// Response
{
"options": {
"challenge": "base64url-encoded-challenge",
"rp": { "name": "Proof", "id": "webel.ai" },
"user": { "id": "...", "name": "admin@example.com", "displayName": "Admin" },
"pubKeyCredParams": [
{ "alg": -7, "type": "public-key" },
{ "alg": -257, "type": "public-key" }
],
"authenticatorSelection": {
"residentKey": "preferred",
"userVerification": "required"
}
}
}/api/auth/2fa/passkey/registerComplete passkey registration with the authenticator response.
// Request
{
"credential": { /* PublicKeyCredential JSON from navigator.credentials.create() */ },
"name": "MacBook Touch ID" // optional: friendly name for this passkey
}
// Response
{
"success": true,
"credentialId": "cred_...",
"name": "MacBook Touch ID",
"createdAt": "2026-04-07T10:00:00.000Z"
}/api/auth/2fa/passkey/authenticate-optionsGenerate WebAuthn authentication options during login. Requires the sessionToken from the login response.
// Request
{ "sessionToken": "tmp_sess_..." }
// Response
{
"options": {
"challenge": "base64url-encoded-challenge",
"rpId": "webel.ai",
"allowCredentials": [
{ "id": "cred_...", "type": "public-key" }
],
"userVerification": "required"
}
}/api/auth/2fa/passkey/authenticateComplete passkey authentication to finish login.
// Request
{
"sessionToken": "tmp_sess_...",
"credential": { /* PublicKeyCredential JSON from navigator.credentials.get() */ }
}
// Response
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIs...",
"admin": {
"id": "adm_...",
"email": "admin@example.com",
"orgId": "clx_org_..."
}
}/api/auth/2fa/passkey/credentialsList all registered passkeys for the current admin.
// Response
{
"credentials": [
{
"credentialId": "cred_...",
"name": "MacBook Touch ID",
"createdAt": "2026-04-07T10:00:00.000Z",
"lastUsedAt": "2026-04-07T14:30:00.000Z"
}
]
}/api/auth/2fa/passkey/credentials/:idRemove a registered passkey. At least one 2FA method (passkey or face) must remain active.
// Response
{ "success": true, "message": "Passkey removed" }Verification Profiles
Profiles are named check configurations created in the admin portal. Reference them by ID or slug to apply different security postures per call.
POST /api/verify
{ "image": "...", "profile": "high-security" }| Check | Description |
|---|---|
| liveness | Detects photo/video replay via passive DCT analysis |
| frameConsistency | Validates inter-frame variance to catch spliced video |
| impossibleTravel | Flags logins faster than 900 km/h from last location |
| embeddingHistory | Clusters past identity templates; flags statistical outliers |
| deviceConsistency | Fingerprints camera + UA; penalises unrecognised devices |
| geoFence | Restricts verifications to an allowed country list |
| timeRestriction | Limits verifications to configured hours and days |
Human Presence Tokens
AI agents can request cryptographic proof that a specific human was present at a specific moment. The result is a short-lived signed JWT — a Human Presence Token (HPT).
Agent creates a session
Receives a one-time verifyUrl to send to the human
Human visits the URL
Completes a live identity scan in their browser
Agent polls for completion
GET /api/presence/sessions/:id until VERIFIED
Agent retrieves the HPT
GET /api/presence/sessions/:id/token
Agent validates claims
POST /api/presence/verify-token — confirms email, nonce, org
/api/presence/sessionsAuth: Authorization: Bearer <service-token>
// Request
{
"orgId": "clx_org_...",
"purpose": "Authorize production deployment",
"email": "alice@example.com", // optional: lock to specific user
"nonce": "8f3a...", // optional: bind to HPT for replay prevention
"ttlSeconds": 300 // optional: HPT lifetime (max 3600, default 300)
}
// Response
{
"sessionId": "clx_sess_...",
"verifyUrl": "https://id.example.com/verify-presence/abc123...",
"expiresAt": "2026-04-06T15:00:00.000Z"
}/api/presence/sessions/:id// Response
{
"sessionId": "clx_sess_...",
"status": "VERIFIED", // PENDING | VERIFIED | EXPIRED | CANCELLED
"verifiedEmail": "alice@example.com",
"verifiedAt": "2026-04-06T14:52:10.000Z",
"expiresAt": "2026-04-06T15:00:00.000Z"
}/api/presence/sessions/:id/token// Response
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"jti": "a1b2c3d4-...",
"verifiedEmail": "alice@example.com",
"verifiedAt": "2026-04-06T14:52:10.000Z"
}/api/presence/verify-token// Request
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expectedOrgId": "clx_org_...",
"expectedEmail": "alice@example.com", // optional
"expectedNonce": "8f3a..." // optional
}
// Response
{
"valid": true,
"sub": "alice@example.com",
"orgId": "clx_org_...",
"userId": "clx_usr_...",
"sessionId": "clx_sess_...",
"purpose": "Authorize production deployment",
"checks": { "compositeScore": 0.87, "liveness": 0.92 },
"issuedAt": "2026-04-06T14:52:10.000Z",
"expiresAt": "2026-04-06T14:57:10.000Z"
}MCP Server
Connect Proof to Claude, Cursor, or any MCP-compatible AI agent. The server exposes three tools over stdio (local) or HTTP+SSE (hosted).
Claude Desktop / Cursor config:
{
"mcpServers": {
"proof": {
"command": "node",
"args": ["/path/to/mcp-server/dist/index.js"],
"env": {
"PROOF_BASE_URL": "https://id.example.com",
"PROOF_SERVICE_TOKEN": "your-service-token"
}
}
}
}| Tool | Description |
|---|---|
| request_human_presence | Create a presence session; returns a verifyUrl to send to the human |
| get_presence_token | Poll for completion and retrieve the signed HPT |
| verify_presence_token | Validate an HPT and return decoded claims |
TypeScript SDK
The official SDK wraps the REST API with full TypeScript types. Works in Node.js, browsers, and React Native.
npm install @webel/sdkVerify an identity
import { ProofClient } from "@webel/sdk";
const client = new ProofClient({
baseUrl: "https://id.example.com",
apiKey: "prf_live_...",
});
const result = await client.verify({ image: base64Image });
if (result.verified) {
console.log(`Verified: ${result.email} (score: ${result.compositeScore})`);
}Full HPT flow
// 1. Create a presence session
const session = await client.createPresenceSession({
orgId: "clx_org_...",
purpose: "Authorize production deployment",
nonce: crypto.randomUUID(),
});
// 2. Send session.verifyUrl to the human (email, Slack, etc.)
console.log("Ask the human to visit:", session.verifyUrl);
// 3. Wait until verified (polls every 2s, times out after 5min)
await client.waitForPresence(session.sessionId);
// 4. Retrieve the HPT
const { token } = await client.getPresenceToken(session.sessionId);
// 5. Validate
const claims = await client.verifyPresenceToken({ token });
console.log("Verified by:", claims.sub); // alice@example.comWebhooks
Proof fires webhooks to your configured endpoints for key events. Configure in Admin → Webhooks.
| Event | Fired when |
|---|---|
| verification.success | An identity is successfully matched |
| verification.failure | A verification attempt fails |
| user.enrolled | A user completes identity enrollment |
| alert.triggered | An alert rule threshold is breached |
{
"event": "verification.success",
"orgId": "clx_org_...",
"timestamp": "2026-04-06T14:52:10.000Z",
"data": { ... }
}Each delivery includes an x-proof-signature header — HMAC-SHA256 of the raw body signed with your webhook secret. Verify it to confirm the request originated from Proof.
Error Reference
All errors return JSON with an error field.
| Status | Meaning |
|---|---|
| 400 | Bad Request — missing or invalid fields |
| 401 | Unauthorized — missing or invalid API key / service token |
| 403 | Forbidden — session key mismatch or email constraint violated |
| 409 | Conflict — session not in the expected state |
| 410 | Gone — session has expired |
| 422 | Unprocessable — identity not recognised, liveness failed, or pipeline hard-fail |
| 429 | Too Many Requests — rate limit exceeded |
{ "error": "Identity not recognised" }