Quests, shop and rank in your game
Show the community's quests, shop, locker, rank, daily reward and surveys in your game, and sell from its shop, with rules that keep the player's coins safe.
The game loop lets a game show the community's quests, shop, locker, rank, daily reward and surveys for a player who is connected to their Discord account, and lets the player act on them from inside the game: start a quest, buy an item, claim it, collect the daily reward. The community's team keeps control of what the game may sell and how much a player may spend.
This page is for the studio's developers. It says what a game can show and do, what has to be true first, the rules about the player's coins, and how your own server receives a purchase.
What this page covers
The thirteen requests are in the API reference under The game loop: from the game. The player has to be connected first: Connect explains how. To send the requests from a game, use connect.hub of the Connect client in the TypeScript SDK, a game kit, or the recipe for any other engine. The kits and the recipe do not have functions for the loop yet.
What a game can show and do
| Request | What it does | Coins |
|---|---|---|
| List the quests | The quests, each with its tasks and the player's progress | |
| Start a quest | Opens a quest for the player | |
| Verify a quest task | Verifies a task whose state CEEJAY can read, and completes it if the check passes | |
| Answer a quest task | Sends the answer to a task that asks a question | |
| List the shop | The items this game may sell, with whether the player can buy each one now | |
| Buy a shop item | Buys an item with the player's coins | Spends |
| Read the locker | What the player owns through the game | |
| Claim an item | Claims a purchase that is ready for the player | Redeems the purchase |
| Get the ranks | The player's level and place, and the top players of the game | |
| Get the balance | The coins, the streak, the perks, the XP boost, and what this device may spend today | |
| Check in | Claims the daily reward | Earns |
| List surveys | The surveys the community shows in this game that the player can answer now | |
| Answer a survey | Sends the player's answers, and grants the survey's rewards the first time |
A quest completed from the game grants the same rewards as in Discord: XP, coins, a badge, a role. The quest's own limits apply, such as how often it can be repeated and the community's daily quest limit.
The loop leaves some things out. A game cannot refund a purchase, change a price, or mark a delivery as fulfilled: your server does that with Complete a shop delivery. It cannot use a perk, skip a quest task or report an event from the game, and it shows no coin leaderboard. The locker shows what the game sold and what the community's team put on its list. A reward a member earned has no shop item, so it does not appear in the locker.
Surveys in the game
A survey is a form from the community's Surveys & Forms page with In game switched on under Where it shows. The team picks the game, or every game, and can make a survey wait for an event your game already logs with Log player events: for example, after match_end has happened 3 times since the survey opened. List surveys returns only the surveys due for the player now, so call it after the moments you log, and show one when it fits: after a match, or from a menu.
Each question comes with its key and a type that says how to ask it. Draw it with your game's own interface and send the answers back by key with Answer a survey. CEEJAY checks them by the rules Discord uses, saves them to the member's response beside answers given in Discord and the Member Hub, and grants the survey's rewards once, wherever the member answers. Nothing a game sends starts a direct message.
What has to be true first
Each of these has its own refusal. They are checked in this order, after the player session itself:
| What | Refusal | What to do |
|---|---|---|
This instance has the loop. Community details reports it as features.gameLoop | 404 with the code not_available | Hide the loop |
The community's team switched the loop on for your game. Get the player's profile reports it as hub.enabled | 409 with the code hub_not_enabled | Hide the loop. The team turns it on |
| The Discord account is in the server | 409 with the code membership_required. The body adds membership, and joinUrl for left and not_member | Send the player to joinUrl. A member who has not passed Discord's membership screening gets pending as membership and no joinUrl: they complete the screening in Discord |
| The member has consented to share their quests, rewards, shop and rank with your game | 409 with the code sharing_needed. The body adds manageUrl | Send the player to manageUrl |
A paused game returns 409 with the code game_paused, and a game directed at children returns 403 with the code audience_not_allowed. Buying and claiming need more, because they spend the player's coins: see The rules about money.
What the member consents to
Consent is part of connecting. For a game that has the loop on, the connect page and the confirm step in Discord state what data the game receives, and consent is not offered as a separate checkbox. Nothing is saved until the member gives consent.
The game receives which quests the member started or finished and their progress, their daily reward streak, perks and XP boost, what they buy through the game and what is in their locker, and their rank in the community. Their name, level and XP also show in the top ten of the game, where its other players can see them.
The consent text states the spending: "This game can spend your coins on its shop items, up to N a day", with the game's daily limit in place of N. For a game whose limit is 0 it reads "This game cannot spend your coins".
A member who consented is asked again when the community's team switches the loop on, raises the daily limit, adds an item type, or adds an item to the game's list. Until they consent, sharing.needed of Get the player's profile lists hub and every request of the loop returns sharing_needed. The member consents again at manageUrl, with nothing but their Discord account.
The rules about money
Reading, quests and the daily reward take nothing from the member. Only Buy a shop item spends coins, and Claim an item redeems a purchase. Both follow these rules, and a game cannot change any of them.
An item is on the game's list. A game sells only the items the community's team put on its list, and only of the item types the game is set to sell, which by default is items delivered by an API. The list is empty until the team adds items, so a game sells nothing at first. An item that is not on the list cannot be bought through the game: Buy a shop item returns the same error as for an item that does not exist. Without this list, one game could buy the items of another.
The price is validated. A game sends the price the player saw as expectedPrice. When the item's price now differs, nothing is bought. A game never decides a price, a balance or a reward.
Spending requires a verified device, when the game asks for it. The community's team sets, for each game, whether a connection where the game supplied the player ID without verification may spend. By default it may not.
| The device | Reading, quests, check in | Buying and claiming |
|---|---|---|
| Verified: the game verified who the player is | Yes | Yes, once the hold period has ended |
| Basic, and the game requires verification (the default) | Yes | No. 403 with the code verified_proof_required |
| Basic, and the team allows a basic connection to spend | Yes | Yes, once the hold period has ended |
Anyone with the game's public key can supply any player ID, so a basic connection does not verify who the player is. Connect explains the two levels.
New devices have a hold period before they can spend. A new connection, a move to another player, and each new device have a hold period before they can spend. The hold period is 24 hours unless the team set another number of hours for the game, and 0 removes it. A device must also be at least as old as the hold period, whether it was approved in Discord or verified at connection. A player on a new phone, or one who reinstalled the game, cannot buy until the hold period ends. The hold that applies is the latest of the connection's hold, the device's hold, and the device's age plus the game's hold hours. It is spending.holdUntil in Get the balance, and a request refused for it is 409 with the code spend_on_hold, holdUntil and retryAfter.
The hold period exists because a stolen refresh token remains valid for 90 days. It gives the member time to see the new device on their connections page and sign it out.
There is a daily limit. The game can spend a set number of coins a day for each member. The day is the UTC day. The limit is 5,000 coins unless the team set another number for the game, and 0 switches spending off. It counts the coins charged through this game since midnight UTC, excluding refunded purchases (a purchase the team rejected without refunding the coins still counts), and it is tracked for each member and each game: a member in two games has two limits. Purchases in Discord do not count. Past it, a purchase is 409 with the code daily_spend_cap_reached, with cap, spentToday, price, resetsAt and retryAfter. An item that costs more than the limit can never be bought through the game. The quests and the daily reward are not counted: they earn coins.
Purchases are idempotent. Every purchase carries an idempotencyKey the game chose, and a repeat with the same key never buys twice. Buying and claiming together are also limited to 60 an hour for one member in one game.
No direct messages. CEEJAY never sends a member a direct message (DM) for anything a game starts: not a purchase, a refund, a quest, a level or a reward. A task that the community's team reviews is completed in Discord, because the team's decision is sent there, and a game never submits an answer for review.
What a game never receives
- A Discord ID, an avatar or anything about another member. The top list of Get the ranks contains a display name, a level and XP for players of this game who consented to share it.
- A code a member owns. Read the locker returns
hasCode: true, and the member reads the code in Discord. - What the member typed when buying, a delivery address, a secret, or the text of a delivery error.
- A link to connect an account on a platform. A task that needs one says which platform, and the member does it in Discord.
- A balance of someone else, or a coin leaderboard.
Every response is private to the player and includes Cache-Control: no-store.
How your server receives a purchase
An item that is Delivered by your API is bought in the game like any other. CEEJAY then sends the signed delivery to the item's delivery URL, as Deliver shop items from your own platform describes. For a purchase made through a game, the body includes player: the game's slug as game, the game's own ID for the player as player_id, and proof (basic or verified), as they were when the member bought. Your server delivers the item to that player.
player is read from the purchase, never from the connection as it is now, so a player who moves to another ID between the purchase and a retry does not redirect the item. A studio that trusts only verified players checks proof and responds failed for a basic player: the player is refunded. Until your server completes a delivery it responded to with pending, the locker shows waitingFor: "studio".
A purchase made in Discord, a reward a member earned and a test delivery carry no player. List shop purchases returns the same values as player, with playerId, and a player erased with Delete a player has no player on a retry. The signature covers the whole body, so a receiver that ignores the field is unaffected.
The order of a first screen
- Renew the player session, or let the Connect client do it, and check that the player is connected.
- Read Get the player's profile. When
hub.enabledisfalseshow nothing of the loop. Whensharing.neededlistshub, send the player tomanageUrl. - Read Get the balance for the coins and for
spending.maySpend, which indicates whether a Buy button should be enabled. - Read List the quests and List the shop, a page at a time.
- Call Read the locker for what the player owns and can claim, and Get the ranks where the screen shows a leaderboard.
Retrying safely
| Request | After a lost response |
|---|---|
| The five reads | Retry it. It changes nothing |
| Start a quest, Verify and Answer a quest task, Claim an item | Retry it. A repeat returns that it was already done |
| Buy a shop item | Retry with the same idempotencyKey. Nothing is bought twice |
| Check in | Do not retry it automatically. A repeat returns checkin_cooldown, which means the reward is already claimed |
Limits
A player session can make 120 reads and 20 writes a minute, and 140 requests of any kind. Rate limits has the numbers.
Related
- Connect and Get the player's profile
- Deliver shop items from your own platform
- Errors lists every code of the loop
- The TypeScript SDK has
connect.hubfor the game.
Connect for any engine
Link a player of a game built with any engine to their Discord account in a community, with seven web requests and nothing else.
Player data
Send fields and events about your players to CEEJAY: how a batch works, what each result means, where the data is stored, and how a player's data is deleted.

