Errors
One envelope, a closed list of codes, and a rule for which ones are worth retrying.
{ "error": { "code", "message", "details"? } }. Branch on code, never on message — the codes are a promise, the wording is not.The envelope#
{
"error": {
"code": "validation_failed",
"message": "body/start Invalid input: expected string, received undefined",
"details": [ { "…": "per-field errors, when there are any" } ]
}
}
Nested under error so you can tell a failure from a success by shape alone, which several connector platforms rely on.
Branch on code. The list below is published and we will not rename an entry. message is a developer-facing hint and its wording can change at any time — it is for your logs, not your control flow.
Every code#
| Code | Status | Meaning |
|---|---|---|
validation_failed | 400 | The request did not match the schema. details carries the field errors. |
unauthorized | 401 | No token, a malformed one, or one that has been revoked. |
insufficient_scope | 403 | A valid key without the scope this operation needs. |
not_found | 404 | No such booking, event type or subscription — for this account. |
slot_unavailable | 409 | That time is not free: taken, outside the schedule, or blocked on the calendar. |
event_type_inactive | 409 | The event type is gone or is not accepting bookings. |
booking_state_changed | 409 | It changed underneath you. Read it again and decide. |
duplicate_booking | 409 | The same person already has a booking near that time. |
idempotency_conflict | 409 | That Idempotency-Key was used with a different body. |
rate_limited | 429 | Over 120 requests a minute. Retry-After says when to come back. |
bad_request | 400 | A request the API could not make sense of at all. |
internal_error | 500 | Our fault. Safe to retry. |
New codes can appear — a new operation may need one — so treat an unrecognised code as a generic failure rather than crashing. That is the only part of this list that can change.
What is worth retrying#
429— yes. Wait forRetry-After, then repeat. See rate limits.500— yes, with back-off. It is our fault.- A timeout or dropped connection — yes, but on a write you cannot know whether it succeeded. Use an
Idempotency-Keyso the retry cannot make a second booking. 400,401,403,404,409— no. The same request will fail the same way. Fix it, or tell the person.
slot_unavailable deserves a word: it is not a transient failure that clears if you try again. Somebody else took the time, or it was never offered. Fetch availability again and pick a different one.
Validation errors#
validation_failed carries details with the specific fields. The message lists them too, so a log line is usually enough to see the problem:
{"error":{"code":"validation_failed","message":"body/eventTypeId invalid_id, body/start Invalid input: expected string, received undefined"}}
If every field looks absent, check you sent Content-Type: application/json — without it the body is never parsed.
Authentication failures#
A 401 always carries a WWW-Authenticate header naming where to authorise, which is how MCP and other automated clients discover the authorization server:
WWW-Authenticate: Bearer resource_metadata="https://api.marubox.jp/.well-known/oauth-protected-resource"
It means one of: no Authorization header, a malformed one, a key that has been revoked, or an account that has since been deleted. It never means "wrong scope" — that is 403 insufficient_scope.
Common questions
Should I ever match on the message text?
No. The wording is a developer hint and changes without notice; the code is the contract. If you need to distinguish two situations that share a code, say so and it can be split properly.I got 409 slot_unavailable but the slot looked free a second ago.
Somebody booked it in between, or it was never in the available list — the same code covers both. Re-fetch availability and choose again; retrying the same request will not help.What does booking_state_changed mean?
The booking changed while you were acting on it — cancelled elsewhere, for instance. Read it again and decide what to do with the current state.Last reviewed 28 September 2026.