WagerStreet · PWallet integration · specification

The Dream API

The PWallet/Line Pros atlas exposes 398 real operations — and still cannot run a casino. This is the specification of the 60 endpoints it should have: for each, the operational gap it leaves today and exactly what it should generate. Back Office #3 ("Lion Command") simulates every one as its reference implementation; this page is the list to hand Line Pros. Companion to the PWallet API Atlas.

60dream endpoints
9families
398real atlas ops today
49GETs the key answers

Source of truth: pwallet-ws-app/docs/dream-api.json, served live at /ws3-dream and rendered in Lion Command's "API Atlas & Dream" page. This page is generated by Softwares/scripts/make-dream-api-article.mjs — regenerate, never hand-edit.

Reporting & BI 9

GET /api/reports/gamePerformance

Per-game performance report

Params fromDate, toDate, category?, provider?, page/limit

The gap today

Today per-game numbers only exist by downloading EVERY wager row via /api/wagers/search (no paging, ~100MB per week) and folding client-side.

What it generates

One row per game for the window: handle (total staked), GGR, wager count, unique players, average bet, theoretical RTP as configured, actual RTP ((handle − GGR) ÷ handle × 100), and hold %. This is the whole casino-manager daily view in one call — which games earn, which run hot (actual RTP above theoretical), which are dead inventory.

GET /api/reports/revenueByDay

Daily revenue series

Params fromDate, toDate, vertical=casino|sports|all

The gap today

The only aggregate today is /api/reports/transactionSummary, which is one lump for a window — no per-day series, no casino/sports split.

What it generates

An array of days, each with handle, GGR, NGR (GGR − bonus cost), deposits, withdrawals, net cash, actives, split by vertical. Feeds every line chart in a dashboard without walking raw wagers.

POST /api/wagers/searchPaged

Paged wager search

Params body: filters + cursor + pageSize (server-capped)

The gap today

/api/wagers/search returns the entire result unpaged — a week of history is ~100MB and a month is un-fetchable. This is the single worst operational gap in the atlas.

What it generates

The same wager rows with a cursor: { rows: [...], nextCursor }. Makes every downstream report incremental and cheap instead of day-walking.

GET /api/reports/topPlayers

Top winning / losing players

Params fromDate, toDate, direction=winning|losing, vertical?, limit=10

The gap today

Ranking players today means folding every wager row for the window per player, client-side.

What it generates

Ranked players with net win/loss from the house's perspective, handle, wager count, verticals played, VIP tier and agent. The daily 'who is beating us / who is feeding us' sheet every book runs on.

GET /api/reports/hourlyActivity

Hourly activity heatmap

Params fromDate, toDate, metric=bets|logins|deposits

The gap today

No time-of-day dimension exists anywhere in the atlas.

What it generates

A 7×24 matrix of activity counts — staffing the cage/support desk, timing campaign sends, spotting bot-like off-hour patterns.

GET /api/reports/cohortRetention

Signup-cohort retention

Params months=12

The gap today

No retention analytics exist upstream; churn is invisible until revenue drops.

What it generates

A matrix: each signup month × months-since-signup → % of the cohort still active and their GGR. The canonical LTV/retention view (standard in GiG Core / EveryMatrix).

GET /api/reports/bonusCost

Bonus cost & liability report

Params fromDate, toDate, bonusId?

The gap today

Bonus operations exist per-player, but no aggregate cost view — the marketing budget is unknowable.

What it generates

Per bonus program: amount granted, converted to cash, forfeited, outstanding (current liability), completion rate, and NGR impact — whether each promotion pays for itself.

GET /api/reports/scheduled

Scheduled report subscriptions

Params CRUD subroutes: POST /api/reports/scheduled, DELETE …/{id}

The gap today

Report templates exist (13 ops) but nothing delivers them on a schedule.

What it generates

Subscriptions binding a report template + period (daily/weekly/monthly) + recipients; the platform emails the rendered report. The 'Monday 8am P&L in the owner's inbox' feature.

GET /api/wagers/search?include=fundSource

Fund source on every wager row (cash vs bonus vs free-spin)

Params existing search params + include=fundSource

The gap today

