Home/Developers
Reseller API v1

Build on PowerWave

Sell airtime, data and bills, collect payments and send payouts from your own website or app with the PowerWave API. Start here: access, authentication, errors and limits.

Every code block, endpoint and section has a Copy button.

Getting started

The PowerWave API is organised in four parts. Pick the one you need - each has its own page with copy-ready requests and responses.

Base URL: https://api.powerwave.ng/v1 · JSON in, JSON out · HTTPS only · live keys only (every purchase, payment and payout is real) - test with small amounts.

Getting access

  1. Create an account and verify to Tier 2 (Tier 3 for Payments and Payouts).
  2. Open the Reseller page and add a reseller business (one per website or app). Save it as a draft and finish it later if you like, then submit it - there is no separate application. Our team reviews each business, and the first one approved makes you a reseller.
  3. Open the business → API & webhooks, create a key (shown once) and optionally lock it to your server's IP. Each key belongs to one business: its references, checkouts, customers, webhooks and /transactions are kept separate from your other businesses. All businesses share your one wallet.
  4. Fund your wallet - purchases and payouts are paid from it.
  5. Payouts only: ask support to approve payout access, then create a separate payout key (Reseller → Payout API) with your server IPs. See Payouts.

Authentication

Send your key in the Authorization header on every request. Never put a key in a website, mobile app or anything a customer can download - call the API from your server only.

KeyLooks likeUsed for
Reseller API keypw_live_ + 40 charactersPurchases, Payments, /transactions. Per business, optional IP lock.
Payout keypw_payout_ + 40 charactersPayouts only. One per reseller, IP allow-list required.
curl https://api.powerwave.ng/v1/ping \
  -H "Authorization: Bearer pw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Requests & responses

POST bodies are JSON (Content-Type: application/json). Every response has the same shape:

{
    "success": true,
    "message": "Purchase successful.",
    "data": {
        "…": "…"
    }
}
{
    "success": false,
    "message": "customer_email is required (the customer's email address).",
    "data": {},
    "error": {
        "code": "validation_error"
    }
}

Amounts are strings in naira with 2 decimals. Dates are ISO 8601 in West Africa Time (+01:00). Responses are never cached.

References & safe retries

Every purchase, checkout and payout needs a reference that is unique in your system (letters, numbers, . - _) - use your own order ID.

  • Same reference + same body (e.g. after a timeout): you get the original result back, header Idempotent-Replay: true. You are never charged twice.
  • Same reference + different body: 409 idempotency_conflict.
  • If nothing was charged (validation error, insufficient balance) the reference is released and you can retry with it.

Errors

HTTPerror.codeMeaning
400invalid_jsonBody is not valid JSON.
401unauthorizedMissing, malformed, invalid or revoked key.
402insufficient_balanceWallet balance too low - nothing was charged. Fund your wallet and retry with the same reference.
403ip_not_allowed / reseller_inactive / business_inactive / api_access_revoked / tier_too_low / account_restrictedYour key is valid but not allowed right now (business_inactive: that reseller business is a draft, pending, rejected or suspended).
403gateway_unavailablePayments need an approved reseller business at Tier 3 with payments switched on.
404not_foundUnknown endpoint or reference.
409idempotency_conflictThat reference was used for a different request.
409request_in_progressThat reference is still processing - poll the GET endpoint for it.
422validation_errorA field is missing or invalid; message says which (for example customer_email is required).
429rate_limitedToo many requests; see Retry-After.
200transaction_failedThe purchase was processed but failed at the provider; it has been refunded to your wallet (success=false, data.status=failed).
500 / 503server_error / service_unavailableOur side or a service is down. For purchases and payouts, check the reference before retrying.

Rate limits

Each key allows 60 requests per minute by default (header X-RateLimit-Limit). Above it you get 429 with Retry-After. The payout key allows 30 per minute. Need more? Contact support.

Go-live checklist

  • Keys stored server-side (environment variables or a secrets manager), never in client code or git.
  • Keys locked to your server's IP address(es) (required for the payout key).
  • A unique reference for every order; on timeouts, retry with the same reference.
  • Handle pending / processing: wait for the webhook or poll - never buy or pay out again.
  • Webhook signatures verified and events de-duplicated by id. See Webhooks.
  • Payments: always send the customer's email (and phone for customer account numbers); deliver only after checkout.paid is verified.
  • Low-balance monitoring with GET /balance, topped up through GET /funding-account.

Questions? Contact us.