Skip to main content

Adrata Developers

Errors

Errors use RFC 7807 problem details with request IDs, docs URLs, retryability, and field errors.

Problem detail
{
  "type": "https://adrata.com/developers/errors#VALIDATION_ERROR",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "One or more fields are invalid.",
  "instance": "/companies",
  "request_id": "req_01J...",
  "error_code": "VALIDATION_ERROR",
  "docs_url": "https://adrata.com/developers/errors",
  "retryable": false,
  "field_errors": [{ "field": "domain", "message": "must be a valid domain", "code": "invalid_format" }]
}

Error codes

Dispatch on error_code, not on detail — the code is stable, the message is not. The type URI is this page with the code as its fragment, so every row below is directly linkable: #RATE_LIMITED lands on the rate-limit row.

CodeStatusRetryMeaning
INVALID_REQUEST400NoThe request was malformed or a parameter was rejected. `detail` names the problem.
UNSUPPORTED_VERSION400NoThe `Adrata-Version` header names a version that is not supported. Omit the header to get the latest stable version.
ROUTE_NOT_FOUND400 / 404NoNo route matches the path. Under `/api/*` this is 400 rather than 404, because CloudFront rewrites origin 404s into the web app shell.
UNAUTHORIZED401NoCredentials are missing, or the presented credentials are not valid for this workspace.
AUTHENTICATION_ERROR401NoThe bearer token could not be verified — expired, malformed, or signed by an unknown key. Refresh and retry once.
FORBIDDEN403NoAuthenticated, but not permitted to perform this action on this resource.
insufficient_scope403NoThe token is valid but lacks a required scope. `detail` lists the scopes the route requires. Lowercase by RFC 6750 convention.
RESOURCE_NOT_FOUND404NoThe resource does not exist, or is not visible to this workspace. The two are deliberately indistinguishable.
CONFLICT409NoThe request conflicts with current state — a duplicate unique key, or a concurrent write. Re-read before retrying.
VALIDATION_ERROR422NoThe request parsed but failed validation. `field_errors` carries the per-field reasons; surface them rather than the summary.
RATE_LIMITED429YesRate limit exceeded. Wait for `Retry-After`, then retry with exponential backoff and jitter.
USAGE_LIMIT_EXCEEDED429YesA plan usage limit is exhausted, not a per-second rate limit. `Retry-After` reflects when the allowance resets, which may be hours.
REQUEST_FAILED4xxNoA client error with no more specific code. Treat as non-retryable and log `request_id`.
INTERNAL_ERROR500YesAn unhandled server error. Retry with backoff; if it persists, send us `request_id`.
DATABASE_ERROR500YesA database call failed. Transient in most cases — retry with backoff.
CACHE_ERROR500YesA cache call failed. Transient — retry with backoff.
SERIALIZATION_ERROR500YesThe response could not be serialized. Retry once, then report `request_id`.
NOT_IMPLEMENTED501NoThe endpoint exists but the behaviour is not available. Retrying can never succeed, which is why this 5xx is not retryable.
SERVICE_UNAVAILABLE503YesA dependency is unavailable. Retry with backoff.
STORAGE_UNAVAILABLE503YesObject storage is unavailable, so an upload or download could not be served. Retry with backoff.

Handling

SDKs raise `AuthError`, `RateLimitError`, `ConflictError`, and `AdrataError`. Log `request_id` and preserve `field_errors` for user-facing validation.

Retry rules

Retry 429s after `Retry-After`, retry explicitly retryable 5xx responses with exponential backoff, and never retry non-idempotent writes unless you sent an `Idempotency-Key`.

The retryable member is authoritative and does not follow the status class: NOT_IMPLEMENTED is a 5xx that will never succeed on retry, so it is sent as retryable: false. Honour the field rather than inferring from the status.