Schemas

Every object the API sends, field by field. Times are RFC 3339 in UTC; amounts are whole numbers of the currency's smallest unit (RWF has none, so 5000 is RWF 5,000).

Envelopes

Every successful answer is wrapped the same way; the object you asked for is in payload.

Success
{
  "success": true,
  "payload": { … },
  "timestamp": "2026-09-12T09:14:02.000Z"
}
Error
{
  "status": "error",
  "message": "amount must be a positive whole number of minor units",
  "timestamp": "2026-09-12T09:14:02.000Z"
}

Check the HTTP status first, then message. Errors and limits lists them.

Payment

One movement of money: a collection, a payout or a refund. Returned by POST /v1/payments, POST /v1/payouts, POST /v1/payments/:id/refund, GET /v1/payments/:id, and inside every payment.* and payout.* webhook.

FieldTypeNotes
iduuidOurs. Store it against your order.
merchant_iduuidYour business.
app_iduuidThe app whose key made it.
modestringtest or live, from the app.
directionstringcollect (money in) or payout (money out). A refund is a payout.
kindstringCASHIN or CASHOUT: the same as direction, for those who prefer it.
amountintegerWhat the customer paid or received.
currencystringRWF
feeintegerOur fee on this payment, at the rate that applied when it happened.
msisdnstringThe customer's number, with the country code: 250781234567.
networkstringmtn, airtel or unknown.
descriptionstringYours. Shown to the customer on the prompt where the network allows.
referencestring | nullYours: your order number. Comes back in webhooks.
metadataobject | nullYours, returned untouched.
statusstringpending, succeeded, failed or cancelled.
failure_codestring | nullWhy it failed, as a code, when the network gave one.
failure_messagestring | nullWhy it failed, in words you can show the customer.
xendly_referencestringOur reference to quote to us, e.g. XP-9F1C-2D3E-4F5A.
initiated_bystringapi or dashboard.
reverses_payment_iduuid | nullOn a refund: the collection it gives back.
created_attimestamp
updated_attimestamp
completed_attimestamp | nullWhen it reached a final status.
settled_attimestamp | nullWhen a collection became withdrawable. Null while it matures.
Example
{
  "id": "0f8c2b7e-1a45-4c31-9d02-6e8f1b2c3d44",
  "merchant_id": "7c1e…",
  "app_id": "a3f1…",
  "mode": "live",
  "direction": "collect",
  "kind": "CASHIN",
  "amount": 5000,
  "currency": "RWF",
  "fee": 195,
  "msisdn": "250781234567",
  "network": "mtn",
  "description": "Order #1043",
  "reference": "1043",
  "metadata": { "order_id": "1043" },
  "status": "succeeded",
  "failure_code": null,
  "failure_message": null,
  "xendly_reference": "XP-9F1C-2D3E-4F5A",
  "initiated_by": "api",
  "reverses_payment_id": null,
  "created_at": "2026-09-12T09:14:02Z",
  "updated_at": "2026-09-12T09:14:37Z",
  "completed_at": "2026-09-12T09:14:37Z",
  "settled_at": null
}

Payment list

GET /v1/payments answers a page of payments and how many match in all.

FieldTypeNotes
paymentsPayment[]Newest first.
totalintegerHow many payments match the filters, across every page.

Checkout session

POST /v1/checkouts answers this. Open url (or pass token to the widget) to let the customer pay.

FieldTypeNotes
iduuidThe checkout.
tokenstringWhat the widget opens. Single use, not a secret worth guarding: it can only pay this checkout.
urlstringThe hosted payment page for this checkout.
amountinteger
currencystringRWF
modestringtest or live.
statusstringopen when created.
expires_attimestampThirty minutes after creation.

Checkout

Inside every checkout.* webhook: the session once nothing more can happen to it.

FieldTypeNotes
iduuid
merchant_id, app_iduuid
modestringtest or live.
amount, currencyinteger, string
descriptionstring
referencestring | nullYours.
metadataobject | nullYours.
msisdnstring | nullThe number it was paid from, or fixed to.
success_url, cancel_urlstring | nullWhere the page sent the customer.
statusstringopen, paid, failed, cancelled or expired.
attemptsintegerPayment attempts made; at most 3.
payment_iduuid | nullThe payment that paid it.
expires_attimestamp
created_attimestamp

Balance

GET /v1/balance, for the app's mode.

FieldTypeNotes
currencystringRWF
modestringtest or live.
pendingintegerCollected but still maturing.
availableintegerYours to withdraw now.
Example
{ "currency": "RWF", "mode": "live", "pending": 24500, "available": 180250 }

Webhook event

What we POST to your webhook URL, signed in X-Xendly-Signature (see Webhooks).

FieldTypeNotes
typestringWhat happened (below).
dataPayment | CheckoutThe object as it is now.
typedataWhen
payment.succeededPaymentA collection went through.
payment.failedPaymentIt did not: declined, wrong PIN, no funds, timed out.
payment.cancelledPaymentThe customer or the network cancelled it.
payout.succeededPaymentA payout or a refund reached the customer.
payout.failedPaymentA payout or refund did not go through; the money stays in your balance.
payout.cancelledPaymentA payout or refund was cancelled.
checkout.paidCheckoutA checkout was paid.
checkout.failedCheckoutEvery attempt failed.
checkout.cancelledCheckoutThe customer gave up.
checkout.expiredCheckoutThirty minutes passed unpaid.

payment.* fires per attempt

A checkout allows several attempts, so a payment.failed inside one is not the end. To decide an order's fate, listen for checkout.*, which fires once.