Sessions
Understanding verification sessions in Bouncer
A session in Bouncer represents a single age verification attempt. Understanding the session lifecycle is crucial for integrating Bouncer effectively.
Session Lifecycle
Status Flow
created → pending → verification_started → completed
↓
verification_kyc → completed
created or pending → completed (success: true) re-verification shortcut
completed (success: false) → verification_started user retries with their ID
any open status → cancelled device sessions onlySession Statuses
| Status | Description |
|---|---|
created | Session created via API, user not yet redirected |
pending | User has visited the verification page |
verification_started | User has begun the liveness check |
verification_kyc | User is performing ID document scanning |
completed | Verification finished (check success field for result; success: false can still change while the session is open) |
cancelled | Device sessions only: replaced by a newer dynamic QR scan on the same device |
Session Parameters
When creating a session, you can configure:
| Parameter | Required | Description |
|---|---|---|
age_target | Yes | Target age to verify (8, 13, 16, 18, 21, 25, or 30) |
redirect_url | Yes | Where to send the user after a successful verification |
webhook_url | No | URL to receive real-time status updates. Keep it to 255 characters or fewer |
force_kyc | No | Require ID verification. The verification cookie can still skip it: to check a document every time, also send force_verification: true (see KYC) |
force_verification | No | Require full verification every time; do not use verification cookie to skip |
metadata | No | Custom data to attach (e.g., order ID) |
expiration_time | No | Session lifetime in seconds, counted from creation (60-3600) |
See the Create Session API for complete request/response details.
Session Expiration
Sessions have a configurable expiration time, counted from when the session is created (expires_at is issued_at plus expiration_time):
- Default: 300 seconds (5 minutes)
- Minimum: 60 seconds
- Maximum: 3600 seconds (1 hour)
After expires_at, the verification page shows Session expired and no new scan can start. A scan that reached Bouncer before expires_at can still be decided after it, and then validation.completed is sent. There is no expired status: the status API keeps returning the last status reached. If a user's session expires, you'll need to create a new one from your site: for sessions created through the API, the Start a new session button on the expired screen does not create one.
Verification Flow
Age Estimation Flow
- User visits verification page
- Liveness check is performed
- System estimates age from facial features
- If liveness is confirmed and estimated age ≥ target age → SUCCESS (unless the session requires an ID document)
- If estimated age < target age, or no estimate is returned → Continue to the KYC flow, on the same page
KYC Flow
When KYC is required (the age estimate did not reach the target, or force_kyc: true):
- After the face scan, user scans their ID document
- System checks that it is a physical document, that its photo matches the user's face, and reads the birth date
- Age calculated from document birth date (if the date cannot be read, from an age estimate on the document photo)
- If age ≥ target age AND all checks pass → SUCCESS
- Otherwise → FAILURE. While the session is open, the user can tap Verify age with your ID on the failure screen and try again, so a failure can later become a success
Failure Reasons
A session can only fail at the ID document check: a face scan alone never fails it. The API does not return a reason (success is false, with no reason field), and the user sees a generic failure screen. Common reasons include:
| Reason | Description |
|---|---|
| Underage | User's verified age is below the target |
| Face mismatch | Face doesn't match ID photo |
| Not a physical document | The document was not recognized as a physical ID document |
Bouncer may add detail in a metadata.facetec block. It is reserved: its content is not part of the API contract and can change, so do not build logic on it.
Polling vs Webhooks
You can check session results two ways:
Polling
Query the Get Session Status API periodically until status is completed and success is true, or until expires_at plus a short grace period has passed. A completed session with success: false can still become true while the session is open, and an abandoned session never reaches completed.
Webhooks (Recommended)
Configure a webhook_url when creating the session to receive real-time notifications. Treat every webhook as possibly the only delivery for that event: confirm the result with the status API, and poll if a webhook does not arrive. The same event can reach you more than once, and one session can send validation.completed with success: false and later with success: true, so process webhooks idempotently by session id and never downgrade a session you have seen as success: true. See Webhooks for details.
Best Practices
- Always set a
redirect_url: It is required, and it is where the user goes after a successful verification (there is no redirect after a failure) - Use webhooks and polling together: React to webhooks, and confirm with the status API, polling if a webhook does not arrive
- Handle expiration gracefully: Provide users a way to restart verification
- Store session IDs: Map sessions to your internal user/order IDs
- Validate success server-side: Never trust client-side success indicators