Skip to main content

Reusable Face Authentication

Verify a user's identity once, then re-authenticate them later with just a selfie — no ID document, no re-onboarding. This is the basis for biometric step-up (e.g. before a high-value action) and, eventually, passwordless login.

A template is a 512-dim face embedding, encrypted at rest (AES-256-GCM). The raw selfie is never stored for this flow.

1. Enroll a template​

POST /face/templates
curl -X POST https://api.dev.valydar.com/face/templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"external_id": "user-123",
"selfie": "<base64-encoded-jpeg>",
"consent": true
}'
FieldTypeDescription
external_idstringYour identifier for the returning user
selfiestringBase64 JPEG/PNG of the user's face
consentbooleanRequired — explicit consent to store a biometric template

Response 201:

{
"template_id": "ftm_...",
"external_id": "user-123",
"model_version": "arcface-512-v1",
"created_at": "2026-09-26T12:00:00Z"
}

Re-enrolling the same external_id replaces the template and invalidates any outstanding challenges. The selfie must pass passive liveness, otherwise the request is rejected with 400.

2. Issue an anti-replay challenge​

POST /face/templates/{external_id}/challenges
curl -X POST https://api.dev.valydar.com/face/templates/user-123/challenges \
-H "Authorization: Bearer YOUR_API_KEY"

Response:

{
"challenge_id": "fch_...",
"nonce": "8f3c…",
"expires_at": "2026-09-26T12:05:00Z",
"ttl_secs": 300
}

The challenge is single-use and time-boxed (default 300 s).

3. Authenticate​

POST /face/templates/{external_id}/authenticate
curl -X POST https://api.dev.valydar.com/face/templates/user-123/authenticate \
-H "Authorization: Bearer YOUR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"selfie": "<base64-encoded-jpeg>",
"challenge_id": "fch_...",
"nonce": "8f3c…"
}'

Response 200:

{
"authenticated": true,
"similarity": 0.93,
"liveness_score": 0.72,
"threshold": 0.45,
"reason": "match above threshold",
"template_id": "ftm_...",
"external_id": "user-123"
}
FieldTypeDescription
authenticatedbooleanWhether the selfie matches the enrolled template
similarityfloat / nullCosine similarity (null if no usable face was found)
liveness_scorefloat / nullPassive-liveness score of the submitted selfie
thresholdfloatMatch threshold (default 0.45)
reasonstringHuman-readable outcome

A below-threshold match, a failed liveness check, or an unusable selfie all return 200 with authenticated: false. The challenge is consumed atomically and bound to the template, its version, and the nonce — a replay, a mismatched nonce, or a challenge issued before a re-enrollment is rejected with 400.

Manage templates​

GET /face/templates/{external_id} # metadata only (never the embedding)
DELETE /face/templates/{external_id} # erase (GDPR); 204 on success

Webhooks​

When a webhook is configured, every authentication outcome fires a face.authentication event with the same body (including external_id and template_id). See Webhooks.

Presentation-attack protection​

Passive liveness is enforced on both enrollment and authentication, so a printed photo or screen replay of the enrolled face never authenticates. Stronger anti-replay (binding the capture itself to a fresh challenge) requires an active-liveness capture — use the Active Liveness step before calling authenticate when you need that guarantee.

Errors​

StatusMeaning
400Missing consent, no usable face, failed liveness, or invalid/reused challenge
404Template not found (or deleted)
503Face recognition model unavailable