d-sports-api under server/games/. Client routes are /api/v1/engage/games/*. The older /api/games/* play routers stay for Daily Spin, Guess the Player, and live arena.
HTTP surface
Base path:/api/v1/engage/games. Catalog is public. Session verbs require a Clerk bearer token. Send X-Client-Version on every call.
locker-singularity is registered on the client with sessionMode: "idle-ledger". It does not call session, action, complete, or forfeit. The server owns the loop at /api/v1/engage/games/locker-singularity/ledger (read, taps, buy, upgrades, prestige). Claim-boost is reserved and always returns 410 BOOST_DISABLED in v1. Offline accrual uses the server lastSeenAt (8 hour cap, 50% efficiency). The client never sends a timestamp and never sends buzz amounts — taps are a count, and the server clamps each call at 100.
These routes are published in OpenAPI (Game Engine tag). See the API guide and the generated Endpoints pages.
Catalog rules
GET /api/v1/engage/games/catalog is additive to GET /api/games/catalog. Keep both. Older clients still read the unversioned route.
Engine rows extend the public catalog fields (id, key, title, description, icon, badge) with:
playable(default true)reason:UPDATE_REQUIREDorNOT_SHIPPEDminClientVersion
key and gameKey to the same slug. Grouping is { daily, weekly } (a { games } payload is also accepted and split the same way).
Unplayable games stay in the list so the client can show Update required.
Session contract
The server is authoritative. The client sends raw input. Outcomes, points, and status come back from the API. Statuses you must handle:ACTIVE | PENDING_RESOLUTION | SETTLING | COMPLETE | FORFEITED | EXPIRED
Projected session fields:
sessionId(orid)gameKey,statusscenario— secrets stripped. Do not expectcorrectIndex,resolutionKey, oranswer.configSnapshot(orconfig)attemptsUsed,maxAttempts,maxPointsPerDay,pointsAwardedteamId,leaderboardId,expiresAt,dayKey
gameKey per UTC dayKey.
Action response: { outcome, session? }. Outcome carries id plus optional label, detail, pointsAwarded, bonuses[], and tier. If the payload has no session id, keep the session you already have.
Complete response: { totalPoints, awarded, requested?, …, status?: COMPLETE | PENDING_RESOLUTION }. Render awarded in the HUD.
Forfeit reasons the client sends: backgrounded, navigated_away, visibility_lost, context_changed, expired.
v1 input
Pick’em and Trivia send{ optionKey } from 2–6 { key, label } options. The client parser rejects leaked secret fields rather than playing them. Revealed keys (revealedCorrectKey / revealedWinningKey) appear only after submit.
- Pick’em — one UTC-day pick. Locks on submit. Stays
PENDING_RESOLUTIONuntil the real-world result settles. - Trivia — immediate resolve. Distinct from locker-room feed polls.
Errors
Do not retry authorization or eligibility errors as if they were transient network failures.
Models (public-safe)
Game—key, cadence,configJson,rewardJson,minClientVersionTeamGame/TeamGameSchedule— team enablementGamePrompt— Pick’em/trivia prompts. Correctness stays server-side.GameSession— unique on(userId, gameKey, dayKey)GameSessionAction,GameRewardIssuance
/api/v1/engage/rep/earn from the playfield.
What this page does not cover
- Admin catalog CRUD and team assignment internals
- Settlement sweeper / cron internals
- Physics helpers used by non-v1 content verticals
- Legacy
/api/games/daily-spin/*,/guess-player/*, and/live/*
