POST /transactions/win
Moonshot sends a settlement for an accepted bet. The callback has its own new
transactionId and the original betId, player, game, currency and session.
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": "b37fb156-c781-4b87-b36b-ea45e6c9a982",
"playerId": "fd63c560-f443-49b5-a721-c40c0e06b7be",
"betId": "f72dc49d-9d88-4193-8a66-8e27b136f0ea",
"isFree": false,
"amount": "0.02",
"currency": "SOL",
"gameSlug": "rusty-ring"
}
amount is the gross payout to credit, including the returned stake where the game
rules include it. It is not the net profit. A 0.01 debit followed by a 0.02
win changes a starting balance of 10 to 10.01.
Accept amount: "0" to close an accepted losing or capped round without credit.
Freeplay losses can explicitly arrive this way, with payoutCoefficient: "0".
Do not require a positive amount or assume every losing round omits the callback.
Support the game's agreed settlement rules and reject amounts exceeding the
original bet's payout cap.
A settlement can include payoutCoefficient as a decimal string. Freeplays also include
freeplayGrantId and freeplayEnrollmentId. Resolve eligibility from the
original bet. Session expiry must not prevent settlement of an accepted stake.
Response
{
"data": {
"balance": "10.01",
"operatorTransactionId": "7051365e-093e-4d83-aebe-74e006c9b08a",
"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.