Bets history¶
Purpose¶
The Bets screen is the universal bet log: every wager across every game (slots, in-house games, sportbook), with filters by user, provider, game, status, date. The view is read-only except for the bet-queue retry endpoint, which lets engineering re-process settle-jobs that crashed.
Audience¶
Customer support (primary, "where's my missing payout"), compliance (provably-fair audit), engineering (retry stuck settles).
Path in admin-fe¶
| Screen | URL | Page |
|---|---|---|
| Bets list | /bets |
ebit-admin-fe/src/app/(dashboard)/bets/page.tsx |
| Per-game bets (drill-in) | /games/[slug] |
ebit-admin-fe/src/app/(dashboard)/games/[slug]/page.tsx |
| Per-challenge bets | /challenges/bets/[id] |
ebit-admin-fe/src/app/(dashboard)/challenges/bets/[id]/page.tsx |
| Per-leaderboard user bets | /leaderboards/[slug] (drill-in) |
covered in leaderboard.md |
Common bets-table component: ebit-admin-fe/src/components/common/GameBetsHistory/.
Backing API endpoints¶
| Endpoint | Source |
|---|---|
POST /admin/bets (paginated find-many) |
apps/api/src/bet/admin.bet.controller.ts:19 |
POST /admin/bet-queue/retry (SuperAdmin) |
apps/api/src/bet/queue/admin.bet-queue.controller.ts:13 |
Frontend wiring: ebit-admin-fe/src/queries/bets/index.ts.
Key actions¶
| Action | Required permission / role | API call | DB tables touched | Audit-logged? |
|---|---|---|---|---|
| List bets (paginated) | user.bets.view |
POST /admin/bets |
Bet, User, Game |
yes |
| Filter by game slug, provider, status, date, user | user.bets.view |
same with where |
same | yes |
| Retry a stuck bet settle | SuperAdmin |
POST /admin/bet-queue/retry { betId } |
re-enqueues into BullMQ bet-settle |
yes |
AdminActionLog rows: every list and retry writes a row.
Filters and views¶
POST /admin/bets accepts a rich where object:
userId— required for per-user view.gameSlug—plinko,roulette,mines,dice,keno,limbo,monkey-run,blackjack,speed-roulette, plus slot slugs (BGaming/PM8/ST8/evogames).provider—house,bgaming,pm8,st8,evogames,sportbook.status—WON,LOST,CANCELLED,PENDING_SETTLE.dateFrom/dateTo— ISO range.amountMin/amountMax— USD-equivalent.sortBy—CREATED_AT(default),AMOUNT_USD,MULTIPLIER.
The provably-fair drill-in (server seed, client seed, nonce) is on the per-bet detail; for in-house games this links to flows/ provably-fair.
Common workflows¶
- "My win didn't credit." CS pulls bets by
userId+dateFrom=today. Looks forstatus=PENDING_SETTLE. If found, escalates: engineering retries viaPOST /admin/bet-queue/retry { betId }. Backend re-enqueues the BullMQ settle job. Watch the logs in Loki — seeobservability.md. - Provably-fair audit. Player asks: "How was this hash computed?" CS opens the bet detail, copies server seed (revealed after
nextSeedRotateAt), client seed, nonce. Validates with the public/casino/games/.../verifyendpoint orflows/provably-fair narrative. - Suspicious wagering pattern. Risk filters
userId+ last 24h, sorts by amount. Looks for sudden 10× wager bumps after a deposit. Cross-references with withdrawals.md and user-profile.md Notes tab. - Provider outage forensics. Filter
provider=bgaming+status=CANCELLED+ window of the outage. Confirm the bets that got rolled back. Cross-reference with provider's incident report. - Per-game RTP sanity. From game-management.md, drill into a game (e.g.
/games/plinko). The bets table on that page filters automatically bygameSlug.
Edge cases / gotchas¶
POST /admin/betsis intentionally a POST, not GET. Filters can grow large (player IDs, game IDs); a POST body is more cache-friendly than a 4 KB query string. The endpoint itself is idempotent and read-only.- Slot bets carry minimal "round" detail. Slot rounds are atomic on the provider side; Evospin only stores aggregate
Betrows. To inspect a single slot spin, you need the provider's session log (BGaming has it; PM8 hides it). PENDING_SETTLEbets do not always need a retry. Some games (sportbook in particular) hold bets until event resolution. Don't retry sportbook bets — check the event status first.bet-queue/retryis SuperAdmin-only on purpose. Retrying the wrong bet ID can double-credit a player.- Search by user is required for fast queries. A bare
POST /admin/bets {}with nouserIdwill scan the full table — fine on staging, slow in production. The UI enforces a date or user filter by default. Bet.outcomeJsonschema differs per game. Plinko haspath[], mines hasrevealedTiles[], roulette haspocket, keno haspicks[] / hits[]. The UI auto-renders byBet.gameSlug.- Sportbook bets are stored as a separate
SportbookBetrecord linked toBet. The unified/admin/betslist includes them but with different fields.
Cross-links¶
- Per-user bets: user-profile.md → Bets tab
- Per-game bets / RTP: game-management.md
- Per-challenge bets: challenges.md
- Bet flow narrative:
flows/→ bet-place / bet-settle - Provably-fair:
flows/→ provably-fair - Stuck-bet runbook:
runbooks/→ "bet not settled"
Sequence — retrying a stuck bet settle¶
sequenceDiagram
actor admin as SuperAdmin
participant admin-fe
participant api
participant queue as BullMQ (bet-settle)
participant pg as Postgres
admin->>admin-fe: open /bets, filter status=PENDING_SETTLE
admin-fe->>api: POST /admin/bets { where: { status: PENDING_SETTLE, userId } }
api->>api: PermissionGuard('user.bets.view')
api->>pg: SELECT Bet WHERE status=PENDING_SETTLE
pg-->>api: rows
api-->>admin-fe: list
admin->>admin-fe: click Retry on a row
admin-fe->>api: POST /admin/bet-queue/retry { betId }
api->>api: RolesGuard + Roles(SuperAdmin)
api->>queue: enqueue settle for betId (priority=0)
api->>pg: INSERT AdminActionLog (method, url, requestBody)
api-->>admin-fe: 200 OK
queue->>pg: UPDATE Bet SET status=WON|LOST, settle Transaction inserted
Note over admin-fe: row will move out of PENDING_SETTLE on next refresh