> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maple.inc/llms.txt
> Use this file to discover all available pages before exploring further.

# 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 and a `message` meant for your logs:

```json theme={null}
{ "_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

| 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) |
| `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](/developer-api/guides/receive-orders#step-5--decide-and-report-progress) and webhook replay endpoints are replay-safe, so retrying them is harmless.

## Error tags

| `_tag`                     | Status | Notes                                                                                                                            |
| -------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `DeveloperApiBadRequest`   | 400    | Malformed request, or invalid values such as unknown event types. 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.                                  |

### 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](/developer-api/concepts/authentication#scopes).
* `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](/developer-api/concepts/idempotency), so a retry after a timeout won't double-apply.
