CEEJAYDocs

Connect

Link a game player to their Discord account in a community, with confirmation from both sides.

Connect links one player of your game to one Discord member of a community on CEEJAY. The player presses a button in your game, authorises the connection with their Discord account on a page CEEJAY serves, and your game receives the member's name, avatar, level, coins and shared roles to show.

This page is for the studio's developers. It explains the flow, step by step, for each of the three ways a connection can start.

What this page covers

Connecting, staying connected and disconnecting, the button a member presses in Discord, what a connection earns, and the webhook events for a connection are here. What happens to a player afterwards (sending their data, reading them, deleting them) is on Player data. To add the button to a game, use the Connect client of the TypeScript SDK, a game kit for Swift, Kotlin, Godot or Flutter, or the recipe for any other engine. The SDKs for Unity and Unreal Engine are preview releases, currently in testing. What a connected game can show and do with the community's quests, shop and rank, including spending a member's coins, is on The game loop.

What you need

  • Connect switched on. It is switched on per instance. Where it is off, every request on this page returns 404 with the code not_available and feature: "players": hide the button.
  • A game set up for the community. We set a game up for you for now; dashboard setup is coming soon. A game has a slug (such as rugby_manager), a name and logo for the connect page, an audience, and the settings in the table below.
  • A public key for the game. It starts with ceejay_pk_, ships inside the game and is not a secret. A game can have up to 5 at a time, so a new build can carry a new key before the old one is retired.
  • For a studio with its own server: a secret key with the players:connect scope. A Full Access key does not include it: see Authentication.

What we save for a game, and what to send us:

SettingWhat it is
Audiencegeneral, mixed or children. A game with a mixed audience must send underAge with every start. A game directed at children cannot use Connect; every request returns 403
Web originsUp to 10, for a browser game: https:// and a host name, or http://localhost for development. A request from a web page on any other origin is refused
App originsUp to 5, for a game wrapped as an app, such as capacitor://localhost
Return URLsUp to 5, each under a key you choose, such as game. An https:// URL, or a custom scheme in reverse-domain form, such as com.studio.game://connect. A request names the key, never a URL
ProofThe project ID of your Firebase or Unity project, or your Steam app ID, when your players sign in with one of them. And whether the game connects verified players only
Join URLWhere to send a player who is not in the community's server, and whether the connect page offers to join
Shared rolesUp to 25 roles of the server the game may be told a member has
Basic connectionsWhether a connection where the game supplied the player ID without verification earns rewards and may spend. By default it does not. See Game rewards and The game loop
Game loopWhether the game shows the community's quests, shop, locker, rank and daily reward. Off until the community's team switches it on
Items it may sellThe item types the game may sell, which by default is items delivered by an API, and the list of up to 200 items the team put on it. The game sells nothing until the list contains an item
Daily spending limitThe most coins one member can spend through the game in a UTC day: 5,000 unless the team set another number. 0 switches spending off
Spending holdThe hours before a new connection or device can spend: 24 unless the team set another number, from 0 to 168

What a game stores

ValueStarts withWhat it isWhere it travels
Public keyceejay_pk_The game's own key. Not a secret. With it alone a game can start sessions and nothing elseAuthorization header
Session secretceejay_cs_Reads the result of one connect session. Returned when the session startsRequest body
Refresh tokenceejay_rt_What a device stores in its secure store after connecting. Replaced each time it is usedRequest body
Player sessioncjps1.A token for 15 minutes, for the requests a game makes for a connected playerAuthorization header

Each is accepted by one kind of request only and is recognised by its prefix. A secret key (ceejay_sk_) never goes into a game.

Confirmation from both sides

Nothing is saved until both sides have confirmed, each seeing the other:

  • The member's consent. The member presses Connect on CEEJAY's page, having been shown the game, the player's name and the player's ID.
  • The game's confirmation. The game shows its player the Discord name and avatar that were authorised, and the player confirms.

