Rollback
Reverse a previously registered bet
155 calls your wallet (signed with X-Marbles-Signature) to reverse a previously registered /bet — for example when a round is cancelled or errors out before settlement. You must refund the stake to the player's balance.
When This Fires
/rollback only fires before /win has been sent for a given bet. Once /win is delivered (regardless of amount, including the amount: 0 loss form), the round is terminal — no /rollback will follow.
Triggers for /rollback:
- The round was cancelled before settlement
- The player's bet timed out during chip placement
/betwas received and acknowledged, but a downstream error required us to reverse the debit before the round settled- Your
/betfailed from our side — it returnedUNKNOWN_ERROR(or any unrecognisedstatus), a non-200, or exceeded the 8-second deadline. We treat the bet as rejected and send a/rollbackfor that transaction to undo any debit that may have landed
You may receive a /rollback for a bet you never accepted
Because a timed-out or errored /bet also triggers a rollback, the referenceTransactionId may point at a transaction your wallet has no record of accepting — or one your wallet actually did debit even though the response never reached us. Handle both: if the bet was debited, refund it; if you have no record of it, return SUCCESS with no balance movement. Never treat an unknown referenceTransactionId as an error.
Not for Reversing Settlements
To reverse a granted-but-unused freebet, use POST /game/free-bets/rewards/cancel — not /rollback. See Freebets & Rewards.
Endpoint
POST /rollbackRequest
POST /rollback HTTP/1.1
Host: your.game.api
X-Marbles-Signature: <signature>
Content-Type: application/json
{
"requestId": "8df0475e-5069-483a-8205-f6089997abc9",
"transactionId": "9ea48131-3a0f-4067-94d0-3212e7e25abb",
"referenceTransactionId": "ea0240f5-483d-434b-a8d4-04dabf61cde3",
"clientSessionId": "0k3cz83bb3h2vn53ocnc7pxw9",
"clientPlayerId": "02mnrpyv2qd9jbwhoniyimxsy",
"roundId": "17cc81fd-df13-4ca4-857d-de0f766dc372",
"gameId": "f1c0b104-f29d-44a9-ae93-e8afcbe3feb9",
"roundClosed": true,
"timestamp": "2025-06-15T14:30:00Z"
}Request Fields
| Field | Type | Description |
|---|---|---|
requestId | string | Unique request identifier (UUID) |
transactionId | string | Identifier for this rollback attempt. Not stable between attempts of the same rollback — never use it as your dedupe key (see Idempotency) |
referenceTransactionId | string | The original bet transaction ID being rolled back — constant across attempts; this is your dedupe key |
clientSessionId | string | The player's session identifier |
clientPlayerId | string | The player's unique identifier |
roundId | string | Per-bet round reference — the same value this bet carried on /bet. Unique per bet, not per game round (see Round IDs) |
gameId | string | The game identifier |
isFree | boolean | true if the original bet was a freebet |
rewardUuid | string | The clientRewardId from the original freebet grant (only present when isFree is true) |
roundClosed | boolean | Whether this transaction closes the round (see below) |
timestamp | string | RFC 3339 UTC timestamp of when the request was created. Fractional seconds vary by callback — parse leniently, do not pin an exact format string |
Round Lifecycle
The roundClosed field indicates whether this is the final transaction for a round:
roundClosed: true- This is the final transaction. The round is cancelled and no more transactions will occur.roundClosed: false- Other bets in the same game round are still in flight (common in multi-chip rounds) — this rollback does not end the round.
Use roundClosed to finalize round records in your system. When true, you can safely mark the round as cancelled/voided for reporting purposes.
The Rollback-to-Zero Rule
Restore the stake — nothing more, nothing less
A /rollback refunds the stake so the balance returns to exactly what it was before the bet — restore the stake, never re-debit, never double-refund.
The net effect of /bet followed by /rollback must be zero: the player ends with the same balance they had before the bet was placed. The rollback payload carries no amount field — look up the bet identified by referenceTransactionId and refund its stake, then stop there. Do not deduct anything, and do not refund a stake that was already refunded — see Idempotency below.
Freebet Rollbacks
When the original bet was a freebet, the request includes isFree: true:
{
"requestId": "...",
"transactionId": "...",
"referenceTransactionId": "...",
"isFree": true,
"rewardUuid": "promo-winter-2025-001",
"roundClosed": true
}isFreeistrueto indicate the original bet was a freebetrewardUuidcontains yourclientRewardIdfor tracking which promotion this freebet belongs to- Pass
isFreeandrewardUuidthrough unchanged so the rollback maps to the correct grant - No balance refund needed (the original freebet had
amount: 0) — mark the freebet as cancelled in your records
Rolling back an already-settled freebet is treated as a duplicate and silently deduped — return SUCCESS, balance unchanged. See Freebets & Rewards for the full reward flow.
Response Contract
Always respond HTTP 200; put the outcome in status. We read the status field, not the HTTP code, to decide what happens to the rollback.
The full status set lives in Error codes — that is the canonical home for the enum across all callbacks. For /rollback the relevant outcomes are:
status | When you return it | What 155 does |
|---|---|---|
SUCCESS | Stake refunded (or already rolled back) | Rollback recorded |
DUPLICATE_TRANSACTION_ERROR | You already processed this rollback | Accepted exactly like SUCCESS — the rollback is recorded and not retried |
UNKNOWN_ERROR | Unexpected operator-side failure | 155 retries |
Never return a non-200 HTTP status or a bare error body — always respond 200 with one of the status values above. A non-200 is read as a transport failure and may trigger retries before the body is parsed.
The one exception is authentication: if you verify X-Marbles-Signature and it fails, reject the request with a non-2xx (e.g. 401) — that's an auth failure, not a rollback outcome. The status values above apply only to requests that passed signature verification. (A bad signature never occurs on genuine 155 traffic, so this can't affect real rollbacks.)
Idempotency
Networks retry, so 155 may resend a rollback it isn't certain landed. Dedupe /rollback on referenceTransactionId — the bet being refunded, which stays constant across every attempt. A replayed /rollback is treated as the original rollback: return plain SUCCESS with the balance unchanged — the stake was already restored once, and there is nothing more to refund.
Do NOT dedupe /rollback on `transactionId`
The per-request transactionId is not stable between attempts — two attempts to roll back the same bet can carry two different transactionId values with the same referenceTransactionId, and after a failed /bet you may receive several attempts within a few seconds. A wallet that dedupes rollbacks on transactionId refunds the same stake once per attempt. This is the opposite of /bet and /win, where transactionId is the dedupe key and is stable across retries.
The replay response matches /win — return SUCCESS — and is asymmetric with /bet, where a replay returns DUPLICATE_TRANSACTION_ERROR. But the dedupe key differs: /bet and /win dedupe on transactionId; /rollback dedupes on referenceTransactionId.
Worked Examples
Success — first /rollback
The stake is refunded and the player's balance returns to exactly what it was before the bet.
HTTP/1.1 200 OK
X-Marbles-Signature: <signature>
Content-Type: application/json
{
"status": "SUCCESS",
"requestId": "8df0475e-5069-483a-8205-f6089997abc9",
"clientPlayerId": "02mnrpyv2qd9jbwhoniyimxsy",
"currency": "USD",
"balance": 1000000
}Replayed /rollback — same referenceTransactionId
155 re-sent the rollback (note: the new attempt carries a different transactionId but the same referenceTransactionId). You return the original rollback result: still SUCCESS, balance unchanged (the stake was already refunded once — do not refund again).
HTTP/1.1 200 OK
X-Marbles-Signature: <signature>
Content-Type: application/json
{
"status": "SUCCESS",
"requestId": "8df0475e-5069-483a-8205-f6089997abc9",
"clientPlayerId": "02mnrpyv2qd9jbwhoniyimxsy",
"currency": "USD",
"balance": 1000000
}Response Fields
| Field | Type | Description |
|---|---|---|
status | string | "SUCCESS" |
requestId | string | Echo back the request ID |
clientPlayerId | string | Echo back the player ID |
currency | string | ISO 4217 currency code |
balance | int64 | Balance after rollback — equals the pre-bet balance, as an integer in the currency's wire precision (see Currencies) |
Error Response
HTTP/1.1 200 OK
X-Marbles-Signature: <signature>
Content-Type: application/json
{
"status": "UNKNOWN_ERROR",
"requestId": "8df0475e-5069-483a-8205-f6089997abc9",
"clientPlayerId": "02mnrpyv2qd9jbwhoniyimxsy"
}See the Response Contract above for what 155 does with each status, and Error codes for the canonical enum.