Integrating with GAME¶
Who is this page for?
Integrators wiring GAME into a product. This is the task-oriented guide: how to do each thing. For the exhaustive endpoint list see REST API Reference; for scoring logic see Strategies.
All paths below are relative to the API base /api/v1 and require
authentication (Authentication).
The mental model¶
A typical integration touches four objects in order:
Game ──contains──► Task ──scored by──► Strategy ──awards──► Points ──► Wallet
You create a game once, add tasks to it, then report user activity against tasks to award points, which accrue into each user’s wallet.
Managing games¶
Create a game¶
POST /api/v1/games
{
"externalGameId": "game-001",
"platform": "web",
"strategyId": "default"
}
externalGameId- your identifier for the campaign (unique per key).platform- free-form label for where the game runs.strategyId- the default strategy for the game’s tasks. Use a built-in id (e.g.default) or a custom one (custom:<uuid>). Omit to fall back todefault.
The response includes the internal gameId (UUID) - address the game by
this id thereafter.
Read, update, duplicate, delete¶
Operation |
Endpoint |
|---|---|
List games (paginated) |
|
Get one game |
|
Update game (partial) |
|
Duplicate a game (with its tasks/params) |
|
Delete a game |
|
Inspect a game’s effective strategy |
|
List enrolled users |
|
PATCH accepts a custom:<uuid> strategy id and validates it against the
custom-strategy registry, so you can move a live game onto a DSL strategy
without recreating it.
Managing tasks¶
Tasks live under a game and inherit its strategy unless they declare their own.
POST /api/v1/games/{gameId}/tasks
{ "externalTaskId": "task-login" }
Operation |
Endpoint |
|---|---|
Create a task |
|
Create many at once |
|
List tasks in a game |
|
Get one task (by external id) |
|
Update a task (partial) |
|
Duplicate a task |
|
Delete a task |
|
Note
Reads use the external task id; mutations that target a specific row (PATCH/DELETE/duplicate) use the internal taskId (UUID). The Domain Model explains the internal/external split.
Awarding points¶
The core write. Report that a user did something worth scoring:
POST /api/v1/games/{gameId}/tasks/{externalTaskId}/points
{
"externalUserId": "user-123",
"data": { "event": "task_completed", "source": "mobile-app" },
"isSimulated": false
}
Response:
{
"points": 1,
"caseName": "BasicEngagement",
"isACreatedUser": true,
"gameId": "4ce32be2-...-0520",
"externalTaskId": "task-login",
"created_at": "2026-02-10T12:30:00Z"
}
Key behaviors:
Lazy users - an unknown
externalUserIdis created on the fly;isACreatedUsertells you whether that happened.``data`` - an arbitrary payload passed to the strategy and stored with the award. Adaptive strategies read it (e.g. completion time).
``caseName`` - the human-readable reason the awarding rule fired; the same field appears in analytics.
``isSimulated`` - when
true, scoring runs but nothing is persisted (no points, no wallet movement). See Strategies.
Idempotency¶
Supply an idempotency key (request header, surfaced into the event as
eventId) and a retried request will not double-award. Pair this with a
correlation id header to trace a request end to end through the logs. This is
what makes point assignment safe to retry on network failure. Under a
concurrent retry race (two retries with the same key in flight at once) the
duplicate is still rejected at the database, so no double-award occurs; the
losing request receives 409 Conflict instead of the original success body
(see Known limitations).
Rate limits¶
Scoring and action endpoints are rate-limited per API key, per IP, and per
external user, plus a daily quota per key. Exceeding a limit returns 429.
Tune the thresholds via the ABUSE_* variables in Configuration Reference;
the model is detailed in Security.
Recording explicit actions¶
Some games award points as a side effect of an action event rather than a direct points call:
POST /api/v1/games/{gameId}/tasks/{externalTaskId}/action
{
"typeAction": "TASK_COMPLETED",
"data": { "durationSeconds": 84, "source": "mobile-app" },
"description": "User completed the task from mobile flow",
"externalUserId": "user-123"
}
This persists a UserActions record (audit + scoring input) and, when the
game requires it, drives point assignment.
Reading points¶
GAME exposes aggregates at every granularity:
Question |
Endpoint |
|---|---|
All points in a game (by task & user) |
|
…with per-award detail history |
|
One user’s total in a game |
|
All users’ points in a task |
|
…with per-award detail |
|
One user in one task |
|
A user’s points across everything |
|
Bulk/filtered points query |
|
Aggregates carry both points (total) and timesAwarded (how many
scoring events produced it).
Wallets & the economy¶
Each user has exactly one wallet tracking a points balance and a coins balance, governed by a conversion rate (see Domain Model).
Operation |
Endpoint |
|---|---|
Read a user’s wallet |
|
Preview a points→coins conversion |
|
Execute a conversion |
|
Each recorded conversion stores the appliedConversionRate captured at the
time, so a recorded conversion can be traced back to the rate that produced it.
The balance update and its ledger row are written in two separate transactions
today, so a crash between them can leave a conversion without a recording row
(see Known limitations).
Corrections and reversibility¶
The wallet ledger is append-only and immutable - existing rows are never
updated or deleted. (A conversion can currently fail to write its ledger row at
all under a crash mid-conversion; see Known limitations.) GAME has no
refund or reversal operation today: no endpoint subtracts points already
awarded or rolls a conversion back, and only two transaction types are ever
written - AssignPoints and ConvertPointsToCoins. The other names in the
WalletTransactions model (refunds, transfers, manual adjustments) are
reserved but unimplemented.
So “how do I undo a point I awarded by mistake?” has no first-class answer yet. Until refunds land, the practical options are:
award a compensating amount through a strategy that accounts for the error, or
correct the source data and re-run the affected scoring out of band.
A ledger-preserving refund/adjustment operation is on the roadmap (see
ROADMAP.md). It is planned to add new transaction types rather than mutate
existing rows, so the audit trail stays intact.
Known limitations¶
GAME is honest about two edge cases in the write path. Neither corrupts data under normal sequential use, but both are worth knowing before you build on them.
Non-atomic points-to-coins conversion¶
Point assignment is atomic: the UserPoints row, the wallet balance
change, and the ledger entry commit in a single transaction (see
ADR 0003: Atomic points write). The points-to-coins conversion
path is not. UserService.convert_points_to_coins
(app/services/user_service.py) writes the new wallet balance and the
ConvertPointsToCoins ledger row in two separate transactions. If the
process dies between them, the coins can move with no ledger row recording the
movement.
Consequence: for conversions only, the ledger is not guaranteed to be a
complete reconstruction of every balance change; assignment history is
unaffected. Wrapping the conversion in one transaction is a planned fix (see
ROADMAP.md). Until then, treat a missing conversion row as the rare
crash-window artifact it is, and reconcile from the wallet balance if needed.
409 on a concurrent retry race¶
Idempotency keys make awards safe to retry
(ADR 0004: Idempotency keys for awards). A sequential retry with the same
key returns the original assignment. But two retries with the same key arriving
at the same instant both pass the “does this key already exist?” check
before either commits, and the database unique constraint then lets exactly one
win. The loser does not double-award - it is rejected with 409 Conflict
rather than receiving the original success body.
Consequence: no double-award ever happens, but a caller that fires concurrent
retries of the same event must read a 409 on the points endpoint as “the
award already went through”, not as a hard failure. Spacing retries (or relying
on the first response) avoids the race entirely.
Analytics, KPIs & exports¶
Need |
Endpoint |
|---|---|
Dashboard summary / log feed |
|
Service health |
|
Data exports (CSV-style history) |
|
Export operations are recorded in an ExportAuditLog for compliance.
A complete integration sketch¶
1. (admin, once) POST /apikey/create → API_KEY
2. POST /games → gameId
3. POST /games/{gameId}/tasks → task(s)
4. (optional) PATCH game/task to attach a strategy (built-in or custom)
5. (per event) POST /games/{gameId}/tasks/{externalTaskId}/points
with an idempotency key → points + caseName
6. (on demand) GET …/points and …/wallet → read state
7. (optional) POST /users/{externalUserId}/convert → spend points
Next: Strategies to control how many points step 5 awards, and REST API Reference for the full surface.