Developer Documentation
Everything you need to integrate Sogo's financial services into your application. Use these guides alongside the interactive API Reference to explore every endpoint.
Account Requirements
Sandbox keys are available to accounts with at least Tier 2 KYC (identity verification) approved - start building and testing in minutes. Live keys require full Tier 3 KYC identity verification and KYB & Compliance (business verification) approval first.
| Requirement | Sandbox keys | Live keys |
|---|---|---|
| KYC Tier 2 (identity verification) | Must be approved | Must be approved |
| KYC Tier 3 (identity verification) | Not required | Must be approved |
| KYB & Compliance (business verification) | Not required | Must be approved |
To meet these key requirements:
- KYC Tier 2 (for Sandbox & Live): Submit basic personal information and verify your identity using a valid ID number (BVN/NIN) from KYC & Limits in your Sogo profile. Approved instantly.
- KYC Tier 3 (for Live): Submit your identity document, proof of address, and video selfie from KYC & Limits in your Sogo profile. Reviewed within 1-2 business days.
- KYB & Compliance (for Live): Submit your business documents - CAC certificate, status report, director information, and supporting compliance documents - from KYB & Compliance in your Sogo profile. Reviewed within 1-3 business days.
Build on sandbox with instant Tier 2 KYC. Sandbox access is active as soon as Tier 2 identity verification is approved. Begin integrating immediately and swap in your live key once Tier 3 and KYB verifications are approved.
Base URLs
The Partner API has two independent environments. Use sandbox for all development and testing - it mirrors live behaviour without touching real money.
# Live https://api.sogo.africa/v1 # Sandbox https://sandbox.sogo.africa/v1
HTTPS is mandatory. All API requests must be made over an SSL-secured connection. Plain HTTP requests are not supported and are rejected with a 403 Forbidden response.
Key prefix determines environment. Live keys start with sogo_sk_live_ and only work on api.sogo.africa. Sandbox keys start with sogo_sk_test_ and only work on sandbox.sogo.africa. Mixing them returns a 403 environment_mismatch.
Environments (Live & Sandbox)
The Sogo Partner API operates in two environments: Live and Sandbox. The sandbox is a full-fidelity replica of the live API that lets you build, test, and debug your integration without touching real money or real utility providers. Every endpoint, request shape, response shape, error code, webhook event, and idempotency mechanism works identically to live.
| Area | Sandbox | Live |
|---|---|---|
| Base URL | https://sandbox.sogo.africa/v1 | https://api.sogo.africa/v1 |
| Key prefix | sogo_sk_test_ / sogo_pk_test_ | sogo_sk_live_ / sogo_pk_live_ |
| Real money movement | No, wallet debits are simulated | Yes |
| Real providers contacted | No, responses are simulated | Yes |
| Webhooks delivered | Yes, to your registered sandbox endpoint | Yes |
| Wallet balance | Fixed NGN 1,000,000.00 (real balance never queried) | Real balance |
| Transaction records | Not persisted; use the purchase response directly | Persisted |
| Rate limits | 10 req/min per key | 300 req/min per key |
Simulating Outcomes
No real providers are contacted in sandbox.
Bills (Airtime, Data, Electricity, Cable TV, Education, Bet Funding)
Outcomes are determined by the last 4 digits of the primary customer identifier (phone, meter_number, smartcard_number, profile_id, account_id):
| Last 4 digits | Purchase outcome | HTTP | Webhook sent? |
|---|---|---|---|
0000 | refunded | 201 | Yes, transaction.refunded |
9999 | processing | 201 | No |
| Anything else | completed | 201 | Yes, transaction.completed |
Gift Cards
Outcomes are determined by the last 4 characters of the additional_info field (for e-code cards) or the first image filename (for physical cards):
| Last 4 chars | Trade outcome | Transaction status | Webhook sent? |
|---|---|---|---|
0000 | cancelled | cancelled | Yes, transaction.cancelled |
9999 | pending | pending | No |
| Anything else | completed | completed | Yes, transaction.completed |
RMB Purchases (Buy orders)
Outcomes are determined by the last 4 characters of the recipient_identifier field:
| Last 4 chars | Trade outcome | Transaction status | Webhook sent? |
|---|---|---|---|
0000 | cancelled | processing initially | Yes, transaction.refunded |
9999 | processing | processing initially | No |
| Anything else | completed | completed | Yes, transaction.completed |
Crypto Sells (Test deposits)
Simulated deposits are triggered via POST /v1/crypto/test-deposit. The outcome is determined by the last 4 characters of the test_reference field:
| Last 4 chars | Deposit outcome | Transaction status | Webhook sent? |
|---|---|---|---|
0000 | Held for compliance review | pending | No |
9999 | Stuck processing | pending | No |
| Anything else | completed | completed | Yes, transaction.completed |
Published Test Identifiers
Copy and paste these directly into your requests:
| Identifier | Endpoint | Behaviour |
|---|---|---|
Phone 09012341234 | Airtime, Data | Succeeds |
Phone 09012340000 | Airtime, Data | Refunded (201) |
Phone 09012349999 | Airtime, Data | Stays processing |
Meter 12345671234 | Electricity verify + purchase | Succeeds |
Meter 12345670000 | Electricity verify + purchase | Refunded (201) |
Smartcard 10001234 | Cable TV verify + purchase | Succeeds |
Smartcard 10000000 | Cable TV verify + purchase | Refunded (201) |
JAMB profile 12341234 | Education verify + purchase | Succeeds |
JAMB profile 12340000 | Education verify + purchase | Refunded (201) |
Bet account 12341234 | Bet funding verify + purchase | Succeeds |
Bet account 12340000 | Bet funding verify + purchase | Refunded (201) |
E-code GIFT-USA-1234 | Gift Card Sell (E-code) | Succeeds |
E-code GIFT-USA-0000 | Gift Card Sell (E-code) | Cancelled (Webhook) |
E-code GIFT-USA-9999 | Gift Card Sell (E-code) | Stays pending |
Alipay ID recipient1234 | RMB Buy (Alipay) | Succeeds |
Alipay ID recipient0000 | RMB Buy (Alipay) | Refunded (Webhook) |
Alipay ID recipient9999 | RMB Buy (Alipay) | Stays processing |
Reference DEP-TX-1234 | Simulate Crypto Deposit | Succeeds (Webhook) |
Reference DEP-TX-0000 | Simulate Crypto Deposit | Held for compliance review (Pending) |
Reference DEP-TX-9999 | Simulate Crypto Deposit | Stuck processing (Pending) |
Sandbox transactions are not persisted. GET /v1/transactions/{reference} always returns 404 in sandbox. Use the transaction object returned directly in the purchase response. Sandbox webhooks fire to your registered sandbox endpoint only, never to live endpoints.
API Keys
Every request must be authenticated with an API key. Send it as a Bearer token in the Authorization header. Alternatively, you can use the X-API-Key header.
POST https://api.sogo.africa/v1/bills/airtime Authorization: Bearer sogo_sk_live_<your_key> Content-Type: application/json
Keys are generated from your profile under the Developer tab. You can create up to 5 key pairs per environment, restrict to specific IP addresses, and scope keys to only the permissions they need.
Live keys require verification. Creating a live key requires both KYC Tier 3 and KYB & Compliance approval. See Account Requirements. If either is not yet approved, the API returns 403 kyc_tier_insufficient or 403 kyb_verification_required. Sandbox keys require KYC Tier 2 to be approved first.
Key Types
Each key pair consists of a secret key and a public key.
sogo_sk_live_/sogo_sk_test_- Secret keys. Full API access. Never expose these in client-side code, mobile apps, or public repositories.sogo_pk_live_/sogo_pk_test_- Public keys. Restricted to read-only operations explicitly allowed for public keys. Safe to embed in client apps.
Treat secret keys like passwords. If a secret key is ever exposed, revoke it immediately from your profile and generate a replacement. Rotating a key immediately invalidates the old one.
Scopes
Scopes control which endpoints your API key can access. Assign the minimum set of scopes your integration needs - you can always add more later. The full list of available scopes is also returned by GET /v1/scopes.
| Scope | Access granted |
|---|---|
| account:read | View account profile and API key context. |
| wallet:read | View wallet balances and transaction history. |
| transactions:read | List, retrieve, and verify transactions. |
| bills:read | Fetch bill catalogues and verify customer identifiers (meter, smartcard, JAMB). |
| bills:write | Submit bill payment purchases: airtime, data, electricity, cable TV, education, ePIN, and bet funding. |
| gift_cards:read | Read gift card catalogue, brands, and sell rates. |
| gift_cards:write | Submit gift card sell orders (physical and e-code). |
| rmb:read | Read RMB exchange rates, available channels, and order limits. |
| rmb:write | Submit RMB buy orders (Alipay, WeChat Pay, Chinese bank transfer). |
| crypto:read | Read supported crypto assets, networks, and exchange rates. |
| crypto:write | Generate deposit addresses for crypto sell. |
If your key is missing a required scope, the API returns 403 invalid_scope. If you send a public key to an endpoint that requires a secret key, it returns 403 secret_key_required.
Idempotency
All write operations on financial endpoints require an Idempotency-Key header. This prevents duplicate transactions if a request is retried due to a network timeout or connection drop.
POST https://api.sogo.africa/v1/bills/airtime Authorization: Bearer sogo_sk_live_<your_key> Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 Content-Type: application/json
Key rules:
- Must be unique per operation. A UUID v4 generated client-side is the recommended format.
- Keys are scoped to your account - the same key used by two different accounts does not conflict.
- A key is permanently and uniquely bound to its transaction. No duplicate charge can ever be created regardless of when you retry - minutes, days, or years later.
- Replayed responses include an
Idempotency-Replayed: trueheader so you can distinguish them from new transactions. - If a request is currently in-flight with the same key, you'll receive a
409 idempotency_request_in_progress. Wait and retry.
Reconciliation: If a network error prevented you from receiving the response, use your original idempotency key to check the outcome - no retry needed:
GET https://api.sogo.africa/v1/transactions/{idempotency-key}?type=bill_payment Authorization: Bearer sogo_sk_live_<your_key>
A 404 means the transaction was never created and it is safe to retry with the same key. A 200 response contains the transaction - check the status field to determine next steps.
Rate Limiting
The API allows 300 requests per minute per live API key and 10 requests per minute per sandbox test API key. Exceeding this limit returns a 429 rate_limit_exceeded response. Implement exponential backoff when you receive a 429.
Monetary Amounts
All numeric amount fields in both requests and responses use major currency units (e.g. 1000 or 1000.00 for ₦1,000.00). Fields ending in _formatted are display strings only and should not be parsed.
This applies to request body amounts (e.g. amount on purchase endpoints), transaction response fields (amount.raw, fee.raw, net_amount.raw), and wallet response fields (balance, withdrawable_balance).
Note: wallets use minor units (kobo for NGN) internally. The API presents them in major units.
Integration Flows
Flow A - Bill purchase (standard)
Use this flow for all bill payment products: airtime, data, electricity, cable TV, education, ePIN, and bet funding.
- Discover providers. Call
GET /v1/bills/catalog(optionally with?type=) to get every supported provider, its slug, full name, and the endpoints available for that bill type. For products with variable plans (data, cable TV, education), follow up with the relevant listing endpoint (e.g.GET /v1/bills/data-plans?network=mtn) to retrieve available variation codes and pricing. - Verify the customer identifier. For electricity, cable TV, and JAMB products, call the corresponding verify endpoint to confirm the meter number, smartcard number, or JAMB profile before submitting payment.
- Generate a UUID client-side as your
Idempotency-Key. Store it before sending the request so you can use it for reconciliation if the response is lost. - Submit the purchase.
POSTto the relevant endpoint with theIdempotency-Keyheader and the validated plan details. - Handle the response. A
201response contains the transaction object. Checktransaction.status- the transaction may beprocessing(fulfilment in progress) orcompletedalready. - Track completion. Subscribe to
transaction.completedandtransaction.refundedwebhooks, or pollGET /v1/transactions/{reference}until the status is final. - Reconcile if no response was received. If a network error or timeout means you never received the API response, look up the outcome by your idempotency key before retrying:
AHTTP
GET https://api.sogo.africa/v1/transactions/{idempotency-key}?type=bill_payment Authorization: Bearer sogo_sk_live_<your_key>
404means the transaction was never created - safe to retry with the same key. A200response returns the transaction; checkstatusto decide next steps.
Flow B - Payment verification
Use this flow when you need to confirm that a payment already completed before fulfilling an order on your side (for example, a customer claims they paid via Sogo).
- Receive the transaction reference from your order flow or the customer.
- Call
POST /v1/transactions/verifywith the expectedreference,amount, andcurrency. - Gate fulfilment on
verified: truein the response. Afalseresult includes areasonfield explaining the mismatch (not_found,not_completed,amount_mismatch,currency_mismatch).
Transaction Status
Every transaction has a status field reflecting its current state. Understanding each status is essential for knowing when to fulfil orders and when it is safe to retry.
| Status | Meaning | Next action |
|---|---|---|
| processing | Request accepted; fulfilment is in progress with the provider. | Poll GET /v1/transactions/{reference} or wait for a webhook. Do not retry. |
| completed | Final success - service was delivered and the charge is settled. | No further action needed. Safe to fulfil your order. |
| failed | Final failure - no charge was made, or funds have already been refunded to the wallet. | Retry with a new Idempotency-Key. |
| refunded | The transaction was reversed and funds returned to the wallet. | Listen for the transaction.refunded webhook event. |
| cancelled | The transaction was explicitly cancelled. | Listen for the transaction.cancelled webhook event. |
Never retry on processing. The provider is actively fulfilling the request. Sending a new request with a different key risks a duplicate charge. Wait for completed or failed via webhook or polling.
Webhooks
Webhooks let Sogo push real-time transaction events to your server instead of you polling for status changes. Register up to 3 webhook endpoints from your profile's Developer tab.
Each endpoint can subscribe to one or more event types. When an event fires, Sogo makes a POST request to your URL with a JSON payload and several identifying headers.
Events
| Event | Trigger |
|---|---|
| transaction.completed | A transaction was successfully processed. |
| transaction.failed | A transaction failed after all processing attempts. |
| transaction.cancelled | A transaction was cancelled (e.g. admin action). |
| transaction.refunded | A transaction was refunded back to the customer's wallet. |
| ping | Sent when you click "Test" on a webhook endpoint. |
Example payload for transaction.completed:
{
"id": "evt_Xk9mQ2rNpL4sYv8F",
"event": "transaction.completed",
"created_at": "2026-05-09T08:30:00+00:00",
"data": {
"object": "transaction",
"id": "8f7243e0-49c7-4f7c-a7a8-2d4e3f7ac001",
"reference": "BILL202605090830A120123",
"type": "bill_payment",
"status": "completed",
"amount": 5000.00,
"fee": 50.00,
"net_amount": 4950.00,
"currency": "NGN",
"description": "Airtime - MTN (08061464003)",
"completed_at": "2026-05-09T08:30:00+00:00",
"created_at": "2026-05-09T08:28:00+00:00"
}
}
Verifying signatures
Every webhook request includes a signature so you can verify it genuinely came from Sogo. Check the X-Sogo-Signature-256 header before processing any payload.
| Header | Description |
|---|---|
| X-Sogo-Event | The event type, e.g. transaction.completed. |
| X-Sogo-Delivery | Unique delivery ID for this attempt (e.g. evt_abc123). |
| X-Sogo-Timestamp | Unix timestamp of when the delivery was sent. |
| X-Sogo-Signature-256 | HMAC-SHA256 of the raw request body, prefixed with sha256=. |
To verify: compute HMAC-SHA256(raw_body, webhook_secret) and compare it to the value after sha256= in the header. Use a constant-time comparison to prevent timing attacks.
$rawBody = file_get_contents('php://input');
$secret = getenv('SOGO_WEBHOOK_SECRET');
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
$received = $_SERVER['HTTP_X_SOGO_SIGNATURE_256'] ?? '';
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit;
}
$event = json_decode($rawBody, true);
Always return a 2xx quickly. Sogo marks a delivery as failed if your endpoint does not respond within 15 seconds. Process events asynchronously and return 200 OK immediately.
Error Codes
When a request fails, the API returns a structured error object. Every error includes a doc_url that links directly to the relevant entry below.
{
"error": {
"code": "authentication_failed",
"message": "Invalid or revoked API key.",
"doc_url": "https://developer.sogo.africa/docs/errors#authentication_failed"
}
}
No API key was provided. Send your key via Authorization: Bearer <key> or the X-API-Key header.
The API key is invalid, expired, or has been revoked. Check the key and regenerate if necessary.
The request was made over non-secure HTTP. All Partner API requests must be made over HTTPS. Plain HTTP requests are not supported.
You are using a live key on the sandbox endpoint, or vice versa. The key prefix must match the host you are calling.
The request originated from an IP address not on the key's allowlist. Update the allowlist in your profile if your server's egress IP has changed.
The API key does not have the scope required to access this endpoint. Create or update your key to include the necessary scope.
This endpoint requires a secret key (sogo_sk_*). Public keys cannot access it.
This endpoint requires an Idempotency-Key header. Generate a UUID client-side and include it with every write request.
A request with this idempotency key is already being processed. Wait a moment and retry.
You have exceeded the requests/minute limit for your API key (300 for live, 10 for sandbox). Implement exponential backoff before retrying.
Your account has been suspended. Contact support to resolve the issue before API access is restored.
Your account is deactivated. Log in to sogo.africa and reactivate it to restore API access.
The Partner API is temporarily unavailable. Check status.sogo.africa for updates.
The sandbox environment is temporarily unavailable. The live environment is unaffected.
The requested resource does not exist on this account. For transactions, check that the reference or idempotency key belongs to the authenticated account.
One or more request fields are missing or invalid. Check the errors object in the response for field-level details.
Your wallet balance is too low to complete this transaction. Top up your wallet and retry.
Customer identifier verification failed. The meter number, smartcard number, account ID, or JAMB profile could not be validated by the provider. Double-check the value and retry.
Your KYC Tier level is insufficient. KYC Tier 2 verification is required to use sandbox API keys, and KYC Tier 3 verification is required to use live API keys. Complete your verification from KYC & Limits in your profile.
KYB & Compliance business verification must be approved before creating live API keys. Submit your business documents from KYB & Compliance in your profile. Sandbox keys are unaffected.