Skip to content

Speedmap Developers

One API for every identity check.

One REST API and one normalised response for NIN, BVN, face, CAC, bank accounts and fraud signals.

Quickstart

First verification in three steps.

  1. 1

    Create your account

    We switch your workspace live after a short compliance review.

  2. 2

    Copy your API key

    Set it as SPEEDMAP_API_KEY in your environment.

  3. 3

    Send one request

    Verify a NIN and read status, match and field_matches.

Full quickstart
POST /v1/verify/nin
curl -X POST http://localhost:4000/v1/verify/nin \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "method": "nin",
  "nin": "98765432109",
  "reason": "financialProducts",
  "consent": {
    "obtained": true
  },
  "match": {
    "last_name": "Griffin"
  }
}'
Response · 200 OK
{
  "reference_id": "spm_3f9c1a7b2e4d6f8a0b1c2d3e",
  "object": "verification",
  "product": "nin",
  "method": "nin_raw",
  "status": "verified",
  "match": true,
  "field_matches": {
    "last_name": true
  },
  "mode": "sandbox",
  "subject": "*******2109",
  "consent": {
    "mechanism": "client_attested"
  },
  "billing": {
    "amount_kobo": 0,
    "currency": "NGN"
  }
}

Platform

What every product shares.

The same keys, schema, error model, webhooks and audit trail, whichever check you run.

API keys

Live keys query the NIMC-designated verification channel and NIBSS and are billed per check. Keys are shown once, stored as hashes and revocable at any time.

Idempotency on every request

Send an Idempotency-Key header. A retry with the same key and body returns the original result, billed once; a different body returns 409 idempotency_key_reused.

One normalised response

Every check returns a verification object with reference_id, status, match, field_matches, a masked subject, consent and billing. Slow providers return 202 pending and resolve on requery.

Bulk and consent

Queue up to 10,000 checks per job and request consent by link, email or USSD, through the same consent gate and audit trail.

Webhooks

Signed webhook events.

Subscribe to verification.completed, bulk.completed and consent.updated. Payloads are assertion-only; fetch detail by reference. Each delivery carries Speedmap-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 of "t.body" with your endpoint secret.

verify-webhook.js
import crypto from "node:crypto";

// Speedmap-Signature: t=1760910843,v1=5f2c…
export function verifySignature(rawBody, header, secret) {
  const { t, v1 = "" } = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=")),
  );
  // Reject events older than five minutes
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(v1);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Errors

One error shape, stable codes.

Every error returns a stable code, a message and, where a check was created, its reference_id. Rate-limited responses include Retry-After, and every response carries RateLimit-* headers.

Consent not yet granted
HTTP/1.1 412 Precondition Failed
Content-Type: application/json

{
  "error": {
    "code": "consent_not_granted",
    "message": "Consent is pending; the data subject must grant it first",
    "reference_id": "spm_8a1d2c4b6e0f9a7b5c3d1e2f"
  }
}
  • 400 invalid_request
  • 401 unauthorized
  • 403 not_entitled
  • 409 idempotency_key_reused
  • 412 consent_expired
  • 422 provider_rejected_request
  • 429 rate_limited
  • 503 provider_unavailable

Start verifying in minutes. Go live when you’re ready.

Create your account in under a minute and explore the full dashboard. We switch your workspace live after a short compliance review.