CEEJAYDocs

Errors

One error shape for every request: a message, a fixed code and the ID of the request.

Every failed request returns a non-2xx status and the same body: a message for a person, a fixed code for your program, and the ID of the request.

400 Bad Request: invalid input
{
  "error": "discordId must be a valid Discord snowflake (17-20 digits)",
  "code": "bad_request",
  "requestId": "req_5f0c2a9b7d3e41f8a6b0c1d2",
  "field": "discordId"
}
FieldWhat it is
errorA message saying what was wrong. Its text can change: do not test it
codeOne of the codes in the table below. The list is closed for version 1: a code is never renamed or removed
requestIdThe ID CEEJAY gave this request: req_ and 24 hexadecimal characters. The header X-Request-Id carries the same value on every response, the successful ones included. Quote it when you ask us about a request
fieldOnly with bad_request, and only when one field is at fault: the name of that field, as you sent it

Some codes add extra fields, such as required and granted, retryAfter, balance or feature. The table below names them, and each reference page lists the errors of its own request.

Act on the code, not on the status

The status follows one rule for everything new since version 1.0.0:

StatusMeaningWhat to do
200Success—
202Complete a quest task found no member for the identifier you sent, so nothing happened. It is not an errorCheck the identifier. The body has data.matched: 0
308The path ends in a slash. The redirect points at the same path without itWrite paths without a trailing slash
400Invalid input: a missing or malformed fieldFix the request. Do not retry unchanged
401Missing, malformed, unknown, revoked or expired key. On a request a game makes: a public key, a session secret, a refresh token or a player session that was not accepted, or a proof that was rejectedCheck the header, then the key's status in the dashboard. For a game, read the code
403Key lacks the required scope, the CEEJAY bot lacks a Discord permission, or the role is one the API never grants or revokes. For Connect: the game cannot be used by this key or from this origin, or the player cannot be connectedReissue the key with the scope named in required, fix the bot's permission, or choose another role. For Connect, read the code
404Member, quest, program, purchase, channel, role, game, connect session or code not found, or this instance does not have the feature yet (not_available). A path that does not exist also returns 404, as an HTML pageVerify the ID and that it belongs to your server, and check the path
405Wrong HTTP methodCheck the method for that request
409The request conflicts with a rule: a quest's rule, a program that is not active, an account that is already linked, a game that is paused, a connect session that is not waiting for this, or an idempotencyKey that cannot be used this wayRead the code. The body names the state that blocked the call
410The result of a connect session can no longer be read (result_gone)Start a new session
413The body is larger than 1 MB. On Set player fields and Log player events the limit is 512 KB, and on the requests of the game loop it is 16 KB. Both return the usual error shape with the code body_too_largeSend less in one request
429Rate limited, by CEEJAY (rate_limited, too_many_attempts on Start connect, or event_rate_exceeded on Log player events) or by Discord (discord_rate_limited)Wait retryAfter seconds when it is there
500Something failed on our side, or this instance has no connection to Discord (discord_not_configured)Retry with backoff. If it persists, send us the requestId
502, 504CEEJAY's bot could not be reached or did not respond in time, on Complete a quest task and Complete a shop deliveryRead the request's page: a shop delivery is safe to retry, a quest task is not always
503, 504The same two codes on the requests of the game loop: 503 with bot_unavailable when nothing was done, 504 with bot_timeout when it is not known whether the request was doneRetry the request. Every request of the loop is safe to retry, apart from Check in, which returns checkin_cooldown once the daily reward is claimed
503CEEJAY had to query a service first and could not, so nothing was changed: Discord, or your community's list of bot master roles. Or this instance cannot store an idempotencyKey yet. Or a game's sign-in service could not be queried to verify a proof (proof_service_unavailable)For the first kind, retry shortly. If it persists, check that CEEJAY's bot is still in your server

Four refusals do not follow that rule. They returned 400 before the codes existed, and they keep 400 for as long as version 1 exists, where a request that conflicts with a rule now returns 409:

codeStatusRequest
quest_not_active400Complete a quest
quest_already_completed400Complete a quest
quest_limit_reached400Complete a quest
insufficient_balance400Deduct currency

So branch on code. A program that treats every 400 as "fix the request" and every 409 as "the request conflicts with a rule" gets these four wrong.

