API Reference
Errors & Rate Limiting

Errors & Rate Limiting


HTTP status codes

CodeMeaning
200 OKRequest succeeded
201 CreatedResource created
202 AcceptedRequest accepted but not yet complete (e.g. registration pending email verification)
400 Bad RequestInvalid request body, bad query parameter, or missing required field
401 UnauthorizedMissing, malformed, revoked, or expired API key / session
402 Payment RequiredThe organisation is read-only because its trial or subscription has ended: reads still work, writes are refused with code: "read_only" until a plan is chosen
403 ForbiddenValid credentials, but insufficient permissions or scope. code: "PLAN_LIMIT_REACHED" can only occur for an organisation with no plan, which is already read-only; no paid plan limits open offboardings
404 Not FoundResource does not exist (or doesn't belong to your organisation)
409 ConflictA duplicate (an open offboarding for the same email, with its id in the body) or a stage gate that blocks a status change (code: "stage_gate")
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorServer-side error

Error response format

All error responses follow this shape:

{
  "error": "A human-readable description of the error"
}

Some errors add a code so a client can act on them without parsing the text: stage_gate, PLAN_LIMIT_REACHED, manager_email_is_employee, unknown_apps and read_only. Where a code exists, the endpoint's page names it; everything else has only the message. In non-production environments the error message is often the raw exception message rather than a generic one, which is useful for debugging but shouldn't be pattern-matched against in client code.


Rate limiting

PurpletGo layers a few different rate limits depending on which endpoints you're calling:

Endpoint groupLimit
POST /api/auth/login10 requests / 15 minutes per IP
POST /api/auth/register5 requests / hour per IP
/api/v1/* (per API key)120 requests / 60 seconds per key
Everything else under /api300 requests / 60 seconds per user (or per IP if unauthenticated)

When the limit is exceeded, the API returns 429 with the standard error envelope:

{
  "error": "Too many requests. Please slow down."
}

Limits enforced by the shared Express rate limiter also send a Retry-After header with the number of seconds to wait. The per-API-key limit on /api/v1 is checked separately in the auth middleware and doesn't set that header - back off and retry after a few seconds if you hit it.