XP
Manage XP rules and inspect per-user XP balances.
XP rules map engagement events to XP awards; they're evaluated automatically on every POST /client/events write. This module also exposes read endpoints for per-user XP balances and two manual adjustment endpoints (/xp/grant, /xp/deduct) for support corrections and promos.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects/:projectId/xp | Create an XP rule. |
| GET | /admin/projects/:projectId/xp | List XP rules. |
| GET | /admin/projects/:projectId/xp/config | Read the project's level curve. |
| PATCH | /admin/projects/:projectId/xp/config | Set xp_per_level and/or level_thresholds. |
| PATCH | /admin/projects/:projectId/xp/:ruleId | Partial update. |
| DELETE | /admin/projects/:projectId/xp/:ruleId | Delete a rule. |
| POST | /admin/projects/:projectId/xp/grant | Manually add XP to a user (support / promo). |
| POST | /admin/projects/:projectId/xp/deduct | Manually subtract XP from a user (inverse of grant). |
| GET | /admin/projects/:projectId/xp/users/:userId | Fetch a single user's XP. |
| GET | /admin/projects/:projectId/xp/users | Paginated leaderboard-style list of users by total XP. |
POST /admin/projects/:projectId/xp
Request
| Field | Type | Required | Default |
|---|---|---|---|
name | string | yes | — |
event_name | string | yes | — |
xp_amount | number | yes | — |
max_per_day | number | null | no | null |
cooldown_seconds | number | no | 0 |
Response 201
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xpcurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp'Curl:
GET /admin/projects/:projectId/xp
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xpcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp'Curl:
GET /admin/projects/:projectId/xp/config
The project's effective level curve.
Response 200
PATCH /admin/projects/:projectId/xp/config
Set the level curve. Provide xp_per_level and/or level_thresholds (at least one). A partial patch preserves the untouched field.
Request
| Field | Type | Notes |
|---|---|---|
xp_per_level | integer | Flat curve: level = floor(total_xp / xp_per_level) + 1. Must be a positive integer. |
level_thresholds | integer[] / null | Explicit cumulative-XP boundaries, strictly increasing. Wins over xp_per_level. null clears it. |
Response 200
persisted is true once the curve is durably stored, false on a project provisioned before the feature shipped (the effective default is returned but not saved). Setting a curve does not retroactively recompute existing user levels — they rebase on each user's next XP write.
Errors
400 VALIDATION_ERROR— neither field supplied.400 INVALID_XP_PER_LEVEL—xp_per_levelis not a positive integer.400 INVALID_LEVEL_THRESHOLDS— not a strictly-increasing array of positive integers (ornull).500 UPDATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xp/configcurl -X PATCH 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp/config' \
-H 'Content-Type: application/json' \
-d '{
"xp_per_level": 500
}'Curl:
PATCH /admin/projects/:projectId/xp/:ruleId
Allowed fields: name, event_name, xp_amount, max_per_day, cooldown_seconds, is_active.
Errors
404 NOT_FOUND.500 UPDATE_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xp/%7B%7BruleId%7D%7Dcurl -X PATCH 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp/%7B%7BruleId%7D%7D'Curl:
DELETE /admin/projects/:projectId/xp/:ruleId
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xp/%7B%7BruleId%7D%7Dcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp/%7B%7BruleId%7D%7D'Curl:
POST /admin/projects/:projectId/xp/grant
Manually add XP to a user — for rewards, promos, or support corrections. Writes a signed (positive) xp_ledger row and bumps the user's total_xp. The user's level is recomputed from the new total so their displayed level stays consistent.
This is a manual adjustment, not an event. It deliberately does not fire the rule-driven side-effects an automatic award would: no level-up engagement events are emitted and achievement criteria are not re-evaluated. The ledger row is written with xp_rule_id = null and a reason of admin_grant (override with reason) so the adjustment is auditable and distinguishable from rule-driven XP. To also award a badge, call /achievements/:id/grant explicitly; to top up a currency, use /currencies/grant.
Request (AdjustXpInput)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
app_user_id | uuid | yes | — | User to grant XP to. A first-time grant creates the user's XP row. |
amount | number | yes | — | Positive integer magnitude of XP to add. |
reason | string | no | admin_grant | Stored on the ledger row for the audit trail. |
Response 201
Errors
400 VALIDATION_ERROR—app_user_idmissing.400 INVALID_AMOUNT—amountisn't a positive integer (fractional amounts are rejected, not truncated).400 INVALID_ID—app_user_idisn't a UUID.500 XP_ADJUST_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xp/grantcurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp/grant'Curl:
POST /admin/projects/:projectId/xp/deduct
The inverse of grant: subtract XP from a user's total. Atomic — returns 400 INSUFFICIENT_XP if the deduction would drive the total below zero (no partial deduct). Like grant, this is a manual adjustment that does not replay rule side-effects; the ledger records the magnitude as a negative delta (so SUM(amount) over a user's ledger reconstructs total_xp) with xp_rule_id = null and a reason of admin_deduct.
Request (AdjustXpInput)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
app_user_id | uuid | yes | — | User to deduct XP from. |
amount | number | yes | — | Positive integer magnitude of XP to subtract. |
reason | string | no | admin_deduct | Stored on the ledger row for the audit trail. |
Response 201
Errors
400 VALIDATION_ERROR—app_user_idmissing.400 INVALID_AMOUNT—amountisn't a positive integer.400 INVALID_ID—app_user_idisn't a UUID.400 INSUFFICIENT_XP— the deduction would drivetotal_xpbelow zero. The message includes the requested amount and the user's current total.500 XP_ADJUST_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xp/deductcurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp/deduct'Curl:
GET /admin/projects/:projectId/xp/users/:userId
Response 200
Errors
404 NOT_FOUND— user has no XP balance yet (no XP-awarding events have fired).
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xp/users/%7B%7BuserId%7D%7Dcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp/users/%7B%7BuserId%7D%7D'Curl:
GET /admin/projects/:projectId/xp/users
Paginated list ordered by total_xp DESC.
Query
| Param | Default |
|---|---|
limit | 50 |
offset | 0 |
Response 200
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/xp/userscurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/xp/users'Curl: