Concepts
Errors
One error envelope for every failure, its types, and every error code the API answers with.
The API uses normal HTTP status codes: 2xx succeeded, 4xx means the request needs to change, 5xx means
something went wrong on Ordstage's side. Every error has the same JSON body:
{
"error": {
"type": "permission",
"code": "insufficient_scope",
"message": "This key can't do that; it needs the bookings:write scope.",
"param": "bookings:write",
"request_id": "req_4f2a9c0d1e7b3a5c6d8e9f01"
}
}| Field | Meaning |
|---|---|
type |
The broad kind of error (below). Always present. |
code |
A stable, machine-readable reason. Always present. New codes may be added; handle unknown ones by type. |
message |
A plain explanation for the developer. It can change; don't parse it. |
param |
The parameter, field or header the error is about, when there is one (null otherwise). |
request_id |
The same value as the Request-Id header. Quote it to support. |
Types
| Type | Status | What to do |
|---|---|---|
invalid_request |
400, 405, 413, 422 | Fix the request (see param). Retrying the same request fails again. |
authentication |
401 | Check the key. See Authentication. |
permission |
403 | The key, plan or business doesn't allow this. Don't retry. |
not_found |
404 | The object doesn't exist, or this key can't see it. |
conflict |
409 | The request clashes with another one (for example an Idempotency-Key reuse). |
rate_limit |
429 | Wait Retry-After seconds, then retry. See Rate limits. |
api_error |
500, 503 | Retry with backoff. If it continues, contact support with the request id. |
An object that belongs to another business always answers 404, never 403, so ids from other businesses can't
be discovered.
Retrying
Retry 429, 500, 503 and network errors with exponential backoff (for example 1, 2, 4, 8 seconds, plus a
random jitter), honouring Retry-After when present. Requests that create something must carry an
Idempotency-Key so a retry can't create it twice. Don't retry other 4xx answers unchanged.
Error codes
Generated from the API itself; the API reference lists the codes each endpoint can answer with.
| Code | Status | Type | Meaning |
|---|---|---|---|
invalid_api_key |
401 | authentication |
Use a valid workspace API key: Authorization: Bearer ord_… |
api_key_in_url |
400 | invalid_request |
API keys go in the Authorization or X-Api-Key header, never in the address. This key has been revoked; create a new one. |
sandbox_not_available |
403 | permission |
Test keys work with the sandbox, which comes with API access. This business's plan doesn't include it. |
insufficient_scope |
403 | permission |
This key doesn't have the scope this request needs. |
module_not_active |
403 | permission |
This business doesn't have the module this request needs. |
api_access_not_allowed |
403 | permission |
API access for this request isn't included for this business. |
plan_upgrade_required |
403 | permission |
The API is included on Growth plans and up. |
rate_limited |
429 | rate_limit |
Too many requests for this key. Wait and try again. |
too_many_invalid_keys |
429 | rate_limit |
Too many requests with invalid keys from this address. Try again in 15 minutes. |
rate_limiter_unavailable |
503 | api_error |
The API is briefly unavailable. Try again shortly. |
idempotency_key_required |
400 | invalid_request |
Send an Idempotency-Key header with this request. |
idempotency_conflict |
409 | conflict |
This Idempotency-Key was already used with a different request. |
idempotency_in_progress |
409 | conflict |
A request with this Idempotency-Key is still running. |
invalid_cursor |
400 | invalid_request |
starting_after must be the id of an object from this list. |
validation_error |
422 | invalid_request |
The request isn't valid. |
not_found |
404 | not_found |
Not found. |
booking_rejected |
422 | invalid_request |
The booking engine refused the request; code says why (for example invalid_time). |
key_creator_not_allowed |
403 | permission |
The person who created this key can no longer do this: changes that need a person are made as them, so they must still belong to the business with a role that may do it. |
not_available_in_sandbox |
409 | conflict |
This isn't available in the sandbox. Use a live key. |
invalid_role |
400 | invalid_request |
Choose owner, admin, manager or member. |
owner_role_required |
403 | permission |
Only an owner can manage owners; this key's creator is an admin. |
last_owner |
400 | invalid_request |
Assign another active owner before removing or changing the last owner. |
cannot_remove_self |
400 | invalid_request |
The key's creator can't remove themselves. |
seat_limit_reached |
403 | permission |
All seats are in use. Add a seat or remove someone first. |
already_member |
409 | conflict |
This person is already a member of this business. |
invitation_exists |
409 | conflict |
An invitation for this email is already pending. Resend it instead. |
invitation_not_pending |
409 | conflict |
This invitation was accepted or revoked. |
invitation_superseded |
409 | conflict |
This person already has access or a newer invitation. |
resend_too_soon |
429 | rate_limit |
Wait a minute before resending this invitation. |
member_inactive |
409 | conflict |
This member's account is turned off. |
password_reset_recently_sent |
429 | rate_limit |
A reset email was sent less than a minute ago. |
webhooks_not_allowed |
403 | permission |
Webhooks aren't included for this business. |
webhook_limit_reached |
409 | conflict |
A business can have 10 webhooks. |
invalid_webhook_url |
422 | invalid_request |
Use a public https:// address. |
unknown_event |
422 | invalid_request |
Choose events from the list of webhook events. |
unknown_calendar |
422 | invalid_request |
Choose calendars from this business. |
ping_not_retryable |
409 | conflict |
Test deliveries aren't retried; send a new test instead. |
assistant_release_not_ready |
409 | conflict |
Resolve the assistant's required setup items before publishing; message lists them in Studio. |
brand_identity_unapproved |
409 | conflict |
Approve the business profile in Studio before publishing. |
main_flow_missing |
409 | conflict |
Create a conversation flow before publishing this assistant. |
critical_action_tests_required |
409 | conflict |
Run passing tests for each action in the flow editor before publishing. |
flow_validation_failed |
409 | conflict |
Fix the assistant's conversation flow in the flow editor before publishing. |
flow_draft_conflict |
409 | conflict |
The flow changed while publishing. Try again. |
assistant_not_resumable |
409 | conflict |
Publish the current setup and resolve required items before resuming. |
test_key_required |
409 | conflict |
Test messages run in the sandbox: send them with a test key (ord_test_…). |
daily_limit_reached |
429 | rate_limit |
The plan's daily limit for test messages is used up. Try again tomorrow. |
reply_not_sent |
409 | conflict |
The channel didn't accept the reply; message says why (for example, its reply window closed). |
assignee_not_available |
409 | conflict |
This member can't take the conversation now; message says why. |
api_error |
500 | api_error |
Something went wrong on our side. Try again; if it keeps happening, contact support with the request id. |
Booking errors
When the booking engine refuses a request (status 422, code booking_rejected in the reference), code names
the exact reason, for example invalid_time, need_resource, need_dates or party_too_large, and message
explains it in words you can show to your team.
Bookings endpoints: the detail field
The bookings endpoints existed before this envelope. Their errors also keep the detail field they always had, so
older integrations keep working. New code should read error.