TchokoPay
API Reference

Charge a number

POST /v1/collect/charge — push a payment prompt straight to a phone.

POST/v1/collect/charge

Sends a mobile money prompt to a number you name, from your own checkout. No hosted page, no redirect — your customer stays where they are and approves on their phone.

Use this when you already have the customer's number and want to keep them in your own flow. If you'd rather not handle numbers or network selection at all, use the hosted checkout instead — it does the picking for you.

Request

curl -X POST https://connect.tchokopay.com/v1/collect/charge \
  -H "Authorization: Bearer tchoko_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4821-attempt-1" \
  -d '{
    "amount": 5000,
    "currency": "XAF",
    "country": "CM",
    "provider": "mtn_cm",
    "phone": "670000000",
    "merchantReference": "order_4821",
    "description": "Order #4821"
  }'

Body

FieldTypeDescription
amountnumberrequiredHow much to collect, in currency. Must sit inside that currency's bounds — see limits.
currencystringrequiredAny currency from GET /v1/limits.
countrystringrequiredISO country of the number. See GET /v1/countries.
providerstringrequiredWhich network to charge. A code from GET /v1/account/providers.
phonestringrequiredThe number to charge, in local format.
merchantReferencestringoptionalYour own identifier, echoed back everywhere.
descriptionstringoptionalShown to the payer where the network supports it.

provider is required, and we check it against the number. If they disagree, the request is refused before any prompt is sent — so a mistyped number costs you a 400 you can show your customer, not a payment that silently never arrives.

Response

{
  "reference": "REQ-1788042194995-AB9B94",
  "status": "PENDING",
  "amount": 5000,
  "currency": "XAF",
  "provider": "mtn_cm",
  "merchantReference": "order_4821",
  "createdAt": "2026-08-29T22:23:15.010Z"
}

202 Accepted, and it returns immediately — we do not hold the connection while the network thinks about it. Nobody has paid at this point. The prompt is on its way to the phone.

Watch for the result one of two ways:

  • A webhook — collect.succeeded or collect.failed. Recommended; you find out the moment it resolves. See webhooks.
  • Polling GET /v1/collect/{reference}. Every few seconds is plenty; the payer has minutes to approve.

What can come back

The refusals below happen before any prompt is sent, so nothing reaches your customer and nothing is charged.

codeWhy
INVALID_REQUESTThe number isn't one we recognise in that country, doesn't match provider, or the amount is outside the currency's bounds.
FORBIDDENThat provider isn't enabled for your account, or this number has been sent too many prompts recently.
UNAUTHORIZEDKey missing, invalid, or revoked.

Once the prompt has gone out, the outcome arrives as a status on the collection rather than an error here — see the status lifecycle.

Sending the same charge twice

Send an Idempotency-Key and a repeat returns the original charge instead of prompting your customer again. Strongly recommended — without one, a retry after a network blip puts two prompts on your customer's phone for a single order.

How often you may prompt one number

There is a limit on how many prompts one number can receive in a short window. Exceed it and you get a FORBIDDEN saying so.

You will not hit this in normal use — it takes retrying the same customer in a loop, which an Idempotency-Key already prevents.

On this page