Skip to content
Skip the menu

API reference

Wallet API

Read a player's balances, grant them free currency and spend it from your backend with the server API.

View as Markdown

On this page

A player has one balance per currency, split in two parts: paid, bought with real money, and bonus, granted or earned for free. This page reads a wallet, credits free currency and debits currency; every change is recorded in the ledger. Selling a currency for real money is a checkout, not a grant. The examples on this page continue from the setup.

Get a wallet

Returns every balance of a player. Use it to show a player their coins, or to check a balance before you decide something.

GET/players/{player}/wallet

Authentication: secret key. Idempotency: not needed.

Path parameters

ParameterTypeDescription
playerstringA GameCoin player id, or ext:<your id> URL-encoded once.
LangageLanguage
curl "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42/wallet" \
  -H "Authorization: Bearer $GAMECOIN_SECRET_KEY"

Response 200 OK: a Wallet.

JSON
{
  "playerId": "665f1c2e8a3b4d5e6f708192",
  "balances": [
    { "currency": "gems", "paid": 0, "bonus": 70, "total": 70 },
    { "currency": "gold", "paid": 0, "bonus": 0, "total": 0 }
  ]
}

Errors

StatusCodeWhenWhat to do
400VALIDATION_FAILEDThe player reference is malformed (PLAYER_REF_INVALID)Encode the ext: reference exactly once.
404NOT_FOUNDThe player does not existAn ext: player exists after its first write; before that, treat the wallet as empty.

Notes

  • balances has one line per active currency, zeros included: here gold is there although the player never received any. A currency that is archived disappears from the list.
  • total is paid + bonus. Only paid comes from real money, which is what a refund takes back.
  • A balance can be negative after a refund of coins the player had already spent (the default of a game; the game setting « Après un remboursement » in the dashboard, which is in French for now, can stop balances at zero instead). The amount spent shows as a debt on paid, and the next credits repay it first. A spend never makes a balance negative.

Grant currency

Credits free currency to a player: a quest reward, a daily gift, a compensation. It is for your backend only: the client API cannot credit a player, except through a gift code that you created.

POST/players/{player}/wallet/grant

Authentication: secret key. Idempotency: required (Idempotency-Key header).

Path parameters

ParameterTypeDescription
playerstringA GameCoin player id, or ext:<your id> URL-encoded once. An unknown ext: player is created.

Body

FieldTypeRequiredRules
currencystringyesA currency code of the catalog: 2 to 24 characters, a lowercase letter then lowercase letters, digits or _ (gems, gold).
amountintegeryesA whole number from 1 to 1,000,000,000,000.
reasonstringnoFree text, at most 500 characters, recorded in the ledger.
metadataobjectnoFree string to string pairs recorded in the ledger: at most 20 entries, keys of 1 to 64 characters (letters, digits, _, ., -), values of at most 500 characters.
LangageLanguage
curl -X POST "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42/wallet/grant" \
  -H "Authorization: Bearer $GAMECOIN_SECRET_KEY" \
  -H "Idempotency-Key: grant-level-5-user-42" \
  -H "Content-Type: application/json" \
  -d '{"currency": "gems", "amount": 100, "reason": "level_up", "metadata": {"level": "5"}}'

Response 200 OK: the ledger entry that was written, and the new balance of the currency.

FieldTypeDescription
entryLedgerEntryThe entry: type grant, with your reason and metadata.
balanceBalanceThe balance of the currency after the grant.
JSON
{
  "entry": {
    "id": "6ac4214bae33663917ec0ca6",
    "type": "grant",
    "currency": "gems",
    "paidDelta": 0,
    "bonusDelta": 100,
    "balanceAfter": { "paid": 0, "bonus": 100, "total": 100 },
    "reason": "level_up",
    "metadata": { "level": "5" },
    "ref": {},
    "createdAt": "2026-10-05T22:14:35.965Z"
  },
  "balance": { "currency": "gems", "paid": 0, "bonus": 100, "total": 100 }
}

Send the same request again with the same key, and you get the same answer with the header Idempotent-Replayed: true, without a second grant:

Bash
curl -i -X POST "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42/wallet/grant" \
  -H "Authorization: Bearer $GAMECOIN_SECRET_KEY" \
  -H "Idempotency-Key: grant-level-5-user-42" \
  -H "Content-Type: application/json" \
  -d '{"currency": "gems", "amount": 100, "reason": "level_up", "metadata": {"level": "5"}}'
HTTP
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Idempotent-Replayed: true

{
  "entry": {
    "id": "6ac4214bae33663917ec0ca6",
    "type": "grant",
    "currency": "gems",
    "paidDelta": 0,
    "bonusDelta": 100,
    "balanceAfter": { "paid": 0, "bonus": 100, "total": 100 },
    "reason": "level_up",
    "metadata": { "level": "5" },
    "ref": {},
    "createdAt": "2026-10-05T22:14:35.965Z"
  },
  "balance": { "currency": "gems", "paid": 0, "bonus": 100, "total": 100 }
}

Errors

