---
title: "API errors"
description: "Every error code the QuotWay API returns - HTTP status, what it means and what to do - with the RFC 9457 problem-details format."
url: "https://www.quotway.com/de/docs/api/errors"
type: "documentation"
category: "api"
updated: "2026-09-30"
locale: "de"
source: "QuotWay - B2B Quote & Negotiation App for Shopify"
---

# API errors

**Read time:** 9 minutes.
**Who it's for:** Developers handling failures from the QuotWay API.

## What does an error look like?

Every error is an [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem-details document with
`Content-Type: application/problem+json`:

```json
{
  "type": "https://www.quotway.com/docs/api/errors#insufficient_scope",
  "title": "Forbidden",
  "status": 403,
  "detail": "This API key is missing the scope(s): write_quotes.",
  "code": "insufficient_scope",
  "request_id": "req_3f9a1c2e7b4d4e0f9a8b7c6d5e4f3a2b",
  "required_scopes": ["write_quotes"]
}
```

| Field | Meaning |
|---|---|
| `type` | A link to this page, at the anchor for the error. |
| `title` | A short, human summary. |
| `status` | The HTTP status, repeated in the body. |
| `detail` | A human-readable explanation of this occurrence. Show it in logs; don't parse it. |
| `code` | **The stable, machine-readable error code. Branch on this.** |
| `request_id` | The request's id (also in the `X-Request-Id` header). Quote it when you contact support. |
| extra members | Some codes add fields - `errors`, `required_scopes`, `required_plan`, `reason`, `line_index`, `floor_discount_pct`, `lines`, `resolution`, `daily_limit`. Each is listed with its code below. |

**Branch on `code`, not on `status` or `detail`.** Several codes share a status (there are six
different `409`s), and `detail` wording can change.

Validation errors (`invalid_request`, `invalid_custom_fields`) add an `errors` array with one entry
per problem:

```json
{
  "type": "https://www.quotway.com/docs/api/errors#invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The request body is invalid.",
  "code": "invalid_request",
  "request_id": "req_8c1d…",
  "errors": [
    { "field": "lines.0.variant_id", "message": "No such product variant in this store." },
    { "field": "buyer.email", "message": "Invalid email address" }
  ]
}
```

## In what order are requests checked?

Knowing the order tells you which error wins when several apply:

1. HTTP method → `405 method_not_allowed`
2. API availability → `503 api_disabled`
3. API key → `401 invalid_api_key` / `401 test_key_on_live_store`
4. IP allowlist → `403 ip_not_allowed`
5. Plan → `402 plan_required`
6. Scopes → `403 insufficient_scope`
7. Rate limits → `429 rate_limited`
8. Request body → `413`, `415`, `400 invalid_json`
9. Idempotency key → `400 invalid_idempotency_key`, `409 idempotency_key_in_use`, `422 idempotency_key_reused`
10. The endpoint itself → everything else (including `429 email_quota_exceeded`)

## Which errors should I retry?

| Retry? | Codes |
|---|---|
| **Yes, after the `Retry-After` header** | `rate_limited` (429), `email_quota_exceeded` (429), `api_disabled` (503), `idempotency_key_in_use` (409) |
| **Yes, with backoff and the same `Idempotency-Key`** | `internal_error` (500), and network failures or timeouts with no response |
| **Only after fixing something** | every other code - the same request will fail the same way |

Use exponential backoff with jitter for 5xx responses and network errors, and always send an
`Idempotency-Key` on POSTs so a retry can't create a second quote or message. See
[Idempotency](/docs/api/idempotency).

## Error codes

### Authentication and access

#### `invalid_api_key` - 401
**Meaning:** The `Authorization` header is missing or isn't `Bearer <key>`, or the key is
malformed, fails its checksum, doesn't exist, has been revoked, or has expired - or the store no
longer has QuotWay installed (uninstalling revokes every key). All of these return
the same response on purpose, so the API never reveals whether a key exists. The response includes
`WWW-Authenticate: Bearer realm="QuotWay API"`.

**What to do:** Check you're sending `Authorization: Bearer qw_live_…` with the complete key (57
characters in total, no quotes or trailing whitespace). In **Settings → Integrations → API keys**,
check the key's status - if it's *Expired* or *Revoked*, create or roll a new one. After a roll, the
old key stops working 24 hours later.

#### `test_key_on_live_store` - 401
**Meaning:** A `qw_test_` key was used, but its store isn't a Shopify development store. Test keys
only work on development stores.

**What to do:** Use a `qw_live_` key for this store.

#### `ip_not_allowed` - 403
**Meaning:** The key has an IP allowlist and this request came from an address that isn't on it.

