TypeScript SDK
Call the CEEJAY API from your server with typed functions, and check the requests CEEJAY sends you.
The SDK has one function for each request in the API reference, with types generated from the same OpenAPI specification as the reference. It also checks the signature of a webhook or a shop delivery, and has a client for a game that links a player to their Discord account and shows the community's quests, shop and rank.
npm install @communitystudios/ceejayThe npm package has no runtime dependencies. The quickstart covers installing the SDK and making your first request.
Two ways in
| Import | It is for | It uses |
|---|---|---|
@communitystudios/ceejay | Your server | Your secret key. Use it on a server only: never in a browser, a mobile app or a game client |
@communitystudios/ceejay/connect | Your game | The game's public key, which is no secret. It rejects a secret key |
The API reference shows each request the SDK sends as cURL and as TypeScript, so you can read either one beside the function it calls.
Create the client
Create an API key in the dashboard under Settings → Developers → API Keys. Grant it only the permissions it needs, and store it in an environment variable.
import { Ceejay } from '@communitystudios/ceejay'
const ceejay = new Ceejay({ apiKey: process.env.CEEJAY_API_KEY })A key belongs to one Discord server, so you never pass a server ID.
Prop
Type
What a function returns
A function returns what is inside data in the response. A function that lists returns the full response, with the list in data and page alongside it. So do quests.reportTask and shop.completePurchase, whose responses contain more than data.
const member = await ceejay.members.get('409876543210987654')
console.log(member.displayName, member.level, member.economy.balance)
const page = await ceejay.quests.list({ limit: 20 })
console.log(page.total, page.data.map((quest) => quest.title), page.page.nextCursor)Every function takes { signal, timeoutMs, maxRetries } as its last argument, for one call. Start with ceejay.community.get(): any valid key can call it, and it reports what the key can do and which features are enabled on your instance.
Write with an idempotency key
Every function that grants, deducts or posts takes an idempotencyKey in its options: all 14 of them. The key is a string you choose, 1 to 128 characters, for this one request. When the same request is sent again with its key, nothing is repeated: the response is the original one, with duplicate: true.
const grant = await ceejay.economy.grant(
{ discordId: '409876543210987654', amount: 250, reason: 'Season prize' },
{ idempotencyKey: 'season-3-prize-409876543210987654' },
)
console.log(grant.amountGranted, grant.balance, grant.duplicate)Use a new key for every separate request, and the same key only to retry one. Each key identifies one request: when one match grants coins and XP, send two keys, such as match-9:coins and match-9:xp. The three writes of ceejay.connect take none, because each is safe to retry as it is. Errors and retries has the rest.
Member IDs are strings
A Discord ID has 17 to 20 digits. A JavaScript number can hold only about 16 digits exactly, so an ID written as a number changes and refers to someone else. Always pass an ID in quotes.
await ceejay.members.get('409876543210987654') // right
// await ceejay.members.get(409876543210987654) // wrong: a number is refused, by the types and when it runsRuntimes
- Node 20 and later
- Deno
- Edge functions, such as Supabase Edge Functions
The SDK consists of ES modules and uses only fetch and Web Crypto. It imports no Node module. @communitystudios/ceejay/connect also runs in a browser, and uses fetch only.
All functions
One function for each of the 36 requests a studio's server makes. ceejay is the client from above. The permission is the one the API key needs: see Authentication for what each one allows.
| Function | Request | Permission | Idempotency key |
|---|---|---|---|
ceejay.community.get | GET /v1/community | any valid key | No |
ceejay.members.get | GET /v1/members/{discordId} | members:read | No |
ceejay.members.link | POST /v1/members/link | members:link | Yes |
ceejay.xp.getBalance | GET /v1/xp/balance | xp:read | No |
ceejay.xp.grant | POST /v1/xp/grant | xp:grant | Yes |
ceejay.xp.deduct | POST /v1/xp/deduct | xp:deduct | Yes |
ceejay.xp.program.grant | POST /v1/xp/program/grant | xp:program:grant | Yes |
ceejay.xp.program.deduct | POST /v1/xp/program/deduct | xp:program:deduct | Yes |
ceejay.programs.list | GET /v1/programs | programs:read | No |
ceejay.economy.getBalance | GET /v1/economy/balance | economy:read | No |
ceejay.economy.grant | POST /v1/economy/grant | economy:grant | Yes |
ceejay.economy.deduct | POST /v1/economy/deduct | economy:deduct | Yes |
ceejay.quests.list | GET /v1/quests/list | quests:read | No |
ceejay.quests.complete | POST /v1/quests/complete | quests:complete | Yes |
ceejay.quests.reportTask | POST /v1/quests/tasks/complete | quests:complete | Yes |
ceejay.roles.list | GET /v1/roles | roles:manage | No |
ceejay.roles.grant | POST /v1/roles/grant | roles:manage | Yes |
ceejay.roles.revoke | POST /v1/roles/revoke | roles:manage | Yes |
ceejay.channels.list | GET /v1/channels | messages:send | No |
ceejay.messages.send | POST /v1/messages/send | messages:send | Yes |
ceejay.events.emit | POST /v1/events/emit | events:emit | Yes |
ceejay.shop.listPurchases | GET /v1/shop/purchases | shop:fulfil | No |
ceejay.shop.completePurchase | POST /v1/shop/purchases/{purchaseId}/complete | shop:fulfil | Yes |
ceejay.connect.createPass | POST /v1/games/{game}/connect/passes | players:connect | No |
ceejay.connect.createSession | POST /v1/games/{game}/connect/sessions | players:connect | No |
ceejay.connect.getSession | GET /v1/games/{game}/connect/sessions/{sessionId} | players:connect | No |
ceejay.connect.listSessions | GET /v1/games/{game}/connect/sessions | players:connect | No |
ceejay.connect.confirmSession | POST /v1/games/{game}/connect/sessions/{sessionId}/confirm | players:connect | No |
ceejay.players.get | GET /v1/games/{game}/players/{playerId} | players:read | No |
ceejay.players.list | GET /v1/games/{game}/players | players:read | No |
ceejay.players.disconnect | POST /v1/games/{game}/players/{playerId}/disconnect | players:connect | No |
ceejay.players.delete | POST /v1/games/{game}/players/{playerId}/delete | players:delete | No |
ceejay.players.setFields | POST /v1/games/{game}/player-fields | players:write | No |
ceejay.players.logEvents | POST /v1/games/{game}/player-events | players:write | No |
ceejay.fieldMap.get | GET /v1/games/{game}/field-map | players:read | No |
ceejay.fieldMap.propose | POST /v1/games/{game}/field-map/propose | players:write | No |
A request that the API reference marks as a legacy endpoint of another request, such as the legacy endpoint of Complete a quest task, has no function: the SDK sends to the newer one.
Where to go next
SDK quickstart
Install the SDK, make your first call and handle an error.
Errors and retries
What a call can throw, and what the SDK retries automatically.
Verifying signatures
Check that a webhook or a shop delivery really comes from CEEJAY.
Connect, from your server
Link a player of your game to their Discord account with a secret key.
Players
Read the players of a game, send data about them, disconnect one and delete the data CEEJAY stores for one.
The field map
Read the names a game sends, and propose new ones for the community to approve.
Connect, inside a game
The same from inside the game, with its public key and @communitystudios/ceejay/connect.
The game loop
Show the community's quests, shop, locker, rank and daily reward in the game, with connect.hub.
C# and .NET
The same functions for a C# server, and the game core for a C# game.
Unity
A preview package that adds Connect and the game loop to a Unity game.
Unreal Engine
A preview plugin that adds Connect and the game loop to an Unreal game.
Dedicated instances
Point the SDK at your own CEEJAY instance.

