Idempotency & retries
How we retry, and what your wallet must do so that a retry never moves money twice.
- The same transaction_id twice is the same movement once. Answer the second call with the body of the first and touch nothing. A retry carries the same body, with a new timestamp and signature.
- Only silence is retried. A 4xx is an answer; asking again after being told no would turn one refusal into several attempts at the same balance.
- A rollback for a movement you never saw is answered with the current balance, and the id is remembered as cancelled. If the bet arrives afterwards, refuse it.
- Currency is fixed by the session. A mismatch is ours to fix, not yours to convert.
- Answer fast. The player is waiting on the spin. Aim for well under a second and do the bookkeeping asynchronously.
Retry timeline
silence on a bet or win
t = 0 s attempt 1 → timeout (your wallet_timeout, 10 s by default, 1–60 s)
+ 2 s attempt 2 → timeout
+ 8 s attempt 3 → timeout
rollback transaction_id "<id>:rollback", reference_transaction_id "<id>"
the movement is recorded as failed; the provider is told the wallet is unreachable| Your answer | Retried? |
|---|---|
| 2xx with a string balance | no: done |
| 4xx except 408 and 429 | no: refused |
| 5xx, 408, 429 | yes |
| No answer within the timeout, connection error | yes |
| 2xx whose body cannot be read | yes |
Our own rollback is best effort. If it too goes unanswered, the provider's later rollback or our reconciliation settles the round; GET /v1/transactions/{id} shows the final status.
A round, as you will see it
sequence
bet transaction_id "pragmatic:5501934" round 7200311 amount "1.50" → balance "98.50"
win transaction_id "pragmatic:5501935" round 7200311 amount "3.00" → balance "101.50"
rollback transaction_id "pragmatic:rb-8812" reference "pragmatic:5501934" → balance "103.00" (only if the provider cancels)Ids
Every id starts with the game provider's code so that two providers counting from one never collide in your books.
