# Errors

> Error format, codes, and what to do about each.

Source: https://docs.delivr.no/conventions/errors/

Errors use HTTP status codes and a JSON body:

```json
{
  "error": {
    "code": "invalid_request",
    "message": "delivery.contact needs name and phone",
    "param": "delivery.contact",
    "requestId": "…"
  }
}
```

- **Branch on `code`**, never on `message`. Messages are for people and may change.
- `param` names the field at fault, when there is one (for an unknown field, its name).
- `details` is present for `price_changed`, `option_unavailable`, `window_full` and `fee_changed`.
- `requestId` is the `X-Request-Id`. Quote it to support.

## Codes

| Status | Code                        | Meaning                                                                        | What to do                                               |
| ------ | --------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------- |
| 400    | `malformed_json`            | The body is not valid JSON.                                                    | Fix the request.                                         |
| 400    | `idempotency_key_required`  | A write without `Idempotency-Key`.                                             | Add a UUID key.                                          |
| 401    | `unauthenticated`           | Missing, unknown or revoked key.                                               | Check the key.                                           |
| 403    | `insufficient_scope`        | The key's scope does not allow this.                                           | Use an `orders` key.                                     |
| 403    | `merchant_suspended`        | Your account is suspended.                                                     | Contact Delivr.                                          |
| 404    | `not_found`                 | No such resource (or another merchant's).                                      | Check the id.                                            |
| 404    | `quote_not_found`           | No such quote.                                                                 | Quote again.                                             |
| 405    | `method_not_allowed`        | Wrong HTTP method.                                                             | Fix the request.                                         |
| 409    | `conflict`                  | The resource is not in a state that allows this.                               | Read it, then decide.                                    |
| 409    | `cancellation_not_allowed`  | The order can no longer be cancelled, such as a window order after its cutoff. | Contact Delivr support if you still need it stopped.     |
| 409    | `idempotency_key_in_flight` | The same key is still running.                                                 | Retry shortly, same key.                                 |
| 409    | `price_changed`             | Dearer than quoted. `details.option` has the new price.                        | Show it, or quote again.                                 |
| 409    | `option_unavailable`        | The option is gone. `details.alternatives` lists others.                       | Offer an alternative.                                    |
| 409    | `window_full`               | The window filled up. `details.alternatives` has the nearest.                  | Offer it, or use `onWindowFull: reschedule`.             |
| 409    | `fee_changed`               | The cancellation fee is above `maxFeeMinor`. `details.fee` has it.             | Decide, then cancel again.                               |
| 409    | `quote_already_used`        | The quote booked another option already.                                       | Quote again.                                             |
| 409    | `external_ref_taken`        | Another live order has this `externalRef`.                                     | Use a unique ref.                                        |
| 409    | `order_ended`               | The order is cancelled or failed.                                              | Read the order.                                          |
| 422    | `invalid_request`           | A field is missing, invalid or unknown (`param`).                              | Fix the request.                                         |
| 422    | `idempotency_key_reused`    | The key was used for a different request.                                      | Use a new key.                                           |
| 422    | `no_delivery_options`       | Nothing can be offered for this delivery.                                      | Do not offer Delivr for it.                              |
| 422    | `manual_quote_required`     | The delivery needs a manual quote (for example very large goods).              | Contact Delivr.                                          |
| 422    | `invalid_address`           | The address was not found.                                                     | Check it, or send coordinates.                           |
| 429    | `rate_limited`              | Too many requests.                                                             | Wait `Retry-After` seconds.                              |
| 500    | `internal`                  | Something went wrong at Delivr.                                                | Retry with the same key; contact support if it persists. |
| 503    | `unavailable`               | Temporarily unavailable.                                                       | Retry with the same key.                                 |

New codes may be added. Handle unknown codes by their HTTP status.