Skip to content

Conventions

This page collects the cross-cutting rules every endpoint follows. Skim it once so the rest of the docs feel familiar.

  • Verification endpoints accept and return application/json.
  • POST /login accepts application/x-www-form-urlencoded (OAuth2 password flow). Every other endpoint is JSON.
  • Responses always include a Content-Type: application/json; charset=utf-8 header.
CodeMeaning
200The request succeeded. The body contains a data envelope for verification calls, or a top-level resource for other calls.
400Validation failed: your request body is missing a required field or contains an invalid value. The detail field names the problem.
401Token is missing, expired, or invalid. Re-authenticate.
402Insufficient units. Top up your wallet before retrying.
403Token is valid, but the user lacks permission for this action.
404The referenced resource does not exist. Most common on /verify-nin/requery with an unknown reference_id.
422Pydantic validation error: typically a malformed payload (wrong type, missing field).
429Rate-limited: slow down and retry with backoff.
5xxRandaVerify is having a bad day. Retry with exponential backoff; if it persists, contact support with your reference_id.

The error body shape is consistent:

{ "detail": "Insufficient units in organisation wallet" }

Verification responses use camelCase for NIMC-sourced fields (e.g. dateOfBirth, firstname, residenceAddressLine1) but RandaVerify normalises them to a consistent snake-friendly schema before returning. You always receive:

FieldSource aliases
ninnin, NIN, identityNumber
fnamefirstname, firstName, fname
mnamemiddlename, middleName, mname
lnamesurname, lastName, lname
dobdateOfBirth, birthdate, dob
phonetelephoneno, mobile, phone, phone1
residenceAdressresidence_AdressLine1, address (note original NIMC mis-spelling preserved)
residenceTowntown, city
residenceLgaresidence_lga, lga
residenceStatestate
stateOfOriginself_origin_state, birthState
lgaOfOriginself_origin_lga, birthLga
imagephoto, base64 JPEG

This way you do not need to know which key NIMC happened to use, RandaVerify returns the same shape every time.

Every verification produces a reference_id of the form REF-XXXXXXXXXXXX. Persist it. You can:

  • Look the record up again later with /verify-nin/requery (no unit charged).
  • Quote it to support when reporting an issue.
  • Use it as your idempotency key for retry-safe persistence on your side.
  • Phone numbers are Nigerian 11-digit format beginning with 0 (e.g. 08012345678).
  • Dates of birth are dd-mm-yyyy (e.g. 03-09-1999) on the demography request side. The dob returned in responses uses the same format.

NIMC validates every verification against a fixed list of allowed reasons. Always fetch the current list from GET /nimc-reasons and pass the key (not the human label) in your request:

Terminal window
curl https://api.randaverify.com/v1/nimc-reasons -H "Authorization: Bearer $TOKEN"
{
"data": [
{ "key": "corporate", "label": "Corporate", "description": "..." },
{ "key": "nyscCheck", "label": "NYSC Check", "description": "..." },
{ "key": "telecommunicationSimReg", "label": "Telecom SIM Reg", "description": "..." }
]
}
  • Every successful verification debits one unit from your org wallet.
  • A failed verification does not debit your wallet (you only pay for results).
  • A re-query does not debit your wallet (idempotent retrieval).
  • You can check your current balance via GET /v1/meorganisation (the unit count is available in admin contexts; for the standard officer flow, see your wallet via the portal).

The single most important rule: do not blindly retry a POST /v1/verify-nin* request on transient errors. Verification POSTs are not idempotent, every successful call mints a new transaction and debits a unit. A naive retry on a network blip will charge your wallet twice.

The right pattern:

OutcomeDid a unit get charged?What to do
HTTP 2xx received with a reference_idYesYou’re done. Persist the reference_id. To “retry” (e.g. user pressed back and re-submitted), use POST /v1/verify-nin/requery with the same reference, it’s free and idempotent.
HTTP 4xx (validation, 402, 403, 404)NoThe request never reached NIMC. Fix the client input and retry safely.
HTTP 5xx or network failure / timeoutMaybe: the request may or may not have completed upstreamDo not retry blindly. Wait 30 seconds, then call /v1/verify-nin/requery with the reference you would have used. If your client never saw a reference, you can also list your recent transactions in the admin portal to confirm whether a charge landed.

Re-queries (POST /v1/verify-nin/requery) and reads (GET /v1/me, GET /v1/nimc-reasons) are idempotent and safe to retry freely.

The RandaVerify API is meant to be called server-to-server, not directly from a customer’s browser. The CORS allow-list on api.randaverify.com is locked to the official RandaVerify domains; any Origin header from partner.com.ng (or any other domain) will be rejected by the preflight.

In practice:

  • ✅ Your backend code (Node, Python, Go, PHP, whatever) calls our API directly. Bearer tokens stay server-side.
  • ❌ Your frontend JavaScript should not call api.randaverify.com directly. CORS will block it, and even if it didn’t, you would be leaking your bearer token to every visitor’s browser.
  • The right pattern: your frontend calls your backend, your backend calls ours. This also lets you cache, rate-limit, log, and apply your own business rules between the two.

If your customer-facing UI needs to surface verification results, fetch them on your backend, store the reference_id, and re-query from your backend later if the user revisits.