CEEJAYDocs

ceejay.players

Read the players of a game who are connected to a Discord member, send data about them, end a connection and delete the data CEEJAY stores for a player.

Read the players of a game who are connected to a Discord member, send data about them, end a connection and delete the data CEEJAY stores for a player.

This page is for a studio's server, once its players are connected: see Connect. The data is stored on the member the player is connected to, and only on members of the community's server. The community's team decides which of your field names are stored, in the field map: see The field map.

Every function takes the same last argument, options: signal, timeoutMs and maxRetries, for that one call. Every function can also raise the errors any request can raise, such as a bad key or the rate limit. See Errors and retries.

Before you start

  • The game is set up for the community first, and gets a slug. Every function here takes it as its first argument, game.
  • The key needs the permission each function names. players:read and players:write are part of a key with every permission (*). players:connect and players:delete are not: tick Act for a player and Delete a player's data when you create the key. ceejay.players.setFields and ceejay.players.logEvents also need players:connect for an entry with underAge: true.
  • The limit is the key's own number of requests per minute. A call with up to 500 players or events counts as one request.
  • No idempotency key. None of the six functions accepts one, so the SDK never retries any of the four writes automatically. Retrying one yourself does no harm: setting a field again changes nothing, events are deduplicated by eventId, and ending a connection or deleting a player that is already gone has no further effect.
  • A player ID is a string of 1 to 128 letters, digits and . _ : @ | + = -, starting with a letter or a digit. A number is never sent: the SDK throws a TypeError.

ceejay.players.get

Reads one player: the Discord member they are connected to, the member profile summary, and the game's approved fields that are not sensitive.

GET /v1/games/{game}/players/{playerId} · requires players:read · API reference: Get a player

Retried by the SDK: Yes. It is a read

Parameters: the game, the player, then options.

Prop

Type

Returns the player, typed as Schemas['PlayerDetail'].

Prop

Type

sharing has agreed, the share categories the member has consented to, and needed, the categories the game has added since. Both are empty for a connection made by your own server. A sensitive field is never returned, whatever permissions the key has.

Errors

StatuscodeWhen
400bad_player_idThe player ID contains a character that is not allowed, or is too long
403community_not_activeThe community removed CEEJAY
403game_not_allowedThe key is limited to other games
403audience_not_allowedThe game is made for children
404player_not_foundNo active connection exists for this player. The body of the error includes held: true when CEEJAY stores data for the ID pending a connection
404game_not_foundThe community has no game with this slug
404not_availableConnect is not enabled on this instance

A paused game can still be read.

Example

ceejay.players.get
const read = await ceejay.players.get('rugby_manager', 'p_19')

console.log(read.player.discordId, read.player.proof, read.member?.level)
console.log(read.fields['std.level'], read.fields['league_position'])

ceejay.players.list

Lists the players of a game who are connected to a Discord member, newest connection first, a page at a time. A player who has not connected is not in the list.

GET /v1/games/{game}/players · requires players:read · API reference: List players

Retried by the SDK: Yes. It is a read

Parameters: the game, an optional object, then options.

Prop

Type

Returns the full response, typed as Schemas['ListPlayersResponse']. The players are in data, and the pagination details are in page.

Prop

Type

Errors

StatuscodeWhen
403community_not_activeThe community removed CEEJAY
403game_not_allowedThe key is limited to other games
403audience_not_allowedThe game is made for children
404game_not_foundThe community has no game with this slug
404not_availableConnect is not enabled on this instance

A limit that is not a whole number, or a cursor that cannot be read, is a bad_request.

Example

ceejay.players.list
let cursor: string | null = null
do {
  const page = await ceejay.players.list('rugby_manager', { limit: 100, ...(cursor ? { cursor } : {}) })
  for (const player of page.data) console.log(player.playerId, player.discordId, player.proof)
  cursor = page.page.nextCursor
} while (cursor)

ceejay.players.disconnect

