Core concepts
Errors
Standard HTTP status codes with a JSON body carrying a stable machine-readable tag and a human-readable message.
The Developer API uses conventional HTTP status codes and returns a JSON body on every error. The body carries a stable _tag you can switch on, a message meant for your logs, and a feedback line saying where to report the error if it does not tell you what to change:
{ "_tag": "DeveloperApiForbidden", "code": "insufficient_scope", "message": "Missing required scope: menus:write", "feedback": "If this error does not tell you what to change, or the API is wrong, report it with POST /v1/feedback and cite this response's x-request-id: https://docs.maple.inc/developer-api/concepts/feedback"}Branch on _tag (and code where present), never on the message text — messages may change, tags won’t.
Status codes
| Status | Meaning | What to do |
|---|---|---|
400 |
The request is malformed or references invalid values | Fix the request; don’t retry unchanged |
401 |
Missing, malformed, or unrecognized credential, or an inactive app | Check the Authorization header and key |
403 |
Valid credential, but not allowed (see code below) |
Adjust scopes or environment |
404 |
The resource doesn’t exist or isn’t visible to your credential | Verify the id and that you’re connected |
409 |
The request conflicts with current state | Resolve the conflict (e.g. the location is already connected) |
422 |
A business rule or unsupported capability prevents the operation | Inspect code and details; change the request or location configuration |
429 |
A quota or rate limit was reached | Wait for the response’s retry_after seconds before trying again |
5xx |
A problem on Maple’s side | Retry with exponential backoff |
4xx bodies are safe to surface to your own logs and dashboards. 5xx responses should be retried with backoff — the order decision and webhook replay endpoints are replay-safe, so retrying them is harmless.
If an error does not tell you what to change, or the API does something these docs say it should not, send feedback and cite the request’s x-request-id.
Error tags
_tag |
Status | Notes |
|---|---|---|
DeveloperApiBadRequest |
400 | Malformed request, or invalid values such as unknown event types. When path parameters, query parameters, headers or the body fail validation, code is invalid_request and the message names the field and what was expected. For menu publishing, the message lists every validation issue. |
DeveloperApiUnauthorized |
401 | The bearer credential is missing, malformed, expired, or belongs to an inactive app. |
DeveloperApiForbidden |
403 | Carries a code: insufficient_scope or wrong_environment. |
DeveloperApiNotFound |
404 | The resource doesn’t exist or your credential can’t see it. |
DeveloperApiConflict |
409 | The request conflicts with current state, e.g. connecting a location another app already holds. |
DeveloperApiInternalError |
500 | Maple could not complete the request. Retry with backoff; if it persists, send feedback with the x-request-id. |
DeveloperApiUnprocessable is a 422 carrying code, message, and optional details. DeveloperApiRateLimited is a 429 carrying retry_after in seconds and the matching Retry-After HTTP header.
Booking error codes
Booking mutation bodies reject unknown fields with 400, including misspelled optional fields such as expectedVersion instead of expected_version. Idempotency keys must contain 1–255 characters.
400:invalid_slot_token,expired_slot_token,invalid_window,invalid_cursor,invalid_phone,location_required,change_required,invalid_blackout_dates; configuration schema failures include validation paths.409:stale_version(details.expected/actual),stale_blackout_dates,stale_configuration,idempotency_key_reused,overlapping_booking,slot_unavailable,hold_expired,hold_consumed,not_changeable,not_cancellable,illegal_transition(details.from_status),waitlist_closed,operation_in_progress(details.operation_id,details.retry_after,details.reason).422:bookings_not_enabled,party_size_out_of_bounds,outside_booking_window,large_party_inquiry_required,date_blocked,capability_unsupported,guest_contact_required,guest_messaging_unavailable,payment_instrument_required,configuration_missing,configuration_rejected,provider_rejected.
For operation_in_progress, retry the original request with its original key. If details.reason is replay_window_expired, stop automatic retries and contact Maple with the operation ID. Never use a new key to work around an uncertain provider outcome. See Take bookings.
provider_rejected is final: the provider refused the request and nothing changed. For example, OpenTable may not allow a reservation to be changed or cancelled through Maple; the venue can still change it. Retrying gets the same answer.
When the provider says why it refused, you get the code a Maple-native location answers with in the same situation, not provider_rejected:
- a time too soon to book or move to:
422 outside_booking_windowwithdetails.reason: inside_min_lead; - a reservation that has started or is about to:
409 not_changeableor409 not_cancellablewithdetails.reason: outside_policy_window; - a time that needs a card the provider can’t take through Maple:
422 payment_instrument_required.
These are final too, and nothing changed.
The 403 codes
A DeveloperApiForbidden always tells you why:
insufficient_scope— the credential is valid but lacks a scope the endpoint requires. CheckGET /v1/meagainst the scopes table.wrong_environment— you used a test credential against live data or vice versa. The credential’s prefix fixes its environment.
Handling errors well
- Switch on
_tag/code, log themessage. The tag is your control flow; the message is for humans debugging. - Don’t retry
4xxunchanged. They mean the request needs to change. The one nuance: a409may clear once the conflicting state is resolved. - Retry
5xxwith exponential backoff. Add jitter to avoid retry storms. - Treat write retries as safe. Order decisions and webhook replay are replay-safe, so a retry after a timeout won’t double-apply.