Shopify + n8n: 5 tests for duplicate webhooks and failed-run recovery

The recent orders/create discussion covers duplicate filtering and protecting the downstream action. Here is a test checklist to make the expected recovery behaviour explicit.

Shopify documents that a webhook can arrive more than once and recommends idempotent processing: webhook delivery guidance.

The easy-to-miss case is a failed first attempt:

  1. Order 1042 is recorded as “seen”.
  2. The workflow fails before creating its CRM order record.
  3. A retry arrives, sees the saved ID, and skips the order.

There is no duplicate, but the intended action never happened. “Seen” and “completed” need different meanings.

For an action intended to happen once per order, define a stable operation key, for example shop:orders-create:order-id:create-crm-order. Keep the same key on retries; a different intended action needs its own key.

Run these checks in a test environment with a mock destination:

Test Expected result
One normal delivery One destination record, with its ID saved as completion evidence.
Replay after successful completion No second destination record.
Two simultaneous deliveries One destination record despite overlapping executions.
Fail after recording the attempt but before the destination write A controlled recovery completes the missing action; it is not permanently skipped as “already seen”.
Destination write succeeds, but its response or the local completion update is lost Recovery confirms the existing result or safely retries using destination-supported idempotency; it does not blindly create another record.

n8n’s Remove Duplicates node can compare values with previous executions. That history alone does not establish whether a later business action completed.

For the destination write, use its atomic uniqueness or idempotency mechanism where available. A separate “claim” row is not proof that an external API call succeeded. For uncertain outcomes, reconcile with the destination; if it cannot support a safe retry or reliable lookup, route the case for manual review.

Record the operation key, attempt status, destination record ID and recovery outcome. Judge the tests by destination records, not only by a green n8n execution.

This is a test checklist, not a tested importable workflow or a guarantee across every API. Destination idempotency rules and retention limits still apply.

Which downstream action in your order workflow must happen only once: a CRM record, a fulfilment task, an invoice, or something else?

“Seen” and “completed” needing different meanings is the whole thing, and I hit it in the engine rather than in a workflow. A step that was skipped had no state of its own to record, so it was rolled into the run’s own COMPLETED, and 27 branches went unprocessed for weeks while every run reported success. The fix was the same shape as your operation key: give the skipped case a real row with a reason, and derive the run’s counts from those rows so they cannot drift from what happened. The part I would ask about your key: it encodes the intended action, so what happens when the intended action changes: do you version the key, and how do you stop a redeploy rerunning everything under a new one?