Quickstart
Create a key and make your first authenticated call in about two minutes.
Make your first call
Create an API key
In the CEEJAY dashboard open Settings → Developers → API Keys and press Generate Key.
Name the key after where it will run, such as Roblox game server or Staging worker. Full
Access is ticked when the dialog opens. Tick only the scopes the key needs instead:
ticking one clears Full Access. For this quickstart that is Members Read and
XP Grant. Then press Generate, and the dialog closes.
The full key is shown once, in a banner above the list of keys that starts Copy your API key now. Copy it immediately into your secret manager; we store only a hash, so it cannot be recovered or displayed again. If you lose it, revoke it with the delete icon at the end of its row and generate a new one.
Make a request
Every request carries the key as a bearer token. Start with Community details, which any valid key can call. It returns which community the key belongs to, the scopes it has and what your instance supports:
curl https://ceejay.communitystudios.xyz/api/v1/community \
-H "Authorization: Bearer $CEEJAY_API_KEY"The next one reads a member's profile. Use the
Discord ID of a member of your community: a member CEEJAY has never seen returns 404.
The examples use the shared CEEJAY service, whose base URL is
https://ceejay.communitystudios.xyz/api/v1. On a dedicated instance the base URL is your
dashboard's URL followed by /api/v1: use the URL you see in your browser when you
open your dashboard. See Base URL.
curl https://ceejay.communitystudios.xyz/api/v1/members/409876543210987654 \
-H "Authorization: Bearer $CEEJAY_API_KEY"Award something
Writes are POST with a JSON body. This grants 250 XP, and the grant is real, so use your own
account or a test member. Send the Content-Type: application/json header with every JSON body:
without it the body is not read as JSON, and the response is a 400. From curl -d it says
discordId is invalid, as if you had left it out. From a client that sends no content type it
says The request body must be a JSON object.
curl -X POST https://ceejay.communitystudios.xyz/api/v1/xp/grant \
-H "Authorization: Bearer $CEEJAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"discordId": "409876543210987654",
"amount": 250,
"reason": "Won the Saturday tournament",
"idempotencyKey": "tournament-2026-w40-first-place"
}'The response shows what the member had before and after, and whether the grant levelled them up:
{
"success": true,
"data": {
"discordId": "409876543210987654",
"previousXP": 8200,
"xpGranted": 250,
"totalXP": 8450,
"previousLevel": 11,
"currentLevel": 12,
"leveledUp": true,
"reason": "Won the Saturday tournament",
"auditId": "1873",
"xp": 8450,
"previousXp": 8200,
"level": 12,
"duplicate": false
}
}xp, previousXp and level are the current field names. totalXP, previousXP and currentLevel return the same
values under their legacy names, kept for backwards compatibility, and are still sent.
The idempotencyKey makes the call safe to retry: if it times out, retry with the same body and the same key,
and the member receives the XP once. The second response is the original response, with duplicate: true. Every request that
creates or changes data takes a key.
Keep keys server-side
An API key grants write access to your community. Never ship one in a browser bundle, mobile app or game client. Call the CEEJAY API from your own backend and let your client talk to that. The API does not accept calls from browsers on other websites.
Related
- Authentication lists every scope.
- Rate limits and Errors say what to expect when a call fails.
- Webhooks send you events instead of you polling for them.
- The TypeScript SDK has a typed function for each request, and the reference pages show the TypeScript call beside the cURL one.

