CEEJAYDocs

Log player events

Log things your players did in the game, up to 500 events in one call, to be shown on member profiles and rewarded.

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

Call it from your server when players do something worth knowing: win a match, enter the cup, finish a level. Each event is added to the member history of the member the player is connected to, where it shows on their profile and counts in segments. An event can also earn the reward the community set for it. Player data explains what an event is and how a retry stays safe.

Path parameters

Prop

Type

Body parameters

Prop

Type

Each entry of events holds:

Prop

Type

Returns

Prop

Type

A batch with problems still returns 200.

status of an eventWhat happened
loggedThe event is in the member history
duplicateThis event ID was logged before. Nothing was added
heldThe player has not connected, and the game holds events for players who have not. The event is held until the player connects
held_limitThe held store is full, or the player already has 50 held events
not_connectedNo active connection exists for this player
not_memberThe player is connected, but the member is not in the server. Events are stored for members only
not_consentedThe member has not consented to sharing the activity category: they connected before the game shared it and have not consented since on their connections page. A connection your own server created carries no consent and is not checked
erasedThe player was erased, and is never held
under_ageYou sent underAge: true. Nothing is stored
not_approvedThe event is not approved in the field map. It is now proposed. Send it again after approval, within 7 days
unknown_eventThe game already has 50 proposed events, so the name was not added
too_oldoccurredAt is more than 7 days back
in_futureoccurredAt is more than 5 minutes ahead
bad_itemThe entry could not be read: message says why. The rest of the batch was processed

This list may grow: handle unknown values. reward is queued when the event earns a reward that CEEJAY's bot grants asynchronously, and none otherwise.

Example

Request
curl -X POST https://ceejay.communitystudios.xyz/api/v1/games/rugby_manager/player-events \
  -H "Authorization: Bearer $CEEJAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "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",
        "occurredAt": "2026-10-07T19:05:00Z"
      }
    ],
    "dryRun": false
  }'

The TypeScript tab calls ceejay.players.logEvents from the TypeScript SDK. It returns the data of the response below.

200 OK
{
  "success": true,
  "data": {
    "results": [
      { "index": 0, "eventId": "m-88213", "status": "logged", "reward": "queued" },
      { "index": 1, "eventId": "m-88214", "status": "not_connected", "reward": "none" }
    ],
    "summary": {
      "logged": 1,
      "duplicate": 0,
      "held": 0,
      "refused": 1,
      "rewardsQueued": 1
    }
  }
}

What an event is

An event is one row of the member history: app Account Linking, the action "Game event", and the event's label with the game's name. It shows on the member's profile, and a segment can filter on members who did something in a game, with a count and a number of days. It does not trigger a webhook: a subscription to all events would receive each one, up to 500 in one call. An automation can be triggered by it when it is set up for "Game event", and only for a verified connection unless the automation also includes unverified connections.

  • Retries are safe because eventId is idempotent. Send the same eventId again and the status is duplicate. Use an ID that names the thing that happened, such as a match number.
  • An event name has to be approved in the field map before it is stored. An unknown name is proposed and gets the status not_approved. Send it again after approval, within 7 days of when it happened.
  • Events take no free text. A quantity is stored only where the event's entry keeps one, and is off by default. Send money and other private values as fields, not as events.
  • Members only. A connected player who is not in the server gets the status not_member.
  • Under age. underAge: true on an event needs the key to hold players:connect as well. Without it the whole call returns 403 with the code insufficient_scope and required: ["players:connect"]. A call can carry at most 50 such events, or it returns 400 with the code bad_request.

Rewards

An event that has a reward list returns reward: "queued", and CEEJAY's bot grants the reward asynchronously: this call does not wait for it. A reward is queued only when the event is less than 24 hours old, the connection meets the verification level the game requires, and the member is in the server. For a reward that is granted once to each member, a member who was already granted the reward for that event is not queued again. See Game rewards.

What the call needs

  • The member history has to be enabled on this instance. Where it is off, or its table is missing, the call returns 404 with the code not_available and feature: "member_history".
  • The activity category has to be one the game shares. Otherwise the whole call returns 409 with the code activity_not_shared.

Limits

In addition to the key's own requests per minute, a game has a limit of events a minute: 3,000 unless another number was set for the game. Above it the whole call returns 429 with the code event_rate_exceeded, retryAfter and a Retry-After header, and stores nothing. A call that is over the limit counts too, so one that keeps going stays rate limited until the window resets.

Body size

The body is read before it is parsed, up to 512 KB. A larger one is refused with 413 and the code body_too_large, in the usual error shape. Send fewer events in one call.

Safe to repeat

The eventId of each event is the key, so the request takes no idempotencyKey. The SDK does not retry automatically; retrying it yourself is safe.

The scope

players:write is part of *, so a Full Access key can call this. A call that says underAge: true for an event also needs players:connect, which * does not include.

Limits

This request is counted against the key's own requests per minute: 60 unless a higher limit was set on it, and never more than 600. Rate limits has the numbers and the headers.

A batch of 500 events is one request.

Errors

StatuscodeerrorWhen
400bad_requestThe request body must be a JSON objectThe body is null, text, a number or a list
400bad_requestThe request body must be JSONThe body is not valid JSON, or could not be read
400bad_requestevents must be a list of 1 to 500 eventsevents is missing, is not a list, or is empty
400too_many_eventsA call can carry up to 500 eventsevents contains more than 500 events
400bad_requestA call can carry up to 50 events with underAge: trueMore than 50 entries of events say underAge: true
400bad_requestdryRun must be true or falsedryRun was sent and is not true or false
403community_not_activeThis community is no longer active on CEEJAYThe community removed CEEJAY from its server
403game_not_allowedThis API key is limited to other gamesThe key is limited to other games of the community
403audience_not_allowedA game made for children cannot connect players or send player dataThe game is set up as made for children. Such a game connects nobody and takes no player data
404not_availableThis is not available on this instance yet.Connect is not enabled for this instance. It is enabled per instance. The body adds feature, which is players. Retrying will not change the response
404not_availableThis is not available on this instance yet.The member history is disabled on this instance, or its table is missing, and events are stored in it. The body adds feature, which is member_history
404game_not_foundGame not foundThe community has no game with this slug, or the game was removed. A slug that is not 2 to 32 lower-case letters, digits and underscores, starting with a letter, gets this response too
409game_pausedThis game is pausedThe game is paused or suspended
409activity_not_sharedThis game does not share the activity category, so events cannot be logged. The team adds it to what the game shares firstThe game does not share the activity category. The community's team adds it to what the game shares first
413body_too_largeThe request body is larger than 512 KB. Send fewer players or events in one callThe body is larger than 512 KB. It is refused before it is parsed
429event_rate_exceededThis game has sent more events this minute than it is allowed. Wait and send them againThe game sent more events this minute than it is allowed (3,000 unless another number was set for the game). The body adds retryAfter, and the Retry-After header carries the same number

Every error body also carries requestId, and a 400 with the code bad_request names the invalid field in field. Match on code, not on the message text, which can change.

The errors every request shares (401, 403 for a missing scope, 405, 429, 500) are on the Errors page.

On this page

Community StudiosPrivacyTerms