Wager rows carry no wallet_type/fund_source, so every hold and RTP figure the warehouse can compute is all-wallet (cash and bonus mixed) — the casino reporting plan (docs/casino-reports-plan.md §2) calls this the single highest-priority platform ask: without it, real cash hold, NGR by wallet, bonus ROI and abuse detection are all unbuildable.

What it generates

Each wager row gains fundSource: 'cash' | 'bonus' | 'free_spin' (and the split stake when one bet draws on both wallets). Finance can then publish cash hold without the bonus contamination caveat, and the bonus ledger can compute true wagering-requirement progress per instance.

Player administration 13

GET /api/customers/{customerId}/activitySummary

One-call player 360

Params days=30

The gap today

A player 360 today needs 11 separate calls (minimal, balances, credits, bonuses ×3, logins, transactions, wagers…).

What it generates

Profile + balances + limits + KYC state + last login/IP + N-day totals (handle, net, wager count, deposits, withdrawals) + recent ledger in one response. Cuts the support screen from 11 round-trips to 1.

GET /api/customers/{customerId}/gameHistory

Player per-game breakdown

Params fromDate, toDate

The gap today

Cannot answer 'what does this player play and how does it treat them' without the full wager dump.

What it generates

Per game/league for one player: handle, net, wager count, actual RTP experienced. Drives VIP-host conversations and RG interventions ('this player is chasing losses on one high-volatility slot').

PUT /api/customers/{customerId}/limits

Set responsible-gambling limits

Params body: dailyDeposit, weeklyLoss, monthlyLoss, wagerMax, sessionMinutes, realityCheckMins (null clears)

The gap today

The atlas has NO limit operations at all — an MGA/UKGC-licensed operator legally cannot run without them.

What it generates

The stored limit set + effective-from timestamps (limit increases must be delayed 24h–7d per most regulations; decreases apply instantly). GET twin returns current limits + pending changes.

POST /api/customers/{customerId}/exclusion

Self-exclusion / cool-off

Params body: kind=SELF|OPERATOR|COOL_OFF, months|days, registry?

The gap today

Exclusion is a licensing requirement (MGA unified registry, GamStop-style) with zero atlas support.

What it generates

The exclusion record: kind, term, start, registry it was propagated to; while active the platform must refuse logins/deposits and suppress ALL marketing. GET twin lists history.

GET /api/customers/exclusions

The self-exclusion register, readable

Params query: updatedSince?, page?, pageSize? — ids and states only, no personal data needed

The gap today

Today this endpoint exists and answers 403 to our key, so we can see only the exclusions recorded in our own back office and not the ones players set on the platform itself. That is the register a player uses. Being unable to read it means we cannot tell "not excluded" from "excluded somewhere we cannot see", and a responsible-gambling guardrail that fails closed must therefore treat every player as unverified.

What it generates

One row per active exclusion: customerId, kind (SELF|OPERATOR|COOL_OFF), start, end or indefinite, and the registry it was propagated to. A history twin, or an updatedSince cursor, would let it be polled cheaply rather than re-read whole.

GET /api/customers/{customerId}/riskScore

Composite risk score

Params —

The gap today

Fraud/AML/RG risk is spread over raw data nobody joins.

What it generates

0–100 composite plus per-factor breakdown: payment risk (velocity, instruments), fraud signals (device/IP sharing), RG behavior score (loss-chasing, night play, limit hits), AML flags. The triage number every queue sorts by.

GET /api/customers/{customerId}/devices

Device & session fingerprints

Params —

The gap today

Login rows carry IPs only; multi-accounting detection is impossible.

What it generates

Device fingerprints (hashed), user agents, IPs with geo, first/last seen, and WHICH OTHER customerIds share any of them — the multi-account/bonus-abuse graph in one call.

GET /api/customers/duplicates

Duplicate account scan

Params match=email|phone|device|ip|payment, threshold

The gap today

Dupe detection today is a client-side join over the full customer dump.

What it generates

Clusters of accounts sharing identity signals, each with a match confidence and combined bonus cost — the daily bonus-abuse review queue.

POST /api/customers/{customerId}/freeze

Soft-freeze account

Params body: reason, until?

The gap today

Only heavyweight status changes exist; investigations need a reversible hold that isn't an exclusion.

What it generates

A freeze record blocking wagers/withdrawals but preserving login + support contact, with audit trail (who froze, why, when it lifts).

GET /api/customers/segments

Server-side segments

