Fabric documentation

Errors

The problem+json error body and the twelve error codes.

Every error is application/problem+json (RFC 9457) with the same fields:

{
  "type": "https://fabric.inc/docs/api/errors#insufficient_scope",
  "title": "Credential lacks the required scope",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "This key does not carry `catalog:write`.",
  "instance": "/api/v1/brands/brd_1/products/prd_9"
}

Branch on code. It is a closed set, it does not change between releases, and it is the only field you should compare. title and detail are for a person reading a log. detail describes this specific error, and its wording can change.

A validation error adds an errors array with one entry per field, so you can point to the input that was wrong:

{
  "code": "invalid_request",
  "status": 400,
  "errors": [{ "path": "limit", "message": "Expected number, received string" }]
}

Codes

invalid_request

400. The body or query could not be understood. Read errors: it names each field and what was wrong with it. Fix the request. Sending it again unchanged returns the same error.

refused

400. The request was understood and declined on its merits. detail gives the reason, which is about your data, not your syntax: for example, a mapping that is not active, or a run with nothing to do. Retrying unchanged will not help.

unauthorized

401. No usable credential. Every cause returns this same body; see below.

forbidden

403. The credential is valid and carries the scope, but the action is not allowed. A scope does not replace a role. Three cases return it: changing organization configuration as a member, writing a global config scope (a platform setting, refused to owners too), and an operation your organization is not entitled to, such as Catalog Builder.

Publishing role checks return refused instead. They run inside the operation, so approving a publish, cancelling one, and activating or rolling back a mapping version all return refused. detail names the role in both cases.

insufficient_scope

403. The credential is valid but does not carry the scope this endpoint requires. detail names the missing scope. Scopes are fixed when a key is created, so you need a new key. A retry will not help.

not_found

404. The resource does not exist, or it belongs to another organization. The API does not tell the two apart; see below.

conflict

409. The request contradicts the current state: reusing an Idempotency-Key with a different body, or starting a run on a brand that already has one in progress. detail says which.

unprocessable

422. The request is well formed and permitted, but cannot be applied to this resource in its current state. For example, approving a value that no AI wrote.

too_large

413. The response would be too large to return synchronously. The usual cause is a publishing export over the row cap for your organization or brand, which is 5,000 products by default. detail gives both the count and the cap. Start a run instead: a run streams its output, can resume, and leaves a record.

rate_limited

429. You are over one of the four budgets. Retry-After says how many seconds to wait, and RateLimit-Reset says when the window resets. See Rate limits.

server_error

500. An error on Fabric's side. Retry with backoff. The body contains no detail; see below.

service_unavailable

503. Fabric is missing configuration it needs for this operation, such as a credential on our side. detail names the missing configuration, because it describes our system and not your data. Contact us. Retrying will not fix it.

All authentication failures return the same 401

A missing, malformed, unknown, revoked or expired key, and a key whose organization was archived, all return the same 401 with the same message.

Different responses would tell a caller which of their guesses was once a real key, which is what someone trying to enumerate keys wants to learn.

When debugging your own integration, check that the header name is spelled correctly, that the key is the full string including the fab_sk_ prefix, and that it has not been revoked.

Resources in another organization return 404

A brandId from another organization returns 404, never 403.

A 403 would confirm that the brand exists. The API never tells you whether an id you do not own is real. Products, views, connections, mappings, runs and webhook endpoints all behave the same way.

500 errors contain no detail

A server error returns the bare server_error body. An internal message can name a brand, a destination or a credential, and the caller who triggered the error should learn none of them. The detail is in our logs, linked to the request.