Three more statuses are named exceptions, because a caller could already read them: 502 for bot_unavailable and bot_timeout on Complete a quest task, 502 and 504 for the same two codes on Complete a shop delivery, and Discord's own status passed on with the code discord_error. The requests of the game loop are newer, and follow the rule: they return 503 and 504 for those two codes, and 409 for the quest rules and insufficient_balance that return 400 above. See the table below.

Every code

These are all the codes of version 1.0.0.

codeStatusWhen
missing_api_key401No Authorization header, or it does not start with Bearer
invalid_api_key401The key does not start with ceejay_sk_, or there is no such key
api_key_revoked401The key was revoked in the dashboard
api_key_expired401The key had an expiry date and it has passed
insufficient_scope403The key does not have the scope the request needs. The body adds required and granted
method_not_allowed405The request does not take this method
rate_limited429The key is over its limit. The body adds retryAfter: see Rate limits
internal_error500Something failed on our side
bad_request400A field is missing or malformed. The body adds field when one field is at fault
not_available404This instance does not have what the request needs yet. The body adds feature, the name Community details reports it under. For Connect and the player requests it is players, and for Log player events, when the member history is off, it is member_history
unknown_platform400Link an account and Complete a quest task, for a platform that is not known
member_not_found404The ID is not a member: CEEJAY has no profile for it, and where Discord is queried, Discord reports that it is not in your server
quest_not_found404Complete a quest, for a quest that is not on your server
program_not_found404The program XP requests, for a program that is not in your community
member_not_enrolled404The program XP requests, for a member who is not in the program
purchase_not_found404Complete a shop delivery, for a purchase that is not yours or is not delivered by your API
channel_not_found404Send a message, for a channel that does not exist, is in another server, or that the bot cannot see
role_not_found404Grant a role and Revoke a role, for a role that is not in your server
role_not_allowed403The same two requests, for @everyone, a managed role, a role that can moderate or manage the server, a role that can mention everyone where @everyone cannot, or a bot master role
bot_missing_permission403The CEEJAY bot lacks the Discord permission, or its role sits too low
quest_not_active400Complete a quest: the quest's status is not active
quest_already_completed400Complete a quest: the member completed it, and it cannot be repeated
quest_limit_reached400Complete a quest: the member reached the quest's per-member limit
insufficient_balance400Deduct currency: the member has less than the amount. The body adds balance, currentBalance and requested
quest_not_started409Complete a quest: before the quest's start date. The body adds startsAt
quest_ended409Complete a quest: after the quest's end date. The body adds endsAt
quest_requires_role409Complete a quest: the member does not have the role the quest requires. The body adds requiredRoleId
quest_requires_level409Complete a quest: the member is below the level. The body adds requiredLevel and level
quest_requires_season_xp409Complete a quest: the member is short of the season XP. The body adds requiredSeasonXp, seasonXp and seasonRunning
quest_requires_program409Complete a quest: the member is not in the quest's program. The body adds programId
quest_requires_quest409Complete a quest: the quest that comes first is not completed. The body adds prerequisiteQuestId
quest_full409Complete a quest: the quest reached its overall limit. The body adds maxCompletions
quest_cooling_down409Complete a quest: the cooldown between two runs has not passed. The body adds availableAt
quest_daily_limit_reached409Complete a quest: the member reached your community's daily quest limit. The body adds dailyQuestLimit, completedToday and resetsAt
program_not_active409The program XP requests: the program is not active. The body adds programStatus
member_status_not_allowed409The program XP requests: the member's status in the program does not allow XP changes. The body adds memberStatus
linked_by_sign_in409Link an account: the member linked the platform by signing in, and you sent a different account
player_already_linked409Link an account: another member already has that account linked
wrong_status409Complete a shop delivery: the purchase has already moved on. The body adds status
idempotency_key_reused409The idempotencyKey was already used for a different request
idempotency_in_progress409The first request with this key is still being processed. The body adds retryAfter
idempotency_outcome_unknown409A first request with this key never finished, and CEEJAY cannot know whether it took effect. It was not retried
idempotency_unavailable503You sent a key, and this instance cannot store idempotency keys yet. Nothing was changed
xp_limit_reached409The change would take XP past the largest number CEEJAY can store. Nothing was changed
balance_limit_reached409The grant would take the member's lifetime total past the largest number CEEJAY can store. Nothing was changed
discord_rate_limited429Discord is limiting the bot. The body adds retryAfter when Discord provided a retry time
discord_unavailable503CEEJAY could not query Discord about whether an ID is a member of your server, so nothing was changed. On Complete a quest task, also when a member's roles could not be read
discord_not_configured500This instance has no connection to Discord yet
discord_errorDiscord's ownDiscord refused the request. The body adds details with Discord's own text
roles_unavailable503Your server's role list could not be read from Discord. Nothing was changed
dashboard_roles_unavailable503Your community's bot master roles could not be read. Nothing was changed
member_roles_unavailable503Complete a quest: the quest requires a role, and the member's roles could not be read from Discord. Nothing was completed
channels_unavailable503List channels: the channel list could not be read from Discord
bot_unavailable502CEEJAY's bot could not be reached or refused the request. On Complete a quest task nothing happened; on Complete a shop delivery, also when the bot could not finish
bot_timeout502, 504CEEJAY's bot did not respond in time: 502 on Complete a quest task, where it is also the response when the bot's response was lost and the task may or may not have been completed, and 504 on Complete a shop delivery