Params CRUD + GET /{id}/members

The gap today

Every segmentation today is a saved client-side filter that goes stale instantly.

What it generates

Named segment definitions (rules over deposits, activity, vertical, VIP, dormancy) materialized server-side; /members streams current membership. Feeds campaigns, bulk actions, and stake-factor policies.

POST /api/customers/{customerId}/notes

Server-side account notes

Params body: text, category, pinned; GET twin lists

The gap today

Notes live only in each back office's local SQLite — support and VIP hosts on different tools can't see each other's history.

What it generates

A shared, audited note stream per player (author, timestamp, category: support/finance/risk/VIP), readable from every console that talks to the platform.

POST /api/customers/{customerId}/passwordReset

Reset a player password

Params body: mode=TEMP_PASSWORD|EMAIL_LINK|SMS_LINK, notify?

The gap today

The atlas has password POLICIES (GET/PUT /api/auth/customers/passwordPolicies) and password VALIDATION, but no operation for support to reset an individual player password — the single most common support request a desk gets. Today the only live workaround is POST /api/loginLink.

What it generates

TEMP_PASSWORD mode: a one-time strong temporary password (returned once, must-change-on-login flag set, all sessions invalidated). LINK modes: a signed, expiring reset link delivered by the chosen channel with delivery status. Every reset lands in the audit log with actor + reason.

POST /api/customers/{customerId}/forceLogout

Kill player sessions

Params —

The gap today

Account-takeover response requires instantly ending every session; the atlas has admin logout only, nothing per-customer.

What it generates

Count of sessions/tokens invalidated (web, wrapper sessions, kiosk). Pairs with passwordReset in the compromise runbook.

Sportsbook 7

GET /api/sports/events

Events catalog

Params fromDate, toDate, league?, status=open|live|settled

The gap today

Events only exist as strings buried in wager-row properties — there is no first-class event entity.

What it generates

Event objects: id, league, sport, matchup, start time, status, market count. The backbone every sports report joins against.

GET /api/sports/events/{eventId}/handle

Total amount bet per game

Params byMarket=1, bySide=1

The gap today

The user's explicit ask — 'total amount bet per game for sports' — currently requires folding the full wager dump.

What it generates

For one event: total handle, bet count, average bet, and the split by market (ML/spread/total/props/parlay legs) and by side — where the money is, which side the book needs.

GET /api/sports/liability

Open liability board

Params league?, sport?

The gap today

The single most important trading number — worst-case payout on open bets — does not exist upstream.

What it generates

Per open event/market: open handle, worst-case and best-case P&L if each outcome hits, current max-bet, bet delay. Sorted by worst case; this is the trader's morning screen (SoftSwiss/GiG ship exactly this).

GET /api/sports/openBets

Unsettled tickets

Params eventId?, customerId?, minRisk?

The gap today

No way to list pending tickets without downloading everything and filtering by status.

What it generates

All unsettled wagers with risk/toWin, event, market, side, placed-at — feeds liability, early cash-out decisions, and void queues.

POST /api/wagers/{wagerId}/void

Void / regrade a wager

Params body: action=VOID|REGRADE, newResult?, reason

The gap today

Trader operations (bad line, palps, late scratch) have no atlas support — today it's a vendor support ticket.

What it generates

The corrected wager + a compensating ledger transaction, with an immutable audit entry (who, why, before/after).

PUT /api/customers/{customerId}/stakeFactor

Player stake-factor profiling

Params body: factor (0.05–5.0), liveDelaySec, note

The gap today

Sharp-player management — the core of bookmaking — has zero atlas support.

What it generates

The player's limit multiplier applied to every market max (0.1 = sharp cut to 10%, 2.0 = trusted recreational doubled), plus live-bet delay. GET twin lists all non-default profiles — the 'sharps board'.

GET /api/sports/leagues

League reference

Params —

The gap today

League/sport taxonomy is embedded in strings; no canonical reference data.

What it generates

Leagues with sport, season dates, default margin target, and per-league handle/GGR rollups for the trailing 30 days.

Casino management 5

GET /api/casino/games

Game catalog with RTP

Params provider?, category?, status?

The gap today

A 'Game catalog config' tag exists (2 ops) but exposes no per-game RTP, provider, or status data.

What it generates

