API reference
Players API
Create and update players, read them back, and issue the tokens your game client needs, with the server API.
On this page
A player is someone who plays your game. Most games identify players by their own user id: GameCoin accepts it as ext:<your id> and creates the player the first time you write to it, so there is nothing to register in advance. A player id is the {player} of every path in this reference: see Referencing a player. The examples on this page continue from the setup.
Create or update a player
Creates the ext: player if it does not exist, otherwise updates the profile fields you send. Use it when a player signs up or changes their profile; you do not need it before a grant, a spend or a checkout, which create the player themselves.
/players/{player}Authentication: secret key. Idempotency: not needed: sending the same profile twice gives the same result.
Path parameters
| Parameter | Type | Description |
|---|---|---|
player | string | A GameCoin player id, or ext:<your id> URL-encoded once. A GameCoin id that does not exist answers 404. |
Body (JSON; every field is optional, and an empty body means {})
| Field | Type | Required | Rules |
|---|---|---|---|
displayName | string or null | no | Name shown in your game: 1 to 80 characters once trimmed. null clears it. |
email | string or null | no | A valid e-mail address of at most 254 characters. Stored, never returned by the API. null clears it. |
country | string or null | no | ISO 3166-1 alpha-2 code such as BE: two letters, upper-cased for you. It is the default country of the player's checkouts. null clears it. |
birthYear | integer or null | no | A whole year between 1900 and the current year. Stored, never returned by the API. null clears it. |
A field you leave out keeps its value; null erases it. Any other field is refused.
curl -X PUT "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42" \
-H "Authorization: Bearer $GAMECOIN_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"displayName": "Alice", "country": "BE"}'const profile = await gamecoin.players.upsert(player, { displayName: "Alice", country: "BE" });
console.log(profile.id, profile.externalId, profile.displayName);profile = gamecoin.players.upsert(player, display_name="Alice", country="BE")
print(profile.id, profile.external_id, profile.display_name)$profile = $gamecoin->players->upsert($player, ['display_name' => 'Alice', 'country' => 'BE']);
echo $profile->id, ' ', $profile->externalId, ' ', $profile->displayName, "\n";name, country := "Alice", "BE"
profile, err := client.Players.Upsert(ctx, player, &gamecoin.PlayerProfile{DisplayName: &name, Country: &country})
check(err)
fmt.Println(profile.ID, *profile.ExternalID, *profile.DisplayName)var profile = await gamecoin.Players.UpsertAsync(player, new PlayerProfile { DisplayName = "Alice", Country = "BE" });
Console.WriteLine($"{profile.Id} {profile.ExternalId} {profile.DisplayName}");Response 200 OK: a Player. The status is 200 whether the player was created or updated.
{
"id": "665f1c2e8a3b4d5e6f708192",
"externalId": "user-42",
"kind": "external",
"displayName": "Alice",
"country": "BE",
"blocked": false,
"createdAt": "2026-10-05T22:14:34.808Z"
}Errors
| Status | Code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_FAILED | An unknown field; displayName empty or longer than 80 characters; email not an address; country not two letters; birthYear not a whole year in range; a player reference that is neither a GameCoin id nor an ext: reference (PLAYER_REF_INVALID); your own id empty, longer than 128 characters or with leading or trailing whitespace | Read details.fieldErrors, fix the field. |
| 404 | NOT_FOUND | A GameCoin id that does not exist | Use ext:<your id>, which is created on the fly, or an id you got from GameCoin. |
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Invalid player profile",
"details": { "fieldErrors": { "birthYear": ["Too small: expected number to be >=1900"] } }
}
}Notes
- It is an upsert: the first call creates the player, later calls change the fields you send. To create a player with no profile, send
{}or nothing. - To erase a field, send it as
null:{"displayName": null}. An empty string is refused, not treated asnull. countryis not checked against the list of countries. Checkouts only sell to European Union countries; a player with another country gets the default countryBEunless you passcountryto Order a currency pack.- The
ext:id of a player is permanent: it is the key you will use in every later call.
Get a player
Returns one player. Use it to look a player up by your own id or by GameCoin id, and to check whether they are blocked.
/players/{player}Authentication: secret key. Idempotency: not needed.
Path parameters
| Parameter | Type | Description |
|---|---|---|
player | string | A GameCoin player id, or ext:<your id> URL-encoded once. |
curl "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42" \
-H "Authorization: Bearer $GAMECOIN_SECRET_KEY"const found = await gamecoin.players.get(player);
console.log(found.id, found.kind, found.blocked);found = gamecoin.players.get(player)
print(found.id, found.kind, found.blocked)$found = $gamecoin->players->get($player);
echo $found->id, ' ', $found->kind, ' ', var_export($found->blocked, true), "\n";found, err := client.Players.Get(ctx, player)
check(err)
fmt.Println(found.ID, found.Kind, found.Blocked)var found = await gamecoin.Players.GetAsync(player);
Console.WriteLine($"{found.Id} {found.Kind} {found.Blocked}");Response 200 OK: a Player.
{
"id": "665f1c2e8a3b4d5e6f708192",
"externalId": "user-42",
"kind": "external",
"displayName": "Alice",
"country": "BE",
"blocked": false,
"createdAt": "2026-10-05T22:14:34.808Z"
}Errors
| Status | Code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_FAILED | The reference is neither a 24-character hexadecimal id nor an ext: reference, or it was URL-encoded twice (PLAYER_REF_INVALID) | Encode the ext: reference exactly once. |
| 404 | NOT_FOUND | The player does not exist | For an ext: player this is normal until your first write: create it with Create or update a player. |
{ "error": { "code": "NOT_FOUND", "message": "Player not found" } }Notes
- A read never creates a player. Only writes do.
- The same player by its GameCoin id (the
idof the response) gives the same answer:
curl "https://gamecoin.apilow.com/api/v1/players/665f1c2e8a3b4d5e6f708192" \
-H "Authorization: Bearer $GAMECOIN_SECRET_KEY"Issue a player token
Issues a token that lets one player act on their own wallet and inventory from a browser or a game client, through the client API. Your backend calls it after it has authenticated the player, and hands the token to the client.
/players/{player}/tokensAuthentication: secret key. Idempotency: not needed: every call issues a new token. Body: none.
Path parameters
| Parameter | Type | Description |
|---|---|---|
player | string | A GameCoin player id, or ext:<your id> URL-encoded once. An unknown ext: player is created. |
curl -X POST "https://gamecoin.apilow.com/api/v1/players/ext%3Auser-42/tokens" \
-H "Authorization: Bearer $GAMECOIN_SECRET_KEY"const { token, expiresAt } = await gamecoin.players.createToken(player);
console.log(expiresAt.toISOString()); // give `token` to the player's browsertoken = gamecoin.players.create_token(player)
print(token.expires_at.isoformat()) # give token.token to the player's browser$token = $gamecoin->players->createToken($player);
echo $token->expiresAt->format(DATE_ATOM), "\n"; // give $token->token to the player's browsertoken, err := client.Players.CreateToken(ctx, player)
check(err)
fmt.Println(token.ExpiresAt) // give token.Token to the player's browservar token = await gamecoin.Players.CreateTokenAsync(player);
Console.WriteLine(token.ExpiresAt); // give token.Token to the player's browserResponse 200 OK: the token and its expiry.
| Field | Type | Description |
|---|---|---|
token | string | The player token, gc_pt_…. An opaque string: do not parse it. |
expiresAt | string | ISO 8601 date, one hour after issue. |
{ "token": "gc_pt_…", "expiresAt": "2026-10-05T23:14:35.000Z" }Errors
| Status | Code | When | What to do |
|---|---|---|---|
| 400 | VALIDATION_FAILED | The player reference is malformed (PLAYER_REF_INVALID) | Encode the ext: reference exactly once. |
| 403 | PLAYER_BLOCKED | The player is blocked | Do not hand out a token to this player. |
| 404 | NOT_FOUND | A GameCoin id that does not exist | Use an existing id, or ext:<your id>. |
Notes
- The token is tied to one player, one game and one environment: it is refused on any other. It lasts one hour: issue a new one when it expires, or when the client gets a
401. - The client sends it as
Authorization: Bearer gc_pt_…next to the publishable key, or gives it to the browser SDK asplayerToken. See Players and tokens. - A token is a credential for that player: do not log it, and send it only over HTTPS. Issuing a new one does not revoke the previous ones, which stay valid until they expire.