Codes of an organisation's key

An organisation's key covers several communities, and each request names one of them in the CEEJAY-Community header. These 2 codes are part of the same closed list, and only requests made with an organisation's key return them.

codeStatusWhen
community_required400The CEEJAY-Community header is missing, or its value is not a server ID
community_not_covered403The community is not in the organisation, is not covered by the key, or has turned off the organisation's keys. The message is the same in all three cases

Codes of Connect

The requests of Connect add these codes. They are part of the same closed list, and they were added in version 1.1.0 of the API.

codeStatusWhen
bad_player_id400A game's ID for a player is missing, is not text, is longer than 128 characters, or contains a character outside the allowed set (letters, digits and . _ : @ | + = -, starting with a letter or a digit). On Link an account only for a platform that is a game, where the body adds field
bad_proof400Start connect and Get connection status: proof cannot be read, or the game has no project saved for that sign-in service
age_answer_required400Start connect and Create a connect session: the game is set up for a mixed audience and underAge was not sent
unknown_return400Start connect and Create a connect session: returnTo is not the key of a return URL saved for the game
wrong_key_kind401A request a game makes was sent a different kind of key or token than the one it accepts
invalid_public_key401A request a game makes carries no public key, or one CEEJAY does not recognise
key_retired401The public key was retired
key_suspended401The public key was suspended
proof_rejected401Start connect and Get connection status: the pass or the token was not accepted. The body adds reason
invalid_session_secret401Get the result and Confirm connect: no session of this game has this secret
invalid_refresh_token401Renew a player session and Disconnect: the value is not a refresh token this instance issued for this game, or it has not been used for 90 days
refresh_reused401Renew a player session: the token was replaced and has been used again, so the device is signed out
invalid_player_session401Get the player's profile and the requests of the game loop: no player session, or one that cannot be read or has expired
connection_ended401Renew a player session, Get the player's profile and the requests of the game loop: the connection or the device was ended. On a renewal the body adds reason
community_not_active403The community removed CEEJAY from its server
audience_not_allowed403The game is set up as directed at children. Every Connect and player request returns it except Disconnect a player and Delete a player, which a studio can always make
under_age403Start connect and Create a connect session: underAge is true. Nothing is stored. On Link an account, for a platform that is a game: the member's connection to it ended because the player is under age
origin_not_allowed403A request a game makes carries an Origin header that is not on the game's list
proof_required403Start connect: the game connects verified players only. Get connection status: the token is of an anonymous sign-in
game_not_allowed403The secret key is limited to other games of the community
game_not_found404The community has no game with that slug, or the game was removed
code_not_found404Start connect and Create a connect session: no pending request matches the code you sent
session_not_found404Get a connect session and Confirm a connect session: the game has no session with that ID
player_not_found404Get a player and Disconnect a player: no active connection has that player ID. On Get a player the body adds held: true when CEEJAY stores data for the ID that is waiting for a connection
game_paused409The game is paused or suspended
session_locked409Start connect and Create a connect session: the code belongs to a pending request that was locked after five failed attempts
session_changed409Confirm connect and Confirm a connect session: the session changed between the member's consent and the game's confirmation
nothing_to_confirm409Confirm connect and Confirm a connect session: the session is not awaiting the game's confirmation
player_blocked409The community blocked the player. On the two confirm requests and on Link an account
player_already_connected409No request returns it as a code yet. It is a value of reason on a connect session that ended
player_taken409Confirm connect and Confirm a connect session: another Discord account is connected to the player and it cannot be moved
member_disconnected409Link an account, for a platform that is a game: the member ended their connection themselves
banned409No request returns it as a code yet. It is a value of reason on a connect session that ended
result_gone410Get the result and Confirm connect: the result of the session can no longer be read. Start again
too_many_attempts429Start connect: too many starts or codes. The body adds retryAfter
proof_service_unavailable503Start connect and Get connection status: the keys of the sign-in service could not be fetched to check a token. Retry