A link, a QR image or a code can be seen by someone else: on a stream, on a shared screen, on a console in a living room. Whoever opens it can authorise with their own Discord account. The game's step is where the player sees that it was not them and selects No, before anything is saved. So there is no default: ask the player every time.

Proof levels

Steam. A game on Steam verifies its player with proof: { type: "steam", token }, where the token is the hex ticket of GetAuthTicketForWebApi("ceejay") and the playerId is the player's SteamID64. CEEJAY checks the ticket with the community's publisher Web API key, saved under Connected Apps › Steam, for the app ID saved in the game's proof settings. A valid ticket gives the verified level. CEEJAY accepts each ticket once, so get a new one for every request.

A game can verify who its player is, or supply only an ID.

LevelHow the game verifies the playerWhat it is for
verifiedA connect pass your own server created, the token Firebase Authentication or Unity Player Authentication issued to the game, a Steam ticket, or a session your own server startedProving the player's identity. A second device that verifies the same player is signed in with no Discord step
basicNothing: the game supplies the ID. An anonymous sign-in counts as basic tooShowing the member in the game on the device that connected

A basic connection never blocks anyone. A verified connection for the same player replaces it, and a new device or a reinstall connects again through Discord. A game can be set up to connect verified players only.

The level also decides what a device may do with a member's coins. A basic device can read, do quests and check in, but it cannot buy or claim unless the community's team allows a basic connection to spend. The game loop has the table.

The game starts it

This is the usual way in. It needs the public key and nothing on your side but the game.

The player presses Connect. If your server signs players in, it first creates a pass with Create a connect pass and passes it to the game.

The game calls Start connect with the player's ID and its proof, if it has one. The response contains url, code, qrUrl and sessionSecret. Store sessionId, sessionSecret and expiresAt immediately, where you store the refresh token: a phone often closes a game while its player is in the browser.

The game opens the link, or shows the code. See Opening the link for each kind of device. Show the code as well while waiting, so a player whose phone does not come back can finish on any device.

The member authorises the connection. In the browser they sign in with Discord, review the data that is shared, and press Connect. The page then says to go back to the game.

The game polls for the result. Call Get the result every 3 seconds, and immediately when the player returns. When status is needs_game_confirmation, the response contains the Discord name and avatar.

The game asks the player to confirm. Show "Connect to" and the name, with Yes and No, and send Confirm connect.

Connected. The response to the confirmation is the approved result: the member card, a player session and a refresh token. Store the refresh token in the device's secure store and discard the session secret.

A session lasts 10 minutes. After that the result is expired, and the button goes back to not connected.

Start connect (cURL)
curl -X POST https://ceejay.communitystudios.xyz/api/v1/connect/start \
  -H "Authorization: Bearer $CEEJAY_PUBLIC_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "playerId": "p_19", "playerName": "Nova77", "returnTo": "game" }'

Your server starts it

Use this when your game contains no CEEJAY code, or when you want your server to make every request. Your server uses a secret key with players:connect, and never the requests a game makes: those are rate limited per IP address, and a server has one.

Your server calls Create a connect session for a player it has signed in. The secret key is the proof, so the session is at the verified level. The response contains url, code, qrUrl and sessionSecret.

Your server passes url to its game, which opens it as in Opening the link. Pass sessionSecret to the game as well only when the game should store the player's tokens.

The member authorises the connection on the connect page.

The game's confirmation. A game that has the session secret sends Confirm connect itself. Otherwise your server reads the session with Get a connect session, your game shows the player the name, and your server sends Confirm a connect session. A server that sent confirmInGame: false pre-confirmed the connection: do that only when the link is opened on the device the player is signed in on and the code is never shown where others can read it.

Your server receives the result from the webhook event player.connected, which CEEJAY sends shortly after the connection is made. Polling is the fallback: Get a connect session for one session, List connect sessions for up to 100.

If your players signed in to your game with Discord, you already know which member each player is. Skip all of this and call Link an account with the game's slug as platform. Your server creates that link without member approval, so the member sees no page, and the game gets no player session for them until they have completed Connect once.

