Responses and Errors
The JSON envelope every endpoint returns, request body limits, and the status codes to handle
The response envelope
Every endpoint answers with the same JSON envelope. A successful response sets
success: true and carries the result under data:
{
"success": true,
"data": { "id": "550e8400-e29b-41d4-a716-446655440000", "slug": "PRD-42" }
}An error response sets success: false and carries a human-readable error.
Three more fields are optional and appear only when the endpoint has them to
give:
{
"success": false,
"error": "...",
"code": "project_team_required",
"details": { "teams": [{ "id": "...", "name": "Platform" }] }
}| Field | Type | Meaning |
|---|---|---|
success | boolean | true on success, false on error. |
data | any | The result. Present only on success. Its shape is documented on each endpoint page. |
error | string | Human-readable message. Present only on error. |
code | string | Optional machine-readable error code. Branch on this, not on error. |
details | object | Optional structured payload that goes with code, carrying what you need to correct the request. |
timestamp | string | Optional time the error was produced. |
Error codes are endpoint-specific; each endpoint page lists the ones it documents. Treat a code you do not recognize as a generic failure of that status.
Request bodies
Send request bodies as JSON. Request bodies are capped at 4 MiB by default;
a body over the cap is refused with 413 and nothing is applied. A few endpoints
set a different cap, which their pages state.
A body that is not valid JSON, or that fails validation, is refused with 400.
For a validation failure, error names each failing field as path: message,
joined with commas:
{
"success": false,
"error": "title: <validation message>, type: <validation message>"
}Query parameters are validated the same way. Many endpoints reject a parameter
combination they cannot honor (for example, offset without limit) rather
than silently ignoring part of it.
Status codes
| Status | When |
|---|---|
200 | The request succeeded. A /v2 batch endpoint also returns 200 when some items failed; see Batch operations. |
202 | Accepted for processing, on the few endpoints that finish work asynchronously. |
400 | Malformed JSON, a validation failure, or a request the endpoint cannot satisfy as written. |
401 | No credential, or the credential is not valid. See Authentication. |
403 | The credential is valid but not allowed to do this: a missing API-key scope, or the caller lacks permission on the resource. |
404 | The resource does not exist in your organization. The message reads <Entity> not found. |
409 | The request conflicts with the resource's current state, such as a duplicate name or a stale precondition. |
410 | The endpoint has been retired permanently. Do not retry. |
413 | The request body, or an asset in it, is over its size limit. |
429 | The request hit a rate limit (see below). |
500 | The server failed. The message is deliberately generic. |
503 | A dependency the request needs could not be reached, so the server could not decide the request. |
A resource in another organization answers 404, not 403: lookups are scoped
to the caller's organization, so the API does not say whether it exists elsewhere.
Rate limits
The REST API has no global per-key request rate limit. Individual endpoints can
still refuse with 429. Document attachment uploads, for example, are
rate-limited; their 429 carries a Retry-After header and
details.retryAfterSeconds:
{
"success": false,
"error": "Attachment upload rate limit exceeded",
"code": "attachment_upload_rate_limited",
"details": { "retryAfterSeconds": 30 }
}When a response carries Retry-After, wait at least that many seconds before
retrying.