Getting started
Errors
Status codes, the error body shape, and what to retry.
Every failure returns a JSON body. The status code tells you the category; the body tells you what to do about it.
Body shape
TODO: the error body shape is not final. It needs to match what the existing session-auth routes already return, and those currently return two different shapes depending on whether the failure came from the Zod validation middleware or from a controller. The public API should pick one.
The two shapes in play today:
{
"success": false,
"message": "Clan not found."
}{
"error": "Validation failed",
"issues": [
{ "path": ["amount"], "message": "Expected number, received string" }
]
}Status codes
| Code | Meaning | Retry? |
|---|---|---|
400 | The request was malformed, or asked for something the clan type forbids | No — fix the request |
401 | Key missing, malformed, or revoked | No |
403 | Key is valid but not scoped to the clan in the path | No |
404 | Clan, member or resource does not exist | No |
409 | Conflicts with the clan's linked Roblox group | No — act on the group instead |
422 | Semantically invalid — a rank that belongs to another clan, say | No |
429 | Rate limited | Yes, after the documented delay |
500 | Server fault | Yes, with backoff |
503 | Dependency unavailable — usually Roblox Open Cloud | Yes, with backoff |
TODO: confirm this list against the real implementation. 422 in particular may
collapse into 400.
Linked clan conflicts
A 409 on a Linked clan carries extra fields so a client can send the user to
the Roblox group rather than only reporting a refusal:
{
"success": false,
"code": "ROBLOX_GROUP_REQUIRED",
"robloxGroupId": "0000000",
"robloxGroupUrl": "https://www.roblox.com/groups/0000000"
}Membership on a Linked clan is owned by the Roblox group. Joins, leaves and ownership transfers all have to happen on roblox.com.
Handling errors in Luau
HttpService:RequestAsync does not throw on a non-2xx response — it returns one
with Success = false. Checking only for a thrown error will silently swallow
every 401 and 429 you ever get.
local HttpService = game:GetService("HttpService")
local ok, response = pcall(function()
return HttpService:RequestAsync({
Url = "https://API_BASE_URL/v0/clans/CLAN_ID",
Method = "GET",
Headers = { ["Authorization"] = "Bearer YOUR_CLAN_API_KEY" },
})
end)
if not ok then
-- Network failure, or HTTP requests disabled in Game Settings.
warn("Request could not be sent:", response)
return
end
if not response.Success then
-- Reached the API; the API said no.
warn("API error:", response.StatusCode, response.Body)
return
end
local clan = HttpService:JSONDecode(response.Body)What to retry
Retry 429, 500 and 503. Nothing else — a 4xx other than 429 will fail
identically no matter how many times you send it, and retrying it just burns your
rate limit.
Use exponential backoff with jitter. A Roblox game server that retries on a fixed interval will synchronise with every other server in your experience and arrive as a spike.