Get started

Sandbox and testing

The sandbox is a full copy of the VerifyMe API and hosted UI with simulated providers. Nothing is billed, no real SMS is sent, and every outcome (success, mismatch, outage) can be triggered with specific test values.

Environments

SandboxProduction
API keysvm_test_...vm_live_...
Publishable keyvm_pk_test_...vm_pk_live_...
GET /v1/ping environmenttestlive
Providers (SMS, email, DigiLocker, PAN, bank, face, liveness)Simulated, deterministicReal providers bound to your tenant
OTP deliveryCode is returned in the response and shown in the UI and developer inboxDelivered by SMS or email only
Webhook targetslocalhost and private addresses allowedPublic HTTPS only
returnUrlHTTP allowedHTTPS required
Events environment fieldtestlive
BillingFree (sandbox plan)Metered
DataTest data only. Never upload real identity documentsReal personal data

Each environment is a separate deployment with its own data. A request, key or webhook endpoint created in the sandbox does not exist in production. The sandbox host is whatever your deployment uses; the samples here use https://kyc.example.com.

Test data

Enter these values in the hosted UI. All of them are deterministic.

Mobile and email OTP

InputBehaviour
Any valid Indian mobile (10 digits, first digit 6 to 9)A random six-digit OTP is generated. It is shown on screen as the sandbox code and in the sandbox inbox
Mobile ending 1234The OTP is always 123456
Mobile ending 0000The simulated SMS gateway times out. The step goes to a delayed state, delivery is retried in the background and the applicant can request a new code
Email starting fail@The simulated mail server is unavailable
Wrong codeINVALID_OTP with attemptsLeft. After the attempt limit (3): OTP_ATTEMPTS_EXCEEDED, request a new code
Wait more than the expiry (300 seconds SMS, 600 seconds email)OTP_EXPIRED
Ask for another code within 30 seconds429 with Retry-After. At most 5 sends per hour per step

Aadhaar via DigiLocker

The DigiLocker step opens a simulator consent page (/mock/digilocker/{txn}) clearly labelled as a sandbox simulator.

ActionResult
Allow accessStep completes. Name TEST APPLICANT (or the name entered earlier in Personal details), date of birth 1990-05-14 (or the entered value), gender M, address 12, MG Road, Bengaluru, Karnataka 560001, a random Aadhaar last four, masked as XXXX XXXX 1234-style in results
DenyDIGILOCKER_NOT_COMPLETED, retryable
Close the window and never answerThe step stays pending (verification.pending) and expires after about 15 minutes; reopen DigiLocker to retry

PAN

PAN format is five letters, four digits, one letter (ABCPK1234F). The 4th character gives the category (P individual, C company, F firm, H HUF, T trust, A AOP).

PANResult
Any valid PANVerified, holder name equals the name entered. Category from the 4th character
5th character T (for example ABCPT1234F)Registry outage. Step shows PROVIDER_UNAVAILABLE, the attempt is not consumed, flag PROVIDER_UNAVAILABLE (severity INFO) is recorded. It is never turned into a rejection
Last character X (for example ABCPK1234X)PAN_INVALID
5th character Z (for example ABCPZ1234F)Verified but the holder name is DIFFERENT PERSON: flag PAN_AADHAAR_MISMATCH (HIGH) when DigiLocker ran first, otherwise PAN_NAME_MISMATCH (MEDIUM)

Bank account

Account numberResult
9 to 18 digits, IFSC HDFC0001234Verified; bank name HDFC Bank. SBIN... gives State Bank of India, ICIC... ICICI Bank, any other prefix Bank
Ending 9999Verified but the holder is SOME OTHER HOLDER: flag BANK_NAME_MISMATCH (MEDIUM). If the flow has blockOnNameMismatch the step fails with BANK_NAME_MISMATCH
Starting 0000BANK_ACCOUNT_INVALID
Ending 1111Gateway timeout: PROVIDER_UNAVAILABLE, not a rejection

IFSC format: four letters, a zero, six letters or digits.

Selfie, liveness and uploads

