CEEJAYDocs

Introduction

Award XP, move currency, complete quests, manage Discord roles, deliver shop items and receive real-time events, from your own game, app or platform.

The CEEJAY API is a REST API over HTTPS. Every request is authenticated with a secret API key, all responses are JSON, and each key is bound to a single Discord server. Events flow the other way as signed webhooks, and AI agents can connect to the same community over MCP.

One part is different: the requests a game makes itself, for Connect and for the game loop, take a public key that ships inside the game or a player session, not a secret key.

Base URL

Every request goes to your dashboard's URL followed by /api/v1. One older endpoint is outside it, and is noted below.

On the shared CEEJAY service the base URL is:

Shared service base URL
https://ceejay.communitystudios.xyz/api/v1

On a dedicated instance it is your instance's dashboard URL followed by /api/v1. Use the URL you see in your browser when you open your dashboard, for example https://dashboard.example.com/api/v1. A key only works on the instance where it was created.

Each reference page shows its endpoint as a path that starts with /v1, such as /v1/xp/grant. The full URL is your dashboard's URL, then /api, then that path. On the shared service, /v1/xp/grant is https://ceejay.communitystudios.xyz/api/v1/xp/grant.

One endpoint is outside /v1: the older Complete a quest task endpoint. Its page shows the whole path, /api/quests/external/complete, and that path goes straight after your dashboard's URL. The base URL above does not reach it. It still works; new code uses /v1/quests/tasks/complete.

Start with Community details

Make Community details your first call. It needs no scope, and returns which community your key belongs to, what the key may do, and which requests your instance supports.

What you can do

Community details

Find out which community a key belongs to, what the key may do and what your instance supports.

Members

Read a member's profile, XP, balance and linked accounts. Link a member's ID in your game to their Discord ID.

Connect

Link a player of your game to their Discord account, from inside the game, with confirmation from both sides.

The game loop

Show the community's quests, shop, locker, rank and daily reward in your game, and sell from its shop with the player's coins.

Player data

Send fields and events about your players, read a player, and delete a player's data, once they are connected. The field map says what data the community stores.

XP

Grant and deduct a member's XP, and read their level.

Program XP

List programs, and grant and deduct XP inside one, such as an ambassador program.

Economy

Grant, deduct and read a member's currency balance, with idempotency keys so a retry never grants twice.

Quests

List quests, mark one completed, or report that a member finished a quest task your platform checks.

Roles and messages

List, grant and revoke Discord roles, list your channels, and post messages or embeds to them.

Shop delivery

Deliver shop items from your own server. CEEJAY sends a signed request when a member buys, you confirm the delivery, and a list shows the deliveries awaiting your confirmation.

Webhooks

Receive signed events the moment something happens in your community, and send your own.

AI agents over MCP

Let Claude Code, Codex or Cursor read your community and draft changes, within the limits you set.

TypeScript SDK

Call the API from your own server with typed functions, check webhook and shop delivery signatures, and connect a game's players to Discord. Not published yet.

Changelog

What changed in the API, and what you can rely on staying the same.

Not in this API yet

The REST API does not expose analytics. Member growth, message volume, sentiment, retention and voice-of-community data live in the dashboard and in scheduled reports. There are no REST endpoints for them. AI agents can read them through MCP.

If you need to pull analytics into a warehouse or BI tool such as Power BI, talk to us. That's a separate surface and we'd rather design it around your actual reporting needs.

Conventions

  • Use HTTPS for every request.
  • Request and response bodies are JSON, and the body of a write is a JSON object. Send Content-Type: application/json on every write. Without it the body is not read as JSON, and the response is a 400: The request body must be a JSON object, or a message that names a field as missing when the body arrives as a form, which is what curl -d sends when you set no content type.
  • Write each path exactly as its reference page shows it, without a trailing slash. A trailing slash returns 308 with a redirect to the path without it, and a path that does not exist returns 404 with an HTML page, not JSON.
  • Discord IDs are snowflakes: numeric strings of 17 to 20 digits. Always send them as strings, never as JSON numbers; they exceed the safe integer range in JavaScript, so an ID sent as a number is refused with a 400. Amounts and other counts are JSON numbers, in requests and in responses.
  • Every successful response is wrapped as { "success": true, "data": … }. data is an object, or a list on the requests that list things, which add "page": { "nextCursor", "limit" } beside it: send nextCursor back as cursor for the next page, and stop when it is null. Three responses also keep the fields they had beside success before: List quests (total, limit and offset), Complete a shop delivery and Complete a quest task.
  • Each field has one current name, in camelCase, in requests and in responses. The member is discordId, a game's own ID for the member is playerId, and a note is reason. Every legacy field name is kept for backwards compatibility and is still accepted and still returned: each reference page lists them. When both names of a field are sent, the older one is used.
  • Within version 1 nothing is renamed or removed. See the Changelog.
  • Times are ISO 8601 strings in UTC, such as 2026-10-03T09:14:22.118Z.
  • Errors return a non-2xx status and { "error": "…", "code": "…", "requestId": "…" }, sometimes with extra fields. Act on the code, which never changes, not on the error message text. A few responses that come before your key is read are not JSON. See Errors.
  • Every response includes headers showing your key's remaining rate limit this minute, in X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and includes X-Request-Id. See Rate limits.
  • Retries: every request that creates or changes data takes an idempotencyKey in its body. Sent again with the same key, it returns the original response, with duplicate: true, and changes nothing. One key is one request, so two requests for one event need two keys. Without a key, a repeated XP grant, message or event happens twice. The requests about players and the field map take no key: setting a field again changes nothing, and an event has its own eventId.

Getting help

Every request made with a key CEEJAY accepts is logged against that key: the endpoint, the method, the status we returned and the caller's IP address. That includes a request refused for a missing scope (403). Of the requests refused by the rate limit (429), the first in each one-minute window is logged and the rest are not. A request with a missing, unknown, revoked or expired key is not logged. If something looks wrong, send us the requestId of the response, or the time and the endpoint, and we can trace the call.

On this page

Community StudiosPrivacyTerms