Skip to content

Responses and errors

Every response is a JSON object with the same four fields, followed by the endpoint’s own data at the top level:

{
"restaurant_timezone": "America/New_York",
"fingerprint": "b82418952c",
"success": true,
"response_at": "11/06/2026 02:15pm",
"item": { "_id": "6650f0c2a1b2c3d4e5f60718", "number": "WD-100234", "balance": 0 }
}
Field Type Meaning
success boolean true if the request did what it asked. Always check this.
restaurant_timezone string or null IANA timezone of the token’s restaurant. See Money and timezones.
fingerprint string or null Short id of the token that made the call.
response_at string Server time of the response, MM/DD/YYYY hh:mmam. Informational only.
…data The endpoint’s payload, e.g. item, items, total. The API reference lists it per endpoint.

The data is merged into the top level, not nested under a data key. Ignore fields you do not recognise: new ones can be added at any time.

When the request does not pass validation (a missing field, a wrong type, a value too long), the response carries error_code: 101 and the messages per field:

{
"success": false,
"error_code": 101,
"errors": {
"number": ["The number field is required."],
"cardholder_email": ["The cardholder email must be a valid email address."]
}
}

errors maps each field name to a list of messages. Validation responses carry only these three fields, without the rest of the envelope.

When the request is valid but cannot be done (no such card, a number already in use, a units total that would go below zero) the response is the full envelope with success: false and errors in the same shape. Most carry no error_code:

{
"restaurant_timezone": "America/New_York",
"fingerprint": "b82418952c",
"success": false,
"response_at": "11/06/2026 02:15pm",
"errors": { "number": ["This card number is already in use."] },
"bot": "¯\\_(ツ)_/¯"
}

Failed responses also include a bot field. It carries no information; ignore it.

error_code Meaning
101 Validation failed; see errors.
403 The token may not do this (HTTP 403, or HTTP 200 on restaurant-scoped routes).
absent A business-rule failure; the message in errors says which.

The messages in errors are written for people and may be translated. Branch on success, error_code and the field name, not on message text.

const res = await fetch(url, { headers: { 'X-KATALYST-AUTH-TOKEN': token } });
const body = await res.json();
if (res.status === 401 || res.status === 403) throw new AuthError(body);
if (!body.success) {
// body.errors: { field: [messages] }; body.error_code === 101 for validation
throw new KatalystError(body.error_code ?? null, body.errors);
}
return body;