Errors
One envelope, the status mapping, and the two refusals that are not what they look like.
Every refusal on this API has the same body:
{
"error": {
"type": "invalid_request_error",
"code": "approve_required",
"message": "…",
"param": "publish.approve",
"request_id": "da06fc13-1f9d-46dc-aa36-cd62607c3386"
}
}Switch on type to decide whether to retry, fix the request, or escalate; code is the specific reason within that
family. param names the offending field when there is one, and is omitted rather than sent empty.
request_id matches the x-string-request-id response header. Quote it when you ask us about a request.
Status codes
| Status | Condition | Retriable |
|---|---|---|
400 | Invalid argument, out of range | Not until you change the request |
401 | Bad or missing API key | No |
403 | feed_builder_disabled on any route, or key_not_attributed on POST /v1/builds | See below |
404 | Unknown build or feed | No |
409 | Already exists, failed precondition, aborted | After the build's state changes |
413 | Body over 1 MiB | No |
429 | Rate limited — honour Retry-After | Yes |
499 | The request was cancelled before it finished | n/a |
503 | The service is temporarily unavailable | Yes |
504 | The request timed out | Yes |
500 | Anything else, with a deliberately generic message | Maybe |
Retry-After on a 429 is always present and never zero.
Two answers that are not what they look like
409, not 400, for a state conflict
Input sent to a finished build, or a gate answered after it stopped being pending, is a 409. Your JSON is fine —
the build moved. Re-read the build's state rather than editing your body.
503, not 401, when the service is unavailable
Never rotate a key in response to a 503. It is ours, and retriable.
403 refusals
feed_builder_disabled
Any route returns 403 with code feed_builder_disabled when your organization does not have access to Composer.
Retry after access is enabled; replacing the key does not change your organization's access.
Build ownership is hidden
A build owned by another organization returns a byte-identical 404 to an unknown id — same status, same code,
same message, built from the id you supplied. Nothing inspects the id, the organization, or the message to choose
between them.
Do not write client code that tries to tell the two apart, because nothing distinguishes them. A 403 describes
your organization's access or the key's attribution, not whether a build exists.
key_not_attributed
POST /v1/builds — which takes no build id, and so reveals nothing about any build — returns 403 with code
key_not_attributed when your key has no user recorded against it.
This is not an authentication failure. The key is genuine, and every read endpoint keeps working with it. Each feed is attributed to the person who requested it. A key with no user recorded, such as one whose creator has left your organization, cannot start a build.
Retrying will not help; generate a new key
Every retry fails identically. Generate a replacement under Feeds → Composer API Keys and the builds it starts will register normally.
It is refused at the start of the build rather than at publication, so you are not billed for a dataset that could never have been delivered.