The member starts it in Discord

A member can also start from the community's server. The community posts the game's button in a channel, and the two sides confirm each other:

The member presses the game's button. Only they see the reply: a single-use code that expires after 10 minutes, with a warning to type it only into the game and never to share it. A member who is connected already sees their player, their signed-in devices and a Disconnect button instead.

The member types the code into the game. A game sends it as code in Start connect, and a server sends it in Create a connect session. The response names the member who generated the code, and the game shows that name to its player before the player confirms. A player who was talked into typing someone else's code sees a stranger's name there.

The member presses Confirm in Discord, once the game has accepted the code. Discord then shows the same review the page shows: the game, the player's name and ID, what is shared, and Connect and Cancel. The message is visible only to the member and mentions no one.

Connected. The member's consent and the game's confirmation are both recorded, and the game polls for the result, as always.

Where the community has not posted the button, or a code was used or has expired, no pending request matches it, and code returns 404 with the code code_not_found. The code a game shows its player is a different one: it is typed on CEEJAY's page, never into a game.

The game always polls for the result. Returning to the game only triggers the next poll immediately, so nothing depends on a phone switching back.

DeviceOpenNotes
Browser gameA pop-up window, opened from the player's own clickOpen a blank window in the click and set its URL when Start connect responds, so the browser does not block it. The page cannot be shown in a frame
Game on a computerThe system browser
iPhone, AndroidThe system's web sign-in sheet, or a browser tabNever an embedded web view: Discord refuses to sign in there
Console, televisionNothingShow code, the URL to enter it at, and the image at qrUrl

The URL to enter a code at is the dashboard URL of the community's instance followed by /connect. qrUrl is an SVG QR code for the link, so a console game needs no QR code of its own.