Every game: id, name, provider, category, THEORETICAL RTP as certified, volatility class, release date, enabled/disabled per brand. The reference table actual-RTP reports compare against.

PUT /api/casino/games/{gameId}/status

Enable / disable a game

Params body: enabled, brandId?, reason

The gap today

Pulling a misbehaving game today is a vendor ticket, not an operation.

What it generates

The updated game status with audit trail — kill-switch for a game paying out anomalously (actual RTP way above theoretical) pending investigation.

GET /api/casino/jackpots

Progressive jackpot feed

Params —

The gap today

Jackpot levels/winners aren't exposed at all.

What it generates

Each progressive: current amount, seed, contribution %, last winner + date. Marketing (jackpot tickers) and finance (jackpot liability is a real balance-sheet line) both need it.

GET /api/casino/sessions/live

Live casino floor view

Params —

The gap today

No real-time presence data — 'who is playing right now' is unanswerable.

What it generates

Current sessions: customerId, game, stake pace, session length, net position. The live floor-manager view; also an RG trigger source (marathon sessions).

PUT /api/casino/lobby

Lobby ordering & scheduling

Params body: brandId, orderedGameIds[], scheduledReleases[]

The gap today

Lobby merchandising (what players see first) is where casino revenue is made; the atlas has nothing.

What it generates

The stored lobby layout per brand with scheduled game releases — an EveryMatrix CasinoEngine-style merchandising control.

Payments & finance 6

GET /api/payments/pending

Unified pending-payments queue

Params family=DEPOSIT|WITHDRAWAL, method?, riskMin?

The gap today

The withdrawal-approval queue today is /api/transactions/search + client-side filtering, with no risk context.

What it generates

Pending items enriched with the player's risk score, KYC state, bonus-abuse flags, lifetime deposits vs withdrawals, and method fees — everything an approver needs on one row.

GET /api/payments/routes

PSP cascade configuration

Params PUT twin to reorder

The gap today

Payment orchestration (MoneyMatrix-style cascading) is invisible — success rates and failover order live only in the PSP's own dashboards.

What it generates

Per method/PSP: current success rate, average settle time, status, and the cascade order (where a declined transaction retries next). PUT reorders the cascade.

GET /api/finance/chargebacks

Chargeback case management

Params status?; POST …/{caseId}/evidence

The gap today

Chargebacks — the #1 card-payment loss vector — have no atlas presence.

What it generates

Cases with reason codes, amounts, deadlines and representment status; POST attaches evidence. Feeds a win-rate report per method/PSP.

GET /api/finance/reconciliation

PSP settlement reconciliation

Params date, provider

The gap today

Nothing verifies that PSP settlement files match the platform ledger.

What it generates

Per provider/day: platform-ledger total vs PSP-reported settlement, fees, and the itemized diff. The daily 'are we actually being paid' check.

POST /api/transactions/{transactionId}/refund

Refund a transaction

Params body: amount?, reason

The gap today

Partial/full refunds are a support-ticket path today.

What it generates

The compensating transaction with linkage to the original, audit-stamped.

GET /api/customers/{customerId}/holds

Balance holds (withdrawal lifecycle)

Params —

The gap today

The proper cashier flow is: withdrawal request → amount moves to HOLD on the balance (visible, not spendable) → back-office approves (hold settles, money leaves) or declines (hold releases). The real atlas has the decision op (PUT /api/transactions/{id}/pendingStatus) but balances expose no hold state — support cannot tell a player why their spendable balance is lower than their total.

What it generates

Every active hold on the wallet: pending withdrawal requests (id, amount, method, requested-at), bonus-rollover locks, and dispute freezes — plus the derived availableTotal / heldTotal / withdrawable numbers the player-facing wallet should show.

Compliance (MGA-grade) 5

GET /api/compliance/amlAlerts

AML alert queue

Params status?, severity?; POST …/{alertId}/status

The gap today

AML monitoring (velocity, structuring, minimal-play flow-through) is a licensing requirement with zero atlas support.

What it generates

Rule-generated alerts: customer, rule, severity, amount, SoW/SoF request state, case status. POST moves a case through OPEN → INVESTIGATING → CLEARED/SAR_FILED with an immutable trail.

GET /api/compliance/sarExport

SAR/STR export

Params alertIds[]

The gap today

Filing a suspicious-activity report means hand-assembling data from a dozen screens.