**What to do:** Add your integration's egress IP address to the key's **Allowed IP addresses**
(exact addresses; CIDR ranges aren't supported), or call from an allowed address.

#### `plan_required` - 402
**Meaning:** The store's QuotWay plan doesn't include the API. The API is on the **Enterprise**
plan (a live trial counts). Extra member: `required_plan` (`"enterprise"`).

**What to do:** The store owner can upgrade in **QuotWay → Billing**. Keys aren't deleted when a
store changes plan - they start working again as soon as the store is back on Enterprise.

#### `insufficient_scope` - 403
**Meaning:** The key is valid but lacks a scope this endpoint needs. Extra member:
`required_scopes` - the missing scope(s).

**What to do:** Scopes can't be added to an existing key. Ask a store Admin to create a new key
with the scopes listed in `required_scopes`, then revoke the old one. See
[Scopes](/docs/api/authentication#scopes).

### Request format

#### `method_not_allowed` - 405
**Meaning:** The path exists but doesn't support this HTTP method (for example `PUT /v1/quotes`).
The `Allow` header lists the methods it does support. This is checked before authentication.

**What to do:** Use a method from the `Allow` header.

#### `invalid_json` - 400
**Meaning:** The request body isn't valid JSON.

**What to do:** Send a well-formed JSON body. Check for trailing commas, unescaped quotes, or a
shell that mangled the payload.

#### `unsupported_media_type` - 415
**Meaning:** The request has a body but its `Content-Type` isn't `application/json`.

**What to do:** Send `Content-Type: application/json`.

#### `payload_too_large` - 413
**Meaning:** The request body is larger than **256 KB**.

**What to do:** Send a smaller body. A quote request allows at most 100 lines - well under the
limit - so a body this large usually means something unintended is being sent.

#### `invalid_request` - 400
**Meaning:** The request is well-formed JSON but something in it is invalid. Extra member:
`errors` - an array of `{ field, message }`, one per problem (when the problem is tied to a field).
Common causes:

- a required body is missing (`A JSON request body is required.`);
- a body field fails validation, or is **unknown** - request bodies are strict, so an unexpected
  key such as `"price"` on a line is rejected rather than silently ignored;
- a line's `variant_id` isn't in this store or its product isn't active; a `customer_id` isn't a
  customer of this store;
- a query parameter is invalid (`limit` outside 1–50, an unknown `status` or event `type`, a date
  that isn't ISO-8601, a malformed `cursor`, `customer_email` used without `read_customer_data`);
- `starting_after` doesn't match an event in the 30-day window;
- a webhook URL is rejected (not HTTPS, an IP address instead of a hostname, a non-standard port,
  credentials in the URL, or an internal or QuotWay-owned host).

**What to do:** Read `errors[].field` and `errors[].message`, fix the request, and send it again.
If you sent an `Idempotency-Key`, use a **new** one for the corrected request (see
[`idempotency_key_reused`](#idempotency_key_reused)).

#### `not_found` - 404
**Meaning:** The resource doesn't exist **in this store** - a quote, document or webhook endpoint
id that's unknown, deleted, or belongs to another store (the API never distinguishes "not yours"
from "doesn't exist"). Also returned for any path under `/v1/` that isn't an API endpoint.

**What to do:** Check the id and that you're using the right store's key. Quote ids are the `id`
field (for example `clz8k2x9f0001qw7h3m4n5p6r`), not the quote number (`QW-1042`).

### Rate limiting and availability

#### `rate_limited` - 429
**Meaning:** The key (120 requests per minute) or the store (300 per minute, across all its keys)
has run out of requests. The `Retry-After` header gives the seconds to wait.

**What to do:** Wait for `Retry-After` seconds, then retry. Spread requests out rather than
bursting; see [Rate limits](/docs/api/rate-limits).

#### `email_quota_exceeded` - 429
**Meaning:** The store has used up its daily allowance of API requests that can email a buyer  - 
creating a quote, posting a buyer-facing message, and sending a proposal. The allowance is **2,000
per day** on a paid plan, **200** during a trial, and **50** on a development store or with a
`qw_test_` key; it refills evenly over 24 hours. Extra member: `daily_limit`. The `Retry-After`
header gives the seconds until the next request is allowed. Internal notes, reads and webhook
management don't count.

**What to do:** Wait for `Retry-After`, then retry. If your integration legitimately needs more,
contact support with your use case.

#### `api_disabled` - 503
**Meaning:** The QuotWay API is temporarily switched off for every store - during maintenance or
an incident. It's checked before authentication, so every request gets it. The response includes
`Retry-After: 300`.

**What to do:** Retry after the `Retry-After` interval, with backoff. Nothing is wrong with your
key or request. Webhook deliveries are not lost during an API outage - use
[`GET /v1/events`](/docs/api/events) to catch up if you need to.

#### `internal_error` - 500
**Meaning:** Something went wrong on QuotWay's side. The error has been logged.

**What to do:** Retry with exponential backoff, re-using the same `Idempotency-Key` on POSTs - a
5xx response is never stored against the key, so the retry genuinely runs again. If it persists,
contact support with the `request_id`.

### Idempotency

#### `invalid_idempotency_key` - 400
**Meaning:** The `Idempotency-Key` header isn't 1–255 printable ASCII characters (spaces aren't
allowed).

**What to do:** Use a UUID (v4) per logical operation.

#### `idempotency_key_reused` - 422
**Meaning:** This `Idempotency-Key` was already used, within the last 24 hours and by the same API
key, for a **different** request (a different path or body).

**What to do:** Generate a new key for every new operation. Re-use a key only to retry the
*identical* request. This includes a request you corrected after a `4xx` - the corrected request is
a new operation and needs a new key.

#### `idempotency_key_in_use` - 409
**Meaning:** A request with this `Idempotency-Key` is still being processed. Usually `Retry-After:
2` is included.

**What to do:** Wait a couple of seconds and retry with the same key. You'll get the original
response, marked `Idempotent-Replayed: true`.

### Creating quotes (`POST /v1/quotes`)

#### `not_eligible` - 422
**Meaning:** The store's quote **targeting rules** don't allow this request - the same rules that
decide where the "Request a quote" button appears. Extra members: `reason`  - 
`NO_MATCHING_PRODUCT_RULE` or `NO_MATCHING_CUSTOMER_RULE` - and, for a product mismatch,
`line_index` (the zero-based index of the first line that didn't qualify).

**What to do:** Remove or replace the ineligible line, or pass a `customer_id` that matches the
store's customer rules. If every product should be quotable from your integration, ask the merchant
to review their [targeting rules](/docs/targeting/targeting-rules).

#### `invalid_custom_fields` - 422
**Meaning:** The store's quote form has custom fields, and the `custom_fields` you sent are missing
a required answer or fail a field's validation. Extra member: `errors` - one entry per field, with
`field` as `custom_fields.<field key>`.

**What to do:** Send the missing or corrected values under `custom_fields`, keyed by each field's
key as configured in the merchant's quote form.

#### `quote_creation_paused` - 409
**Meaning:** The store's subscription is frozen (for example after a failed payment), so new quotes
can't be created. Existing quotes can still be read.

**What to do:** Retry later. The store owner needs to resolve billing in Shopify.

### Sending proposals (`POST /v1/quotes/{id}/send-proposal`)

#### `no_staged_proposal` - 409
**Meaning:** Nobody has priced and saved a proposal for this quote in QuotWay yet. The API sends the
**staged** proposal; it never sets prices.

**What to do:** Have a team member open the quote in QuotWay, price it in the proposal editor and
save it as a draft, then call the endpoint again.

#### `below_price_floor` - 409
**Meaning:** At least one line in the staged proposal is priced below the store's price floor (its
maximum discount below catalog). Extra members: `floor_discount_pct`, `lines` (each with `title` and
`discount_pct`) and `resolution`.

**What to do:** Either ask the merchant to review the prices, or - if sending below the floor is
intended - send the request again with `{"confirm_below_floor": true}`. That is the same explicit
confirmation the admin asks for, and it's recorded.

#### `invalid_proposal` - 422
**Meaning:** The staged proposal can't be sent as it stands - for example a line is missing a price,
or a fixed-amount discount is larger than the proposal subtotal. `detail` says why.

**What to do:** Fix the proposal in QuotWay's proposal editor, save it, and try again.

#### `not_editable` - 409
**Meaning:** The quote isn't in a state a proposal can be sent from. Proposals can only be sent
while the quote is `submitted`, `in_review` or `countered`.

**What to do:** Fetch the quote and check its `status`. If a proposal is already out, wait for the
buyer to respond.

#### `state_conflict` - 409
**Meaning:** The quote changed while the send was being prepared (someone else acted on it at the
same moment), so QuotWay stopped rather than overwrite that change.

**What to do:** Fetch the quote again, check it still needs sending, and retry with a **new**
`Idempotency-Key`.

#### `send_incomplete` - 409
**Meaning:** The proposal version was created, but a later step of the send didn't finish. The
quote may be partly updated.

**What to do:** **Don't retry blindly.** Open the quote in QuotWay (or fetch it) and check its
status and `version_number` before doing anything else.

### Webhook endpoints (`/v1/webhooks`)

#### `limit_reached` - 409
**Meaning:** The store already has the maximum of **20** webhook endpoints.

**What to do:** Delete an endpoint you no longer use (`DELETE /v1/webhooks/{id}`, or in
**Settings → Integrations → Webhooks**), then create the new one.

## Related

- [Rate limits](/docs/api/rate-limits)
- [Idempotency](/docs/api/idempotency)
- [Authentication](/docs/api/authentication)
