API Authentication
Every LumiID request is authenticated with your Secret Key — a private
bearer token passed in the Authorization header. One header is all it takes
to access Nigeria's sovereign identity rails.
Start authenticating right now
Copy these two headers, swap in your key, and your first verified request is ready to send. That's the entire auth contract — no OAuth flows, no token exchange, no expiry timers.
Authorization: LUMI_SANDBOX_SK_xxxxxxxxxxxxxxxx Content-Type: application/json
How authentication works
LumiID uses a single Secret Key passed as a bearer token in the Authorization header.
This key both identifies your account and authorises access — keep it private, rotate it regularly,
and never expose it in client-side code.
Header name
Authorization
Your secret API key. Authenticates your account and authorises every request. Treat this like a password — never log it, never expose it client-side.
- Required on every request
- Server-side only — never in browsers
- Never commit to version control
- Rotate immediately if compromised
- Different values for Sandbox / Live
Full request examples
Here's a complete authenticated NIN verification call across every major language.
curl --request GET \ 'https://api.lumiid.com/v1/kyc/nin?nin=12345678901' \ --header 'Authorization: LUMI_SANDBOX_SK_xxxxxxxxxxxx' \ --header 'Content-Type: application/json'
import requests, os headers = { "Authorization": os.getenv("LUMIID_SECRET_KEY"), "Content-Type": "application/json", } response = requests.get( "https://api.lumiid.com/v1/kyc/nin", headers=headers, params={"nin": "12345678901"}, ) print(response.json())
const axios = require('axios'); const response = await axios.get( 'https://api.lumiid.com/v1/kyc/nin', { params: { nin: '12345678901' }, headers: { 'Authorization': process.env.LUMIID_SECRET_KEY, 'Content-Type': 'application/json', }, } ); console.log(response.data);
<?php $ch = curl_init('https://api.lumiid.com/v1/kyc/nin?nin=12345678901'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: ". getenv('LUMIID_SECRET_KEY'), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true); var_dump($data);
package main import ( "fmt" "net/http" "os" ) func main() { req, _ := http.NewRequest( "GET", "https://api.lumiid.com/v1/kyc/nin?nin=12345678901", nil, ) req.Header.Set("Authorization", os.Getenv("LUMIID_SECRET_KEY")) req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() fmt.Println(resp.Status) }
Test your credentials live
Paste your Sandbox key below to run a live authentication check directly from this page. Nothing is stored — your key is used only for this single request.
Credentials entered here are sent directly to api.lumiid.com and are never stored or logged by this documentation page.
Authentication error reference
Every auth failure returns a machine-readable JSON error body alongside the HTTP status. Here's every code you'll encounter and exactly how to fix it.
Unauthorized
Missing, malformed, or incorrect Authorization key. Check that the header is present and exactly as copied from the dashboard.
wrong_env_key
You're using a Sandbox key against a Live endpoint (or vice versa). Key prefixes must match the environment.
LUMI_SANDBOX_SK_* for sandbox; LUMI_LIVE_SK_* for productionForbidden
Your credentials are valid, but your account doesn't have permission for this specific endpoint or service tier.
ip_not_whitelisted
IP Whitelisting is enabled on your account but the request originated from an unlisted IP address.
rate_limit_exceeded
You've exceeded the request limit for your plan. The Retry-After header tells you exactly how many seconds to wait.
Retry-After seconds, or upgrade your plan{
"error": "Unauthorized",
"message": "Invalid or missing API credentials",
"status_code": 401
}
Rate limits
Limits apply per API key per rolling 60-second window. The response headers tell you exactly where you stand after every call.
Sandbox
100 req/min
Free · simulated data
Starter
500 req/min
Live data · pay per call
Growth
2 000 req/min
Volume discounts
Enterprise
Custom
SLA + dedicated infra
X-RateLimit-Limit: 500 # your plan's limit per minute X-RateLimit-Remaining: 487 # requests left this window X-RateLimit-Reset: 1717200060 # Unix timestamp when window resets Retry-After: 34 # seconds to wait (only on 429s)
Security guardrails
Follow these practices to keep your integration bulletproof in production.
Use environment variables
Store your key in .env files locally and your platform's secrets manager (AWS Secrets Manager, Vercel env vars, etc.) in production. Never hardcode.
Rotate keys every 90 days
Generate a new key in the dashboard, deploy it, then revoke the old one. Zero downtime if you stage the rollout.
Enable IP Whitelisting
Lock your Live key to your server's static IP addresses. Even a leaked key is useless without a matching IP.
Never call from browsers or mobile apps
Always proxy LumiID requests through your own backend. Your Secret Key must never appear in client-side code, network logs, or mobile bundles.
Monitor 401 spikes
A sudden spike in 401 responses can signal a misconfiguration or a credential leak. Alert on them internally.
Add .env to .gitignore
A single accidental commit can expose your key to your entire team and public repos. Add it before you write your first line of code.