Idempotency & Safe Retries
Idempotency & Safe Retries
Using reference_no to make transaction and disbursement creation safe to retry
Creating money-movement requests — Generate Transaction (Payments API) and Process Disbursement (Disbursement API) — is the one place where a careless retry can cost real money. This page explains how reference_no protects you and how to retry safely when a request fails or times out.
reference_no Is Your Idempotency Key
Both write endpoints require a reference_no that you supply:
POST /v1/biller/transactions/generate(Generate Transaction)POST /v1/api/disbursement/process(Process Disbursement)
A reference_no can be used once. After it has been used, the server will not process that same reference_no again — a repeat submission does not create a second transaction or a second payout. Creating a genuinely new, separate transaction requires a new reference_no.
Because you choose the value and it can only be used once, reference_no acts as a natural idempotency key. You do not need a separate idempotency header — the business key you already send is the guarantee.
This is what protects you from double-charging a customer or paying a recipient twice. It only works if you keep the same reference_no across retries of the same intent. Generating a fresh reference_no for a retry defeats the protection and can result in a duplicate payout.
Generate reference_no Once, Persist It Before the Call
Treat reference_no as belonging to the intent to pay, not to an individual HTTP request.
Generate it once per intent
Create a single, unique reference_no when the payment or payout is first decided — one value per customer checkout or per payout instruction.
The Safe-Retry Rule
When a create call fails ambiguously — a timeout, a dropped connection, or a 5xx where you never saw a response — you don’t know whether the server processed it. Do not guess.
On an ambiguous failure, do not resubmit with a new reference_no, and do not assume the first attempt failed. Either mistake can create a second, real payout.
Instead, resolve the uncertainty by looking up the original reference_no:
Look up the reference_no
Query the transaction by its reference_no (see Looking Up Transactions) to find out whether the first attempt actually went through.
Prefer webhooks for the outcome itself. Configure callback_webhook_url (see Webhooks & Redirects) so you’re notified when the transaction settles, and treat the reference_no lookup as the tool for resolving “did my create call land?” after an ambiguous failure.