Skip to main content
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 and a message meant for your logs:
{ "_tag": "DeveloperApiForbidden", "code": "insufficient_scope", "message": "Missing required scope: menus:write" }
Branch on _tag (and code where present), never on the message text — messages may change, tags won’t.

Status codes

StatusMeaningWhat to do
400The request is malformed or references invalid valuesFix the request; don’t retry unchanged
401Missing, malformed, or unrecognized credential, or an inactive appCheck the Authorization header and key
403Valid credential, but not allowed (see code below)Adjust scopes or environment
404The resource doesn’t exist or isn’t visible to your credentialVerify the id and that you’re connected
409The request conflicts with current stateResolve the conflict (e.g. the location is already connected)
5xxA problem on Maple’s sideRetry 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.

Error tags

_tagStatusNotes
DeveloperApiBadRequest400Malformed request, or invalid values such as unknown event types. For menu publishing, the message lists every validation issue.
DeveloperApiUnauthorized401The bearer credential is missing, malformed, expired, or belongs to an inactive app.
DeveloperApiForbidden403Carries a code: insufficient_scope or wrong_environment.
DeveloperApiNotFound404The resource doesn’t exist or your credential can’t see it.
DeveloperApiConflict409The request conflicts with current state, e.g. connecting a location another app already holds.

The 403 codes

A DeveloperApiForbidden always tells you why:
  • insufficient_scope — the credential is valid but lacks a scope the endpoint requires. Check GET /v1/me against 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 the message. The tag is your control flow; the message is for humans debugging.
  • Don’t retry 4xx unchanged. They mean the request needs to change. The one nuance: a 409 may clear once the conflicting state is resolved.
  • Retry 5xx with 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.