Appearance
Verification and redirect flow
AgeRail uses a hosted flow: your backend creates a session, your frontend sends the user to its verification_url, and AgeRail chooses the next browser route from the verification result.
1. Create the session
Send POST https://api.agerail.com/api/v1/sessions/create with an X-API-Key header and these fields:
| Field | Required | Purpose |
|---|---|---|
target_age | Yes | Minimum age, as an integer from 13 through 99 |
success_url | No | Optional per-session Success URL; omitted uses the registered Success URL |
cancel_url | No | Optional per-session Cancel URL; omitted uses the registered Cancel URL |
Register the Success URL, Cancel URL, and Webhook URL in the tenant dashboard's Settings screen. For a supplied success_url or cancel_url, the path and query may vary: its lowercased host and port must exactly match a host and port from one of those registered callback URLs. The gateway returns HTTP 422 for an unregistered host, if the tenant has no registered URL for an omitted success or cancel destination, or if the request includes the removed webhook_url field.
The gateway stores the effective success and cancel URLs on the session and uses only the registered Webhook URL. These destinations are frozen for the session's lifetime, so later tenant configuration changes do not alter an existing flow.
2. Send the user to verification_url
Use the verification_url from the HTTP 201 response. Do not construct the hosted URL from the session_id yourself.
3. Handle the outcome
Only a VERIFIED result automatically redirects the browser to the session's success_url.
| Result status | Browser behavior |
|---|---|
VERIFIED | Redirects to success_url with the signed result query parameters |
ROUTED_TO_FALLBACK | Routes to the hosted fallback page; its return action uses cancel_url |
SPOOF_DETECTED | Routes to the hosted error path; it does not redirect to success_url |
The verified redirect appends exactly these query parameters:
| Parameter | Value |
|---|---|
token | {expires_ts}.{base64url_mac}, returned in the gateway signature field |
session | The verification session UUID |
status | VERIFIED |
is_adult | The string true for the verified result |
For example:
text
https://merchant.example/age-check/success?token=1753099500.SIGNED_MAC&session=018f2f43-7d8b-7a9c-b123-123456789abc&status=VERIFIED&is_adult=trueFor the composite token, verify the MAC over session|status|is_adult|expires_ts with lower-case true or false, then reject it when the current Unix time is greater than expires_ts. The expiry is inside the token and the MAC payload; never accept a separate unsigned expiry parameter.
Treat the browser redirect as user-controlled input. Confirm production completion using one of the methods in Confirming a pass.
When the document check is on
If your tenant turns on the document check in Settings, a shopper whose estimated age is refused may be offered a check of an identity document on the hosted page. The ROUTED_TO_FALLBACK result the shopper's page receives at that moment is then not final: the session stays PENDING while the shopper decides, its expiry time moves later, and no webhook is sent yet. The decision arrives once, when the document check ends: the webhook's status is VERIFIED or ROUTED_TO_FALLBACK, and a webhook's ROUTED_TO_FALLBACK is final; the status lookup reads PASSED or FALLBACK. A document check the shopper abandons, or that cannot be completed, ends the session as EXPIRED with no webhook, as any session that is never completed ends; the status lookup then reads EXPIRED.
If your webhook signing secret is rotating, follow the receiver grace and deadline guidance in Webhooks.
Session statuses
A verification session is always in exactly one of five statuses:
| Session status | Meaning |
|---|---|
PENDING | The session is created and the end user has not completed verification yet. |
PASSED | The estimated age met the decision cutoff for the session's target_age or, where the document check is on, the document check passed. A signed result token was issued. |
FALLBACK | The estimated age was below the cutoff and, where the document check is on, the shopper declined it or it did not pass. The user was routed to the hosted fallback page. |
SPOOF | The liveness check failed — the camera input did not look like a live person. |
EXPIRED | The session stayed PENDING past its expiry time and was never completed. |
The verification result statuses from step 3 map onto session statuses one-to-one: VERIFIED → PASSED, ROUTED_TO_FALLBACK → FALLBACK, SPOOF_DETECTED → SPOOF. While a document check is open, the ROUTED_TO_FALLBACK the shopper's page receives leaves the session PENDING; see When the document check is on.
Recover a session status
From your backend, use the status lookup to confirm the current session state. Send GET https://api.agerail.com/api/v1/sessions/{session_id}/status with the same tenant X-API-Key authentication used for session creation. The success response contains exactly status and status_at:
json
{
"status": "PENDING",
"status_at": "2026-07-23T12:34:56Z"
}status_at is an ISO 8601 UTC timestamp with a Z suffix. It records when the active state took effect:
| Status | status_at source |
|---|---|
PENDING | created_at |
PASSED | completed_at |
FALLBACK | completed_at |
SPOOF | completed_at |
EXPIRED | expires_at |
The lookup returns current state only: it returns no signature or is_adult value. Signed redirects and signed webhooks remain the portable proof channels. The authenticated lookup response must not be forwarded as cryptographic proof.
Lookup-driven expiry persists only the EXPIRED status transition; it emits no webhook, lifecycle event, or new metric. If a session expires before any completion webhook arrives, use the lookup response as current state, not as a new signed completion event.
For lookup-driven confirmation, use this bounded polling pattern. Wait 1, 2, 4, 8, then 15 seconds; repeat the 15-second interval. Stop polling when the status is terminal or after five minutes, whichever comes first. Terminal statuses are PASSED, FALLBACK, SPOOF, and EXPIRED.
The endpoint returns 401 for missing or invalid API keys, 403 for a disabled tenant, 404 for both missing and cross-tenant session IDs, 409 when a completed session lacks its completion timestamp, and 422 for a malformed UUID. See Errors for details.
Obtain the optional estimated age
By default, the verification response, webhook, and redirect contain only the signed result and its status, and no estimated age. SessionStatusOut also remains exactly status and status_at. If your tenant enables estimated age visibility, your backend can obtain the estimate through the separate GET /api/v1/sessions/{session_id}/detected-age operation only for sessions whose shoppers accepted the visibility disclosure. The estimate is stored only for those consented sessions and is deleted when its session record is deleted; session records currently have no fixed automatic deletion period.
Authenticate the GET with the same tenant X-API-Key used to create the session. The response has session_id, nullable detected_age, and one of four availability values:
availability | detected_age | Meaning |
|---|---|---|
not_consented | null | The shopper did not accept the visibility disclosure for this session. |
pending | null | The shopper accepted the clause, but verification has not reached a terminal state. |
not_estimated | null | The session is terminal and consented, but no estimate was stored. |
available | Number | The consent-bound estimate is available for this session. |
Use a verified completion webhook as the signal to make this second call; the webhook itself never contains detected_age. A missing session and a session belonging to another tenant both return the same HTTP 404 with {"detail":"Session not found"}. Switching sharing off stops new estimates from being stored but does not revoke estimates already collected under consent.
Confirming a pass
Choose among three equal, first-class confirmation methods based on your integration shape:
is_adult is the estimate that the person appears to be above the configured threshold at the moment of the check, a probabilistic signal, not a confirmed fact, and the merchant's application decides what to do with it.
Redirect token
Use this when you need an immediate in-browser confirmation.
For the success redirect, verify the token MAC and expiry as described in step 3. Also require the expected session, status=VERIFIED, and is_adult=true values.
Webhook
Use this when your backend is event-driven.
For a webhook, verify the delivery signature and timestamp. Also require the expected session_id, status=VERIFIED, and is_adult=true values.
Lookup
Use this when your backend does not use webhooks; follow the bounded polling pattern in Recover a session status.
For a status lookup, authenticate with the tenant X-API-Key and require status=PASSED. The response confirms current state for that tenant, but it is not portable proof.
For redirect tokens and webhooks, a valid HMAC proves that the signed values came from AgeRail and were not changed. The signed status and session fields determine whether that authentic result represents the pass you expected.
See the Quickstart, test mode, or the API Reference for the surrounding integration.