On this page
Test your Myaza KYC integration end to end: webhooks, polling, the full result payload (biodata, facial match, OCR data, captured-media URLs), and every failure branch, all without calling any government database or consuming paid verification credits.
| Environment | Key prefix | Real IDs? | Test IDs? | Charged? |
|---|---|---|---|---|
| Sandbox | pk_test_ / sk_test_ | rejected | canned result | never |
| Production | pk_live_ / sk_live_ | yes | treated as real | yes |
Sandbox accepts only published test IDs. A real (non-test) idNumber is rejected
immediately at POST /verify with 422 only_test_ids_allowed.
All sandbox results are fully fake: fake biodata, fake government photos, fake OCR, and fake gov-DB data. No external API is ever called.
How test IDs work
Pass a test ID in the idNumber field of POST /verify. The last one or two digits
encode the scenario; everything before is zero-padding so the value stays format-valid
for its ID type. Example for NG BVN:
00000000001 → verified
00000000002 → not_found
00000000004 → selfie_mismatch
00000000007 → document_expired (document IDs only)
00000000012 → provider_errorScenario reference
Scenarios 5 and 6 follow your workflow's identity details policy. When the workflow records every field the scenario disagrees on (5: first and last name; 6: first name and date of birth) instead of failing on it, the test ID verifies with dataMatch: false, exactly as a real mismatch would.
| Idx | Scenario | Status | reasonCode | Applies to |
|---|---|---|---|---|
| 1 | verified | verified | — | all |
| 2 | not_found | not_found | identity_not_found | all |
| 3 | gov_validation_failed | failed | gov_validation_failed | all |
| 4 | selfie_mismatch | failed | selfie_mismatch | all |
| 5 | gov_data_mismatch | failed | gov_data_mismatch | all |
| 6 | document_data_mismatch | failed | document_data_mismatch | document |
| 7 | document_expired | failed | document_expired | document |
| 8 | document_type_mismatch | failed | document_type_mismatch | document |
| 9 | document_unreadable | failed | document_unreadable | document |
| 10 | document_blurry | failed | document_blurry | document |
| 11 | id_number_not_found | failed | id_number_not_found | document |
| 12 | provider_error | error | provider_error | all |
| 13 | unsupported_id_type | error | unsupported_id_type | all |
| 14 | media_not_found | error | media_not_found | all |
| 15 | insufficient_credits | error | insufficient_credits | all |
| 16 | system_error | error | system_error | all |
| 17 | id_type_not_enabled | error | id_type_not_enabled | all |
| 18 | document_verification_disabled | error | document_verification_disabled | document |
| 19 | gov_db_check_disabled | error | gov_db_check_disabled | all |
| 20 | sandbox_not_found | not_found | sandbox_not_found | all |
| 21 | sandbox_failed | failed | sandbox_failed | all |
| 22 | age_restricted | failed | age_restricted | all |
| 23 | age_unverified | failed | age_unverified | all |
| 24 | document_photo_no_face | failed | document_photo_no_face | document |
Scenarios 22 and 23 follow your workflow's age restriction. Scenario 22 returns a date of birth just outside your own limits (a year under the minimum, or a year over the maximum) and scenario 23 returns none, so both fail exactly as a real holder would. On a workflow with no age restriction, both verify. If your workflow sends that case to review instead of declining it, scenario 22 verifies with the out-of-range date of birth and your decision rule sends it to review; scenario 23 always fails.
Scenario 24 follows your workflow's settings for the photo on the document. It stands for a document ID whose selfie could only be compared with the photo printed on the document, where no face could be read on that photo. If your workflow compares that ID with the photo on the document and declines the case, it fails with document_photo_no_face. If your workflow sends that case to review, it verifies with facialMatchSkipped: "no_face_on_document", and your decision rule on verification.documentPhotoNoFace sends it to review. If your workflow does not compare that ID with the photo on the document, it verifies.
Document-only scenarios (6–11, 18) are not published for number-only IDs (BVN/NIN/vNIN).
The verified test ID per ID type
Change the trailing digits per the table above to pick a different scenario.
| Country | ID type | Format | verified test ID |
|---|---|---|---|
| NG | bvn / nin | 11 digits | 00000000001 |
| NG | vnin | 16 chars | 0000000000000001 |
| NG | passport | A + 8 digits | A00000001 |
| NG | drivers-license | TST + 8 digits | TST00000001 |
| NG | pvc | 19 digits | 0000000000000000001 |
| GH | ghana-card | GHA-NNNNNNNNN-N | GHA-000000001-0 |
| GH | voters | 10 digits | 0000000001 |
| GH | ssnit | C + 12 digits | C000000000001 |
| GH | passport | G + 8 digits | G00000001 |
| KE | national-id | 8 digits | 00000001 |
| KE | passport | K + 8 digits | K00000001 |
| ZA | national-id | 13 digits | 0000000000001 |
| ZA | passport | Z + 8 digits | Z00000001 |
| CI | cni | CI + 9 digits | CI000000001 |
| CI | residence-card | CR + 9 digits | CR000000001 |
Example: number-only ID
curl -X POST https://your-server/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" }
}'Document IDs
In sandbox, OCR is never run; a fake OCR result is returned instead. Either let
the SDK document flow default to the verified fixture (no ID number collected), or
pick a scenario by passing the document test ID in idNumber (e.g. A00000007 for an
expired passport) or via the sandboxOutcome override below.
Business (KYB)
Business verification never calls a real registry outside production: a registry lookup incurs an external check cost in whichever environment requests it, so sandbox and development serve canned data instead.
- Any registration number is accepted and resolves to a verified fixture that echoes
the name you searched for. The catalogue values still pick their scenario:
RC0000001is verified,RC0000002is not found. A business picked from the real company search (which does work in every environment) can therefore be checked on sandbox against fixture data. sandboxOutcomeon the submit ("verified"or"not_found") pins the result for any number, and is ignored silently in production.- The sandbox company carries a full key-people set for exercising due
diligence: a 60% beneficial owner, directors, a signatory, a persona
named "Pep" that the sandbox screening stub flags (set
SCREENING_PROVIDER=stub), and a corporate shareholder,Sandbox Holdings Ltd(RC0000900), whose own register resolves to a person majority-owner and a second company above it (RC0000901), so ownership-chain look-through, effective percentages, and the depth limit are all testable at no cost.
Address presence
Presence verification has its own personas and a compressed clock so a multi-day watch
is testable in minutes; they are documented with the feature on the Address
Intelligence page. In short: outside production the
watch window runs in minutes, and an externalUserId containing
presence_verified, presence_fail or presence_inconclusive seeds a canned
observation stream through the real scorer. A plain id gets no seed, so you can drive
the ingest endpoint yourself.
Overrides
Both go in metadata and are ignored in production.
sandboxOutcome: force any scenario regardless of the test ID, e.g."sandboxOutcome": "selfie_mismatch".sandboxDelayMs: how longpending → completedtakes (0–60000 ms). Defaults to ~1200 ms; set0for an instant result.
Captured media
Media uploaded under a test ID is kept for 3 days, then the bytes are replaced with a
placeholder (an image for stills, a sample clip for videos). The data.media URLs in
webhooks keep working; they just serve the placeholder after the sweep.
Webhook simulator
From a webhook endpoint's detail page (or POST /api/dashboard/webhooks/:id/simulate
with { "event": "verification.completed" }), fire a realistic, correctly-signed sample
payload of any event at your endpoint to test signature verification and your handler,
without running a verification. It uses the real delivery path (HMAC signing + retry).
Notes
- Sandbox verifications are never charged, even for the
insufficient_creditsscenario. - Test IDs bypass the ID-type allowlist, so a new organisation can test immediately. Use
the
id_type_not_enabledscenario to simulate a revoked allowlist. - Branch on the stable
reasonCode, not the human-readablereason.