> For the complete documentation index, see [llms.txt](https://docs.thndr.io/integration/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thndr.io/integration/server-webhooks/transactions.md).

# Transactions

THNDR calls this endpoint to notify the Operator of a transaction request, instructing them to either debit or credit the player’s balance.

#### Endpoint

```url
POST {operatorURL}/thndr/transactions
```

### Bet Transaction

The **BET** Transaction is sent by THNDR to notify the Operator of a wager placed by a player.

The Operator must process this request to **debit the player’s balance** (and optionally record a potential reward).

<br>

```json
{
   "type": "BET",
   "transactionId": "a395479b-7d31-406e-abab-2dd66a61d5ee-BET",
   "requestedAt": "2025-08-21T14:30:00.123Z",
   "userId": "{USER_ID}",
   "sessionId": "{SESSION_ID}",
   "roundId": "a395479b-7d31-406e-abab-2dd66a61d5ee",
   "currency": "USD",
   "amount": 120,
   "gameId": "solitaire",
   "reward": 200,
   "serviceFee": 20
}
```

* `transactionId` - Unique transaction identifier. Must be persisted by the Operator for idempotency and rollback handling.
* `requestedAt` - The date and time when the request was initially made, in ISO 8601 format (e.g., 2025-08-21T14:30:00.123Z).
* `userId` - Player identifier on the Operator’s platform.
* `sessionId` - The session identifier provided by the Operator when the game was launched.
* `roundId` - Identifier of the game round this transaction belongs to.
* `currency` - Player's wallet currency provided by the Operator when the game was launched (read more in [Currencies](/integration/server-webhooks/currencies.md)).
* `amount` - Bet amount, in the smallest unit of the currency (e.g., cents for USD, pence for GBP).
* `usdAmount` *-* Optional. Transaction amount in USD cents; present only when the match currency is USD, carrying the original pre-conversion USD value (may differ from `amount`).
* `gameId` - Game identifier. Possible values: `solitaire`, `blocks`, `twentyone`, `blackjack`, `slots`, `plinko` .
* `reward` - The possible payout in the smallest unit of the currency if this bet wins (shown for info, do not credit yet).
* `serviceFee` - Service fee portion included in `amount` (info-only); Operator must debit **only `amount`**, not `amount + serviceFee`.

{% hint style="warning" %}
For tournament entries, `reward` and `serviceFee` are not included in the BET payload. Instead, a `tournamentId` field is included to identify the tournament the round belongs to.<br>

```
{
   "type": "BET",
   "transactionId": "a395479b-7d31-406e-abab-2dd66a61d5ee-BET",
   "requestedAt": "2025-08-21T14:30:00.123Z",
   "userId": "{USER_ID}",
   "sessionId": "{SESSION_ID}",
   "roundId": "a395479b-7d31-406e-abab-2dd66a61d5ee",
   "currency": "USD",
   "amount": 120,
   "gameId": "solitaire",
   "tournamentId": "{TOURNAMENT_ID}"
}
```

* `tournamentId` *-* Identifier of the tournament this round belongs to. Present only when the entry was made via a tournament flow; absent for regular duels.<br>
  {% endhint %}

#### Error Handling

If the player does not have sufficient funds, the Operator must return:

```
HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "errors": [
    {
      "code": "INSUFFICIENT_BALANCE",
      "isClientSafe": true
    }
  ]
}
```

{% hint style="info" %}
You must pass "isClientSafe": true in the response for it to be passed to the client.
{% endhint %}

### Win Transaction

The **WIN** transaction is sent by THNDR to notify the Operator that a player has won a round.

The Operator must process this request to **credit the player’s balance**.

```json
{
   "type": "WIN",
   "transactionId": "a395479b-7d31-406e-abab-2dd66a61d5ee-WIN",
   "requestedAt": "2025-08-21T14:30:00.123Z",
   "userId": "{USER_ID}",
   "sessionId": "{SESSION_ID}",
   "roundId": "a395479b-7d31-406e-abab-2dd66a61d5ee",
   "currency": "USD",
   "amount": 200,
   "gameId": "solitaire",
   "roomId": "{ROOM_ID}",
   "tournamentId": "{TOURNAMENT_ID}"
}

```

* `transactionId` - Unique transaction identifier. Must be persisted by the Operator for idempotency and rollback handling.
* `requestedAt` - The date and time when the request was initially made, in ISO 8601 format (e.g., 2025-08-21T14:30:00.123Z).
* `userId` - Player identifier on the Operator’s platform.
* `sessionId` - The session identifier provided by the Operator when the game was launched.
* `roundId` - Identifier of the game round this transaction belongs to.
* `currency` - Player's wallet currency provided by the Operator when the game was launched (read more in [Currencies](/integration/server-webhooks/currencies.md)).
* `amount` - Win amount, in the smallest unit of the currency (e.g., cents for USD, pence for GBP).
* `usdAmount` - Optional. Transaction amount in USD cents; present only when the match currency is USD, carrying the original pre-conversion USD value (may differ from `amount`).
* `gameId` - Game identifier. Possible values: `solitaire`, `blocks`, `twentyone`, `blackjack`, `slots`, `plinko`.
* `roomId` - Identifier for the game room. Players sharing the same roomId are participating in the same PvP match.
* `tournamentId` *(optional)* - Identifier of the tournament this round belongs to. Present only for tournament rounds.

### Lose Transaction

The **LOSE** transaction is sent by THNDR to notify the Operator that a player has lost a round.

The Operator might process this request to **record a loss**.<br>

```json
{
   "type": "LOSE",
   "transactionId": "a395479b-7d31-406e-abab-2dd66a61d5ee-LOSE",
   "requestedAt": "2025-08-21T14:30:00.123Z",
   "userId": "{USER_ID}",
   "sessionId": "{SESSION_ID}",
   "roundId": "a395479b-7d31-406e-abab-2dd66a61d5ee",
   "currency": "USD",
   "gameId": "solitaire",
   "roomId": "{ROOM_ID}",
   "tournamentId": "{TOURNAMENT_ID}"
}
```

* `transactionId` - Unique transaction identifier. Must be persisted by the Operator for idempotency.
* `requestedAt` - The date and time when the request was initially made, in ISO 8601 format (e.g., 2025-08-21T14:30:00.123Z).
* `userId` - Player identifier on the Operator’s platform.
* `sessionId` - The session identifier provided by the Operator when the game was launched.
* `roundId` - Identifier of the game round this transaction belongs to.
* `currency` - Player's wallet currency provided by the Operator when the game was launched (read more in [Currencies](/integration/server-webhooks/currencies.md)).
* `gameId` - Game identifier. Possible values: `solitaire`, `blocks`, `twentyone`, `blackjack`, `slots`, `plinko` .
* `roomId` - Identifier for the game room. Players sharing the same roomId are participating in the same PvP match.
* `tournamentId`  - Identifier of the tournament this round belongs to. Present only for tournament rounds.

### Draw Transaction

The **DRAW** transaction is sent by THNDR to notify the Operator that a player has tied a round.

The Operator must process this request to **credit the player’s balance**.

```json
{
   "type": "DRAW",
   "transactionId": "a395479b-7d31-406e-abab-2dd66a61d5ee-DRAW",
   "requestedAt": "2025-08-21T14:30:00.123Z",
   "userId": "{USER_ID}",
   "sessionId": "{SESSION_ID}",
   "roundId": "a395479b-7d31-406e-abab-2dd66a61d5ee",
   "currency": "USD",
   "amount": 200,
   "gameId": "solitaire",
   "roomId": "{ROOM_ID}"
}
```

* `transactionId` - Unique transaction identifier. Must be persisted by the Operator for idempotency and rollback handling.
* `requestedAt` - The date and time when the request was initially made, in ISO 8601 format (e.g., 2025-08-21T14:30:00.123Z).
* `userId` - Player identifier on the Operator’s platform.
* `sessionId` - The session identifier provided by the Operator when the game was launched.
* `roundId` - Identifier of the game round this transaction belongs to.
* `currency` - Player's wallet currency provided by the Operator when the game was launched (read more in [Currencies](/integration/server-webhooks/currencies.md)).
* `amount` - Win amount, in the smallest unit of the currency (e.g., cents for USD, pence for GBP).
* `usdAmount` - Optional. Transaction amount in USD cents; present only when the match currency is USD, carrying the original pre-conversion USD value (may differ from `amount`).
* `gameId` - Game identifier. Possible values: `solitaire`, `blocks`, `twentyone`, `blackjack`, `slots`, `plinko` .
* `roomId` - Identifier for the game room. Players sharing the same roomId are participating in the same PvP match.

### Rollback Transaction

The **ROLLBACK** transaction is sent by THNDR to request the Operator to **reverse a previously processed BET transaction**, in cases of error, cancellation, or game rollback.

The Operator must ensure that the original transaction is undone and the player’s balance is correctly updated.

```json
{
   "type": "ROLLBACK",
   "transactionId": "a395479b-7d31-406e-abab-2dd66a61d5ee-ROLLBACK",
   "requestedAt": "2025-08-21T14:30:00.123Z",
   "originalTransactionId": "a395479b-7d31-406e-abab-2dd66a61d5ee-BET",
   "userId": "{USER_ID}",
   "sessionId": "{SESSION_ID}",
   "roundId": "a395479b-7d31-406e-abab-2dd66a61d5ee",
   "currency": "USD",
   "gameId": "solitaire"
}
```

* `transactionId` - Unique transaction identifier. Must be persisted by the Operator for idempotency.
* `requestedAt` - The date and time when the request was initially made, in ISO 8601 format (e.g., 2025-08-21T14:30:00Z).
* `originalTransactionId` - The transactionId of the original BET transaction being rolled back.
* `userId` - Player identifier on the Operator’s platform.
* `sessionId` - The session identifier provided by the Operator when the game was launched.
* `roundId` - Identifier of the game round this transaction belongs to.
* `currency` - Player's wallet currency provided by the Operator when the game was launched (read more in [Currencies](/integration/server-webhooks/currencies.md)).
* `gameId` - Game identifier. Possible values: `solitaire`, `blocks`, `twentyone`, `blackjack`, `slots`, `plinko` .

{% hint style="warning" %}
Important: If a system crash occurs before receiving a response from the BET transaction, you may receive rollback requests for transaction ids that were never persisted in your database (i.e., the user was not credited). These requests must be ignored, and a 200 status code must be returned.
{% endhint %}

### Signature Verification

Verify the request signature using raw request body as the request payload. More in the [Request Signing](/integration/server-webhooks/request-signing.md) section.
