String docs

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

StatusConditionRetriable
400Invalid argument, out of rangeNot until you change the request
401Bad or missing API keyNo
403feed_builder_disabled on any route, or key_not_attributed on POST /v1/buildsSee below
404Unknown build or feedNo
409Already exists, failed precondition, abortedAfter the build's state changes
413Body over 1 MiBNo
429Rate limited — honour Retry-AfterYes
499The request was cancelled before it finishedn/a
503The service is temporarily unavailableYes
504The request timed outYes
500Anything else, with a deliberately generic messageMaybe

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.