API v1 (deprecated)
v1 and v2 run side by side against the same projects, API tokens, consents, sessions and dashboards. You can move one call at a time — there is no cut-over and no data migration on your side.
1. Choose the identity value
v1 keys an end-user by X-User-Fullname. v2 uses user_id in the request body.
Both resolve to the same identity record, so whatever string you send today as
X-User-Fullname is the value to send as user_id — enrollments, consents
and history carry over untouched.
If you have been sending real names, this is the moment to switch to an opaque ID. Note that changing the value creates a new identity: the user will need to re-enroll and re-consent under the new ID, so migrate the value deliberately (and only if you can re-onboard).
user_id must match ^[A-Za-z0-9._:@-]{1,128}$. A name containing spaces is
rejected with 400 invalid_request.
2. Map the endpoints
| v1 | v2 | Notes |
|---|---|---|
POST /enroll/face | POST /v2/enrollments/face | File part renamed to face |
POST /enroll/voice | POST /v2/enrollments/voice | File part renamed to voice; phrase moves into request |
POST /process-video | POST /v2/liveness | Anti-spoofing only — see step 4 |
| — | POST /v2/face-verify | Face identity check, split out of process-video |
| — | POST /v2/voice-verify | Voice identity check, split out of process-video |
POST /match-faces | POST /v2/face-match | image file part renamed to reference_image |
POST /docauth/check | POST /v2/documents/check | |
POST /avf/infer | POST /v2/deepfake/checks | Returns 201; poll instead of streaming |
GET /avf/status/{id} | GET /v2/deepfake/checks/{id} | Plain JSON read; the v1 SSE stream still exists |
POST /sessions/start | POST /v2/sessions | |
POST /sessions/end/{id} | POST /v2/sessions/{id}/end | |
POST /images / GET /images/{email} | PUT / GET /v2/users/{user_id}/reference-image | Keyed by user_id, not email |
POST /users/delete/cascading | DELETE /v2/users/{user_id} | Console session required, as in v1 |
| — | GET /v2/users/{user_id} | New: enrollment state for a user |
/scores/* | /v2/scoring-systems/* | Console session; managed from the dashboard |
/custom-consents/* | /v2/consents/* | Same semantics |
/webhooks/* | /v2/webhooks/* | Console session, as in v1 |
/api-consent/*, /api-transactions/*, /api-projects/public/apikeys and the
warmup endpoints are unversioned and unchanged — keep calling them as you do
today.
3. Move headers into the body
Everything that was an X-* business header is now a field in the JSON
request part. The old headers are stripped on v2 routes — sending them has
no effect.
| v1 header | v2 field |
|---|---|
X-User-Fullname | request.user_id |
X-Session-ID | request.session_id |
X-Action-Trigger | request.trigger |
X-Use-Prefilled-Video | request.use_session_video (boolean, /v2/face-match only) |
X-Inhouse-Docauth | request.provider: inhouse | idscan |
X-Inhouse-MRZ | request.mrz_provider: inhouse | idscan |
phrase, vocabulary form fields | request.phrase, request.vocabulary |
Authorization, X-Request-Id, X-Client-App, X-Client-Version,
X-App-Version, X-Device-Info and X-Geo-Location stay headers.
New in v2: request.services.exclude lets a call skip services that are enabled
on the token — e.g. {"services": {"exclude": ["visual_speech_recognition"]}}
on /v2/liveness. v1 had no request-side equivalent. (The X-Removed-Services
response header is unchanged: it still tells you that consent-gated services
were dropped from the call.)
Before (v1):
curl 'https://api.biometrysolutions.com/api-gateway/process-video' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --header 'X-User-Fullname: 8f14e45f' \ --header 'X-Session-ID: 0f9c2a1e' \ --form 'phrase=one two three four five six'After (v2):
curl 'https://api.biometrysolutions.com/api-gateway/v2/liveness' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --form 'request={"user_id":"8f14e45f","session_id":"0f9c2a1e","phrase":"one two three four five six"};type=application/json' \4. Split one process-video call into several
This is the only behavioural change, and it is deliberate. In v1, one
/process-video call ran every service enabled on your token — anti-spoofing
and identity — and returned one blended verdict.
In v2:
POST /v2/livenessruns Face Liveness Detection, Active Speaker Detection and Visual Speech Recognition.POST /v2/face-verifyruns Face Recognition against the enrolled template.POST /v2/voice-verifyruns Voice Recognition against the enrolled template.
Each call returns its own decision. If your v1 integration relied on the
single blended scoring_result, you now compose the per-call decisions
yourself — ending a session does not currently produce a combined verdict.
Pass the same session_id to all of them so the Console still shows one
attempt, and use use_session_video on /v2/face-match to avoid re-uploading
the video.
5. Update response parsing
| v1 | v2 |
|---|---|
data["Face Liveness Detection"] | data.face_liveness_detection |
data["Active Speaker Detection"] | data.active_speaker_detection |
data["Visual Speech Recognition"] | data.visual_speech_recognition |
data["Face Recognition"] | data.face_recognition |
data["Voice Recognition"] | data.voice_recognition |
scoring_result (string or object) | decision.status |
score | decision.score |
decision_reasons | decision.reasons |
message | meta.message |
{"error": "text"} | {"error": {"code": "…", "message": "…"}, "meta": {"request_id": "…"}} |
Scores keep the 0–100 scale and the per-service payloads
(code, description, result, score) are unchanged — only the keys and the
wrapper move. See the Error catalog for the new codes.
6. What is not in v2 yet
- No combined session decision.
POST /v2/sessions/{id}/endcloses the session; it does not score the calls made inside it. use_session_videois honoured only by/v2/face-match. Liveness and the verify endpoints still take an uploaded file.- No per-modality unenroll. Removing a single face or voice template means deleting the whole user.
Keep using the v1 endpoint where one of these matters to you; v1 is not going
away without an announced deprecation window, Deprecation/Sunset headers and
notice on this portal.
Scoring systems
Scoring configuration moved under /v2/scoring-systems with versioned reads.
| Operation | v1 | v2 |
|---|---|---|
| List | GET /api-gateway/scores | GET /api-gateway/v2/scoring-systems |
| Create | POST /api-gateway/scores | POST /api-gateway/v2/scoring-systems |
| Read | GET /api-gateway/scores/{id} | GET /api-gateway/v2/scoring-systems/{id} |
| Update | PATCH /api-gateway/scores/{id} | PATCH /api-gateway/v2/scoring-systems/{id} |
| Delete | DELETE /api-gateway/scores/{id} | DELETE /api-gateway/v2/scoring-systems/{id} |
| Read a past version | GET /api-gateway/scores/{id}/{version} | GET /api-gateway/v2/scoring-systems/{id}/versions/{version} |
| Project default | GET /api-gateway/scores/default | GET /api-gateway/v2/scoring-systems/default |
| Create from a preset | POST /api-gateway/scores/default/{project_id} | POST /api-gateway/v2/scoring-systems/presets/default |
| Dry-run against a payload | POST /api-gateway/scores/test/{scores_id} | POST /api-gateway/v2/scoring-systems/{id}/test |