# Errors

Error envelope:

```
{ "error": { "code": "...", "message": "...", "retry_after": 12 },

  "meta": { "request_id": "...", "api_version": "v1", "edge": true } }
```

`meta.request_id` and `meta.api_version` are always present. `retry_after` appears on `429` only. `meta.edge` is `true` only when the edge at `api.dzbuild.app` wrote the answer itself, for example an authentication or rate-limit refusal or a failed key check, instead of passing the platform's answer through unchanged.

| Code                    | HTTP | Meaning                                                                                                                                                                                                                                                                      |
| ----------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unauthorized`          | 401  | Missing/invalid Authorization header                                                                                                                                                                                                                                         |
| `forbidden`             | 403  | Authenticated but lacks scope, pilot enrollment, or an active Enterprise plan (merchant API keys need Enterprise; installed-app tokens work on every plan). An app token also gets it, with `Apps cannot use this endpoint`, on `/v1/keys`, `/v1/webhooks` and `/v1/changes` |
| `app_uninstalled`       | 403  | App token: the app is no longer installed on this store                                                                                                                                                                                                                      |
| `app_suspended`         | 403  | App token: DZBuild suspended or rejected the app                                                                                                                                                                                                                             |
| `app_not_approved`      | 403  | App token: the app is in test mode and this store does not belong to its developer                                                                                                                                                                                           |
| `app_plan_required`     | 403  | App token: the store's plan is below the app's minimum plan; `message` names the plan                                                                                                                                                                                        |
| `not_found`             | 404  | Resource doesn't exist or doesn't belong to your store                                                                                                                                                                                                                       |
| `bad_request`           | 400  | Validation error; see `message`                                                                                                                                                                                                                                              |
| `idempotency_key_reuse` | 422  | The `Idempotency-Key` was already used with a different method, path or body; send a new key                                                                                                                                                                                 |
| `method_not_allowed`    | 405  | Path exists for another method                                                                                                                                                                                                                                               |
| `payload_too_large`     | 413  | The request body is larger than 1 MB                                                                                                                                                                                                                                         |
| `rate_limited`          | 429  | Per-minute limit; `Retry-After` header included. App tokens also have a per-install limit of 120 requests per minute, checked before the store's                                                                                                                             |
| `too_many_concurrent`   | 429  | Too many expensive calls in flight (images, AI generate, courier calls, home page section writes); retry in a few seconds                                                                                                                                                    |
| `quota_exceeded`        | 402  | Monthly cap hit; upgrade or wait                                                                                                                                                                                                                                             |
| `server_error`          | 500  | Unexpected server error; safe to retry idempotent calls                                                                                                                                                                                                                      |
| `server_error`          | 502  | `api.dzbuild.app` could not check your key with the platform; retry shortly                                                                                                                                                                                                  |

A `details` object is reserved for future structured validation output. No endpoint emits it today, so don't branch on it.

## Retrying a failed write

A `4xx` returned by a write's handler is stored against your `Idempotency-Key` for 24 hours, on `https://api.dzbuild.app` and on the `dzbuild.com/api/v1` alias alike: retrying with the same key and body replays that same error, flagged with `Idempotency-Replay: 1`, without re-running anything. Fix the request and send it with a **new** key; the old key answers `422 idempotency_key_reuse` for the changed body. Refusals that happen before the handler runs, for example `401`, the pilot, plan and `app_*` answers with `403`, `429`, or a missing or malformed `Idempotency-Key`, are not cached, and neither are `5xx` responses, so the same key can be retried safely. See [Idempotency](https://dzbuild.com/api-docs/idempotency.md).

Always include `meta.request_id` when contacting support.
