Currencies
Virtual currency definitions, admin grants, per-user transaction history.
Currencies are named tokens (code like "gems") with optional cap (max_balance) and auto-recharge rules. user_balances holds per-user state. Grants are performed atomically under a FOR UPDATE row lock with a pre-flight zero-row insert so first-time grants can't race.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects/:projectId/currencies | Create a currency definition. |
| GET | /admin/projects/:projectId/currencies | List currency definitions. |
| PATCH | /admin/projects/:projectId/currencies/:currencyId | Partial update. |
| DELETE | /admin/projects/:projectId/currencies/:currencyId | Delete. |
| POST | /admin/projects/:projectId/currencies/grant | Grant currency to a user (admin source). |
| POST | /admin/projects/:projectId/currencies/spend | Debit currency from a user (admin source). |
| GET | /admin/projects/:projectId/currencies/transactions/:userId | Paginated transaction history for a user. |
POST /admin/projects/:projectId/currencies
Request
| Field | Type | Required | Default |
|---|---|---|---|
code | string | yes | — |
name | string | yes | — |
description | string | no | null |
is_premium | boolean | no | false |
initial_balance | number | no | 0 |
max_balance | number | no | null (uncapped) |
auto_recharge_amount | number | no | null |
auto_recharge_interval_hours | number | no | null |
When initial_balance > 0, that starting amount is spendable. On a user's first grant or spend of this currency, Amba lazily credits the starting balance into the ledger as a system transaction before applying the operation, so the balance read and the summed transaction history stay in agreement and the starting amount can be spent immediately. A currency with initial_balance of 0 credits nothing — the user starts empty.
Response 201
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/currenciescurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/currencies'Curl:
GET /admin/projects/:projectId/currencies
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/currenciescurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/currencies'Curl:
PATCH /admin/projects/:projectId/currencies/:currencyId
Allowed fields: name, description, is_premium, initial_balance, max_balance, auto_recharge_amount, auto_recharge_interval_hours.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/currencies/%7B%7BcurrencyId%7D%7Dcurl -X PATCH 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/currencies/%7B%7BcurrencyId%7D%7D'Curl:
DELETE /admin/projects/:projectId/currencies/:currencyId
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/currencies/%7B%7BcurrencyId%7D%7Dcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/currencies/%7B%7BcurrencyId%7D%7D'Curl:
POST /admin/projects/:projectId/currencies/grant
Grant currency to a user. Clamped to max_balance if set; a fully-clamped grant still returns 201 with a zero-delta currency_transactions row for auditability.
Request (GrantCurrencyInput)
| Field | Type | Required | Description |
|---|---|---|---|
app_user_id | uuid | yes | Recipient. |
currency_code | string | yes | Must exist in currency_definitions. |
amount | number | yes | Must be finite and > 0. |
reason | string | no | Free-form reference id stored on the transaction. |
idempotency_key | string | no | ≤255 chars. Repeating the same key for this user + currency replays the original transaction without re-applying the balance change. See Idempotency. |
Response 201
Response 200 (idempotent replay)
A repeat with a previously-seen idempotency_key (same user + currency) returns the original transaction with "idempotent_replay": true and HTTP 200 — the balance is not changed. See Idempotency.
Errors
400 INVALID_AMOUNT— amount isn't a positive finite number.404 CURRENCY_NOT_FOUND— unknowncurrency_code.500 GRANT_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/currencies/grantcurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/currencies/grant'Curl:
POST /admin/projects/:projectId/currencies/spend
Debit currency from a user. Atomic — returns 400 INSUFFICIENT_FUNDS if the balance is too low. No partial debit; the request either fully succeeds or fully fails.
The ledger records the spend as a negative amount (signed delta), so SUM(amount) across a user's transactions reconstructs the current balance.
Request (SpendCurrencyInput)
| Field | Type | Required | Description |
|---|---|---|---|
app_user_id | uuid | yes | User to debit. |
currency_code | string | yes | Must exist in currency_definitions. |
amount | number | yes | Positive, finite. The endpoint records it as a negative delta on the ledger. |
reason | string | no | Free-form reference id stored on the transaction. |
idempotency_key | string | no | ≤255 chars. Repeating the same key for this user + currency replays the original transaction without re-debiting. A spend that failed with INSUFFICIENT_FUNDS stores no key, so that key can be retried. See Idempotency. |
Response 201
Response 200 (idempotent replay)
A repeat with a previously-seen idempotency_key (same user + currency) returns the original transaction with "idempotent_replay": true and HTTP 200 — the balance is not changed. See Idempotency.
Errors
400 INVALID_AMOUNT— amount isn't a positive finite number.400 INSUFFICIENT_FUNDS— current balance is belowamount. The error message includes both values. No key is stored, so the sameidempotency_keymay be retried after the user tops up.404 CURRENCY_NOT_FOUND— unknowncurrency_code.500 SPEND_FAILED.
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/currencies/spendcurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/currencies/spend'Curl:
Idempotency
Both grant and spend accept an optional idempotency_key (string, ≤255 chars). Sending the same key again for the same app_user_id + currency_code makes the operation exactly-once:
- The first request applies the balance change and responds
201with"idempotent_replay": false. - Any later request with the same key returns the original transaction unchanged, responds
200with"idempotent_replay": true, and does not re-apply the delta.
The key is permanent — it's anchored to the ledger row it created, with no expiry window. The caller controls the dedup window through what they encode into the key. To cap a reward at "one grant per share per day," embed the date: idempotency_key: "share:{userId}:{yyyy-mm-dd}". The first call that day lands; the rest are no-op replays; the next day's key is different so the grant lands again — no guard collection of your own required.
A spend that fails with INSUFFICIENT_FUNDS stores no key (no ledger row was written), so the same key can be retried after the user tops up.
The same idempotency_key field is available on the amba_currencies_grant and amba_currencies_spend MCP tools, with identical semantics.
GET /admin/projects/:projectId/currencies/transactions/:userId
Paginated transaction history for a single user, most recent first.
Query
| Param | Default |
|---|---|
limit | 50 |
offset | 0 |
Response 200
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/currencies/transactions/%7B%7BuserId%7D%7Dcurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/currencies/transactions/%7B%7BuserId%7D%7D'Curl: