Developer Documentation

Build with MID

Verify customers with a QR scan and a biometric approval, and receive government-verified claims to your backend — without storing raw identity data.

99.99%
Availability SLO
<200ms
API p95 target
99.9%
Liveness decision benchmark
S256
PKCE supported

Performance values are targets enforced by the Mid.Core automated benchmark suite.

Base URL

MID exposes a production and a sandbox environment. Test against sandbox until you go live.

Base URLs
Production   https://api.mobid.io/v1
Sandbox      https://api.dev.mobid.io/v1

Authentication

Server-to-server endpoints are authenticated with your application's API key and an HMAC-SHA256 request signature. Send three headers:

HeaderValue
X-API-KeyYour application API key (mk_test_… in sandbox, mk_live_… in production)
X-TimestampCurrent time in milliseconds (must be within 5 minutes)
X-SignatureHMAC-SHA256( secret, `${apiKey}:${timestamp}:${JSON.stringify(body)}` )
Signing a request (Node.js)
import crypto from 'crypto';

function midHeaders(apiKey, secret, body) {
  const timestamp = Date.now().toString();
  const payload = `${apiKey}:${timestamp}:${JSON.stringify(body)}`;
  const signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');
  return {
    'Content-Type': 'application/json',
    'X-API-Key': apiKey,
    'X-Timestamp': timestamp,
    'X-Signature': signature,
  };
}

Keep your secret server-side

The API key and secret are issued per application in your merchant dashboard. Never expose the secret in client-side code or public repositories. See Authentication for the full reference.

Merchant API access

Dashboard access and application access use different credentials. Your merchant team uses a JWT to configure applications; your backend uses each application's API key and secret to call signed APIs.

AccessCredentialUsed for
Merchant dashboard APIAuthorization: Bearer <merchant_jwt>Business profile, documents, applications, keys, and team
Application APIX-API-Key + X-Timestamp + X-SignatureOAuth requests, status polling, token exchange, and other server APIs

Create

POST /v1/merchants/applications issues a client ID plus sandbox key and secret.

Use

Sign each server request with the credentials for that application and environment.

Rotate

POST /v1/merchants/applications/:id/rotate-key replaces the key immediately and keeps the secret.

Sandbox and live are separate

New applications receive an mk_test_… key immediately. An mk_live_… key is issued during live approval and cannot be used before the application's live status is approved. See application setup and key rotation.

Quick Start

1

Create a merchant account

Register at mobid.io/register and verify the email code we send you.

2

Create an application

From your dashboard, create an application to get instant sandbox API keys.

3

Request a verification

Call POST /v1/oauth/requests (signed) to get a QR code your user scans and approves with biometrics.

4

Receive verified claims

MID posts the approved, verified claims to your signed webhook. Go live after a quick business review.

Your first verification request

Create a request for the verified claims you need. MID returns a sessionId and a QR payload.

Node.js
const body = {
  recipient: '+2348012345678',        // the user's enrolled MID phone
  scopes: ['openid', 'profile', 'identity'],
  claims: ['name', 'dob', 'address', 'nin', 'liveSelfie'],
  redirectUri: 'https://yourapp.com/mid/callback',
  callbackUrl: 'https://yourapp.com/api/mid/webhook',
  codeChallenge,                      // PKCE S256 challenge
  codeChallengeMethod: 'S256',
};

const res = await fetch('https://api.dev.mobid.io/v1/oauth/requests', {
  method: 'POST',
  headers: midHeaders(API_KEY, API_SECRET, body),
  body: JSON.stringify(body),
});
const { data } = await res.json();
// data.sessionId, data.qr — render the QR for the user to scan

const statusBody = { sessionId: data.sessionId };
const statusRes = await fetch('https://api.dev.mobid.io/v1/oauth/requests/status', {
  method: 'POST',
  headers: midHeaders(API_KEY, API_SECRET, statusBody),
  body: JSON.stringify(statusBody),
});
// When approved: { data: { status: 'APPROVED', claims, approvedAt, ... } }

Full walkthrough

See Verify with MID for the complete QR + Biometric OAuth2 flow, including status polling, returned claims, and the PKCE token exchange.

Explore the docs

Rate limits & errors

Every response includes X-RateLimit-Remaining and X-RateLimit-Reset headers. Back off on a 429.

StatusMeaning
200 / 201Success
400Bad request — missing or invalid parameter
401Unauthorized — invalid API key, signature, or expired timestamp
403Forbidden — OAUTH not enabled for this application, or live access not approved
404Not found — user not enrolled, or session/resource missing
429Too many requests — rate limit exceeded
500Server error
Error response format
{
  "error": true,
  "message": "recipient, redirectUri, and codeChallenge are required"
}

Security best practices

Sign every request

Use the HMAC signature and a fresh timestamp on each call.

Verify webhooks

Validate X-Webhook-Signature with your client secret before trusting a callback.

Keep secrets server-side

Never expose API keys or secrets in client-side JavaScript.

Rotate & scope keys

Use sandbox keys for testing; request live keys only at go-live.

Use HTTPS only

All API and webhook traffic must use TLS 1.2 or higher.

Store the minimum

Persist only the verified claims you need — MID holds the rest.