Codes of player data

The player requests add these codes. They are part of the same closed list, and they were added in version 1.1.0 of the API.

codeStatusWhen
too_many_players400Set player fields: players contains more than 500 players
too_many_events400Log player events: events contains more than 500 events
body_too_large413Set player fields and Log player events: the body is larger than 512 KB. A request of the game loop that takes a body: it is larger than 16 KB. It is refused before it is parsed
activity_not_shared409Log player events: the game does not share the activity category, so no event can be logged. The whole call is refused
event_rate_exceeded429Log player events: the game sent more events this minute than it is allowed (3,000 unless another number was set for the game). The body adds retryAfter, and the Retry-After header carries the same number
field_name_taken409No request returns it yet. The community's team sees this error when approving a game's field and the community already has fields with both names it could take

A batch that has problems is not an error: Set player fields and Log player events return 200 with a status for each player or event. The statuses and the problems are values of those responses, not codes of this list.

Three codes are in the list and no request returns them yet: field_name_taken, player_already_connected and banned. The API file names them under x-not-answered-yet on its ErrorCode schema.

Codes of the game loop

The requests of the game loop add these 14 codes. They are part of the same closed list, and they were added in version 1.1.0 of the API. The message of each is on the reference page of the request that returns it.

codeStatusWhen
hub_not_enabled409Any request of the loop: the game does not show the community's quests, shop and rank, because the community's team has not switched the loop on for it
sharing_needed409Any request of the loop: the member has not consented to share their quests, rewards, shop and rank with the game, or has to consent again. The body adds manageUrl
membership_required409Any request of the loop: the Discord account is not in the server. The body adds membership, which is pending, left or not_member, and joinUrl for the last two
verified_proof_required403Buy a shop item and Claim an item: the device connected without verification, and the game only lets verified players spend
spend_on_hold409Buy a shop item and Claim an item: a new connection or device is still within its hold period and cannot spend yet. The body adds holdUntil and retryAfter
daily_spend_cap_reached409Buy a shop item: the purchase would exceed the daily limit. The body adds cap, spentToday, price, resetsAt and retryAfter
item_not_found404Buy a shop item: the item does not exist, is switched off, or is not on the game's list. The three cases return the same error
purchase_refused409Buy a shop item: the item cannot be bought now. The body adds reason and message
price_changed409Buy a shop item: the item's price differs from expectedPrice. The body adds price and expectedPrice
not_claimable409Claim an item: the purchase is in a state that cannot be claimed. The body adds status
quest_task_not_done409Verify a quest task and Answer a quest task: the verification failed, or the answer is incorrect. The body adds message
quest_task_unavailable409Verify a quest task and Answer a quest task: the task cannot be done this way. The body adds reason and, for needs_connect, platform
checkin_cooldown409Check in: the daily reward was already claimed. The body adds nextCheckInAt and retryAfter
economy_disabled409List the shop, Buy a shop item, Get the balance and Check in: the community has switched its economy off
survey_not_found404Answer a survey: the survey does not exist in this community
survey_unavailable409Answer a survey: the survey cannot be answered now. The body adds reason (not_open, closed, paused, role, already_answered, not_due, answer_in_discord or not_here) and a message to show the player

In the SDK, spend_on_hold and daily_spend_cap_reached are thrown as a CeejaySpendBlockedError: see The game loop, inside a game.

Statuses the game loop gives to codes that existed

Eighteen codes were in the list before the game loop. The requests of the loop return them with the status below, which follows the rule that a request that conflicts with a rule is 409. The older requests keep the status they have always had and never change it.

