Errors and retries

How Quiver reports errors, and how to retry bookings safely.

Building a reliable integration means handling the cases where a request doesn't succeed first time. This page explains what Quiver returns when something goes wrong, and how to retry safely.

Error format

Every error returns a non-2xx HTTP status and a JSON body with a human-readable message:

{ "error": "task: with merchantOrderId 1234567890 already exists as taskId 228760" }

The message names the field or resource at fault, followed by the reason. Log the full message — it usually tells you exactly what to fix.

Status codes

HTTPMeaningRetry?
400Invalid or missing fields❌ No — fix the request first
401 / 403Missing or invalid API key❌ No — check your x-api-key header
404No such resource❌ No — check the ID
409Already exists⚠️ Usually success — see below
429Too many requests✅ Yes, after a pause
500Unexpected error our end✅ Yes, with backoff
502 / 503 / 504Temporarily unavailable✅ Yes, with backoff

Duplicate orders: your safety net

Quiver de-duplicates bookings on merchantOrderId. You cannot accidentally create two deliveries for the same order, which makes retrying a booking safe by design.

If a booking already exists for that order ID, you'll receive one of these:

"already exists as taskId N"

The booking succeeded previously — most likely on an earlier attempt you didn't see the response to. The message includes the task ID.

✅ Treat as success. Store the task ID. Do not retry.

"creation already in progress"

An identical request is still being processed right now (for example, your retry arrived before the first attempt finished).

✅ Treat as success. Wait a few seconds, then query the delivery by merchantOrderId to get the task ID.

Always send the same merchantOrderId when retrying. It's what makes de-duplication work. Never generate a new one for a retry — that would create a second delivery.

Recommended retry strategy

  1. Retry on 5xx and network timeouts — these are transient.
  2. Use exponential backoff: wait 1s, then 2s, 4s, 8s, 16s between attempts. Don't retry in a tight loop.
  3. Keep trying for a few minutes. Brief interruptions usually resolve well within that.
  4. Never retry a 400. The request itself is wrong; retrying produces the same result.
  5. Reuse the same merchantOrderId on every attempt.
  6. If it still fails, the order is not booked. Fall back to your normal process and contact us with the merchantOrderId.

Example

async function bookDelivery(payload, attempt = 0) {
  const res = await fetch("https://api.quiver.london/task/deliveries", {
    method: "POST",
    headers: {
      "x-api-key": process.env.QUIVER_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload), // same merchantOrderId every attempt
    signal: AbortSignal.timeout(10000),
  });

  if (res.ok) return res.json();

  const body = await res.json().catch(() => ({}));

  // Already booked — this is a success, not a failure
  if (res.status === 409 || /already exists|already in progress/.test(body.error || "")) {
    return { alreadyBooked: true, detail: body.error };
  }

  // Client error — retrying won't help
  if (res.status >= 400 && res.status < 500) {
    throw new Error(`Quiver rejected the request: ${body.error}`);
  }

  // Server error — back off and retry
  if (attempt < 5) {
    await new Promise(r => setTimeout(r, 1000 * 2 ** attempt));
    return bookDelivery(payload, attempt + 1);
  }

  throw new Error(`Quiver unavailable after ${attempt} retries: ${body.error}`);
}

Timeouts

CallSuggested client timeout
Quotes5 seconds
Bookings10 seconds

A timeout does not mean the booking failed — the request may have been processed after your client gave up. Retry with the same merchantOrderId; de-duplication will tell you if it already went through.

Quotes expire

A quote reflects live delivery capacity and goes stale after roughly 2 minutes, or as soon as any detail changes. If booking fails because the quote is stale, request a fresh quote and book again — don't cache or reuse quotes.

If Quiver is unavailable

Should our API be unreachable for an extended period:

  • Your retry logic should carry you through short interruptions automatically.
  • If you use our Shopify, WooCommerce, BigCommerce, Wix or Squarespace apps, orders are retried for you — no action needed.
  • For direct API integrations, queue failed bookings and replay them once service resumes. Because bookings de-duplicate on merchantOrderId, replaying is safe even if some already went through.
  • Contact [email protected] with affected merchantOrderId values if anything looks unresolved.