155API

Bet

Register a player bet


Called when a player places a bet. You must deduct the bet amount from the player's balance.

Endpoint

POST /bet

Request

POST /bet HTTP/1.1
Host: your.game.api
X-Marbles-Signature: <signature>
Content-Type: application/json

{
  "requestId": "8df0475e-5069-483a-8205-f6089997abc9",
  "transactionId": "ea0240f5-483d-434b-a8d4-04dabf61cde3",
  "clientSessionId": "0k3cz83bb3h2vn53ocnc7pxw9",
  "clientPlayerId": "02mnrpyv2qd9jbwhoniyimxsy",
  "roundId": "17cc81fd-df13-4ca4-857d-de0f766dc372",
  "gameId": "f1c0b104-f29d-44a9-ae93-e8afcbe3feb9",
  "amount": 1000000,
  "currency": "USD",
  "meta": {
    "betType": "PickWinner",
    "selection": [
      "40e5e0eb-cc5a-4a81-a157-7a4b641c05df",
      "17fcb2c6-f0ac-4855-98ef-9e7e3fd738bd",
      "bb65a881-a4fe-4b65-aae5-e085a9f96cd3"
    ]
  },
  "timestamp": "2025-06-15T14:30:00.609Z"
}

Request Fields

FieldTypeDescription
requestIdstringUnique request identifier (UUID)
transactionIdstringUnique transaction identifier for this bet
clientSessionIdstringThe player's session identifier
clientPlayerIdstringThe player's unique identifier
roundIdstringPer-bet round reference. Unique per bet — a player placing several chips in one game round produces several /bet calls, each with its own roundId (see Round IDs)
gameIdstringThe game identifier
amountint64Bet amount, an integer in the currency's wire precision (see Currencies)
currencystringISO 4217 currency code
isFreebooleantrue if this is a freebet (see below)
rewardUuidstringThe reward identifier for freebets
metaobjectAdditional bet metadata
meta.betTypestringType of bet placed
meta.selectionarraySelected items for the bet
timestampstringRFC 3339 UTC timestamp of when the request was created. Fractional seconds vary by callback — parse leniently, do not pin an exact format string

Freebet Requests

When a player uses a freebet, the request includes additional fields:

{
  "requestId": "...",
  "transactionId": "...",
  "amount": 0,
  "currency": "USD",
  "isFree": true,
  "rewardUuid": "promo-winter-2025-001",
  "meta": { ... }
}
FieldDescription
amountAlways 0 for freebets (no real money deducted)
isFreetrue to indicate this is a freebet
rewardUuidYour clientRewardId from when you granted the freebet

For freebets, do not deduct any balance from the player. The amount will be 0. See Freebets & Rewards for more details.

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 bet.

statusWhen you return itWhat 155 does
SUCCESSBet accepted, stake debitedBet is live
INSUFFICIENT_BALANCE_ERRORPlayer can't cover the stakeBet rejected, surfaced to player
DUPLICATE_TRANSACTION_ERRORThis transactionId was already processedTreated as the original; no re-debit
BET_LIMIT_REACHED_ERROROperator bet-limit hitBet rejected
BONUS_ERRORFree-bet/reward problemBet rejected
UNKNOWN_ERRORUnexpected operator-side failureBet rejected — a /rollback for this transactionId follows (you may receive several attempts within a few seconds, each with a different rollback transactionId but the same referenceTransactionId). The /bet is not re-sent

Never return a bare failed to create bet message or a non-200 HTTP status — always return one of the status values above. ROUND_CLOSED is not a bet status: it's a 155-side condition, not something you return.

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 bet outcome. The status values above apply only to requests that passed signature verification. (A bad signature never occurs on genuine 155 traffic, which is always correctly signed, so this can't affect real bets.)

See Error codes for the canonical enum across all callbacks.

Idempotency

Replaying a /bet with the same transactionId must return DUPLICATE_TRANSACTION_ERROR with no balance change — it is a duplicate, not a new bet (network-retry safety). Do not debit the stake a second time.

This is asymmetric with /win: a replayed /win returns the original SUCCESS result, whereas a replayed /bet returns DUPLICATE_TRANSACTION_ERROR.

Aggregator (Hub88) integrations use a different status set — this page is for direct integrations.

Success Response

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

FieldTypeDescription
statusstring"SUCCESS"
requestIdstringEcho back the request ID
clientPlayerIdstringEcho back the player ID
currencystringISO 4217 currency code
balanceint64New balance after bet, in the currency's wire precision (see Currencies)

Error Response

HTTP/1.1 200 OK
X-Marbles-Signature: <signature>
Content-Type: application/json

{
  "status": "INSUFFICIENT_BALANCE_ERROR",
  "requestId": "8df0475e-5069-483a-8205-f6089997abc9",
  "clientPlayerId": "02mnrpyv2qd9jbwhoniyimxsy"
}

Error Statuses

StatusDescription
INSUFFICIENT_BALANCE_ERRORPlayer does not have enough balance
DUPLICATE_TRANSACTION_ERRORThis transactionId was already processed (idempotent replay)
BONUS_ERRORBet does not pass free-bet / reward rules
BET_LIMIT_REACHED_ERRORPlayer has reached their betting limit
UNKNOWN_ERRORBet could not be registered

For an expired or invalid player session, return UNKNOWN_ERROR. There is no dedicated session-state status on the wallet callbacks — any status outside the enum above is treated the same as UNKNOWN_ERROR (the bet is rejected and the player sees a generic failure).

A failed or timed-out /bet triggers a /rollback

If your /bet returns UNKNOWN_ERROR (or an unrecognised status), responds non-200, or exceeds the 8-second deadline, we treat the bet as rejected and send a /rollback for that transactionId — even if your wallet actually accepted and debited it after the response was lost. Implement /rollback to tolerate both cases (refund if debited; SUCCESS with no movement if unknown). If the transactionId was already processed on your side, return DUPLICATE_TRANSACTION_ERROR — a duplicate is never rolled back.

See the Response Contract above for what 155 does with each status, and Error codes for the canonical enum.

On this page