Getting Started
API Key Authentication
Every server-to-server call to Infinity Africa is authenticated with an API key — a long-lived credential scoped to your merchant account. There's no OAuth dance, no token refresh: generate a key once, send it on every request.
Two ways to authenticate
API key — for your backend
Send Authorization: Bearer <INFINITY_API_KEY> (or the equivalent X-API-Key: <key> header) on any request originating from your own server (a website checkout, a mobile app's backend, an ecommerce platform's order webhook). This is what these docs cover.
Dashboard session — for the Infinity Africa portal only
The merchant dashboard itself authenticates with a Supabase Auth session token, also sent as Authorization: Bearer <access_token> — Infinity Africa tells the two apart by prefix, not by header. You won't use this in your own integration — it's only relevant if you're embedding the Infinity Africa dashboard itself.
Generating a key
Full API credentials are only ever generated inside the authenticated Merchant Portal — sign in at Merchant Portal, go to API Keys, choose Sandbox or Live, name the key, and check the scopes it needs (least privilege — a checkout integration only needs collections:write, for example). There is no public endpoint to generate a key — only POST /v1/merchant/api-keys, which requires a signed-in dashboard session:
/v1/merchant/api-keysCreate an API key for your own merchant — the plaintext key is shown once, in the response.
MERCHANT_ADMIN, DEVELOPER (dashboard session){
"name": "Production checkout server",
"environment": "live",
"scopes": ["collections:write", "payment_links:read"],
"ip_whitelist_enabled": false,
"continue_without_ip_whitelist": true
}{
"success": true,
"data": {
"id": "8f14e...",
"name": "Production checkout server",
"environment": "live",
"key_prefix": "inf_live_9f2a1c3b",
"key_last4": "d8e7",
"scopes": ["collections:write", "payment_links:read"],
"ip_whitelist_enabled": false,
"continue_without_ip_whitelist": true,
"plaintext_key": "inf_live_9f2a1c3bd8e7...",
"status": "active",
"created_at": "2026-08-14T09:00:00Z"
}
}Copy the plaintext key now — it won't be shown again
plaintext_key is only ever returned once, on creation. Infinity Africa stores only a SHA-256 hash — never the plaintext, never a recoverable/encrypted form — so there is no “reveal” button anywhere, including for Infinity Africa staff. If you lose a key, there's exactly one fix: rotate it (see below).Scopes
Every key is issued with an explicit set of scopes — it can only call the endpoints those scopes cover. A request with a key missing the required scope gets a 403, not a partial success.
| Scope | Grants |
|---|---|
| collections:write | Push USSD/STK/Selcom Pesa collections, generate Dynamic QR codes. |
| collections:read | Read back collection status (dashboard-scoped listing). |
| payment_links:write | Create and cancel payment links. |
| payment_links:read | Fetch a payment link by ID. |
| invoices:write | Create, edit, send, and cancel invoices. |
| invoices:read | Fetch an invoice by ID. |
| transactions:read | Look up a transaction by reference. |
| webhooks:manage | Configure the webhook URL, events, and secret. |
Sandbox vs. Live
Every key is scoped to an environment. Sandbox keys (prefix inf_sandbox_...) run against a fully simulated provider — nothing settles for real, so you can build and test your entire integration risk-free. Live keys (inf_live_...) move real money. Sandbox keys never expire on their own; both can be revoked at any time.
Both are self-service — creating a Sandbox key never requires approval. A Live key is also created directly from the dashboard, the moment your business account is approved, KYC-verified, and has pricing assigned; there is no separate “request production access” step or waiting on Infinity Africa staff. Before that, creating a Live key returns:
{
"success": false,
"error": {
"code": "production_access_restricted",
"message": "Production API keys are available after your business account is approved."
}
}Using your key
Attach it as a header on every request — never as a query parameter or in the request body, where it's more likely to end up logged somewhere:
GET /v1/transactions/TXN-4821AB HTTP/1.1
Host: api.infinityafrica.net
Authorization: Bearer inf_live_9f2a1c3bd8e7...IP whitelisting (optional)
Every key makes a choice, at creation, between two options — there is no default forced on you:
- Continue without IP whitelisting (the default) — the key authenticates from any server IP. Simpler, and fine for most integrations.
- Enable IP whitelisting — requests only succeed from IPs you explicitly approve. Add them from the Merchant Portal's IP Allowlist page (label, IP or CIDR, environment); each starts
pendinguntil a Super Admin approves it. A request from an unapproved IP gets a 403, and the attempt is logged.
Enforced for live traffic only
Revoking or rotating a key
/v1/merchant/api-keys/{key_id}/revokeRevoke a key immediately — any request using it afterward gets 401.
MERCHANT_ADMIN, DEVELOPER (dashboard session)/v1/merchant/api-keys/{key_id}/rotateRevoke a key and create its replacement (same name/environment/scopes/IP-whitelist choice) in one call. The new plaintext key is returned once, same as creation.
MERCHANT_ADMIN, DEVELOPER (dashboard session)Rotate keys periodically from the Merchant Portal, revoke any key that may have leaked (committed to a public repo, shared in a support ticket, etc.) immediately rather than waiting for a scheduled rotation — and if you simply lose a key before copying it down, rotate is also how you recover: there is no way to view a key's secret again after creation, by design.
Keep it secret, keep it server-side
Never ship an API key to a browser, mobile app bundle, or public repo