codeStatus in the game loopMessage
quest_not_found404Quest not found
quest_not_active409Quest is not active
quest_not_started409Quest has not started yet
quest_ended409Quest has ended
quest_already_completed409Quest already completed and is not repeatable
quest_limit_reached409Max completions per user reached
quest_requires_role409The member does not have the role this quest requires
quest_requires_level409The member has not reached the level this quest requires
quest_requires_season_xp409The member has not earned the season XP this quest requires
quest_requires_program409The member is not in the program this quest belongs to
quest_requires_quest409The member has not completed the quest that comes before this one
quest_full409Quest has reached its completion limit
quest_cooling_down409The member has to wait before completing this quest again
quest_daily_limit_reached409The member has reached this community's daily quest limit
insufficient_balance409Insufficient balance
purchase_not_found404Purchase not found
bot_unavailable503This could not be done right now and nothing was changed. Try again in a few seconds
bot_timeout504The answer did not arrive in time, and the request may or may not have been done. Send the same request again: it is safe to repeat

A refusal about a quest also includes message, text the game can show to the player.

Errors with extra fields

403 Forbidden
{
  "error": "Insufficient scopes",
  "code": "insufficient_scope",
  "requestId": "req_5f0c2a9b7d3e41f8a6b0c1d2",
  "required": ["economy:grant"],
  "granted": ["xp:grant", "xp:read"]
}
400 Bad Request: insufficient balance
{
  "error": "Insufficient balance",
  "code": "insufficient_balance",
  "requestId": "req_5f0c2a9b7d3e41f8a6b0c1d2",
  "currentBalance": 120,
  "requested": 500,
  "balance": 120
}
401 Unauthorized: a proof that was not accepted
{
  "error": "The proof of the player was not accepted",
  "code": "proof_rejected",
  "requestId": "req_5f0c2a9b7d3e41f8a6b0c1d2",
  "reason": "expired"
}
409 Conflict
{
  "error": "Program is not active",
  "code": "program_not_active",
  "requestId": "req_5f0c2a9b7d3e41f8a6b0c1d2",
  "programStatus": "paused"
}

Key and limit errors

These come before any request runs, and every request can return them.

StatuscodeerrorCause
401missing_api_keyMissing or invalid Authorization header. Expected: Bearer ceejay_sk_xxxNo Authorization header, or it does not start with Bearer (capital B, then a space). A key in the query string is not read, and a browser preflight (OPTIONS) carries no key, so both get this response too
401invalid_api_keyInvalid API key format. Expected: ceejay_sk_xxxThe key does not start with ceejay_sk_
401invalid_api_keyInvalid API keyNo such key
401api_key_revokedAPI key has been revokedThe key was revoked in the dashboard
401api_key_expiredAPI key has expiredThe key had an expiry date and it has passed
400community_requiredThis is an organisation’s key: name the community in the CEEJAY-Community headerAn organisation's key without the CEEJAY-Community header, or with a value that is not a server ID. The call still counts toward the key's rate limit
403community_not_coveredThis key doesn’t cover that communityThe community is not in the organisation, is not covered by the key, or has turned off the organisation's keys. The call still counts toward the key's rate limit
403insufficient_scopeInsufficient scopesThe key does not have the scope the request needs. The body adds required and granted. The call still counts toward the rate limit
405method_not_allowedMethod not allowedThe request does not take this method. The key is checked first, so the call still counts toward the rate limit
429rate_limitedRate limit exceededOver the key's limit. The body adds retryAfter, and the Retry-After header carries the same number of seconds. The limit is checked before the scope, so a key that is over its limit gets 429 from a request it has no scope for
429rate_limitedRate limit exceededThe IP address sent 60 secret keys CEEJAY does not recognise within a minute. Every key from that IP address is refused for the rest of the minute, before it is looked up, except one CEEJAY recognised moments ago. The body adds retryAfter, and the Retry-After header carries the same number. This response carries no X-RateLimit headers: no key is known. A caller using its own key from its own IP address is not affected
500internal_errorInternal server errorSomething failed on our side

A 401 for a secret key carries X-Request-Id and no rate limit headers: no key is known, so there is no limit to report.

Errors every request of a game shares

The requests a game makes with its public key or a player session ("Connect: from the game" in the reference) do not take a secret key, so the table above does not apply to them. These come before any of them runs, in this order:

