Overview
The Hyparrow Identity Verification (KYC) API lets you confirm the identity of people and businesses and resolve bank account details in real time. It wraps our verification network behind a single, consistent interface so you can:- Verify individuals — BVN, NIN, voter’s card, phone number, driver’s license, and international passport.
- Verify businesses — CAC company lookups, shareholders, directors, secretary, and TIN.
- Run risk checks — domestic and global AML screening, credit history, and face comparison.
- Resolve bank accounts — NUBAN account-name lookup, lookup by bank code, and bank guessing.
/api/v1:
The
/kyc/identity and /kyc/verify groups are simply two verification
options offered by our verification network. Some products (for example BVN
and NIN) appear in both — pick the one whose response shape and pricing best
fits your use case.Authentication
Every request must be authenticated with your API key and secret, sent as headers:Sandbox testing. Point your integration at
https://sandbox.hyparrow.cloud/api/v1 and use a test key (prefixed
pk_test_ / sk_test_). Sandbox calls are not charged and never hit the live
verification network. In the sandbox:- Any identifier of all ones (
11111111111) → the not-found failure path. - Any other identifier, including all zeros, → a success, with an identity deterministically derived from the value you sent.
Billing model
Verification calls are billed in one of two ways. On every call we authorize billing before hitting the verification network:- Subscription quota. If you have an active subscription whose plan covers the API you’re calling and quota remains, the call consumes one unit of quota and costs nothing extra.
- Pay-as-you-go (PAYG). If you have no subscription covering that API, the per-call price is deducted from your wallet balance (in NGN).
How fallthrough works
When a subscription’s quota for a specific API is exhausted, the plan’soverflowBehavior decides what happens:
hard_block(default) → the call is rejected with 402 Payment Required (Quota exceeded for this API on your current plan). Subscribe again or wait for the period to renew.payg_fallthrough→ the call falls through to PAYG and the per-call price is charged to your wallet.
Quota alerts
Subscriptions send email alerts at 80% and 100% quota usage for each API so you’re never surprised by a hard block.Typical flow
1
Check your balance (and optionally subscribe)
Call
GET /kyc/balance to see your wallet balance. Browse GET /kyc/plans
and GET /kyc/pricing to compare a subscription against PAYG. If a plan
fits your volume, subscribe with POST /kyc/subscribe.2
Call a verification endpoint
Send a
POST to the verification you need — for example
/kyc/identity/bvn/basic. Billing is handled automatically: quota first,
then PAYG.3
Read the result
A successful response returns
{ "success": true, "data": { ... } }, where
data is the verification network’s own reply forwarded unmodified — so the
identity fields sit one level deeper, at data.data. See
Response envelope before you write your parser. Use
GET /kyc/history to audit past calls, what they cost, and how they were
billed.Account & billing endpoints
All billing endpoints are authenticated with your API key and secret.Check balance
Response
List plans
Response
quotas object maps each API code (the internal identifier you also see
in /kyc/pricing and /kyc/history) to the number of included calls for the
plan period.
Subscribe to a plan
string
required
The UUID of the plan to subscribe to. Get valid IDs from
GET /kyc/plans.Response
402 Response
Subscriptions are stackable — you can hold several at once, and quota is
drawn from whichever active plan covers the API being called. Use
GET /kyc/subscriptions to see them all.Call history
GET /kyc/history supports page (default 1) and limit (default 20, max
100) query parameters.
Response
billingType is sub when the call drew from quota (then amountCharged is
0) or payg when it was charged to the wallet. group is the endpoint group
that served the call — identity, verify or nuban. status is success or
failed; a failed call is always refunded, so it will have a matching
VER-REFUND- entry on GET /transactions.
Response envelope
Every verification response has this two-layer structure:success and the HTTP status are Hyparrow’s contract and are stable.
Everything inside data originates upstream: the envelope keys above are
consistent in practice, but the fields of the innermost data vary by product
(BVN basic returns different fields to a CAC lookup or an AML screen), and field
names are the network’s, not ours. Treat unknown keys as additive.
Verification option A — /kyc/identity/*
All endpoints are POST and return { "success": true, "data": { ... } } with
the record at data.data, as described above.
Example: BVN basic
Response
string
The primary phone number on the BVN record. Note the trailing
1 — there is
no field named phoneNumber.string
A secondary phone number, when the BVN record carries one. Frequently an empty
string — most records have only one number. Always present in the response.
string
A base64-encoded JPEG passport photograph of the BVN holder, returned on every
successful lookup. It is typically around 18KB of characters and dominates
the response size.
/kyc/identity/bvn/advanced returns the same envelope with a larger inner
record.
Example: NIN
Response
The inner
data fields above are representative, not exhaustive — the NIN
record is passed through from the verification network and may include
additional fields (including a base64 image). Run the call once in the
sandbox against a real-shaped payload before you rely on a specific field.Verification option B — /kyc/verify/*
All endpoints are POST and return { "success": true, "data": { ... } }.
Boolean name match
Thenin and bvn/boolean-match endpoints in this group perform a
name-match check rather than returning a full record:
Response
BVN iGree consent (3 steps)
Some BVN products require the user’s explicit consent via OTP. The iGree flow is a sequence:1
Initiate
POST /kyc/verify/bvn/igree/initiate with { "bvn": "..." } returns a
sessionId.2
Request OTP
POST /kyc/verify/bvn/igree/otp with { "sessionId": "...", "method": "sms" }
delivers an OTP to the BVN holder.3
Fetch details
POST /kyc/verify/bvn/igree/fetch with { "sessionId": "...", "otp": "..." }
returns the consented BVN record.Account lookup — /kyc/nuban/*
Example: NUBAN lookup
Response
/kyc/nuban/lookup-with-bank and supply a
bankCode from GET /kyc/nuban/banks. Not sure which bank an account belongs
to? /kyc/nuban/guess-banks returns the likely candidates.
Errors & edge cases
Every non-2xx response carries acode — a stable, machine-readable
classification — alongside a human-readable error. Switch on code. The
error string is prose intended for your logs and may be reworded at any time;
it is not part of the contract.
404 and 502 are deliberately different statuses. A mistyped BVN is a
client error — nothing is broken — so it is a 404 and a conventional
“retry on 5xx” policy leaves it alone. A 502 means the network genuinely
failed and retrying is the right thing to do. You never need to inspect prose
to tell them apart.RECORD_NOT_FOUND
The verification network has no record for the identifier you supplied.
You are not charged — the authorization is rolled back.
data.message is the verification network’s own explanation, always a
string and safe to log. It is descriptive, not a contract — branch on
code, not on this text. data is null when the network offered no
explanation.UPSTREAM_ERROR
The verification network failed, timed out, or was unreachable — or rejected
us rather than your input. Nothing is wrong with your request; retry with
backoff. You are not charged.
VALIDATION_ERROR
The request body was rejected before the verification network was called, so
the call is free — a malformed identifier never costs you a lookup.
BVNs and NINs are validated as exactly 11 digits before dispatch, so
field names the offending JSON property when the failure is attributable to
one.{"bvn":"1234"} and {"bvn":"abcdefghijk"} are caught here rather than
being spent upstream.A 400 is also returned if the authenticated identity has no API client to
bill against:INSUFFICIENT_BALANCE / QUOTA_EXCEEDED
Either a PAYG call your wallet balance cannot cover, or a subscription whose
quota for that API is used up (on a
hard_block plan) or which doesn’t cover
the API at all.Authentication required
The API key/secret headers are missing, or don’t match a key.
Wrong environment or inactive key
A test key used against production or a live key against the sandbox, or a key
that is disabled, expired, or calling from a non-allow-listed IP.
Handling failures
Driving these paths in the sandbox. An identifier of all ones
(
11111111111) returns the RECORD_NOT_FOUND 404; any other well-formed
value succeeds. Send a short or non-numeric BVN for the 400. Fund and drain a
test wallet to exercise the 402 paths. The magic values do not exist in
production.Canonical paths
The verification groups are/kyc/identity/*, /kyc/verify/* and
/kyc/nuban/*. These are the only supported paths and the only ones that
appear in the API reference.
On an older path? Integrations written before these routes were
introduced may be calling a legacy prefix named after the underlying provider.
Those aliases still work and return byte-identical responses, but they are
deprecated: they are not documented, they will not gain new endpoints, and
they leak an implementation detail into your codebase.Migrate by swapping the prefix — the rest of the path, the request body and
the response are unchanged. If you are unsure which prefix you are on, or want
a removal timeline before you plan the work, contact
support@hyparrow.com.

