Errors
Service outages and gateway errors normalise into one error model.
A failed call comes back in the same JSON body no matter where it failed. A cancellation that Catch Table refuses and a rate limit raised by the gateway share one structure, so you write error handling once and reuse it across every service in the catalog.
The body of every non-2xx response looks like this. provider and retryable are optional; the other three fields are always present.
{
"error": {
"code": "provider_unavailable",
"message": "Catch Table did not answer within the gateway deadline.",
"request_id": "req_01J8QGX2M4",
"provider": "catch-table",
"retryable": true
}
}code: a stable identifier your code can branch on. One of the eight values in the table below.message: a human readable explanation. Never parse it.request_id: the identifier of this request. Quote it when contacting support.provider: present only when the error originates from the underlying service.retryable: true when retrying the same request with backoff can succeed.The HTTP status puts an error into a broad class; code names the exact case. The mapping is fixed.
| HTTP | code | Meaning | Retry |
|---|---|---|---|
| 400 | invalid_request | The request failed schema validation. | No |
| 401 | unauthorized | The managed key is missing or invalid. | No |
| 403 | scope_denied | The key's scopes do not allow this service or API. | No |
| 404 | not_found | The resource in the path does not exist. | No |
| 409 | conflict | The request conflicts with the current state of the resource, for example a slot that has been taken. | No |
| 422 | provider_rejected | The service's own policy refused the request. | No |
| 429 | rate_limited | The request went over the rate limit. A Retry-After header accompanies it. | Yes |
| 502 | provider_unavailable | The underlying service did not answer. | Yes |
The provider field tells you where the error came from. When it is present, the request made it through the gateway and was refused or failed on the service side. Errors that end at the gateway, such as 401, 403 and 429, carry no provider field. Conversely, 422 provider_rejected and 502 provider_unavailable always originate at the service, so the field is always set on them.
The distinction decides your response. Gateway errors are fixed by fixing the request itself: the key, the scope, the schema. Service errors reflect state on the service's side, such as a cancellation policy, slot inventory or availability, so changing the request often changes nothing.
Every error carries a request_id. Gateway logs and the service call trail are joined on this one identifier, so quoting it in a support request lets APIFuse trace the failure without a reproduction. Log code together with request_id for any error you did not expect.
Calling on a reservation whose venue cancellation window has closed returns a 422. If you do not have a sandbox key yet, the walks through issuing one.
curl -X DELETE "https://api.frism.dev/v1/catch-table/reservations/rsv_01J8QDGT2K" \
-H "Authorization: Bearer frism_test_k3xample"{
"error": {
"code": "provider_rejected",
"message": "The venue's cancellation window has closed.",
"request_id": "req_01J8QH54N8",
"provider": "catch-table",
"retryable": false
}
}Always branch on code. The message exists to be shown to a human or written to a log, and its wording can change without notice. Avoid branching on the status alone as well: the status only names the class, and a more specific code can be added under the same status later.
const response = await fetch(
"https://api.frism.dev/v1/catch-table/reservations/rsv_01J8QDGT2K",
{
method: "DELETE",
headers: { Authorization: "Bearer frism_test_k3xample" },
},
);
if (!response.ok) {
const { error } = (await response.json()) as {
error: {
code: string;
message: string;
request_id: string;
provider?: string;
retryable?: boolean;
};
};
switch (error.code) {
case "provider_rejected":
// Policy refusal: tell the user instead of retrying.
break;
case "rate_limited":
case "provider_unavailable":
// Retry with backoff.
break;
default:
// Log unexpected codes together with the request_id.
console.error(error.code, error.request_id);
}
}Which errors are worth retrying, and how to back off, is covered in the . The full error responses for each API live in that service's API reference, for example .