OneGuard

Errors

Every failure uses the same envelope as success, with data: null:

{
  "statusCode": 404,
  "errorCode": "not_found",
  "message": "Resource not found",
  "data": null
}

Branch on errorCode — it is a stable contract. message can be reworded at any time and is meant for logs and humans, not for if statements.

Error codes

| errorCode | HTTP status | When | | --- | --- | --- | | bad_request | 400 | The request is malformed: a missing required field, a bad value, an out-of-range limit, and similar. | | unauthorized | 401 | No access token, an expired one, or the API key behind it is disabled, deleted, or replaced. See Authentication. | | forbidden | 403 | The token is valid, but the key's permission (or the caller's role on that specific resource) doesn't allow this action. | | not_found | 404 | The resource doesn't exist, or belongs to another organization. These are deliberately indistinguishable — a caller can never use this API to probe for the existence of something it doesn't own. | | conflict | 409 | The request collides with an in-progress operation — currently only idempotency_in_progress (see below). | | unprocessable_entity | 422 | The request is well-formed but cannot be applied — currently only idempotency_key_reuse (see below). | | invalid_cursor | 400 | The cursor query parameter isn't a value this API issued. See Pagination. | | rate_limited | 429 | The organization has used up its request allowance for the current window. See below. | | payload_too_large | 413 | The request body is over 1 MB. | | internal_error | 500 | Something failed on our side. message never contains a stack trace or exception text — quote the X-Request-Id header if you contact support. |

Rate limits

{
  "statusCode": 429,
  "errorCode": "rate_limited",
  "message": "...",
  "data": null
}

with a Retry-After header (seconds) telling you how long to wait. Limits apply per organization, per billing cycle, and cover reads and writes together — using up the allowance with reads blocks writes too, and vice versa. Back off and retry after the Retry-After interval rather than immediately; retrying immediately just spends another request hitting the same limit.

Retrying safely

A POST that creates something is not safe to blindly retry on a network timeout — you might create the resource twice. Send an Idempotency-Key header on every creating POST, and a retry with the same key is guaranteed to return the original result rather than a duplicate. See Idempotent writes for the exact rules.

GET, PATCH and DELETE are generally safe to retry as-is: GET has no side effects, and a PATCH/DELETE that repeats after a previous attempt actually succeeded either applies the same change again harmlessly or returns 404 not_found for a resource that's already gone — neither is an error worth treating specially.