Testing a verification integration end to end in sandbox
Use the published sandbox test IDs, whose last digits pick the outcome, to drive every result your code must handle, then test your webhook receiver with signed simulated events before switching to live keys.

Charles Archibong, Co-founder
· 5 min read

Key takeaways
- Sandbox accepts only published test IDs; their last digits choose the scenario.
- Test the failures you will branch on, not only the happy path.
- sandboxOutcome and sandboxDelayMs in metadata force a scenario and control timing; production ignores them.
- Test the webhook receiver separately with signed simulated events, including resends and rotations.
Test every outcome your code will handle, including the failures, by driving each one deliberately with the sandbox test IDs. In Myaza Trust's sandbox, the last digits of a published test ID choose the scenario: verified, not found, a selfie mismatch, an expired document, a provider error, and so on. Nothing external is called and nothing is charged, so you can run the full matrix as often as you like.
Then test your webhook receiver on its own, with signed simulated events, resends and a failing response. Most integration bugs found after launch are in the failure paths and the receiver, not in the happy path everyone tested by hand.
How sandbox behaves
Sandbox is selected by the key prefix: pk_test_ for the client SDKs, sk_test_ for your backend. According to the sandbox guide, three rules apply:
Only published test IDs are accepted. A real ID number is rejected at submission with
422 only_test_ids_allowed, so real ID numbers stay out of your test data.Results are fake. Biodata, government photos, document reads and database results are canned. No government database or other external service is called.
Nothing is charged, including the insufficient-credits scenario.
Test IDs also bypass your organisation's ID-type allowlist, so a new organisation can start testing immediately.
Sandbox and production are separate: keys, verifications, webhook endpoints and configuration do not cross between them. Whatever you set up in sandbox, you set up again for production.
How a test ID picks the outcome
A test ID is zero-padding followed by the scenario number, so it stays valid for its ID type. For a Nigerian BVN:
00000000001 → verified
00000000002 → not_found
00000000004 → selfie_mismatch
00000000012 → provider_errorEach ID type has its own format for the same idea: A00000001 for a Nigerian passport, GHA-000000001-0 for a Ghana Card, 00000001 for a Kenyan national ID. Change the trailing digits to change the scenario. The full table of 24 scenarios and every ID type's format is in the guide.
A verified BVN test from your backend, following the documented example:
curl -X POST https://trust.myaza.app/api/kyc/verify \
-H "Authorization: Bearer pk_test_xxx" \
-H "Content-Type: application/json" \
-d '{
"country": "NG",
"idType": "bvn",
"idNumber": "00000000001",
"userData": { "firstName": "Chidi", "lastName": "Okafor", "dateOfBirth": "1990-04-12" },
"metadata": { "requestId": "test-001" }
}'For document IDs, sandbox never reads the image. Let the SDK document flow default to the verified fixture, or pick a scenario by passing the document's test ID in idNumber, for example A00000007 for an expired passport.
The outcomes worth testing, grouped by what your code does
Twenty-four scenarios is more than most integrations need to exercise one by one. Group them by the branch in your code each one reaches:
Your code's branch | Scenarios to run | What to assert |
|---|---|---|
Pass | 1 | Access is granted only after your backend receives the result |
The ID does not exist | 2 | You ask the user to check the number, keyed on |
Retake a photo | 4 | The user is sent to the right capture step, not to a dead end |
Fix the input | 5 | The message matches the |
New document | 7 | The user is asked for a current document |
Myaza's side failed | 12 | The user sees a generic message; your team sees the |
Configuration problem | 17 | An alert reaches your team, and the user is not blamed |
Some scenarios follow your workflow's own settings, which is useful because it tests your configuration rather than a fixed answer. Scenarios 5 and 6 fail by default, but verify with dataMatch: false when your workflow records those identity details instead of failing on them. Scenarios 22 and 23 follow your age restriction: 22 returns a date of birth just outside your limits, 23 returns none, and both verify on a workflow with no age restriction. Scenario 24 follows your settings for matching the selfie to the photo printed on a document, and verifies when your workflow does not use that photo for the ID.
Forcing outcomes and timing
Two overrides go in metadata and are ignored in production:
sandboxOutcomeforces any scenario regardless of the test ID, for example"sandboxOutcome": "selfie_mismatch". Useful when the SDK's document flow does not collect an ID number.sandboxDelayMscontrols how long a verification takes to settle, from 0 to 60,000 milliseconds, defaulting to roughly 1,200.
Use the delay deliberately. Set it to 0 for fast automated tests. Set it to tens of seconds to test the part people forget: what your app shows while it waits, whether the user can leave the screen and come back, and whether a slow webhook leaves the screen stuck.
"metadata": {
"requestId": "test-slow-002",
"sandboxOutcome": "document_expired",
"sandboxDelayMs": 30000
}While you are there, submit the same requestId twice. You should get the existing verification back, not a second one, which is the behaviour your retry logic depends on.
Test the decision, not only the checks
If your workflow has decision rules, the checks are only half the answer. Run each scenario through the published sandbox workflow and confirm the outcome your policy expects: a selfie_mismatch that should go to review arrives as in_review, not declined. Remember that verification.completed reflects the checks alone; the workflow's verdict follows as verification.status_updated, and your backend should act on that one.
Business verification has its own sandbox fixtures. Any registration number is accepted and resolves to a verified fixture, RC0000001 is verified and RC0000002 is not found, and the sandbox company carries a set of key people for exercising due diligence.
Test the webhook receiver separately
Create a sandbox webhook endpoint and use the simulator on its page, which sends a realistic, correctly signed sample of any event through the real delivery path, without running a verification. The webhook testing guide recommends a sequence; the steps that catch most bugs are:
Confirm the receiver verifies the V2 signature over the raw body and returns
2xxquickly.Resend a delivery from the dashboard and confirm your side effect happens once, because your deduplication is keyed on the event ID.
Return a non-
2xxresponse temporarily and confirm a retry is scheduled.Rotate the secret and confirm both signatures are accepted during the grace period.
Deliver two events out of order and confirm your handler still ends in the right state.
Before you switch to live keys
Media uploaded under a test ID is kept for three days and then replaced by a placeholder, so do not build tests that depend on the original images later. When the matrix passes, the environments guide lists what production needs:
Your business profile is approved for production. Until then, production verify and upload requests are rejected with
business_not_approved.You have created live keys: publishable for the client, secret for backend results.
You have registered production webhook endpoints and verified their signatures.
Your credit balance is funded and a low-balance threshold is set.
You have swapped test keys for live keys.
Add one item of your own: point the SDK at your production workflow ID, since workflows are scoped to one environment.
Run one real verification of your own in production before opening the flow to customers. Sandbox proves your code handles every outcome; that first live run proves the configuration you copied across is the one you meant.
Sources

Charles Archibong
Co-founder
Charles Archibong co-founded Myaza Trust. He writes about identity verification, financial technology, and the practical work of building trusted digital services.


