error.code and a human-readable message. Branch your error handling on error.code rather than on the HTTP status alone — the same status can carry different codes, and the codes tell you whether the fix is in your request or in your data.
The codes
Six distincterror.code values were observed in testing.
The three validation codes carry different details
FST_ERR_VALIDATION, BAD_REQUEST, and VALIDATION_ERROR all mean “your request is wrong”, but they are not interchangeable in code. Each attaches a different payload, so a single parser written against one of them will break on the other two.
Write your error formatter to check for
error.details before reading it, and to fall back to error.message and then the top-level message. That covers all three codes without special-casing any of them.no_available_users_found_error
This is not an error.code. It is a domain message that arrives in the top-level message field alongside error.code: "INTERNAL_ERROR" and a 500 status.
start was not a time that GET /slots/ returned. Availability is the product of schedules, buffers, minimum notice, booking limits, connected-calendar busy times, and timezone rules; a time that looks free on a calendar is frequently not bookable.
POST /slots/reserve does not protect you from this. Reserving a time that GET /slots/ never offered returns success; the failure surfaces later, at POST /booking/.
Two error shapes that are not the envelope
Not every failure comes back in the{ success, message, error } envelope. Two do not, and both will break a client that assumes it.
Unknown route or wrong HTTP method
Mistype a path, or call a real path with the wrong verb, and the response comes from the router rather than the API. There is nosuccess field, and error is a plain string instead of an object.
Rate limiting
A429 returns its own shape, with a retryAfter in seconds and no error.code.
retryAfter seconds, then back off exponentially. See Rate limits.
Handling errors in practice
1
Branch on the HTTP status first
It is the only field guaranteed to be present. It tells you immediately whether you are in envelope territory (
400, 401, 422, 500 from the API) or not (404 from the router, 429 from the limiter).2
Then read error.code
Use it to decide whose problem this is: a
4xx code means fix the request; INTERNAL_ERROR means read message before assuming anything.3
Surface message, not code, to humans
The
message field names the offending field or the domain rule. FST_ERR_VALIDATION on its own tells a user nothing.4
Retry only 429
Everything else in this list is deterministic. A retry loop on
500 will burn your rate-limit allowance producing the identical failure.These six codes are the ones observed in live testing. They are not guaranteed to be the complete set — always keep a default branch that logs the raw body for any
error.code you do not recognise.Debugging resources
Quickstart
From a fresh API key to a confirmed booking in five requests.
API integration
Envelopes, pagination, and the behaviours that cause most first-build bugs.
Rate limits
The headers, the 429 body, and how to back off cleanly.
API Reference
Every endpoint, with schemas and a live “Try it” playground.