The source of truth for this data is
serialize_catalog() in the verification service, served live at GET /v1/sandbox/scenarios/. A drift-tested copy is generated into docs/sandbox/SANDBOX_REFERENCE.md in the service-didit-verification repo via pipenv run python scripts/generate_sandbox_reference.py. This page mirrors that reference — always trust the live catalog endpoint if anything here looks stale.Environment model
Sandbox is a per-application mode (ApplicationDetail.mode == SANDBOX), the same idea as Stripe test mode. While a session runs under a sandbox application:
- Every external provider (OCR, AML, face match, liveness, NFC, phone, email, IP, POA, database validation, document AI) is mocked — no third party is ever called.
- Sessions are never billed and the balance check is bypassed.
- A per-application quota of 500 session creates / 24h is enforced.
- Webhooks carry
"environment": "sandbox"(live sessions carry"environment": "live"). - The hosted flow exposes the session environment so the UI can warn users not to upload real PII.
- The uploaded image is ignored: outcomes come from the scenario / magic value, not the pixels.
How to switch between live and sandbox
Live and sandbox are separate applications, so test traffic and production data never mix. Each application has amode of live or sandbox:
- Use a sandbox-mode application’s API key to get mocked, unbilled, simulatable sessions.
- Use a live-mode application’s API key for real verifications.
sandbox_scenario field on session create and the simulate endpoints are only accepted on sandbox applications; they are rejected on live applications.
Sandbox in the hosted verification flow
Sessions created under a sandbox application render extra UI in the hosted verification flow (theurl returned by POST /v3/session/):
- Pre-flow scenario picker. Before capture starts, the UI shows a picker of scenario outcomes. Choosing one calls
POST /v3/session/{session_id}/sandbox/arm/(see Arm (or re-arm) the session before the flow starts below), then continues into the flow with that scenario’s magic values already applied. - Sample documents. Document-capture steps offer the sandbox sample documents listed below instead of requiring a real upload. The pixels are ignored either way, but the samples make the step feel like a real capture.
- Sandbox tools. A visible sandbox banner and tools panel remind the tester they are in test mode and surface the same scenario controls, so it is always obvious the session cannot touch real providers, real PII, or your balance.
Scenarios
Each scenario is a named bundle of magic values. Pass its slug assandbox_scenario on session create (or to the simulate endpoint) to reproduce the outcome.
review_* scenarios exist so you can test manual-review UX (the console review queue, In Review webhooks, reviewer approve/decline actions) without needing a decline-grade input. The two kyb_* scenarios above set expected_details.registration_number instead of expected_details.first_name: they only apply to Business Verification (KYB) sessions.
Magic values
A magic value is a specific input that makes one mocked feature emit a specific risk. Set the input field to the magic value (directly, or via a scenario) to trigger it.The 10 sample documents
These are the user-facing sandbox templates. Sample specimen images for each are maintained in theservice-didit-verification repo under docs/sandbox/sample-documents/<slug>.png (added separately; they may not all be present yet).
OTP codes and email opt-in
- Phone OTP: the sandbox phone check accepts only the magic code
123456. - Email OTP: the sandbox email check accepts only the magic code
123456.
Driving outcomes: scenarios, magic values, arming, and the simulate endpoint
There are four ways to control a sandbox session’s outcome.1. sandbox_scenario at session create
Set the scenario slug when creating the session (POST /v3/session/). The create flow expands the scenario’s bundled magic values into the session’s input fields, so the mocked providers emit the intended outcome through the real workflow pipeline.
2. Magic values typed by hand
Set the individual input fields yourself (for exampleexpected_details.first_name = "SANDBOX_OCR_DOCUMENT_EXPIRED", or user_email = "sandbox+aml_hit@didit.test"). See the magic values table above for the full registry.
3. Arm (or re-arm) the session before the flow starts
Set or replace the armed scenario on a session that already exists, as long as it hasn’t progressed pastNot Started. This is the endpoint the pre-flow scenario picker calls, and you can call it yourself to build a custom picker or test harness:
- Authenticate with the session’s own
Session-Token(returned assession_tokenfrom session create), not your application API key: this is a client-facing endpoint. - Returns
400for an unknown scenario slug, and400once the session has moved pastNot Started("Scenario can only be armed before the verification starts."). - Returns
404when theSession-Tokenbelongs to a live (non-sandbox) application, or doesn’t match thesession_idin the path. - Re-arming replaces the previous scenario. It clears only the magic values the previous scenario wrote, and leaves anything the user already typed by hand (email, first name, etc.) untouched. For example: arm
decline_phone_high_risk, then armapprove- the phone number reverts to unset, but a customer-entered email survives both calls.
4. The simulate endpoint
Force a terminal status after the fact:- Console / API key (requires the
write:sessionsprivilege):POST /v3/session/{session_id}/simulate/ - Hosted flow (
Session-Tokenheader):POST /v3/session/{session_id}/sandbox/simulate/
new_status, scenario, risk, comment, node_id — all optional, but at least one of new_status, scenario, or risk is required. When only scenario or risk is given, new_status defaults to that scenario’s / risk’s default status, and the matching warning Log is written.
Where sandbox_scenario appears
The armed scenario slug (null if none is armed) is echoed back on some surfaces and intentionally left off others:
If your integration needs to branch on the armed scenario server-side, read it from the webhook envelope or either decision endpoint (
v1 or v3).
Usage and billing
Sandbox usage is tracked, never charged:- Every mocked call still records a usage event (
is_sandbox: true, priced at0.0000) so sandbox volume is visible for debugging, but it is never added to your usage totals, never decremented from balance, and never appears on an invoice. - The Business Console’s balance, top-up, and live-usage surfaces filter sandbox usage out by default. The top-up usage endpoints accept an
environmentparameter (live, the default, orsandbox) so sandbox volume can be inspected separately - always at zero cost and with no free-tier consumption. - Email and phone verification bill per request rather than per session outcome, and sandbox has no way to fully mirror that at the webhook layer. Sandbox email/phone checks still record a usage event per request at price
0.0000, same as every other mocked feature: this is an accepted, known divergence from live billing behavior, not a bug. - Database validation’s response shape in sandbox intentionally diverges from the live per-provider shape, since there is no real provider to mirror. Treat sandbox database-validation payloads as a stand-in for the outcome (status and warning code), not a byte-for-byte preview of a live provider response.