What it generates

A regulator-format bundle per case: identity, account history, flagged transactions, prior alerts — ready to attach to the FIU filing.

GET /api/compliance/screening/{customerId}

PEP & sanctions screening

Params refresh=1 to re-run

The gap today

PEP/sanctions checks (OFAC, EU, UN lists) are mandatory at onboarding and periodically; the atlas has nothing.

What it generates

Match results with list, score, and disposition workflow (confirmed false positive / true match → freeze + report).

GET /api/compliance/regulatoryReturn

Regulatory return export

Params period=YYYY-MM, jurisdiction

The gap today

MGA (and every EU regulator) requires periodic returns — player counts, GGR by vertical, RG statistics, player-funds balances. Assembling them is days of manual work.

What it generates

The complete return dataset for the period: GGR/NGR by vertical and jurisdiction, tax due, new/active/excluded player counts, limit-setting statistics, and the player-funds segregation balance (liabilities vs segregated account).

GET /api/audit/log

Back-office audit log

Params actor?, fromDate, toDate, operation?

The gap today

adminUser operations exist but nothing records WHO did WHAT to WHOM — an auditor's first request.

What it generates

Every privileged action: actor, operation, target entity, before/after values, IP, timestamp. Immutable, exportable.

Bonus engine & CRM 9

POST /api/bonus/freeSpins

Free-spin grants

Params body: customerIds[]|segmentId, gameId, spins, spinValue, expiresAt

The gap today

The Bonus tag (17 ops) is cash-bonus only — free spins, the #1 casino retention tool, are absent.

What it generates

Grant records per player with redemption tracking (spins used, winnings converted to bonus balance, expiry). GET twin reports redemption + cost per campaign.

GET /api/bonus/abuseFlags

Bonus-abuse detection

Params —

The gap today

Multi-account bonus farming silently drains every promotion; detection today is manual cross-referencing.

What it generates

Players flagged by rule: duplicate-cluster membership, bonus-to-deposit ratio outliers, min-rollover-then-withdraw patterns, per-flag evidence. Feeds the duplicates queue and grant blocklists.

POST /api/messaging/campaigns

Segmented campaign send

Params body: segmentId, channel=email|sms|push|onsite, template, schedule

The gap today

Mailer ops exist (6) but only raw sends — no segments, no scheduling, no stats.

What it generates

A campaign object; GET …/{id}/stats returns delivered/opened/clicked/converted (deposits within attribution window) and per-player suppressions (RG/comm-prefs). Closes the marketing loop.

GET /api/gamification/tournaments

Tournaments & leaderboards

Params CRUD + GET /{id}/leaderboard

The gap today

