Why does one paid order sometimes become two print jobs?
A storefront tells production about a new order through a webhook: an HTTP request fired when the order is paid. Networks time out after the request already arrived. Platforms redeliver an event the receiver never acknowledged. A buyer double-clicks pay, or a support agent resends a stuck order by hand. Every one of those is ordinary, expected behaviour, not a bug in the storefront. The bug is on the receiving side, if it treats a second delivery of the same event as a second order.
The thesis of this page is that duplicate orders are a design gap, not a rare accident. Any system that accepts webhooks will receive a duplicate eventually; the webhook delivery model exists precisely because at-least-once delivery, not exactly-once, is what a sender can promise without holding a connection open forever. The receiver has to supply the missing guarantee: given the same event twice, create exactly one job, and return the same result both times.
We can integrate a web-to-print front end with an existing MIS, ERP or production scheduler where that system exposes a sanctioned interface. Idempotent job creation is the specific mechanism this page owns; the wider automation that turns an order into a routed, tracked production job is scoped under print production workflow automation, and the MIS-side job model it writes into is covered in print MIS and ERP integration.
Key takeaways
- Webhook delivery is at-least-once by design, so a receiver that assumes exactly-once delivery will eventually create a duplicate job.
- An idempotency key, not the HTTP request itself, is what a dedup store checks before deciding whether an event is new.
- A dedup window has to outlive the sender's retry policy, or a very late retry becomes a new, undetected duplicate.
- Three lookup outcomes cover every case: the key is new, the key was already handled, or the key is mid-flight and the caller should retry shortly.
- Nightly reconciliation catches what the real-time path misses, such as a mismatch between an order id and its line items.
How does an idempotent submission decide what to do?
-
Webhook event
- Input
- A storefront order event, possibly a retry or a redelivery
- Outcome
- A payload with an order id and an idempotency key
-
Idempotency key
- Input
- The event payload
- Outcome
- A stable key, usually the storefront's order id plus line number
-
Dedup store lookup
- Input
- The key, checked against previously handled keys
- Outcome
- New, seen, or locked (another request for this key is mid-flight)
-
Job creation or return
- Input
- The lookup result
- Outcome
- A new job on first delivery, the same stored job id on a repeat
-
Reconciliation
- Input
- Daily order and job lists from both systems
- Outcome
- A report of orders without jobs and jobs without orders
The idempotency key is the part most implementations get wrong. A raw request body is not a safe key, because two genuinely different requests can carry the same body, and the same logical request can arrive with a slightly different body on retry. The key has to be something the sender and receiver both agree identifies "this exact order line," typically the storefront's own order id combined with a line number, following the same pattern documented for the HTTP Idempotency-Key request header: the client generates a unique value once per logical operation and resends the identical value on every retry of that operation.
What does the dedup store actually check?
A dedup store is a small, fast lookup, not a copy of the order. For each key it has seen, it records the key, when it was first seen, and the job id or result that submission produced. A new submission goes through three checks in order.
-
Is the key new?
If the dedup store has never seen this key, the request proceeds: a job is created, and the key is stored with that job's id before the response is returned.
-
Is the key already handled?
If the key exists and its result is stored, the receiver returns that same stored job id immediately, without creating anything. This is what makes a retried webhook harmless: the second, third and tenth delivery of the same event all return the same answer.
-
Is the key mid-flight?
If the key exists but no result is stored yet, another request for the same key is still being processed. The receiver holds the second request briefly and re-checks, rather than racing to create two jobs at once. This is the case a naive dedup check, which only looks for a finished result, usually misses.
Idempotent methods are a well-established concept in HTTP semantics: the same request, applied more than once, has the same effect as applying it once. A webhook receiver has to build that guarantee itself, because the underlying HTTP POST that delivers the event is not idempotent on its own; the idempotency has to live in how the receiver handles the payload, not in the transport.
How long does the dedup window need to last, and what happens when it expires?
The dedup window is how long the receiver keeps a key before it is eligible to be treated as new again. It has to outlive the sender's own retry policy, including any backoff and manual resend by a support agent, or a late retry after the window closes is indistinguishable from a genuinely new order. A common approach keeps the key for a fixed period well beyond the platform's documented maximum retry duration, then relies on the same order id appearing in a full daily order export to catch anything the real-time window missed.
The failure case worth naming explicitly is a matching order id with different line items or a different total, arriving as a second delivery. That is not a simple retry, and returning the original stored job would silently drop a real change. This case does not get auto-resolved; it is flagged as a mismatch for a named person to check, because deciding whether it is a legitimate order edit or a corrupted retry needs an answer the storefront's event alone does not carry.
Why does reconciliation still matter if idempotency works?
Idempotency prevents duplicates created through the normal event path. It does not catch a webhook that never arrived at all, a job created through a manual fallback during an outage, or a key collision nobody anticipated. A daily reconciliation job compares the storefront's order list against the production system's job list and reports two kinds of gap: orders with no matching job, and jobs with no matching order. Both are investigated the same day they appear, rather than discovered weeks later when a customer asks where an order went, or when a job nobody ordered reaches the loading dock.
Which teams hit this hardest?
Independent software vendors and agencies running a web-to-print storefront for multiple client accounts feel this first, because a duplicate bug affects every tenant at once and is usually reported by a client's customer, not caught internally. Print businesses replacing a manual order-entry process with automation often introduce the risk for the first time, since manual entry had a person implicitly deduplicating by recognising a familiar order. High-volume storefronts with promotional traffic spikes see more retries and more double-clicked checkouts exactly when the dedup logic matters most, which is the wrong time to discover it was never built.
Our view
Position of the CEO, Netbase JSC, 30 September 2026: idempotency is not an edge case to handle later; it is the first thing to design, because every webhook integration eventually receives a duplicate whether or not anyone planned for it. We treat "what happens on a retry" as an acceptance question from day one of a workflow automation or MIS integration engagement, not a bug fix after a client reports a doubled job. A reconciliation report that nobody reads is not a safety net, so the report needs a named owner who checks it daily, not a dashboard that exists to be pointed at after an incident.
What evidence stands behind this page?
Netbase JSC, which operates Web2Print Solutions, has delivered 50+ web-to-print platforms. Web2Print Solutions is the web-to-print engineering service of Netbase JSC. Delivery records cited on this site belong to Netbase JSC; where no delivered example of a capability is published, the page says so rather than implying a record. No workflow automation or MIS integration delivered under the Web2Print Solutions name is published as a case on this site.
Check whether your integration has this gap
Send a project brief naming the storefront platform and webhook events involved, the production system or MIS that receives the job, and one duplicate order your team has had to spot and cancel by hand. If the storefront also needs a broader integration built from scratch, headless and custom web-to-print covers that starting point.
Frequently asked questions
No. Any receiver of storefront webhooks, including a packaged connector, can create duplicates if it does not check an idempotency key before creating a job. The fix is the same regardless of what built the storefront side.
No, and it should not try to. Retries exist because networks and receivers fail in ordinary ways, and a sender that gives up after one attempt loses genuine orders during any receiver outage. The dedup logic belongs on the receiving side.
They should not, if the key is built from the storefront's own order id and line number, which the storefront already guarantees are unique. A collision under that scheme usually indicates a bug in key generation, which reconciliation will surface as a mismatch.
Fast enough not to make the webhook response slow, typically a single indexed lookup. The mid-flight case, where a second delivery arrives before the first has finished, is the part that needs care, not raw speed.
References (3)
- IETF, RFC 9110, HTTP Semantics, section 9.2.2, Idempotent Methods, accessed 2026-09-30.
- IETF HTTPAPI Working Group, The Idempotency-Key HTTP Header Field (Internet-Draft), accessed 2026-09-30.
- Stripe, Idempotent Requests, accessed 2026-09-30.