# Errors

This page gathers the errors described across the other pages so you can handle them in one place. The API answers every error with the same JSON envelope; the OAuth endpoints use the RFC 6749 shape instead.

## The API error envelope[​](#the-api-error-envelope "Direct link to The API error envelope")

```
{

  "error": { "code": "forbidden", "message": "Missing scope: orders:write" },

  "meta": { "request_id": "8f2c1a9d4b7e6035", "api_version": "v1" }

}
```

`error.code` is stable and meant for your code. `error.message` is for humans and can change. Quote `meta.request_id` when you write to DZBuild about a call. Some errors add fields inside `error`, for example `retry_after` on a `429`.

## API status codes[​](#api-status-codes "Direct link to API status codes")

| Status | `error.code`            | Message                                                            | Cause and what to do                                                                                                                                                                                                                                                                              |
| ------ | ----------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `bad_request`           | varies                                                             | The `Idempotency-Key` header is missing or malformed on a `POST`, `PATCH` or `DELETE`, the body is not valid JSON, or a path id is not numeric. Fix the request; send a new key.                                                                                                                  |
| `401`  | `unauthorized`          | `Missing Authorization header`                                     | No `Authorization` header.                                                                                                                                                                                                                                                                        |
| `401`  | `unauthorized`          | `Unsupported Authorization scheme`                                 | The header does not start with `Bearer`.                                                                                                                                                                                                                                                          |
| `401`  | `unauthorized`          | `Invalid or revoked API key`                                       | The token is wrong, or the merchant uninstalled your app. Treat it as an uninstall unless you know otherwise.                                                                                                                                                                                     |
| `402`  | `no_credit`             |                                                                    | The store's WhatsApp wallet is empty. Nothing was queued or charged. Retrying does not help; the merchant tops up in the dashboard.                                                                                                                                                               |
| `402`  | `quota_exceeded`        |                                                                    | The store reached a monthly request quota DZBuild set for it. Retrying does not help.                                                                                                                                                                                                             |
| `403`  | `forbidden`             | `Missing scope: <scope>`                                           | The token lacks the scope the endpoint needs. Some writes need the read scope too; see [Scopes](https://dzbuild.dev/scopes.md).                                                                                                                                                                   |
| `403`  | `forbidden`             | `Apps cannot use this endpoint`                                    | `/v1/keys`, `/v1/webhooks` and `/v1/changes` are closed to install tokens.                                                                                                                                                                                                                        |
| `403`  | `app_uninstalled`       | `This app is no longer installed on this store`                    | The install is no longer active. Stop using the token and delete the store's data.                                                                                                                                                                                                                |
| `403`  | `app_suspended`         | `This app has been suspended by DZBuild`                           | DZBuild suspended or rejected the app. Calls work again once the suspension is lifted.                                                                                                                                                                                                            |
| `403`  | `app_not_approved`      | `This app is in test mode and only runs on its developer's stores` | The app has not been approved yet and the store does not belong to you.                                                                                                                                                                                                                           |
| `403`  | `app_plan_required`     | `This app requires the ... plan`                                   | The store's plan, or an expired paid plan, is below the app's minimum plan. Tell the merchant which plan is needed.                                                                                                                                                                               |
| `403`  | `addon_not_active`      |                                                                    | WhatsApp only. The merchant has not turned on the WhatsApp Sender addon.                                                                                                                                                                                                                          |
| `403`  | `plan_required`         |                                                                    | Home page sections only. The write would leave more sections than the store's plan allows. `error` carries `plan` and `cap`.                                                                                                                                                                      |
| `404`  | `not_found`             |                                                                    | No record with this id on this store.                                                                                                                                                                                                                                                             |
| `404`  | `section_not_found`     |                                                                    | Home page sections only. No section with this id on the store's home page.                                                                                                                                                                                                                        |
| `409`  | `already_sent`          |                                                                    | WhatsApp only. This template was already sent for this order. `error` carries the existing message `id` and `status`.                                                                                                                                                                             |
| `409`  | `write_conflict`        |                                                                    | Home page sections only. Another write changed the layout first, or the `version` sent on `PUT` is not the current one. When `error` carries `sections` and `version`, retry from them; otherwise read the layout again.                                                                          |
| `422`  | `idempotency_key_reuse` |                                                                    | The same `Idempotency-Key` was sent with a different method, path or body. Use one key per operation.                                                                                                                                                                                             |
| `422`  | WhatsApp codes          |                                                                    | `unknown_template`, `invalid_language`, `invalid_number`, `suppressed`, `template_not_approved`, `empty_param`. Nothing was charged. Fix the cause and call again with a new key. See [WhatsApp API](https://dzbuild.dev/whatsapp.md).                                                            |
| `422`  | home page section codes |                                                                    | `invalid_settings` (`error.fields` lists the refused settings, on `PUT` those of the first refused section), `invalid_section_type`, `limit_reached`, `invalid_order`, `no_changes`. Fix the request and call again with a new key. See [Home page sections](https://dzbuild.dev/home-layout.md). |
| `429`  | `rate_limited`          | `Per-minute API limit exceeded for this app install`               | Your install's budget of 120 requests per minute is used up. Wait `retry_after` seconds (also in the `Retry-After` header) and resend with the same `Idempotency-Key`.                                                                                                                            |
| `429`  | `rate_limited`          | `Per-minute API limit exceeded for this store`                     | The store's shared budget is used up. Same handling.                                                                                                                                                                                                                                              |
| `429`  | `rate_limited`          | `Per-minute API limit exceeded for this key`                       | The gateway's ceiling of 600 requests per minute per store. Same handling.                                                                                                                                                                                                                        |
| `429`  | `too_many_concurrent`   |                                                                    | Too many costly operations at once (image uploads, courier calls, home page section writes). `retry_after` is 5 seconds.                                                                                                                                                                          |
| `500`  | `send_failed`           |                                                                    | WhatsApp only. The message could not be queued. Retry later.                                                                                                                                                                                                                                      |
| `502`  | `server_error`          | `Key lookup failed, retry shortly`                                 | The gateway could not check the token with DZBuild. Retry after a short wait with the same `Idempotency-Key`.                                                                                                                                                                                     |

## Retry or not[​](#retry-or-not "Direct link to Retry or not")

| Answer                            | Retry                                                                                | With                                                                            |
| --------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| `429`                             | Yes, after `retry_after` seconds                                                     | The same `Idempotency-Key`                                                      |
| `5xx` and `502`                   | Yes, after a short wait                                                              | The same `Idempotency-Key`; these answers are never stored, so the request runs |
| `400`, `401`, `403`, `404`, `422` | No. Fix the cause first                                                              | A new `Idempotency-Key`, because a stored `4xx` is replayed for 24 hours        |
| `402`                             | No. The merchant has to act                                                          |                                                                                 |
| `409 already_sent`                | No. The message exists                                                               |                                                                                 |
| `409 write_conflict`              | Yes, from the `sections` and `version` in `error`, or after reading the layout again | A new `Idempotency-Key`                                                         |

## OAuth errors[​](#oauth-errors "Direct link to OAuth errors")

Before DZBuild has matched your `client_id` and `redirect_uri`, it shows an error page to the merchant and never redirects. After that, it redirects to your `redirect_uri` with an `error` parameter:

| `error`                     | Cause                                                                                                                                |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `unsupported_response_type` | `response_type` is not `code`.                                                                                                       |
| `invalid_request`           | `state` missing or longer than 1024 characters, `code_challenge` not 43 base64url characters, or `code_challenge_method` not `S256`. |
| `invalid_scope`             | A scope not registered on the app, not allowed for apps, or a `scope` parameter longer than 512 characters.                          |
| `access_denied`             | The merchant clicked Deny.                                                                                                           |

The token endpoint answers JSON with `error` and `error_description`:

| HTTP  | `error`                  | Cause                                                                                                                                                                                    |
| ----- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401` | `invalid_client`         | Missing or wrong `client_id` or `client_secret`.                                                                                                                                         |
| `400` | `unsupported_grant_type` | `grant_type` is not `authorization_code`.                                                                                                                                                |
| `400` | `invalid_grant`          | Unknown, expired or used code, a code issued to another app, a different `redirect_uri`, a verifier that does not match, or no selected store could be installed.                        |
| `500` | `server_error`           | A platform fault. Send the merchant through the authorize step again.                                                                                                                    |
| `403` | none, HTML body          | The request carried no `User-Agent` header: `dzbuild.com` answers a `POST` without one with a challenge page instead of JSON. Send one, for example `my-app/1.0 (+https://example.com)`. |

A claimed code is spent even when the exchange fails, so start a new authorize request instead of retrying with the same code. The full order of checks and the error pages are on the [OAuth](https://dzbuild.dev/oauth.md) page.

## Webhook delivery failures[​](#webhook-delivery-failures "Direct link to Webhook delivery failures")

A delivery succeeds when your server answers `2xx` within 10 seconds. Any other status, a redirect, a timeout or a connection error counts as a failed attempt. DZBuild tries each delivery up to 5 times with growing waits, then marks it dead. After 10 failed attempts in a row on one install, that endpoint is disabled; press Verify in the developer console to turn it back on. Details and the schedule are on the [Webhooks](https://dzbuild.dev/webhooks.md) page.

Your own verification should answer `401` when the signature or the timestamp check fails. Because that is a non-`2xx` answer, DZBuild counts it as a failed attempt and retries; a request that keeps failing verification is not from DZBuild, or your signing secret is out of date.
