Errors & rate limits
Status codes, the error body, and what to do when you are throttled.
Errors come back as JSON with a stable code. Branch on the code, log the message, and
show neither to an end user.
{
"error": {
"code": "encryption_key_unconfirmed",
"message": "That key was never confirmed, so its private half may not exist anywhere.",
"request_id": "8f3d1b47-6a2e-4c90-b5d8-0e71fa3c26b9"
}
}Every signed request is answered with a Request-Id header, error or not, and it carries
the same id. Quote it in Discord and we can find the exact call in request logs,
where it is the id in the last column. Refused requests are logged there too — so a 401 you
cannot explain from your side is a row we can read from ours.
Status codes
| Status | Meaning | What to do |
|---|---|---|
400 | The request is malformed | Fix the call; retrying won't help |
401 | Not signed in | Sign in again |
403 | Authenticated, but not permitted | Check the account is connected and the scope is granted |
404 | No such resource, or not yours | Treat as gone; don't retry |
409 | Conflicts with current state | Re-read, then decide |
429 | Rate limited | Back off — see below |
5xx | Ours | Retry with backoff; we're already paged |
The ways a key request is refused
encryption_key_unconfirmed— the key exists but nobody confirmed the private half reached disk, so it cannot be assigned to an endpoint. Confirm it on Keys.encryption_key_in_use— an endpoint still names it. Assign that endpoint another key before revoking this one; the message says which URL is holding it.encryption_key_unusable— the bytes registered are not a usable X25519 public key. Nothing in 32 raw bytes names a curve, so this is only knowable at first use — register a fresh key withopenssl genpkey -algorithm x25519and assign it.
Rate limits
Every response carries the current window:
stridee-ratelimit-limit: 600
stridee-ratelimit-remaining: 573
stridee-ratelimit-reset: 41reset is seconds until the window rolls. A 429 also carries Retry-After; honour it
rather than your own backoff, and jitter your retries so a fleet doesn't resynchronise
into the next window.
Limits are per account. If you are polling to stay current, webhooks will cost you a fraction of the requests and get you the data sooner.
Pings are capped separately, at 10 per endpoint per minute.
Retrying safely
GETis safe to retry. So isDELETE— a second one gets a404, which is the state you wanted.- Writes take an
Idempotency-Keyheader. Reuse the same key on a retry and you get the original response back rather than a second resource. - Retry
429and5xx. Never retry400,403or404; the answer will not change.
Something wrong or missing on this page? Tell us in Discord.