Skip to main content

POST /transactions/bet

Moonshot calls your callback backend to place a stake. For Rusty Ring, the first hit or block funds the pending round; later actions do not debit it again.

Every callback uses canonical signed headers and responses. sessionId echoes the partner reference from /launch; operatorId is the Moonshot-issued operator UUID. operatorSlug identifies the configured signing relationship. Never substitute your local provider ID for operatorId.

The JSON request body is unwrapped; successful responses use { "data": ... }. Amounts and balances are decimal strings in asset units, not atomic integers.

Request​

{
"operatorId": "8e563380-2a27-41b1-8be5-2cdd7d1ec271",
"operatorSlug": "your-partner-slug",
"sessionId": "partner-session-123",
"transactionId": "a043a575-409e-4a97-8d31-a40407d0bc20",
"playerId": "fd63c560-f443-49b5-a721-c40c0e06b7be",
"betId": "f72dc49d-9d88-4193-8a66-8e27b136f0ea",
"isFree": false,
"amount": "0.01",
"currency": "SOL",
"gameSlug": "rusty-ring"
}

Atomically validate the partner, player, game, currency, session, limits and balance; record (authenticated operatorSlug, transactionId) and debit once. For freeplays, isFree: true also requires freeplayGrantId and freeplayEnrollmentId and an eligible grant; never debit the regular wallet for a freeplay. Optional operatorMaxWin is a decimal string supplied on freeplay bets; persist it with the accepted grant/usage limits.

Response​

{
"data": {
"balance": "9.99",
"operatorTransactionId": "b8a86f01-f5bd-49ba-adfe-2b07b3fce116",
"statusCode": 1000
}
}

1000 means accepted. For business rejection, return HTTP 200 with a non-success statusCode, unchanged balance, transaction ID and errorMessage; 5100 denotes insufficient/invalid balance, 5000 denotes server error. An identical duplicate returns the original stored response (including 1000 for an accepted operation), not a new debit/credit or a 3000 rejection. HTTP 200 with 5000 is a business response, not a transport 5xx; it does not trigger the HTTP client's server-error retry. If a temporary error needs an HTTP retry, return a signed HTTP 5xx before committing; if committed, replay the stored result on the next attempt.

For an authenticated protocol/state conflict, return a signed HTTP 409, for example:

{
"error": {
"code": "CONFLICT",
"message": "Original accepted bet does not match the callback",
"retryable": false
}
}

Do not include tokens, secrets or internal diagnostics. Failed transport/server attempts may be retried using the same transaction ID and payload. A timeout does not prove the transaction failed: replay the stored result after commit.