ceejay.connect
Link a player of your game to their Discord account in the community, from your own server.
This page is for a studio whose server signs its players in. Your server knows who the player is, so CEEJAY trusts your server's identification: a connection made this way is at the verified level. A game that has no server of its own uses the public key that ships inside it, with the client on Connect, inside a game.
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. Both are on
Errors and retries.
Before you start
- The game is set up for the community first, and gets a slug: a short name in lower
case, with underscores where a name has spaces. Every function here takes that slug as
its first argument,
game. - The key needs the permission
players:connect. A key with every permission (*) does not include it: tick Act for a player when you create the key. A server with this permission can sign in any player who is already connected, and can end a session, so keep that key on your server and nowhere else. - The rate limit is 600 requests per minute for one game, across the five functions. They do not count against the key's own rate limit.
- No idempotency key. None of the five takes one, so the SDK never retries any of the three writes automatically. Retrying one yourself is safe: a retried request creates another pass, a new session for a player ends the previous one, and a confirmation or decline that was already given does not change.
The two ways to use it
The game stores the tokens. Your server creates a pass with ceejay.connect.createPass
and passes it to the game. The game starts the session itself, with the pass as its proof,
and stores the player's tokens. Your server does nothing more.
Your server does everything. Your server starts the session with
ceejay.connect.createSession and passes the link, the code or the QR image to the game.
The player opens the link and approves with their Discord account. Your server polls the
session with ceejay.connect.getSession until its status is needs_game_confirmation,
shows the player the Discord name, and sends the player's choice with
ceejay.connect.confirmSession.
Nothing is saved until both parties have approved: the Discord member, on CEEJAY's connect page or in Discord, and the game.
async function connectPlayer(playerId: string, askPlayer: (discordName: string | null) => Promise<boolean>) {
const started = await ceejay.connect.createSession('rugby_manager', { playerId, playerName: 'Nova' })
if (started.status === 'approved') return started.sessionId
// Hand started.url, started.code or started.qrUrl to the game, then read the session
// every few seconds until it ends. It lasts 10 minutes.
for (;;) {
await new Promise((resolve) => setTimeout(resolve, 3000))
const session = await ceejay.connect.getSession('rugby_manager', started.sessionId)
if (session.status === 'needs_game_confirmation') {
const accept = await askPlayer(session.member?.displayName ?? null)
const after = await ceejay.connect.confirmSession('rugby_manager', started.sessionId, { accept })
if (after.status === 'approved') return after.sessionId
} else if (session.status !== 'pending' && session.status !== 'needs_confirmation') {
// approved, or an end: declined, cancelled, expired, locked or refused.
return session.status === 'approved' ? session.sessionId : null
}
}
}Rewards and roles the community has set for connecting are granted by CEEJAY's bot within seconds of approval. Once the player is connected, send data about them and read it with Players, and agree the field names with the community in The field map.
ceejay.connect.createPass
Creates a connect pass for one player your server has signed in. Pass it to your game. The game sends it as its proof when it starts a connect session, and the session is then at the verified level.
POST /v1/games/{game}/connect/passes · requires players:connect · API reference: Create a connect pass
Retried by the SDK: No. Retrying it yourself creates a second pass, which is harmless
Takes the game, one object, then options.
Prop
Type
Returns the pass, typed as Schemas['ConnectPass'].
Prop
Type
Errors
| Status | code | When |
|---|---|---|
400 | bad_player_id | playerId contains a character that is not allowed, or is too long |
400 | bad_request | The body cannot be parsed, or playerName is not a string of at most 80 characters |
403 | community_not_active | The community removed CEEJAY |
403 | game_not_allowed | The key is limited to other games |
403 | audience_not_allowed | The game is directed at children, and such a game cannot connect players |
404 | game_not_found | The community has no game with this slug |
404 | not_available | This instance does not support Connect yet |
409 | game_paused | The game is paused |
A game or a playerId that is not a string never leaves your server: the SDK throws a
TypeError.
Example
const pass = await ceejay.connect.createPass('rugby_manager', { playerId: 'p_19', playerName: 'Nova' })
// Send pass.pass to the game, over your own connection to it.
console.log(pass.pass, pass.expiresAt)ceejay.connect.createSession
Starts a connect session for a player your server has signed in. With code, it attaches
that player to the session of a member who generated a code in Discord and typed it into the
game.
POST /v1/games/{game}/connect/sessions · requires players:connect · API reference: Create a connect session
Retried by the SDK: No. Retrying it yourself starts a new session, which ends the previous one
Takes the game, one object, then options.
Prop
Type
Returns the session, typed as Schemas['StartedConnectSession'].
Prop
Type
Errors
| Status | code | When |
|---|---|---|
400 | bad_player_id | playerId contains a character that is not allowed, or is too long |
400 | unknown_return | returnTo is not the key of a return URL saved for the game |
400 | age_answer_required | The game is set up for a mixed audience, and underAge was not sent |
400 | bad_request | The body cannot be parsed, or a field has the wrong type |
403 | under_age | You sent underAge: true. Nothing is stored, and a connection this player has is ended |
403 | community_not_active | The community removed CEEJAY |
403 | game_not_allowed | The key is limited to other games |
403 | audience_not_allowed | The game is directed at children |
404 | code_not_found | No open session has the code you sent |
404 | game_not_found | The community has no game with this slug |
404 | not_available | This instance does not support Connect yet |
409 | session_locked | The code belongs to a session that was locked by this failed attempt, its fifth. The member starts again |
409 | game_paused | The game is paused |
Example
const session = await ceejay.connect.createSession('rugby_manager', {
playerId: 'p_19',
playerName: 'Nova',
returnTo: 'game',
})
if (session.alreadyConnected) {
console.log('This player is connected already')
} else {
console.log('Open', session.url, 'or type', session.code, 'before', session.expiresAt)
}A player who typed a code a member generated in Discord:
import { CeejayError } from '@communitystudios/ceejay'
try {
const session = await ceejay.connect.createSession('rugby_manager', { playerId: 'p_19', code: 'K7M-2QX' })
console.log(`Connect to ${session.member?.displayName ?? 'this Discord account'}`)
} catch (error) {
if (error instanceof CeejayError && error.code === 'code_not_found') {
console.log('That code is not open. Make a new one in Discord')
} else {
throw error
}
}ceejay.connect.getSession
Reads one connect session: its status, who approved on the Discord side once someone has, and the connection once it is approved.
GET /v1/games/{game}/connect/sessions/{sessionId} · requires players:connect · API reference: Get a connect session
Retried by the SDK: Yes. It is a read
Takes the game, the session, then options.
| Argument | Type | Required | What it is |
|---|---|---|---|
game | string | Yes | The game's slug. It is the first argument |
sessionId | string | Yes | The sessionId that ceejay.connect.createSession returned. It is the second argument |
Returns the session, typed as Schemas['ConnectSession'].
Prop
Type
A session of a paused game can still be read.
Errors
| Status | code | When |
|---|---|---|
403 | community_not_active | The community removed CEEJAY |
403 | game_not_allowed | The key is limited to other games |
403 | audience_not_allowed | The game is directed at children |
404 | session_not_found | The game has no session with this ID. A session of another game or community returns the same error |
404 | game_not_found | The community has no game with this slug |
404 | not_available | This instance does not support Connect yet |
Example
const session = await ceejay.connect.getSession('rugby_manager', 'kT3v9QmZ1xY7bLw2RfA8cN')
if (session.status === 'approved' && session.connection) {
console.log(session.connection.playerId, 'is connected to', session.connection.discordId)
}ceejay.connect.listSessions
Reads up to 100 connect sessions of one game by their IDs, for a server that follows several at once.
GET /v1/games/{game}/connect/sessions · requires players:connect · API reference: List connect sessions
Retried by the SDK: Yes. It is a read
Takes the game, one object, then options.
Prop
Type
Returns the full response, typed as Schemas['ListConnectSessionsResponse']. The
sessions are in data, and page is alongside it.
Prop
Type
Errors
| Status | code | When |
|---|---|---|
400 | bad_request | ids is missing, or is not 1 to 100 session IDs separated by commas |
403 | community_not_active | The community removed CEEJAY |
403 | game_not_allowed | The key is limited to other games |
403 | audience_not_allowed | The game is directed at children |
404 | game_not_found | The community has no game with this slug |
404 | not_available | This instance does not support Connect yet |
Example
const open = ['kT3v9QmZ1xY7bLw2RfA8cN', 'Zp4c81LmQe7YtR2vXw9KdA']
const sessions = await ceejay.connect.listSessions('rugby_manager', { ids: open.join(',') })
for (const session of sessions.data) {
console.log(session.sessionId, session.status)
}ceejay.connect.confirmSession
Confirms or declines the connection for the game, from your server, for a game that has no session secret. Read the session first, show your player the Discord name it returns, and send the player's choice. There is no default: the player decides.
POST /v1/games/{game}/connect/sessions/{sessionId}/confirm · requires players:connect · API reference: Confirm a connect session
Retried by the SDK: No. Retrying it yourself is safe: a confirmation that was already given does not change, and a decline for a session that has ended changes nothing
Takes the game, the session, one object, then options.
Prop
Type
A confirmation completes a session your server or the game started, and the connection is saved. For
a session a member started in Discord it moves the session to needs_confirmation, and
the member confirms there.
Returns the session after the confirmation or the decline, typed as Schemas['ConnectSession']: the
same fields as ceejay.connect.getSession returns, which are sessionId, status,
reason, expiresAt, member and connection.
Errors
| Status | code | When |
|---|---|---|
400 | bad_request | The body cannot be read, or accept is not true or false |
403 | community_not_active | The community removed CEEJAY |
403 | game_not_allowed | The key is limited to other games |
403 | audience_not_allowed | The game is directed at children |
404 | session_not_found | The game has no session with this ID |
404 | game_not_found | The community has no game with this slug |
404 | not_available | This instance does not support Connect yet |
409 | nothing_to_confirm | You sent a confirmation, and the session is not awaiting one right now |
409 | session_changed | The session changed since it was read. Read it again, and show the player its current state |
409 | player_blocked | The community blocked this player |
409 | player_taken | The player is connected to another Discord account |
409 | game_paused | The game is paused. A decline is still accepted |
Example
import { CeejayError } from '@communitystudios/ceejay'
try {
const session = await ceejay.connect.confirmSession('rugby_manager', 'kT3v9QmZ1xY7bLw2RfA8cN', { accept: true })
console.log(session.status, session.connection?.discordId)
} catch (error) {
if (error instanceof CeejayError && error.code === 'nothing_to_confirm') {
console.log('The member has not approved yet')
} else {
throw error
}
}ceejay.shop
List the purchases pending fulfilment by your server, and complete the delivery of a shop item that is delivered by your API.
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.