Ends the connection of a player, when your server requests it. The member's connected role is removed, the player's devices are signed out, the values CEEJAY stores for the player are cleared from the member, and player.disconnected is sent. The member can connect again.

POST /v1/games/{game}/players/{playerId}/disconnect · requires players:connect · API reference: Disconnect a player

Retried by the SDK: No. Calling it again yourself has no further effect: there is no connection left to end

Parameters: the game, the player, then options. It sends no body.

Prop

Type

Returns the response, typed as Schemas['DisconnectPlayerResult'].

Prop

Type

Errors

StatuscodeWhen
400bad_player_idThe player ID contains a character that is not allowed, or is too long
403community_not_activeThe community removed CEEJAY
403game_not_allowedThe key is limited to other games
404player_not_foundNo active connection exists for this player
404game_not_foundThe community has no game with this slug
404not_availableConnect is not enabled on this instance

A key with * does not include players:connect. This function also works for a paused game and for a game made for children: a studio can always disconnect.

Example

ceejay.players.disconnect
import { CeejayError } from '@communitystudios/ceejay'

try {
  await ceejay.players.disconnect('rugby_manager', 'p_19')
} catch (error) {
  if (error instanceof CeejayError && error.code === 'player_not_found') {
    console.log('This player is not connected')
  } else {
    throw error
  }
}

ceejay.players.delete

Deletes all data CEEJAY stores for one player ID in one game. Use it when a player requests deletion of their data.

POST /v1/games/{game}/players/{playerId}/delete · requires players:delete · API reference: Delete a player

Retried by the SDK: No. Calling it again yourself is safe: when there is nothing to delete, the response contains zeros

Parameters: the game, the player, then options. It sends no body.

Prop

Type

Returns the response, typed as Schemas['DeletePlayerResult'].

Prop

Type

First the connection is ended, and the bot has up to 5 seconds to remove the role and send player.disconnected. Then the following are removed: every connection of the player in this game, with its tokens and consent history; the member's values for the game's fields, current and previous; the player's game events in the member history, and the history entries that show the player connected or disconnected; the player's sessions and passes; queued jobs for the player; and all data stored for the player if they had not connected. Rewards that were already granted are kept, along with any block the community set on the player and an erasure marker.

Errors

StatuscodeWhen
400bad_player_idThe player ID contains a character that is not allowed, or is too long
403community_not_activeThe community removed CEEJAY
403game_not_allowedThe key is limited to other games
404game_not_foundThe community has no game with this slug
404not_availableConnect is not enabled on this instance

A key with * does not include players:delete. This function also works for a paused game and for a game made for children: a studio can always delete.

Example

ceejay.players.delete
const done = await ceejay.players.delete('rugby_manager', 'p_19')

console.log(done.removed.connection, done.removed.values, done.removed.events, done.removed.pending)

ceejay.players.setFields

Sends values about your players, to be stored on the members they are connected to. Up to 500 players in one call, each with up to 40 fields.

POST /v1/games/{game}/player-fields · requires players:write · API reference: Set player fields

Retried by the SDK: No. Calling it again yourself changes nothing: a field is set, not added

Parameters: the game, one object, then options.

Prop

Type

A value is text (up to 500 characters), a number, true, false, or null to clear the field. A field that is not in fields is left as it is. A name is matched first against the name your game uses for the field, then against the field's key. A name that matches no field is submitted for approval by the community's team and returns not_approved. underAge: true on a player stores nothing and ends their connection. It also needs the players:connect permission, and a call can contain at most 50 of them.

Returns the response, typed as Schemas['SetPlayerFieldsResult'].

Prop

Type

A batch with problems still returns a response. The status of a player is written, unchanged, held, held_limit, not_connected, not_member, erased, under_age or bad_item. Each item of problems has the field you sent and a code: unknown_field, not_approved, unused, bad_value, not_a_choice, not_consented, needs_verified_proof or managed. A player connected at the basic level does not accept fields in the purchases or location category: each is a problem with the code needs_verified_proof, and the player's other fields are still written. Treat the lists as open-ended.

