Error JSON shape
Every error response — regardless of status code — uses the same envelope:details field with structured context (typically validation errors):
HTTP status codes
404 is intentional for hidden resources. If you query a resource that exists but your workspace can’t see, you get
404 (not 403) so the response doesn’t leak whether the resource exists.Common error codes
Codes worth special handling in client logic:UNAUTHORIZED (401)
Your session cookie or bearer token is missing or expired. Re-authenticate the user (or, for the extension, request a new token).
FORBIDDEN (403)
Authenticated, but the user doesn’t have the right workspace role or doesn’t own the resource. Common on admin-only endpoints (billing changes, member management, share updates).
BUDGET_EXCEEDED (402)
Workspace has run out of credits. The user needs to upgrade or wait for the next billing cycle. The Billing overview explains the credit model.
RATE_LIMIT_EXCEEDED (429)
You’re hitting an endpoint too often. The response includes Retry-After and X-RateLimit-* headers — see the section below.
VALIDATION_ERROR (400 / 422)
Request body or query failed schema validation. The details field tells you exactly which fields are wrong. Surface this to the user — don’t retry blindly.
CONFLICT (409)
Something already exists (duplicate invite, subscription already in this state) or you’re racing another writer. Re-fetch the current state before retrying.
STRIPE_CARD_DECLINED / STRIPE_PAYMENT_FAILED (402)
Stripe sanitized error codes for billing operations. Don’t expose raw Stripe error messages — surface a friendly retry / “update card” CTA.
INTERNAL_ERROR (500)
Connie failed. Retry with exponential backoff up to a few attempts; if it persists, contact support.
Rate limits
Connie rate-limits most endpoints using a Redis-backed sliding window. Limits are tiered by plan and applied at multiple scopes (per-user, per-workspace, or per-IP depending on the endpoint).Default tiers
These are starting points — individual endpoints may be tighter (e.g. checkout, password reset). Inspect the response headers for the actual limit applied.
Rate-limit response headers
Every rate-limited response — both successes and 429s — includes:Recommended client behaviour
1
Check Remaining proactively
On every response, read
X-RateLimit-Remaining. If it’s low, slow down — don’t wait for a 429.2
Respect Retry-After
On a
429, sleep for the number of seconds in Retry-After before the next request. Don’t retry sooner — you’ll just get another 429.3
Backoff for 5xx
For
5xx, use exponential backoff with jitter — start at 1 second, double up to ~30 seconds. Cap retries at 4–5 attempts.4
Don't retry 4xx (except 429)
A
400, 403, 404, or 422 won’t change on retry — fix the request first.Worked example
A typical client-side handler:body.code keeps your error handling stable across API versions — the codes are part of the contract, the human-readable strings are not.