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
| Code | Status |
|---|---|
invalid_request | 400 |
refused | 400 |
unauthorized | 401 |
forbidden | 403 |
insufficient_scope | 403 |
not_found | 404 |
conflict | 409 |
unprocessable | 422 |
too_large | 413 |
rate_limited | 429 |
server_error | 500 |
service_unavailable | 503 |
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.