StatusCodeWhenWhat to do
400IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or emptySend one: see Idempotency.
400VALIDATION_FAILEDAn unknown field (paid, bonus…); amount not an integer between 1 and 10¹²; currency malformed; reason too long; metadata with more than 20 entries (METADATA_TOO_LARGE), an invalid key or a value that is not a string; a key that is not 1 to 100 printable ASCII charactersRead details.fieldErrors.
403PLAYER_BLOCKEDThe player is blockedNothing can be written for this player until the player is unblocked.
404NOT_FOUNDThe currency is not in the catalog (or is archived), or a GameCoin id does not existCheck the code against Get the catalog.
409IDEMPOTENCY_CONFLICTThe key was already used with a different requestUse a new key for a new grant, and the same key only to retry the same one.
409LIMIT_REACHEDThe balance would exceed 1,000,000,000,000,000Grant less.
JSON
{
  "error": {
    "code": "IDEMPOTENCY_CONFLICT",
    "message": "Idempotency-Key already used with a different request"
  }
}

Notes

  • A grant always credits the bonus part, whatever the currency. paid coins only come from packs bought with real money, and a body field named paid is refused. The one exception is a player in debt (see Get a wallet): the credit repays the debt first, and the entry's paidDelta and bonusDelta show where it went.
  • Derive the key from the event you reward, such as quest-17-user-42, so that a job that runs twice grants once. The SDKs generate a key for you if you give none, and expose the header as replayed (Replayed in Go and C#) on the result.
  • reason and metadata are for you: they come back in the ledger. Keep a quest id or a reward name there, never anything personal.
  • The first write on an unknown ext: player creates it.

Spend currency

Debits a currency from a player: they use coins, you charge for a revive, an entry fee, an unlock. To sell an item for coins, use Buy an item with currency: it debits and delivers in one step.

POST/players/{player}/wallet/spend

Authentication: secret key. Idempotency: required (Idempotency-Key header).

Path parameters

ParameterTypeDescription
playerstringA GameCoin player id, or ext:<your id> URL-encoded once. An unknown ext: player is created, with an empty wallet.

Body

FieldTypeRequiredRules
currencystringyesA currency code of the catalog (2 to 24 characters, lowercase).
amountintegeryesA whole number from 1 to 1,000,000,000,000.
reasonstringnoFree text, at most 500 characters, recorded in the ledger.
metadataobjectnoFree string to string pairs: at most 20 entries, keys of 1 to 64 characters (letters, digits, _, ., -), values of at most 500 characters.
LangageLanguage
curl -X POST "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42/wallet/spend" \
  -H "Authorization: Bearer $GAMECOIN_SECRET_KEY" \
  -H "Idempotency-Key: spend-revive-user-42" \
  -H "Content-Type: application/json" \
  -d '{"currency": "gems", "amount": 30, "reason": "revive"}'

Response 200 OK: the ledger entry that was written (type spend, with negative deltas) and the new balance.

FieldTypeDescription
entryLedgerEntryThe entry. bonusDelta and paidDelta say where the coins were taken from.
balanceBalanceThe balance of the currency after the spend.
JSON
{
  "entry": {
    "id": "6ac4214ceab6d4425553aff6",
    "type": "spend",
    "currency": "gems",
    "paidDelta": 0,
    "bonusDelta": -30,
    "balanceAfter": { "paid": 0, "bonus": 70, "total": 70 },
    "reason": "revive",
    "metadata": {},
    "ref": {},
    "createdAt": "2026-10-05T22:14:36.491Z"
  },
  "balance": { "currency": "gems", "paid": 0, "bonus": 70, "total": 70 }
}

When the balance is too low, nothing is spent and the answer is 409 INSUFFICIENT_FUNDS, with what was needed and what the player has. Here is how to handle it:

LangageLanguage
curl -X POST "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42/wallet/spend" \
  -H "Authorization: Bearer $GAMECOIN_SECRET_KEY" \
  -H "Idempotency-Key: spend-unlock-user-42" \
  -H "Content-Type: application/json" \
  -w "\nHTTP %{http_code}\n" \
  -d '{"currency": "gems", "amount": 5000, "reason": "unlock"}'
JSON
{
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "Insufficient funds",
    "details": { "required": 5000, "available": 70 }
  }
}

Errors

StatusCodeWhenWhat to do
400IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or emptySend one: see Idempotency.
400VALIDATION_FAILEDAn unknown field; amount not an integer between 1 and 10¹²; currency malformed; reason too long; metadata invalid; a key that is not 1 to 100 printable ASCII charactersRead details.fieldErrors.
403PLAYER_BLOCKEDThe player is blockedNothing can be written for this player until the player is unblocked.
404NOT_FOUNDThe currency is not in the catalog, or a GameCoin id does not existCheck the code against Get the catalog.
409INSUFFICIENT_FUNDSThe balance is lower than amount: details.required and details.availableOffer the player a way to get more coins, such as a pack. Nothing was spent.
409IDEMPOTENCY_CONFLICTThe key was already used with a different requestUse a new key for a new spend.

Notes

  • Replay. The same key with the same request returns the original response again, with the header Idempotent-Replayed: true, and changes nothing.
  • Bonus coins are spent first, then paid ones, which is why the example above leaves paidDelta at 0. A game can reverse this order in its settings (« Ordre de dépense » in the dashboard, which is in French for now). The entry records both deltas.
  • A spend is all or nothing: it never takes part of the amount and never leaves a negative balance. After a refund left a player in debt, their available amount is 0.
  • A request that failed with INSUFFICIENT_FUNDS is not stored: once the player has more coins you can retry it with the same key.
  • A spend on an unknown ext: player creates the player, then fails with INSUFFICIENT_FUNDS.
  • The browser can spend too, with the player's own token: Spend currency in the client API.