> 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/operations/nfr-and-failures.md).

# Non-Functional Requirements & Failure Handling

## Non-functional requirements

**Idempotency.** All PAM→Wynta mutations carry client-generated IDs (`consume_txn_id`, `wager_tnx_id`, event envelope `event_id`); all Wynta→PAM webhooks carry `event_id`. Consume replays return **`409 Conflict`** — the PAM stores `consume_txn_id` before calling and treats a `409` for a known ID as "already recorded". Webhook replays are deduped by the PAM on `event_id`.

**Ordering.** Webhooks may arrive out of order. The PAM applies signed deltas and uses `resulting_balance` for immediate drift detection; per-player sequence numbers, if provided, take precedence.

**Latency.** Promo validation and grant (player-facing, synchronous): p95 < 500 ms. Wallet webhooks: delivery target < 5 s; the PAM UI tolerates brief staleness.

**Retries & DLQ.** Webhook retries with exponential backoff ≥ 24 h; DLQ swept by reconciliation. PAM retries transient S2S failures with the same idempotency key.

**Concurrency.** Wagering-progress updates are serialized per player-bonus in Wynta (no double release). Consume requests for the same player are safe to send concurrently — chunk debits are transactional.

**Consume model (client-first).** The PAM — the external client — debits its own Bonus Wallet at spend time, then notifies Wynta via `bonus_consume_bonus`. The `BONUS_CONSUMED` webhook is a confirmation matched by `consume_txn_id`, never a second debit. Wynta's ledger is final authority; a rejected notification means the PAM reverses its local debit.

**Security.** The User Bonus API authenticates with the `X-Client-Id` header (resolved to `site_id` server-side) — an identifier, not a signature — so calls must be strictly server-to-server over TLS with network-level controls (IP allow-listing / private networking); `client_id` never ships in browser or app code. Inbound webhooks carry the proposed timestamped HMAC-SHA256 signature with replay protection and secret rotation — see [Webhook Authentication](/bonus/webhooks/wallet-update.md#authentication). PII event properties (`email`, `phone`, `ip`) are hashed before persistence; payloads carry no PII beyond `user_id`.

**Auditability.** Wynta's ledger (grants, chunk releases, per-chunk consumption, reversions, forfeits, expiry events) is the audit trail; configuration changes carry actor fields and full history endpoints. The PAM retains webhook receipts and reconciliation reports per compliance requirements.

**Budget enforcement.** Grant-time budget checks are atomic across Configure → Subhead → Head caps (`DAILY`/`WEEKLY`/`MONTHLY`); a cap breach can never leak a grant to the PAM.

## Failure scenarios

| Scenario                                             | Behaviour                                                                                                                        |
| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Webhook lost                                         | Retry → DLQ → reconciliation corrects PAM wallet                                                                                 |
| Duplicate consume notification from PAM              | `409 Conflict` on the replayed `consume_txn_id`; PAM treats known-ID 409 as already-recorded; single ledger entry                |
| Wynta rejects consume notification (ledger mismatch) | Client wallet has drifted — PAM reverses its local debit and flags for targeted reconciliation                                   |
| Consume notification undeliverable (Wynta down)      | PAM's local debit stands; notification queued and retried with the same `consume_txn_id`; reconciliation closes any residual gap |
| Game round voided after consume                      | PAM calls `bonus_revert_bonus_consumption`; chunk-exact reversal; webhook restores wallet                                        |
| Voided wager after chunk release                     | Progress reduced; released chunks are **not** clawed back                                                                        |
| Budget cap reached at grant evaluation               | No grant created — no ledger entry, no webhook; the code may still have validated earlier (validation is not a reservation)      |
| Wynta unavailable during wager stream                | PAM buffers/replays wager events (deduped on `wager_tnx_id`); release evaluation catches up                                      |
| Balance drift via `resulting_balance`                | PAM flags player; targeted reconciliation run                                                                                    |
| Chunk expires between consume and revert             | Reverted amount follows expiry rules; webhook deltas make the outcome explicit                                                   |

## Open questions

1. **Game contribution rates** — `bet_placed` carries `game_category`/`game_provider`; confirm whether contribution weighting per category exists or all qualifying wagers count at 100%.
2. **Wager progress attribution** — the ledger tracks progress per chunk (`chunks[].wager_amount`); confirm overflow carry-over to the next chunk and rounding on the final chunk (e.g. 3 × ₹166.67 vs ₹500.01 requirement).
3. **Multiple active bonuses** — enforce a single active wagering bonus per chip type (recommended), or attribute wager contribution across concurrent bonuses? How does `priority` interact?
4. **Expiry countdown display** — the PAM can show countdowns from `chunk_expiry_days` / `bonus_expiry_days` (transaction detail) and the proposed `expires_at` on webhook chunks; confirm the webhook carries expiry timestamps at grant time.
5. **Webhook envelope sign-off** — confirm the proposed schema (incl. per-player sequence numbers) with the PAM (SR) team, and version it (`v1`) from day one for multi-PAM future-proofing.
6. **`release_bucket`** — confirm intended semantics and whether the PAM needs it in webhook payloads.
7. **Unauthenticated endpoints** — `POST /validate-code`, `POST /consume/{consume_txn_id}/revert`, and the referral/applicable-codes reads require no `X-Client-Id`; confirm the intended network-level protection for these, particularly revert (a mutation reachable with only a `consume_txn_id`).
8. **`BONUS_RELEASE` event & `system_auto_apply`** — the applicable-codes endpoint references backend auto-application "when a `BONUS_RELEASE` event fires"; confirm the definition of this internal event and how `system_auto_apply` differs from the player-visible `auto_apply` flag.
9. **Reconciliation cadence/cutoff** — daily proposed; confirm with finance/compliance.