Errors

StatuscodeWhen
400too_many_playersMore than 500 players in one call
403community_not_activeThe community removed CEEJAY
403game_not_allowedThe key is limited to other games
403audience_not_allowedThe game is made for children
404game_not_foundThe community has no game with this slug
404not_availableConnect is not enabled on this instance
409game_pausedThe game is paused
413body_too_largeThe body is larger than 512 KB. Send fewer players in one call

A body with the wrong shape, such as players that is not a list, or more than 50 players with underAge: true, returns bad_request. A call with underAge: true and a key without players:connect returns 403 with the code insufficient_scope. A player ID that is a number is never sent: the SDK throws a TypeError.

Example

ceejay.players.setFields
const result = await ceejay.players.setFields('rugby_manager', {
  players: [
    { playerId: 'p_19', fields: { league_position: 3, club: 'Harbour Town', last_played: '2026-10-07T21:10:00+02:00' } },
    { discordId: '409876543210987654', fields: { club: null } },
  ],
})

for (const entry of result.results) {
  console.log(entry.index, entry.status, entry.written, entry.problems.map((problem) => `${problem.field}: ${problem.code}`))
}
console.log(result.summary.written, result.summary.refused)

ceejay.players.logEvents

Logs actions your players took in the game, to be shown on the profile of the member they are connected to, counted in segments and rewarded. Up to 500 events in one call.

POST /v1/games/{game}/player-events · requires players:write · API reference: Log player events

Retried by the SDK: No. Calling it again yourself is safe: events are deduplicated by eventId

Parameters: the game, one object, then options.

Prop

Type

occurredAt can be at most 5 minutes ahead and at most 7 days back. An event name that is not approved in the field map is not stored: it is submitted for approval and returns not_approved. The game has to share the activity category. underAge: true on an event stores nothing and ends the player's connection. It also needs the players:connect permission, and a call can contain at most 50 of them.

Returns the response, typed as Schemas['LogPlayerEventsResult'].

Prop

Type

The status of an event is logged, duplicate, held, held_limit, not_connected, not_member, not_consented, erased, under_age, not_approved, unknown_event, too_old, in_future or bad_item. reward is queued when the event earns a reward that CEEJAY's bot grants shortly afterwards, and none otherwise. not_consented means the member consented to sharing but not the activity category.

Errors

StatuscodeWhen
400too_many_eventsMore than 500 events in one call
403community_not_activeThe community removed CEEJAY
403game_not_allowedThe key is limited to other games
403audience_not_allowedThe game is made for children
404game_not_foundThe community has no game with this slug
404not_availableConnect is not enabled on this instance, or member history is switched off. The body of the error includes feature: players or member_history
409game_pausedThe game is paused
409activity_not_sharedThe game does not share the activity category. The whole call is rejected
413body_too_largeThe body is larger than 512 KB. Send fewer events in one call
429event_rate_exceededThe game exceeded its event limit for this minute. retryAfter is the number of seconds to wait

A call with underAge: true and a key without players:connect is 403 with the code insufficient_scope.

Example

ceejay.players.logEvents
import { CeejayError } from '@communitystudios/ceejay'

try {
  const result = await ceejay.players.logEvents('rugby_manager', {
    events: [
      { eventId: 'm-88213', playerId: 'p_19', event: 'match_won', occurredAt: '2026-10-07T19:02:00Z', quantity: 1 },
      { eventId: 'm-88214', playerId: 'p_77', event: 'match_won' },
    ],
  })
  console.log(result.summary.logged, result.summary.rewardsQueued)
} catch (error) {
  if (error instanceof CeejayError && error.code === 'event_rate_exceeded') {
    console.log('Send these again in', error.retryAfter, 'seconds')
  } else {
    throw error
  }
}

On this page

Community StudiosPrivacyTerms