> For the complete documentation index, see [llms.txt](https://docs.wynta.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.wynta.com/bonus/flows/consumption.md).

# Bonus Consumption & Reversal

## Flow 4 — Consume bonus (client wallet first, then notify Wynta)

**Trigger:** the player places a wager funded (fully or partly) from bonus.

**Order of operations — the PAM debits its client wallet first, then notifies Wynta:**

1. **Client wallet debit (PAM):** the PAM checks its local Bonus Wallet **Available** balance for the chip type, debits the bonus portion of the wager, and lets gameplay proceed. If Available is insufficient, the PAM rejects the bonus spend locally — no call to Wynta.
2. **Consume notification (PAM → Wynta):** the PAM immediately notifies Wynta via `bonus_consume_bonus` (S2S) with a unique `consume_txn_id`.
3. **Ledger write (Wynta):** Wynta validates and debits the amount across **RELEASED** chunks — earliest `expires_at` first, then earliest `released_at` — writing each chunk debit under `consume_txn_id`.
4. **Confirmation (Wynta → PAM):** Wynta emits the **`BONUS_CONSUMED`** webhook. The PAM does **not** debit again — it matches the webhook to its step-1 debit by `consume_txn_id` and reconciles its balance against `resulting_balance`.

```
Player          PAM (Client) — external client         Wynta Bonus
  │  wager 50     │                                    │
  │──────────────▶│ 1. wallet: Available 40 → 20       │
  │               │    (consume 20 from client wallet) │
  │               │ 2. POST /consume             │
  │               │    {consume_txn_id, bonus: 20}     │
  │               │───────────────────────────────────▶│ 3. debit chunks,
  │               │                                    │    write ledger
  │               │◀──── 4. webhook BONUS_CONSUMED ────│
  │               │    match by consume_txn_id;        │
  │               │    verify resulting_balance        │
```

**Endpoint:** `POST /api/v1/user-bonuses/consume` (S2S, requires the [`X-Client-Id` header](/bonus/api-reference/overview.md#authentication-client-id-header)):

```json
{
  "user_id": "USER_001",
  "consume_txn_id": "TXN20250519001",
  "wager_amount": "300.00",
  "bonus_amount": "100.00",
  "chip_type": "cash",
  "wager_tnx_id": "WAGER_REF_001",
  "game_id": "GAME_ROULETTE_01",
  "round_id": "ROUND_999"
}
```

Response `201 Created` echoes `consume_txn_id` with the ledger `txn_id` and `consumed_amount`. The service **auto-selects the user's next available released chunk** — the PAM never picks chunks. Monetary values are decimal strings.

**Rules:**

* **Idempotency:** `consume_txn_id` must be unique per consumption. Generate and **store it before calling** the API. A retry with the same ID returns **`409 Conflict`** — treat a `409` for an ID you have recorded as "already consumed" (success), not an error. `409` is also returned when no released chunk is available, or when reverting an already-reverted consumption.
* **Wynta rejects the notification** — `409` *no released chunk available* (a `409` for an unknown `consume_txn_id` state), `404` unknown record, or `400` validation error: the client wallet has drifted — the PAM must **reverse its step-1 debit** and flag the player for targeted reconciliation. Wynta's ledger is final authority.
* **Notification delayed/failed (Wynta unreachable):** the PAM queues and retries the consume notification with the same `consume_txn_id`. The client debit stands in the meantime; reconciliation catches anything the retry queue loses.
* **Missing webhook:** if `BONUS_CONSUMED` doesn't arrive within the delivery SLA but the API call succeeded, the ledger entry exists — the webhook will arrive via retry or the gap is closed at reconciliation. The PAM must not double-debit either way.

## Reversal — voided rounds

**Endpoint:** `POST /api/v1/user-bonuses/consume/{consume_txn_id}/revert`

Mirroring the consume order, the PAM credits its client wallet back and notifies Wynta:

1. PAM restores the amount to its wallet (Available += amount) when the round is voided.
2. PAM calls the revert endpoint with the original `consume_txn_id` in the path (no auth header required); the `200 OK` response returns the reversed `amount` and `chip_type`. Reverting twice returns `409 Conflict`.
3. Wynta reverses the consumption **chunk by chunk** — exact, because every consumption is recorded per chunk — and emits **`BONUS_CONSUMPTION_REVERTED`**.
4. The PAM matches the webhook by `consume_txn_id` (no double credit) and verifies `resulting_balance`.

If a chunk expired between consume and revert, the reverted amount follows the expiry rules rather than silently returning to Available — the webhook deltas make the outcome explicit; the PAM adjusts to match.

## Player-facing queries

| API                                      | Purpose                                                                                                                                   |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /{user_id}/summary`                 | Authoritative `bonus_balance` / `pending_bonus` / `wagering_required` per chip type — drift checks, progress bars, support tooling        |
| `GET /{user_id}/transactions?chip_type=` | Flat paginated ledger — every `grant` / `released` / `consumed` / `expiry` / `forfeited` event is its own entry (limit 1–200, default 50) |
| `GET /{user_id}/transactions/{txn_id}`   | Full grant detail **including chunks, forfeit record, and expiry events** — ground truth for disputes and reconciliation                  |
