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.
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.
Production https://api.mobid.io/v1
Sandbox https://api.dev.mobid.io/v1Authentication
Server-to-server endpoints are authenticated with your application's API key and an HMAC-SHA256 request signature. Send three headers:
| Header | Value |
|---|---|
| X-API-Key | Your application API key (mk_test_… in sandbox, mk_live_… in production) |
| X-Timestamp | Current time in milliseconds (must be within 5 minutes) |
| X-Signature | HMAC-SHA256( secret, `${apiKey}:${timestamp}:${JSON.stringify(body)}` ) |
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
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.
| Access | Credential | Used for |
|---|---|---|
| Merchant dashboard API | Authorization: Bearer <merchant_jwt> | Business profile, documents, applications, keys, and team |
| Application API | X-API-Key + X-Timestamp + X-Signature | OAuth 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
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
Create a merchant account
Register at mobid.io/register and verify the email code we send you.
Create an application
From your dashboard, create an application to get instant sandbox API keys.
Request a verification
Call POST /v1/oauth/requests (signed) to get a QR code your user scans and approves with biometrics.
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.
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
Explore the docs
Verify with MID
GuideQR + Biometric OAuth2 verification
View docsMerchant Onboarding
GuideRegister, verify, and go live
View docsWebhooks
GuideSignature verification and events
View docsAPI Reference
ReferenceComplete REST endpoint reference
View docsIdentity Verification
IdentityDocument and biometric checks
View docsDigital Signatures
SigningElectronic document signing
View docsRate limits & errors
Every response includes X-RateLimit-Remaining and X-RateLimit-Reset headers. Back off on a 429.
| Status | Meaning |
|---|---|
| 200 / 201 | Success |
| 400 | Bad request — missing or invalid parameter |
| 401 | Unauthorized — invalid API key, signature, or expired timestamp |
| 403 | Forbidden — OAUTH not enabled for this application, or live access not approved |
| 404 | Not found — user not enrolled, or session/resource missing |
| 429 | Too many requests — rate limit exceeded |
| 500 | Server error |
{
"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.