Gamification (tournaments, missions, leaderboards — Soft2Bet/Smartico's whole business) has no atlas presence.

What it generates

Tournament definitions (vertical, scoring rule, prize pool, dates) and live leaderboards. POST creates; the platform scores automatically from wager flow.

GET /api/gamification/missions

Missions / achievements

Params CRUD

The gap today

Same gap as tournaments.

What it generates

Mission definitions (rule, reward, active window) + completion counts + per-player progress. Drives the 'quests' retention surface.

GET /api/customers/{customerId}/churnScore

Churn prediction

Params —

The gap today

Dormancy is only visible after it happens.

What it generates

A 0–1 churn probability with drivers (deposit frequency decay, session gap growth, net-loss streak) and a recommended intervention (reload offer / VIP call / rest nudge). Batch twin: /api/reports/churnRisk lists the top-risk cohort.

POST /api/bonus/coupons

Coupon-code bonus templates (RTG-style)

Params body: code, kind=DEPOSIT_MATCH|FREE_CHIP|CASHBACK, matchPct, maxBonus, sticky, rollover (e.g. 40 for 40x), rolloverBase=B|D+B, maxWin (fixed | multiple-of-deposit | none), allowedGames, expiryDays; GET lists, PUT updates/deactivates

The gap today

RTG-branded casinos run their entire promotion economy on coupon codes redeemed at the cashier — sticky (non-cashable) bonuses, 30x–80x playthrough, max-cashout caps, game restrictions. The atlas Bonus tag (17 ops) can assign amounts to players but has no template/coupon concept, no sticky flag, no max-win cap, and no game weighting.

What it generates

The stored coupon template with every knob above. Redemptions reference the code; the platform enforces sticky-at-withdrawal removal, rollover accrual with per-category game weights, and the max-win cap at cashout time.

GET /api/customers/{customerId}/bonusWallet

Player bonus wallet (rollover next to balance)

Params —

The gap today

The player must SEE their remaining rollover next to their balance — and support must see the same number. The atlas has GET /api/bonus/{id}/rollover per bonus, but nothing aggregated, nothing with sticky/max-win context, and nothing shaped for the wallet display.

What it generates

The exact wallet strip the player-facing site renders: cash balance, bonus balance with sticky flag (sticky bonus is removed at withdrawal, not cashed), TOTAL remaining rollover in dollars (e.g. "wager $3,120 more to unlock withdrawals"), per-grant breakdown (code, granted, rollover done/required, expiry), and any active max-win caps. One call, one strip.

GET /api/bonus/instances

Bonus instance ledger with instance ids stamped on wagers and transactions

Params customerId?, status?, fromDate/toDate

The gap today

Assigned bonuses come back as an undated blob per customer with no stable instance id, and neither wager rows nor BONUS transactions reference which grant they belong to — so wagering-funnel completion, per-campaign cost and abuse recycling (plan §1-E) can only be approximated. Second-priority platform ask after fundSource.

What it generates

One row per bonus instance (stable bonusInstanceId, template/coupon code, granted/converted/forfeited/expired amounts, rollover done vs required, timestamps), with the same bonusInstanceId stamped on every wager row and BONUS-family transaction it touches. Turns the bonus ledger from grant-side accounting into a true instance roll-forward.

Platform 3

GET /api/brands

Multi-brand management

Params CRUD; every report accepts brandId?

The gap today

One license, several skins is the standard European structure (GiG/EveryMatrix are multi-brand-first); the atlas is single-brand.

What it generates

Brand list (skin domain, markets, currency) and makes brandId a first-class filter on every reporting endpoint.

GET /api/stream/events

Real-time event stream (SSE)

Params types=wagers,transactions,logins,alerts

The gap today

Everything today is polling; a live dashboard hammering /search endpoints is the direct cause of the 100MB problem.

What it generates

A server-sent-events stream of platform happenings as they occur — live tickers, instant alert delivery, and incremental warehouse sync (linepros-sync could tail this instead of re-walking days).

GET /api/affiliates/commissionPlans

Affiliate commission plans

Params CRUD; GET /api/affiliates/{id}/earnings

The gap today

Agent ops exist but only flat structures — revshare/CPA/hybrid plans (NetRefer's Maltese bread and butter) are absent.

What it generates

Plan definitions (revshare %, CPA trigger, hybrid, negative-carryover policy) and per-affiliate computed earnings per period, with the player-level detail behind each number.

Provider integrations (RTG / BetSoft) 3

GET /api/providers

Game-provider registry

Params —

The gap today

The platform serves BetSoft and RTG titles but exposes nothing about the integrations themselves — which providers are wired, over which contract, in what health.

What it generates

One row per provider: integration model (BetSoft = CWS seamless-wallet ExtSystem callbacks per WagerStreet/docs/betsoft-integration.md; RTG = seamless one-wallet), game-server host, bankId/operator id, connection status, and 24h callback volume. The provider-ops overview panel.

GET /api/providers/{providerKey}/callbacks

Callback health per method

Params fromDate, toDate

The gap today

When BetSoft retries betResult or hash validation starts failing, today nobody sees it until players complain about stuck balances.

What it generates

Per callback method (authenticate/balance/betResult/refundBet/bonusRelease/bonusWin/tournament for BetSoft): call volume, average latency, hash failures, idempotent replays served, business-error histogram (300 insufficient funds, 302 unknown refund tx, 500 invalid hash). The alarm feed for the wallet integration.

POST /api/providers/{providerKey}/reconcile

Round reconciliation run

Params body: date

The gap today

BetSoft echoes request params inside <REQUEST> for reconciliation, and rounds carry roundId/isRoundFinished — but there is no operation to reconcile provider rounds against wallet transactions.

What it generates

A diff for the day: provider-reported rounds/amounts vs wallet ledger — orphaned bets (debited, round never finished), unmatched wins, refunds without originals. Anything non-empty becomes a finance ticket.