Skip to content

Sales ​

Every sale is the same two calls, whatever the product: airtime, data, ZESA, bills or vouchers.

Quote, then sell ​

  1. POST /quotes with product, recipient and, for ranged products, amount (USD) or face_value. The quote:
    • checks the recipient with the provider: a meter's name, a biller account;
    • prices the sale for you: face_value, fee and total_debit (what leaves your balance);
    • checks your balance and limits;
    • holds the price for 5 minutes.
  2. POST /transactions with quote_id, your reference and an Idempotency-Key header. Money is reserved and the sale is sent.

A quote funds exactly one sale. Using it twice answers 409 QUOTE_USED; after 5 minutes it's 409 QUOTE_EXPIRED.

Statuses ​

StatusMeaningWhat you do
pendingSent, outcome not final yet. Your money is reserved.Wait for the webhook or poll. Never buy again.
completedDelivered.Give your customer fulfilment (token, PIN, units).
failedNot delivered. Your money is back.Tell your customer; they can try again with a new quote.
reversedWas completed, then the provider undid it. Your money is back.Contact your customer.

Retrying safely ​

Every sale needs a reference (your own id, e.g. your order number) and an Idempotency-Key (a UUID you generate). Together they make retries safe:

  • Same key, same reference: you get the original sale back with 200 and Idempotency-Replayed: true. Nothing is sold twice.
  • Same key or reference with different details: 409 IDEMPOTENCY_CONFLICT. Use a new reference for a new sale.

So if a request times out, or your server crashes mid-call, send exactly the same request again.

Receipts ​

Completed sales return receipt: everything you need to print for your customer (product, recipient, amount, token or PIN, units, Xash and provider references, time). You issue the receipt to your customer.

Branches and tills ​

Send branch_id (one of your branch codes) and terminal_id on the quote to tag the sale. An API key limited to a branch tags every sale with it automatically. The portal and GET /transactions?branch_id= report by branch. You have one balance for all branches.

Limits ​

Sandbox has no limits. Live sales are capped per sale and per day by your tier; see Going live. Over a limit you get 409 LIMIT_EXCEEDED with error.limit naming which one.

Xash Partners API. Sandbox keys never move real money.