Developers

API wey your team go really enjoy

REST wey you fit predict, idempotency keys, signed webhooks and sandbox wey get simulated rails, so you fit integrate everything before even one franc move.

Open account
We build am for developers

Your first collection na one request

Clean REST, JSON envelope wey you fit predict, and request id on every response. The sandbox be exactly like production: same API, same statuses, same webhooks.

  • Sandbox and production keys, scoped per app
  • HMAC-signed webhooks with replay protection
  • Idempotency keys on every call wey dey move money
  • Typed errors wey go tell you exactly wetin to fix
  • OpenAPI spec, Postman collection and llms.txt, all don publish
  • Hosted checkout sessions and flat callbacks for CRM, ERP and shop connectors
POST /v1/collections
curl https://api.capayglobal.com/v1/collections \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: 9f1c-collect-01" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "2500",
    "currency": "XAF",
    "country": "CM",
    "method": "mobile_money",
    "msisdn": "2376XXXXXXXX",
    "merchant_reference": "order-1042"
  }'
201 Created
{
  "status": "success",
  "data": {
    "id": "col_9xKQ2mD41Vb8",
    "object": "collection",
    "status": "pending",
    "amount": "2500",
    "currency": "XAF",
    "country": "CM",
    "method": "mobile_money",
    "merchant_reference": "order-1042",
    "fees": {
      "platform_fee": "50",
      "provider_fee": "0",
      "total_fee": "50",
      "bearer": "merchant"
    },
    "net_amount": null,
    "livemode": false,
    "created_at": "2026-08-30T12:04:11Z"
  },
  "request_id": "req_b0f3d2c1"
}

Your first collection

One request dey create payment intent, dey return request id and, for mobile money, dey push the prompt go the payer phone. Sandbox dey behave exactly like production.

Collect your test keysCreate account and your sandbox keys go dey your developer page immediately. Production keys go open after business verification.
POST /v1/collections
curl -X POST https://api.capayglobal.com/v1/collections \
  -H "Authorization: Bearer sk_test_XXXXXXXXXXXX" \
  -H "Idempotency-Key: col-order-10482-a1b2c3" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "5000",
    "currency": "XAF",
    "country": "CM",
    "method": "mobile_money",
    "msisdn": "+237650000001",
    "merchant_reference": "order-10482"
  }'
Response
{
  "status": "success",
  "data": {
    "id": "col_7Qm3k1Xa9Zr4",
    "object": "collection",
    "status": "pending",
    "amount": "5000",
    "currency": "XAF",
    "auth_model": "STK_PROMPT",
    "livemode": false
  },
  "request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
201 Created
Amount na decimal string for the API boundary and minor units for the ledger, so nothing dey lost. Every call wey dey move money dey take idempotency key and every response dey carry request id wey you fit quote give support.

Server SDKs

Thin, typed wrapper on top of the same REST API, with idempotency and signature check already inside.

TypeScript and NodeWe go publish am soon@capay/sdk
Import
import { Capay } from '@capay/sdk';

E never dey npm. The import line for up na the final one, so your integration no go change when dem publish am.

Read the guide
PythonWe go publish am sooncapay
Import
from capay import Capay

E never dey PyPI. The import line for up na the final one, so your integration no go change when dem publish am.

Read the guide
PHPWe go publish am sooncapay/capay-php
Import
use Capay\CapayClient;

E never dey Packagist. The import line for up na the final one, so your integration no go change when dem publish am.

Read the guide

Verify every webhook

We dey compute signature on the raw body and compare am for constant time, and any timestamp older than 300 seconds we go reject am. Never trust webhook wey you never verify.

X-Capay-Signature
# T is the t= value from X-Capay-Signature; delivery.json is the raw body as received.
T=1767225600
SECRET=whsec_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

{ printf '%s.' "$T"; cat delivery.json; } \
  | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1
# Compare the printed hex with each v1= value in the header (either may match
# during a secret rotation), and reject the delivery if |now - T| > 300 s.
Signature header
X-Capay-Signature: t=1767225600,v1=6f8b4c2d9e0a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9e1f0a2b4c6d8e0f1a3b5c
X-Capay-Event-Id: evt_2n4Kq8Zr41Xa
X-Capay-Event-Type: collection.completed
Delivery and retries

Delivery wey fail dey retry on fixed schedule: 1m, 5m, 30m, 2h, 6h, 12h, 24h, 24h. Endpoint wey fail 8 times back to back go disable and we go tell you, so dead endpoint no go quietly swallow events.

Two signing secrets fit dey active same time, so you fit rotate without losing event. Reject anything outside the 300 second window and store the event id so replay no go do damage.

The rest of the kit

Specs wey machine fit read, collection wey you fit import, and the tools wey dey read dem.

llms.txtThe whole documentation flatten for coding assistant, so your AI go answer about our API from our own text.OpenAPI specThe full contract, generated from the API. Import am build client for any language.Postman collectionEvery endpoint with example body and environment variable, ready to run against sandbox.
Command lineE dey comeCreate test payments, tail events and replay webhook to your own machine.@capay/cli
MCP serverE dey comeGive your coding assistant read access to your sandbox so e go answer with your real data.@capay/mcp
Checkout scriptE dey comeOpen the hosted checkout inside modal for your own page instead of redirect.https://pay.capayglobal.com/capay.js

Start to collect payments all over Africa today

Open free account, build with the sandbox and go live once we verify you.

Open account

Anytime you ready, tell us about your corridors and volumes.