Skip to content
VerathYour digital identity. Reimagined.

Developers · KYC API Quickstart

Your first NIN check, in minutes.

One API for NIN, vNIN and BVN checks that keeps answering when a source goes down. No request signing and no SDK needed: an API key and one HTTPS call.

1

Get a test key

Create a business account. Confirm your email, name your business and set up an authenticator app. The sandbox opens straight away. In the console, open Developers and create a test key. It starts with vk_test_ and is shown once, so store it as a secret.

Test keys only ever reach sandbox identities: no real data, no charge.

2

Make your first check

Send the key as a bearer token. Every check needs a purpose: KYC-ONB (account opening), KYC-REV (periodic review), LEND (credit decision) or EMPL (employment screening). Add names and a date of birth to get a match result for each. customer_ref is your own ID for the person, and an Idempotency-Key makes retries safe: a repeat within 24 hours returns the same check and isn’t charged again.

curl -X POST https://api.verath.io/v1/kyc/nin/basic \
  -H "Authorization: Bearer $VERATH_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: onb_cust_1042" \
  -d '{"nin":"70000000006","first_name":"Ngozi","last_name":"Lagbaja","dob":"1994-05-14","purpose":"KYC-ONB","customer_ref":"cust_1042"}'
EndpointWhat it does
POST/v1/kyc/nin/basicNIN: name, date of birth and gender matched to the record
POST/v1/kyc/nin/premiumNIN, plus photo, origin and residence for businesses approved for records
POST/v1/kyc/vninVirtual NIN token: you never handle the raw NIN
POST/v1/kyc/bvn/validateBVN: per-field match results
GET/v1/kyc/checks/{reference_id}Fetch a check again, for example after a webhook
POST/v1/kyc/linksA link the person completes on their phone (ID number match or face check)
GET/v1/walletYour wallet: balance, held, available, alert level and this month's spend (in kobo)
3

Read the result

Verath is match-first: you get whether the details you sent match the official record, not the record itself. Businesses approved for records also get the record fields once, in data, when the source’s contract allows it (data_policy: "records").

200 OK
{
  "reference_id": "chk_3c5e1f0a9b2d4c7e8f6a1b2c3d4e5f60",
  "object": "check",
  "check": "nin_basic",
  "status": "completed",
  "found": true,
  "source": "registry",
  "source_ref": "sbx_8f29a712c4e01b5d",
  "id_number": { "type": "nin", "last_digits": "0006" },
  "match": { "first_name": 100, "last_name": 100, "dob": 100 },
  "match_result": { "first_name": "match", "last_name": "match", "dob": "match" },
  "route": [{ "source": "registry", "outcome": "found", "latency_ms": 412, "ref": "sbx_8f29a712c4e01b5d" }],
  "latency_ms": 431,
  "error": null,
  "cost": { "billed": false, "amount_kobo": 0, "currency": "NGN" },
  "customer": { "id": "cus_9a7b…", "customer_ref": "cust_1042" },
  "purpose": "KYC-ONB",
  "consent_ref": null,
  "mode": "test",
  "created_at": "2026-11-11T15:32:00.112Z",
  "completed_at": "2026-11-11T15:32:00.543Z",
  "retry": null,
  "data": null,
  "data_policy": "match_only"
}
FieldMeaning
foundWhether the number exists on the record. false is an answer too, and is billed like one.
match_resultFor each field you sent: match (95+), partial (50–94: a typo, a middle name used as a first name, day and month swapped) or no_match. Face and liveness: high, medium or low.
matchThe 0–100 score behind each result, computed by Verath, so it means the same whichever source answered.
id_numberThe last 4 digits of the number you sent. Verath never stores the full number.
source, source_ref, routeWhich source answered (by label), its reference for the call, and every source tried with its timing.
costWhat this check was billed. Pending and failed checks are never billed.

Every check also appears in the console under Customers, with a PDF certificate you can file. See Reading a check in the Verath Console. For the console's home page, see The Verath Console home page.

4

Test numbers and outages

These fictional identities work with test keys. Any other number is “not found”.

TypeTest numberWhat happens
NIN70000000006Ngozi Lagbaja: found
NIN70000000001Ada Okafor: found
NIN70000000002Not found
NIN70000000003Registry down on the first try: pending, then found on the automatic retry
NIN70000000004Registry times out: fails over to licensed source B, found
NIN70000000005Never resolves: verification.failed after 72 hours, never billed
vNINVT00000000000003Ngozi Lagbaja: found
vNINVT00000000000001Ada Okafor: found
BVN22200000004Ngozi Lagbaja: found
BVN22200000001Ada Okafor: found
BVN22200000002Not found
BVN22200000003Pending, then found

Test person: Ngozi Lagbaja. Use her details anywhere your app asks for them. She’s fictional: the email can’t receive mail and the phone number is for the sandbox only.

FieldValue
First name / last nameNgozi / Lagbaja
Date of birth1994-05-14 (14 May 1994)
Gender / nationalityFemale / Nigerian
NIN / vNIN / BVN70000000006 / VT00000000000003 / 22200000004
Emailngozi.lagbaja@example.com
Phone0803 000 0006 (+234 803 000 0006)
AddressPlot 42, Example Crescent, Off Admiralty Way, Lekki Phase 1, Eti-Osa, Lagos State 106104
Occupation / companyProduct Designer / NaijaTech Solutions (placeholder)
Username@ngozi_lagbaja

To see failover, send X-Simulate-Outage: registry (the first source fails, the next answers) or X-Simulate-Outage: all (nothing answers: the check goes pending) with a test key.

5

Pending checks and webhooks

If no source answers within a few seconds, you get 424 upstream_unavailable with the check’s reference_id and status: "pending". Verath keeps your request encrypted and retries on its own for up to 72 hours. You’re never billed for a pending or failed check.

Add an HTTPS endpoint in the console’s Developers page to receive verification.completed when a pending check (or a link) finishes, and verification.failed after 72 hours without an answer. Or poll GET /v1/kyc/checks/{reference_id}. Webhooks carry match results only, never record fields. Verify the Verath-Signature header with your endpoint’s secret before trusting a delivery:

import { createHmac, timingSafeEqual } from "node:crypto";

// Verify a Verath webhook before trusting it.
export function verifyVerathWebhook(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // replay protection
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}

Wallet. GET /v1/wallet returns your balance in kobo, what’s held for running checks, what’s available and this month’s spend. Set a low-balance alert level in the console’s Billing page and subscribe to wallet.low_balance to hear when the available balance drops below it (at most once a day).

6

Errors

Errors return JSON with error.code and a plain message, and every response carries a Request-Id header. Quote it to support.

StatusWhat to do
400 invalid_payloadA field is missing or malformed. The message names it.
401Missing or wrong API key.
402 insufficient_funds / budget_exceededTop up the wallet, or raise the monthly cap in the console.
403The product isn't enabled for your business, or live checks aren't open to it yet.
409This Idempotency-Key was used with a different body, or the first request is still running.
424 upstream_unavailableNo source answered. The check is pending and retried automatically for up to 72 hours. Never billed.
429Too many requests. Wait for the Retry-After header.
7

Go live

  1. Send your company details in the console (Settings → Company). Verath reviews them within one working day.
  2. Once approved, create a live key (vk_live_) and fund your wallet.
  3. Switch the console to Live to see live checks, spend and the request log.

The full API reference (OpenAPI) is in the console under Developers. Questions about pricing or going live: sales@verath.io. Something not working: support@verath.io. Source health is on the status page.