Idempotency & Safe Retries

Using reference_no to make transaction and disbursement creation safe to retry

View as Markdown

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.

1

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.

2

Persist it before the first API call

Save the reference_no to your own database before you send the request. If your process crashes mid-request, you still know which reference_no was in flight and can look it up rather than starting over with a new one.

3

Reuse the same value on every retry

Any retry of the same intent must send the identical reference_no. Never allocate a new one just to “try again.”

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:

1

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.

2

If it exists

The request succeeded. Use the returned status — do not send it again. Resubmitting the same reference_no will be rejected anyway.

3

If it does not exist

The request never landed. It is safe to resubmit with the same reference_no.

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.

Summary

SituationDo this
First attempt at a new payment/payoutGenerate a new reference_no, persist it, then send.
Retrying after a timeout or ambiguous errorReuse the same reference_no; look it up first to see if it already succeeded.
Genuinely new, separate transactionUse a new reference_no.
Confirmed the original succeededDon’t resend — the reference_no is already spent.