Skip to main content
Enable local rails for the currencies you want to accept in your Axra dashboard before calling this endpoint.

Create a collection

POST /business/collections Create a collection and return deposit instructions for the customer. Amounts are in major local currency units (for example, 10000 NGN). customer.name and customer.email are optional. Both are stored on the collection record for your own reconciliation and shown in your Axra dashboard; they are not forwarded to the underlying payment provider. customer.phone is required for mobile money — it is forwarded to the provider so the deposit prompt reaches the right number. customer.networkId is optional but strongly recommended on mobile money. It names the payer’s operator — MTN, Orange, Wave — and comes from networks[] on GET /business/local-rails/channels. On a corridor with more than one operator the payer is on exactly one of them, and a collection sent to the wrong one fails. If you omit networkId we infer the operator from customer.phone, which is correct for the overwhelming majority of numbers but cannot detect a ported number — one that has moved between operators and kept its original prefix. If your checkout already asks the payer which wallet they use, send it and we will use your answer instead of guessing. If you send a networkId that isn’t currently routable for the corridor we reject the collection with a 400 and the code NETWORK_NOT_ROUTABLE, rather than quietly routing the payer to a different operator. An explicit networkId is treated as a fact about the payer, not as a hint.

Reading channel on the response

Every collection response carries a channel object with two ids that are not interchangeable:
  • channel.networkId — the operator network we routed this collection to. This is the value customer.networkId echoes back, and the one to quote when asking us about routing.
  • channel.channelId — the underlying provider’s own internal channel id. It is null whenever the provider gave us none, which is common on bank and hosted-page collections. Treat it as opaque: it is not an operator, and it is never a substitute for networkId.

Bank transfer (NGN)

Response

Mobile money (KES — M-PESA)

customer.phone is required for MoMo collections. KE resolves to a single operator, so customer.networkId is unnecessary here. On a multi-operator corridor — Cameroon (MTN / Orange), for example — send it. See Choosing the right mobile-money operator.
Response
Do not assume rail: "momo" means kind: "momo_prompt". Some mobile-money providers confirm on a hosted page instead of an on-device push. In Côte d’Ivoire and Senegal, Wave returns kind: "hosted_page" on a momo rail, and the payment cannot complete unless you send the customer to instructions.url. Always branch on instructions.kind, never on channel.rail.
After paying, the customer is returned to the URL you set once in Settings → Payer return URL, or via PUT /v1/business/config (returnUrl). It must be https on your verified business website domain. If you leave it empty, customers see Axra’s payment confirmation page.
Per-request successUrl on collections is deprecated and ignored. Set the return URL on the business, not on each collection.
Response
Handle both shapes with one branch:

Instant EFT (ZAR — Ozow)

For hosted-page rails, redirect the customer to instructions.url. After they pay, they return to your configured payer return URL (or Axra’s confirmation page if none is set). Per-request successUrl is deprecated and ignored.
Response

Handle the completion webhook

Fulfill orders when Axra delivers collection.completed, not when you return deposit instructions to the customer.
  1. Subscribe to collection.completed on your webhook endpoint (or include it in the endpoint event list).
  2. Verify X-Axra-Signature against the raw request body using your webhook secret. See Webhooks.
  3. Read data.object — the payload mirrors GET /business/collections/{id}.
  4. Mark the order paid and release goods or services only after status is completed.

Channel discovery

List channels before creating a collection when your UI needs to show available rails dynamically, or to discover the per-channel minimum and maximum amounts.
Each channel in the response includes min and max in the local currency’s major units. min: 0 means no lower bound; max: 0 means no upper bound. Filter or sort on these in your UI so customers don’t see rails their amount doesn’t qualify for.

Choosing the right mobile-money operator

networks[] lists the mobile-money operators available on the corridor. A channel is not an operator — a channel is how we route, an operator is the wallet the payer actually holds money in. Where networks[] returns more than one entry, the payer is on exactly one of them, and a collection sent to the wrong operator fails at the provider. Two ways to handle it:
  1. Ask the payer (recommended). Show the operator names from networks[], and pass the chosen networkId as customer.networkId on create. This is the only approach that is correct for a ported number, where the phone prefix no longer indicates the operator.
  2. Let us infer it. Omit customer.networkId and we derive the operator from customer.phone. Correct for the overwhelming majority of numbers, but blind to porting.
rail on a networks[] entry is optional and is omitted when the provider does not tell us which rail the network runs on. A missing rail does not mean the network is unusable — it means we will not guess. Until 2026-09-10 every entry was reported as momo, which advertised bank networks as mobile-money operators; treat an absent rail as “unknown”, not as “not mobile money”. Operator availability changes without notice — read networks[] per corridor rather than hard-coding IDs.

Mobile money on a multi-operator corridor (XAF — Cameroon)

Cameroon carries both MTN and Orange. Read networks[] from GET /business/local-rails/channels, let the payer pick, and pass the chosen networkId:
Omit networkId and we infer MTN from the 672 prefix. Send it and we use your value — which is the only way to be right when the payer has ported their number between operators.

Minimums and maximums

Local-rail channels have provider-side minimum (and sometimes maximum) amounts. If you call POST /business/collections with an amount that doesn’t fit any channel for the requested rail and country/currency, you get a 400 with the actual minimum surfaced:
These limits are set by our settlement partners per corridor and change without notice — always read them from /business/local-rails/channels rather than hard-coding values in your client.

Idempotency

Pass idempotencyKey on create to dedupe retries within one hour. A duplicate key returns the original collection instead of minting a second virtual account.

Polling

Webhooks are the source of truth. If you must poll, call GET /business/collections/{id} about every five seconds while status is pending or processing. Use GET /business/collections/{id}/refresh to force a provider resync before giving up.

Status reference

Error codes

API Reference

Full endpoint documentation.

Payer return URL

Set the URL customers return to after paying. Collections successUrl is ignored.