To bring the player back, send returnTo with the key of one of your return URLs. The connect page then offers "Return to the game", a plain link to that URL with ceejay=return and session (the session's ID) appended. It contains no secret.

What the member sees

The connect page is CEEJAY's, at the dashboard's URL. It shows the community's name and icon and your game's name and logo, in the language you sent as locale.

  1. Sign in. The game, the player's name and the player's ID, and "Continue with Discord". When the game sent no proof, the page says the game could not verify who the player is.
  2. Discord's own screen, which asks the member to authorise the connection.
  3. Review. Who is signed in, with a way to use a different Discord account. The game and the player. What the community's team will see. What the game will receive: which Discord account it is, the nickname, level, XP and coins, and the shared roles. The game's data controller. Then Connect and Cancel.
  4. Back to the game. "Go back to the game to finish. It will ask you to confirm" and the Discord name.

Instead of connecting, the page shows a different screen when a connection already exists:

The page showsWhen
Sign another device inThis Discord account is already connected to this player
Keep or moveThis Discord account is connected to another player of the game. A Discord account can connect one player per game
Connect it hereThe player is connected to another Discord account, and may be moved here. Connecting it here disconnects it there
Already connectedThe player is connected to another Discord account at the verified level, and this session is basic. The real owner connects with proof
Cannot be connectedThe Discord account is banned from the server, or the community blocked the player

Where the community offers it, a player who is not in the server can tick "Also join". Joining is a separate authorisation on Discord's side.

A member manages their connections at the dashboard's URL followed by /connect/mine, with nothing but their Discord account: what is shared, which devices are signed in, and Disconnect. The game gets that URL as manageUrl.

Staying connected

A player session lasts 15 minutes. The device renews it with Renew a player session, which also replaces the refresh token. A refresh token is valid for 90 days from its last use.

On start-up a game looks, in this order, for:

  1. An open session it stored. Keep polling for its result.
  2. A refresh token. Renew.
  3. A proof token from its sign-in service. Call Get connection status, and start with the proof when the player is connected.

The button in your game has four states:

StateWhenThe game shows
Not connectedNo refresh token and no open session, or a renewal returned connection_endedConnect
WaitingAn open session is stored. It survives a restart, and ends after 10 minutesThe code, and the confirmation question when the game's confirmation is pending
ConnectedA valid player session existsThe member card from Get the player's profile
Needs attentionThe renewal returned no player session, or sharing.needed is not empty"Join the server" or "Rejoin the server" with joinUrl, or a link to manageUrl

Three rules keep a device signed in: store the new refresh token before you use the new player session, renew one call at a time, and when a response was lost send the token you still have. Renew a player session explains why.

A second device

A connection supports up to 5 devices. A sixth signs out the least recently used device.

A new device has a hold period before it can spend a member's coins, however it was approved: see The rules about money.

  • With proof. When the connection is verified and was made with the member's own consent, a new device that verifies the same player is approved immediately by Start connect: alreadyConnected is true, and no one is asked.
  • Without proof. The device goes through Discord again. The page shows "Sign another device in", and the member sees every signed-in device on their connections page.

A player who is not in the server

The connection is saved all the same. The member card says not_member or left, no player session is issued, and joinUrl is where to send the player. The refresh token still works for Disconnect, and a renewal returns a player session once the player has joined.

Age

A game is set up with an audience, and there is no default.

  • A game directed at children cannot use Connect: every request returns 403 with the code audience_not_allowed. The two exceptions are Disconnect a player and Delete a player: a studio can always end a connection and delete a player's data.
  • A game for a mixed audience sends underAge with every start. Without it the start returns 400 with the code age_answer_required.
  • underAge: true returns 403 with the code under_age and stores nothing. When the player is verified, any connection they have is ended.
  • underAge: true on a player or an event in Set player fields or Log player events ends the connection in the same way. Because it ends a connection, the key needs players:connect for it.
  • A connection that ended this way is not linked again by a key: Link an account returns 403 with the code under_age for that member and game, until the member connects again through Connect.

Disconnecting

A player can always end their own connection:

  • In the game, with Disconnect and the refresh token. It works whether or not they are in the server.
  • On their connections page, with nothing but their Discord account.
  • Through the community's team, who can end it for them.

A member who disconnected themselves is not linked again by a key: Link an account returns 409 with the code member_disconnected until the member connects again. A studio's server ends a connection with Disconnect a player, and deletes the player data CEEJAY stores with Delete a player. Either sends player.disconnected.

From a browser game

The requests a game makes can be sent from a web page.

  • The page's origin has to be on the game's list. Any other origin returns 403 with the code origin_not_allowed. A request with no Origin header, which is what a native game sends, passes.
  • Send the headers Authorization and Content-Type and no other. The response to the browser's preflight request allows exactly those two.
  • No cookie is read, and Access-Control-Allow-Credentials is never sent.

Testing on a live community

A public key can be made in test mode. A session started with a test key accepts consent only from someone who owns the server or has a role with the Administrator permission. Everyone else sees that the page is not available, so a studio can try the whole flow on a live community without touching its members.

Limits

The requests a game makes are counted per IP address, and the Connect requests of a server per game. Rate limits has the numbers.

The two API files

  • /openapi.json describes the requests a secret key makes: the five Connect ones, and the eight about players and the field map.
  • /openapi-connect.json describes the seven requests of Connect that a game makes, and the eleven requests of the game loop.

After a player is connected

Once connected, the player can earn rewards, sync data and trigger webhook events:

  • Rewards. A community can grant a reward for connecting, and for the events a game logs. CEEJAY's bot grants them shortly afterwards, and the result's reward shows the reward status. See Game rewards.
  • A role. A community can assign the connection role to connected members. The role is removed when the connection ends.
  • Player data. Your server sends a player's fields and events with Set player fields and Log player events, reads a player with Get a player and List players, and settles the field names with the community in the field map. See Player data.
  • Webhook events. player.connected and player.disconnected notify your server when a connection is made and when it ends.
  • The game loop. A game can show the community's quests, shop, locker, rank and daily reward for the connected player, and sell from its shop with the player's coins. See The game loop.

What follows

These are not in this release. Nothing on this page depends on them.

On this page

Community StudiosPrivacyTerms