Transactions and statuses
The transaction types, the three statuses, which transitions are possible, and how to resolve a PENDING transaction.
Every movement of money is a Transaction: one row, one id, one status. Whatever endpoint created it, you read it back the same way and it obeys the same state rules.
Transaction types
| Type | Created by | Direction |
|---|---|---|
COLLECTION |
POST /transactions/collect, hosted checkout |
Money in |
PAYOUT |
POST /transactions/disburse and its payout-credit-transfer alias |
Money out |
WITHDRAWAL |
POST /transactions/withdraw |
Money out, to your own settlement account |
PAY_BILL |
POST /transactions/pay-bill, pay-bill-ext |
Money out, to a biller |
SEND_MONEY |
POST /transactions/send-money |
Two-leg: collect then disburse |
MONEY_REQUEST_DISBURSEMENT |
The Request Money flow | Money out |
The three statuses
| Status | Means | What you should do |
|---|---|---|
PENDING |
Dispatch is in flight, or awaiting confirmation from the rail. We do not know yet. | Wait for a webhook, or reconcile. Do not assume either outcome. |
SUCCESS |
Confirmed by the rail. Money arrived. | Fulfil. |
FAILED |
Rejected, or confirmed failed. Money did not leave. A ledger debit taken for it is returned to your balance. | Do not fulfil. Retry with a new reference if you still want to pay. |
There is no fourth value. If you ever see something not on this list, treat it as PENDING -
unknown, resolve it before acting.
Terminality
Note
For
PAYOUTandWITHDRAWAL,SUCCESSandFAILEDare terminal statuses: once one of these resolves, reconciliation does not change it, and you can build on it as final.
The status records what the rail told us. A bank or network can occasionally recall or return a payment after the fact, outside the API. That is rare, it is handled with you directly through support, and it never silently rewrites a status you have already read. Other flows have their own reconciliation logic; if you need to rely on terminality for a specific path (a bill payment, say, rather than a direct payout), ask us to confirm it.
Why mobile money is usually PENDING first
A wallet collection needs the payer to approve a prompt on their handset. We return as soon as the
request is accepted by the rail, which is before the human has touched anything - so PENDING is
the normal, healthy first response, not a warning sign.
Disbursement varies by destination. Many payouts resolve in the same request, but some wallet
networks confirm later, so a payout can also come back PENDING. Handle both on every
destination rather than assuming one per network.
Resolving a PENDING transaction
Webhooks first. Configure a webhookUrl and we push the outcome as soon as we have it. This
is the lowest-latency, lowest-cost option and it is what production integrations should rely on.
See Webhooks overview.
Reconcile when you need to ask. Each flow has a reconcile endpoint that re-checks the rail and updates our record:
| Flow | Endpoint |
|---|---|
| Collection | POST /transactions/collect/{id}/reconcile |
| Payout | POST /transactions/payout-credit-transfer/{id}/reconcile (alias: /transactions/disburse/{id}/reconcile) |
| Withdrawal | POST /transactions/withdraw/{id}/reconcile |
| Bill payment | POST /transactions/pay-bill/{id}/reconcile, POST /transactions/pay-bill-ext/{id}/reconcile |
| Send money | POST /transactions/send-money/{id}/reconcile |
Read without re-checking. GET /transactions/find/{id} returns our stored state. Use this
once a webhook has already told you the transaction resolved - it is cheaper than a reconcile and
does not touch the rail.
Tip
Reconcile on a backoff - 10s, 30s, 2m, 5m, then every 15 minutes - rather than in a tight loop. Combine it with webhooks: webhooks handle the normal case, the reconcile sweep catches anything whose delivery you missed.
Querying
You query with our id, returned in the response that created the transaction. There is
currently no lookup by your own reference - persist the id.
| Endpoint | Returns |
|---|---|
GET /transactions/find/{id} |
The transaction |
GET /transactions/details/{id} |
The transaction with expanded detail |
GET /transactions/{id}/journey-snapshot |
Step-by-step history of what happened and when |
GET /transactions/list |
Your transactions, paginated and filterable |
journey-snapshot is the first thing to pull when something looks wrong - it shows each stage of
the journey with timestamps, and it is the most useful thing to attach to a support ticket.
GET /transactions/{id}/send-money-snapshot is an older name for the same endpoint. It still
works, but use journey-snapshot in new code.
Filtering the list
| Query parameter | Filters on |
|---|---|
pg, ipp |
Page number and items per page |
from, to |
Creation date range |
status |
PENDING, SUCCESS or FAILED |
type |
A transaction type from the table above, e.g. PAYOUT |
recipientStatus |
The recipient-side status |
q |
Free-text search over the description and the recipient's name, email, country and number |
Receipts
GET /transactions/receipts/{id} returns the receipt for a SUCCESS transaction, where {id} is
the transaction id. It needs the TRANSACTIONS_READ scope. A transaction that is PENDING or
FAILED has no receipt yet.
Distinguishing "never seen" from "failed"
| You get | Means |
|---|---|
404 Transaction not found |
We have no record of this id. Nothing was created. |
200 with data.status: "FAILED" |
It exists and definitively failed. |
They are distinguishable by HTTP status code, not by the body alone.
Next
Amounts and charges - which amount field is which.