InputResult
Selfie image (JPEG, PNG or WebP) of 1,500 bytes or moreScore between 72 and 98, passes the default threshold of 60
Selfie image under 1,500 bytesScore 38. FACE_MISMATCH and retry. After the attempt limit (3 by default) the step completes with flag FACE_BELOW_THRESHOLD (HIGH) and goes to manual review. If the flow sets onFail: block the step fails with FACE_VERIFICATION_FAILED
Liveness: 3 to 10 frames totalling more than 3,000 bytesLive, score 92
Liveness: smallerLIVENESS_FAILED (HIGH)
Document or photo that is not JPEG, PNG, WebP (or PDF where allowed)FILE_TYPE_NOT_ALLOWED. The type is detected from file content, not the file name
File larger than 8 MBFILE_TOO_LARGE
A file containing the standard EICAR test stringRejected by the malware scan: DOCUMENT_SCAN_FAILED (HIGH) and a security event
PDF with embedded JavaScriptDOCUMENT_SCAN_FAILED
Geo photo without location when the step requires itGEO_REQUIRED; otherwise it completes with flag GEO_UNAVAILABLE (LOW)

Address and pincode

The pincode lookup is simulated from the first two digits: 11 New Delhi, 12 Gurugram, 14 Ludhiana, 17 Shimla, 20 Lucknow, 30 Jaipur, 38 Ahmedabad, 40 Mumbai, 41 Pune, 45 Indore, 49 Raipur, 50 Hyderabad, 56 Bengaluru, 60 Chennai, 64 Coimbatore, 68 Kochi, 70 Kolkata, 75 Bhubaneswar, 78 Guwahati, 80 Patna. Other prefixes return not found, so the applicant fills city and state manually. Pincodes cannot start with 0.

  • Full name accepts letters, spaces, dots, apostrophes and hyphens. Date of birth is YYYY-MM-DD; under the flow's minAge (18) gives UNDERAGE.
  • The consent step requires accepted: true; otherwise CONSENT_REQUIRED.

Sandbox inbox

Because no real SMS or email leaves the sandbox, every message the simulated providers "send" is kept for one hour (the latest 30 per channel) and shown in the portal's developer sandbox inbox. The recipient is masked as it would be in logs. The API behind it is GET /api/portal/sandbox/messages, available only in the sandbox (404 in production). The OTP is also in the step response as sandboxCode, which is what the hosted UI displays under the input.

Webhook sink and simulated events

  • Sink: POST /api/portal/webhooks/sandbox-sink (the portal button) creates an endpoint whose URL is /demo/sink/{endpointId} on the sandbox host. It verifies the signature like a correct consumer and records the last 30 deliveries. See Webhooks.
  • Simulated events: POST /api/portal/sandbox/emit with { "type": "onboarding.approved", "requestId": "vm_req_..." } emits a single event with data.simulated: true. Useful to exercise your handler for events that normally need a human (approve, reject, revert).
  • Real end-to-end: submit a request in the hosted UI, then approve or reject it as a reviewer in the portal's review queue. The review policy of your tenant decides whether decisions are manual (MANUAL, the default), automatic when no HIGH flag exists (AUTO) or when no flag other than INFO exists (HYBRID).

Simulating failures

To testDo this
Provider outage (never a false rejection)PAN with 5th character T, bank account ending 1111, mobile ending 0000
Wrong data / mismatch flagsPAN 5th character Z, bank account ending 9999
Low face match and manual reviewSelfie under 1,500 bytes
Rejected uploadUpload a text file renamed .jpg, or the EICAR string
Link expiryCreate with expiresInHours: 1 and wait; or cancel and re-open the link to see the cancelled page
Re-issued linkPOST .../reissue-link, then open the old link: it shows "not valid"
Rate limitingSend many requests quickly (default 600 per minute) and read X-RateLimit-* and Retry-After
Webhook retriesPoint an endpoint at a URL that returns 500, watch attempts at 30 s, 2 min, 10 min in GET /v1/webhook-events
IdempotencyRepeat a create with the same Idempotency-Key (replayed) and with a changed body (409 IDEMPOTENCY_KEY_REUSED)
Custom domainIn the sandbox, domain verification can be simulated so you can see tenant-domain links without DNS

Resetting

There is no one-click reset of sandbox data. To start clean: cancel open requests with POST .../cancel, use new externalReference values (or leave the block_active duplicate policy off), revoke old keys, and delete old webhook endpoints. Requests expire by themselves after the TTL.

Going live

  1. Complete the go-live checklist.
  2. Replace the sandbox host with the production host and vm_test_ with vm_live_ keys, and the publishable key if you embed.
  3. Re-create webhook endpoints in production (they are not copied) and store the new secrets.
  4. Re-save return URL allow-list and allowed origins for the production workspace.
  5. Bind real providers and publish your flow in production, then run one real end-to-end verification yourself.
  6. Remove any sandbox sink endpoints and demo credentials from shared environments.
Docs version 1.0.0API version v1Last updated 1 Oct 2026