StatuscodeerrorCause
429rate_limitedRate limit exceededMore than 600 requests in a minute from one IP address, across every request a game makes. The body adds retryAfter, and the Retry-After header carries the same number
404not_availableThis is not available on this instance yet.This instance does not offer Connect or the game loop yet. The body adds feature, which is players. Hide the button
401invalid_public_keyInvalid public keyNo Authorization header, a value that does not have the form of a public key, or a key CEEJAY does not know
401wrong_key_kindThis request takes another kind of keyThe bearer value is another kind of key or token than the request takes: a secret key sent from a game, a public key sent where a player session belongs
401invalid_player_sessionInvalid or expired player sessionOn Get the player's profile and the requests of the game loop: no player session, or one that cannot be read or has expired. Renew it
403community_not_activeThis community is no longer active on CEEJAYThe community removed CEEJAY from its server
401key_retiredThis public key has been retiredThe public key was retired. Ship the new key before you retire the old one
401key_suspendedThis public key has been suspendedThe public key was suspended
404game_not_foundGame not foundThe game was removed
403audience_not_allowedA game made for children cannot connect players or send player dataThe game is set up as directed at children. Such a game cannot connect players
409game_pausedThis game is pausedThe game is paused or suspended
403origin_not_allowedThis origin is not on the list saved for this gameThe request carries an Origin header that is not on the game's list. A request with no Origin header passes
405method_not_allowedMethod not allowedThe request does not take this method
500internal_errorInternal server errorSomething failed on our side

Every one of these responses includes X-Request-Id and the three rate limit headers, a 401 included: a game's request is counted against its IP address before its key is read.

The five Connect requests a server makes with a secret key share the key and limit errors above, with two differences. A Full Access key gets 403 with the code insufficient_scope, because players:connect is never part of *. And their 429 is about the game's own limit of 600 a minute, not the key's: see Rate limits.

The player requests (Get a player to Propose to the field map) share the key and limit errors too, and use the key's own limit. A Full Access key has players:read and players:write, and gets 403 with the code insufficient_scope on Disconnect a player (players:connect) and Delete a player (players:delete), and on a call to Set player fields or Log player events that sets underAge: true (players:connect).

Errors about an idempotency key

Every request that changes data accepts an idempotencyKey. Four responses are about the key itself:

StatuscodeerrorWhat to do
409idempotency_key_reusedThis idempotencyKey was already used for a different requestUse a key of its own for each request. One key is one request, across every request of the API
409idempotency_in_progressThe first request with this idempotencyKey is still running. Try again in a few seconds.Wait retryAfter seconds (the Retry-After header says the same) and retry
409idempotency_outcome_unknownAn earlier request with this idempotencyKey may or may not have been applied. It was not sent again. Use a new key to send it again.Sent by Send a message, Emit an event, Complete a quest task and Buy a shop item. Check what happened, then use a new key if it has to be retried
503idempotency_unavailableidempotencyKey cannot be used on this community yet, so nothing was changedWaiting does not change this response. Community details reports it as features.retryKeys: ask us to complete the update

Buy a shop item, which a game sends with a player session, is the one request of the game loop that takes a key, and it requires one. A key sent there belongs to the member, the game and the connection, and is retained for 30 days.

Responses that come before your key is read

CEEJAY returns these before any of its own code runs, so they are not JSON objects: they have no error, no code and no requestId, and they carry no X-Request-Id header.

StatusResponseCause
400The plain text Invalid JSONThe Content-Type is application/json and the body is not valid JSON
413The plain text Body exceeded 1mb limitThe body is larger than 1 MB
404An HTML pageThe path does not exist under /api/v1. Check it against its reference page
308A redirectThe path ends in a slash. The redirect goes to the same path without it, so write paths without a trailing slash

Calls that go through Discord

Grant a role, Revoke a role and Send a message ask Discord to do the work, and pass its response on:

StatuscodeerrorCause
403bot_missing_permissionBot lacks permission to manage this role or Bot lacks permission to send messages in this channelThe bot's Discord permissions or role position
404member_not_foundMember or role not found in this serverDiscord found no such member in your server
404channel_not_foundChannel not found or bot has no accessThe channel does not exist in your server, or the bot cannot see it
429discord_rate_limitedDiscord API rate limited, try again laterDiscord is limiting the bot. The body adds retryAfter, and the response includes Retry-After, when Discord provided a retry time
the status Discord sentdiscord_errorDiscord API error: and that statusAnything else Discord refused. The body adds details with Discord's own text
500discord_not_configuredThis instance cannot reach Discord yetThis instance has no connection to Discord

The two role requests first look the role up in your server's role list, which CEEJAY reads from Discord and caches for up to a minute. Then they check that it is not one of your community's bot master roles, which CEEJAY reads on every call. These responses come from those checks, before Discord is asked to change anything:

