本文へスキップ

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 problem-details document with Content-Type: application/problem+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 409s), and detail wording can change.

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

{
  "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.

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.

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).

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.

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 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.

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.

解決しませんか?サポートチームがお手伝いします。

サイトの利用状況を把握するため、分析用Cookieを設定したいと考えています。必須ではありません。拒否してもサイトの動作は変わらず、選択はいつでも変更できます。変更はこちらから: プライバシーページ.