Read the locker
List what the connected player owns through this game, with the status of each purchase.
GET /v1/player/locker · for player session
Use it to show what the player has bought and what is waiting to be claimed. It lists the purchases made through the game, and purchases of items on the game's allow list made anywhere, such as in Discord, newest first. Earned rewards have no shop item and are not included. Send the player session as the bearer token. The request needs the game loop. For the three conditions (game loop enabled for the game, member consent, server membership) and the error each returns, see What has to be true first.
Query parameters
Prop
Type
Returns
data is a list of purchases, newest first. The response also includes page, with nextCursor and limit.
Prop
Type
claim contains:
Prop
Type
Each entry of fields contains:
Prop
Type
options contains at most 25 roles and 25 giveaways, read from CEEJAY's cache of the server, so a role that is not cached is left out:
Prop
Type
status and waitingFor have the values of Buy a shop item. status=open, the default, lists claimable and pending. status=all also includes delivered, refunded, rejected, expired and failed from the last 30 days.
A code is never sent
hasCode: true means a code exists. The member reads it in Discord. What the member typed when buying, and the delivery details, are not returned either.
Claiming
A claimable purchase is claimed with Claim an item. When claim.needsInput is true, ask the player for each entry of claim.fields. For a field whose kind is role or giveaway, the player picks one of claim.options.roles or claim.options.giveaways, and the game sends its id as text.
Paging
Pagination works as in List the quests. An item can repeat or be skipped if the list changes between two pages, so key it by the purchase's id.
Example
curl "https://ceejay.communitystudios.xyz/api/v1/player/locker?status=open&limit=20" \
-H "Authorization: Bearer $CEEJAY_PLAYER_SESSION"The TypeScript example uses connect.hub.listLocker from the Connect client in the TypeScript SDK. The player must be connected first: the client manages and refreshes the player session. It returns the full response below, with the list in data.
{
"success": true,
"data": [
{
"id": 4242,
"itemId": 7,
"itemName": "Gold skin",
"itemType": "api",
"imageUrl": null,
"pricePaid": 500,
"status": "claimable",
"waitingFor": null,
"statusLabel": "Ready to claim",
"createdAt": "2026-10-08T12:00:00.000Z",
"claimedAt": null,
"expiresAt": null,
"claim": {
"needsInput": true,
"fields": [
{
"key": "title",
"kind": "text",
"label": "Name on the kit",
"max": 40
}
],
"options": {
"roles": [
{
"id": "1332398278477746300",
"name": "Gold club",
"color": "#f94801"
}
],
"giveaways": [
{
"id": 31,
"title": "Weekly draw",
"endsAt": "2026-10-15T18:00:00.000Z"
}
]
}
},
"hasCode": false
}
],
"page": {
"nextCursor": null,
"limit": 50
}
}Limits
120 reads a minute for one player session. The three X-RateLimit headers report that count. Every request made with a player session also counts toward 140 a minute. See Rate limits. Over a limit the response is 429 with the code rate_limited, retryAfter and a Retry-After header.
Errors
| Status | code | error | When |
|---|---|---|---|
400 | bad_request | status must be open or all | status is any other value, or was sent more than once |
400 | bad_request | limit must be a whole number from 1 to 100 | limit is above 100, negative, a fraction or text |
400 | bad_request | cursor is not one this list gave. Send page.nextCursor as it was answered | cursor was not issued by this list |
401 | connection_ended | This connection has ended | The connection or this device was ended since the player session was made. Discard the stored tokens and show the player as disconnected. See Renew a player session for the reasons |
409 | hub_not_enabled | This game does not show the community's quests, shop and rank | The game loop is disabled for the game. Do not show the community's quests, shop or rank in the game |
409 | sharing_needed | The member has not consented to sharing their data with this game | The member has not consented to share their quests, rewards, shop and rank with the game, or must consent again. The body includes manageUrl: send the player there |
409 | membership_required | The member has to be in the server to use this | The Discord account is not in the server. The body includes membership (pending, left or not_member) and, for left and not_member, joinUrl, the URL to send the player to |
503 | bot_unavailable | This could not be done right now and nothing was changed. Try again in a few seconds | CEEJAY's bot could not be reached or was too busy, and nothing was changed. The body includes retryAfter, in seconds: retry after that many seconds |
504 | bot_timeout | The 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 | The request timed out, so it is not known whether it was processed. Retry the same request: it is safe to repeat |
Every error body also includes requestId, and a 400 with the code bad_request identifies the invalid field in field. Match on code; message text can change.
The errors every request of a game shares (the key, the state of the game, the origin, 405, 429, 500) are on the Errors page, and the errors specific to the game loop are under Codes of the game loop.