StatuscodeerrorCause
403role_not_allowedA message saying which kind of role it isThe role is @everyone, is managed by Discord or another app, can moderate or manage the server, can mention everyone in a server whose @everyone role cannot, or is a bot master role
404role_not_foundRole not found in this serverYour server has no role with that ID
503roles_unavailableCould not read this server's roles from Discord, so nothing was changedThe role list could not be read. Nothing was changed
503dashboard_roles_unavailableCould not check whether this role gives access to the CEEJAY dashboard, so nothing was changedThe bot master roles could not be read. Nothing was changed

Validation rules worth knowing

  • Send Content-Type: application/json with every JSON body. Without it the body is not read as JSON, and the response is a 400. With no content type, or text/plain, it is The request body must be a JSON object. With a form content type, which curl -d sends when you set none, it names a field as missing, as if you had left the field out. For example discordId must be a valid Discord snowflake (17-20 digits), even though the body contains a valid discordId.
  • The body of a POST must be a JSON object. null, a string, a number and a list return 400 with The request body must be a JSON object. Complete a shop delivery and Complete a quest task return 400 with their own first message instead.
  • Each field has one current name, such as discordId, playerId, reason and idempotencyKey. Every name a request accepted before is still accepted: each reference page lists the older names. When both names of one field are sent, the older one is used, and nothing is refused for sending both.
  • discordId must be a string of 17–20 digits. A JSON number cannot represent all the digits: its last ones would change, so it could name a different member. It is refused with 400, like any other value that is not a string, and so is a roleId, a channelId or an ID in allowedRoleIds sent that way. On Complete a quest task a discordId that is not a string of 17 to 20 digits returns 202, not 400.
  • Text is sent as a JSON string. reason on the XP and currency requests, content on Send a message, and taskKey and evidence on Complete a quest task return 400 for a number, a list or an object. On Complete a quest task an xHandle, wallet, github or twitch can also be a whole number, which is read as its digits. Any other value that is not a string returns 202.
  • Text and JSON that CEEJAY stores cannot contain a null character (U+0000). It returns 400 with the name of the field, such as reason cannot contain a null character, and nothing is changed or sent. This covers reason, every idempotencyKey, proof on Complete a quest, the platform, ID and name on Link an account, data on Emit an event, status and category on List quests, evidence and the identifier a member is looked up by on Complete a quest task, and message on Complete a shop delivery.
  • Text that CEEJAY stores as a key or inside JSON must be well-formed Unicode. Half of a surrogate pair, such as the JSON "\ud800" or an emoji cut in two, returns 400 in an idempotencyKey, in reason on Grant program XP and Deduct program XP, in proof on Complete a quest, in data on Emit an event, in evidence on Complete a quest task and in message on Complete a shop delivery.
  • proof on Complete a quest and data on Emit an event can nest lists and objects up to 32 levels deep. A deeper value returns 400.
  • amount must be a positive whole number, sent as a JSON number. XP grants cap at 100,000 per call, and so do program XP grants and deductions. Currency grants cap at 1,000,000. XP deductions and currency deductions cap at 2,147,483,647, the largest whole number the database stores. An amount above the cap returns 400.
  • A questId and a programId are whole numbers from 1 to 2,147,483,647, sent as a JSON number or as a string of digits written without leading zeros. A fraction, a negative number or a larger one returns 400. So does such a purchase ID in a path.
  • Balances, totals and other counts in a response are always JSON numbers, never text. CEEJAY's own IDs of quests, programs and purchases are numbers in every response.
  • Deducting more currency than a member has returns 400 with their current balance. A balance never goes negative.
  • Deducting XP below zero floors at zero rather than failing.
  • A currency grant that would take a member over the community's balance cap credits only the amount that fits and reports that in the response. It is not an error.
  • The requests that change a member's XP, currency, quests or linked accounts (Grant XP, Grant program XP, Grant currency, Complete a quest and Link an account) only write to a member of your server. An ID CEEJAY has no profile for is looked up in Discord first, and one that is not in your server returns 404 with the code member_not_found. A member who has only just joined can take up to 15 seconds to be found: wait 15 seconds before retrying. Complete a quest task makes the same check on a discordId, and returns 202 for one that is not in your server. When CEEJAY cannot confirm with Discord whether an ID is a member, the response is 503 with the code discord_unavailable and nothing is written. The requests that read a member behave as before.

Each reference page lists the errors its own request can send.

On this page

Community StudiosPrivacyTerms