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
| HTTP | Meaning | Retry? |
|---|---|---|
| 400 | Invalid or missing fields | ❌ No — fix the request first |
| 401 / 403 | Missing or invalid API key | ❌ No — check your x-api-key header |
| 404 | No such resource | ❌ No — check the ID |
| 409 | Already exists | ⚠️ Usually success — see below |
| 429 | Too many requests | ✅ Yes, after a pause |
| 500 | Unexpected error our end | ✅ Yes, with backoff |
| 502 / 503 / 504 | Temporarily 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
merchantOrderIdwhen 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
- Retry on 5xx and network timeouts — these are transient.
- Use exponential backoff: wait 1s, then 2s, 4s, 8s, 16s between attempts. Don't retry in a tight loop.
- Keep trying for a few minutes. Brief interruptions usually resolve well within that.
- Never retry a 400. The request itself is wrong; retrying produces the same result.
- Reuse the same
merchantOrderIdon every attempt. - 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
| Call | Suggested client timeout |
|---|---|
| Quotes | 5 seconds |
| Bookings | 10 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
merchantOrderIdvalues if anything looks unresolved.