API preview. The Verath API is in development. Endpoints, fields and behaviour shown here illustrate the planned design and may change before general availability. Sandbox credentials are issued to early partners on request.
Authentication
Every request must carry all three of the following. Requests failing any check are rejected with 401.
Mutual TLS
Present the client certificate issued to your partner account.
Authorization
Bearer token: your VERATH_API_KEY.
Verath-Signature
t=<unix seconds>,v1=<hex HMAC-SHA256 of “t.body” with your signing secret>
Endpoints
/v1/eventsPreviewReport an identity check
Send one signed event each time you verify or use a NIN or BVN. The response never reveals whether the identity is enrolled with Verath. Duplicate partner_ref values are de-duplicated.
{
"id_type": "NIN" | "BVN",
"identity_hash": "<hex HMAC-SHA256>",
"hash_key_version": 1,
"institution": { "name": "string", "category": "bank | fintech | digital_lender | telco | other" },
"purpose": "kyc_check | account_opening | loan_application | …",
"purpose_detail": "optional string",
"occurred_at": "ISO 8601 timestamp",
"partner_ref": "your unique reference"
}202 Accepted
{ "event_id": "evt_8f21c0a9", "status": "accepted" }/v1/events/batchPreviewImport historical checks
Upload up to 1,000 past events per request so newly enrolled owners can see their history.
{ "events": [ { …event }, { …event } ] }202 Accepted
{ "accepted": 998, "rejected": [ { "index": 4, "error": "invalid_purpose" } ] }/v1/events/{event_id}PreviewGet event status
Retrieve an event you reported and any owner response linked to it.
200 OK
{
"event_id": "evt_8f21c0a9",
"partner_ref": "kyc-2026-10-07-55102",
"owner_response": "not_me" | "confirmed" | null,
"dispute_id": "dsp_4c7e19",
"dispute_status": "sent" | "acknowledged" | "investigating" | … | null
}/v1/disputes/{dispute_id}PreviewUpdate a dispute
Tell the owner how their “Not me” report is progressing. Only disputes on events you reported can be updated.
{ "status": "acknowledged | investigating | account_closed | resolved | rejected", "case_ref": "optional", "note": "optional" }200 OK
{ "dispute_id": "dsp_4c7e19", "status": "account_closed" }/v1/lock-checksPlannedCheck identity lock
Ask whether an identity is locked before opening an account. Designed to fail open: if Verath is unavailable, proceed with your normal controls and flag the account.
{ "id_type": "BVN", "identity_hash": "…", "hash_key_version": 1, "purpose": "account_opening" }200 OK
{ "locked": true, "action": "require_owner_approval", "check_id": "lck_2b81" }Webhooks
Verath POSTs JSON to the HTTPS endpoint you register, signed with the same Verath-Signature scheme. Respond 2xx within 10 seconds; failed deliveries are retried with backoff.
| Event | Description | Status |
|---|---|---|
dispute.createdThe owner tapped “Not me” on an event you reported. | The owner tapped “Not me” on an event you reported. | Preview |
event.confirmedThe owner confirmed an event (“This was me”). | The owner confirmed an event (“This was me”). | Preview |
dispute.updatedA dispute’s status changed. | A dispute’s status changed. | Planned |
lock.approval_grantedThe owner approved a one-time unlock for your institution. | The owner approved a one-time unlock for your institution. | Planned |
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);
}Errors
Errors return JSON: { "error": { "code": "…", "message": "…" } }
| HTTP | Code | Meaning |
|---|---|---|
| 400 | invalid_payloadA field is missing or malformed. | A field is missing or malformed. |
| 401 | invalid_signatureSignature, timestamp or certificate could not be verified. | Signature, timestamp or certificate could not be verified. |
| 403 | key_version_retiredThe hash_key_version has been rotated out. | The hash_key_version has been rotated out. |
| 409 | duplicate_eventAn event with this partner_ref was already accepted. | An event with this partner_ref was already accepted. |
| 429 | rate_limitedToo many requests. Retry with exponential backoff. | Too many requests. Retry with exponential backoff. |
| 5xx | server_errorRetry with backoff. Events are idempotent by partner_ref. | Retry with backoff. Events are idempotent by partner_ref. |
Rate limits & retries
- Limits are set per partner and shared with your credentials. Exceeding them returns 429 with a Retry-After header.
- Retry 429 and 5xx responses with exponential backoff and jitter, reusing the same partner_ref.
- Never block a customer journey on a Verath call. Queue events and send them asynchronously.
- Hash keys are versioned. During rotation, send the new hash_key_version as soon as you receive it.
