AIOctober 18, 20263 min

Retry a marketing automation without duplicate actions

Use a stable idempotency key and durable operation state to retry after a lost response without creating duplicate records or notifications.

The cover shows steel ratchet wheel with a single pawl in sharp focus.

What if the write succeeded but the response disappeared?

The difficult retry case is a successful operation whose acknowledgment never reaches the caller. The caller sees a timeout and retries. Without a stable identity and durable state, the receiver may create another inquiry, order or notification even though the user intended only one action.

n8n documents error workflows and retry-related execution metadata. Those features help route and inspect failures, but they do not establish exactly-once delivery. The message-state contract below is an application-level design that still needs tests against its actual database and downstream receiver. [Handle errors gracefully]

A message-state table for the ambiguous timeout

Illustrative inquiry operation
StepDurable receiver stateCaller behavior
First submitNo operation yetCreate request ID and retain it
Receiver commitsInquiry saved with request ID; outbox queuedWait for acknowledgment
Response lostInquiry remains savedTreat outcome as unknown
Retry same ID and payloadReturn existing receipt; no new inquiryDisplay the confirmed receipt
Retry same ID, different payloadReject conflictAsk caller to resolve the changed operation
Notification worker retriesSame outbox item and delivery identityRecord outcome separately from inquiry save

Generate the identifier once for the logical operation. If every network retry gets a new ID, the receiver cannot distinguish a retry from a new request. Conversely, reusing an old ID for a genuinely new inquiry can suppress legitimate work. Define the operation’s lifecycle and when a user action begins a new one.

A usable idempotency fixture

Teaching fixture with synthetic identifiers; no contact data is required.
{
  "requestId": "fixture-request-001",
  "operation": "create-inquiry",
  "payloadHash": "fixture-payload-v1",
  "state": "saved",
  "receiptId": "fixture-receipt-001",
  "notification": "queued"
}

The receiver should enforce uniqueness at the durable storage boundary and compare the normalized payload associated with the key. A preliminary “does this key exist?” lookup followed by a separate unprotected insert can race. Test two simultaneous requests, not just two sequential calls.

Keep request IDs and payload hashes out of public analytics when they can identify an operation or user. The operational store needs them for reconciliation; the reporting event can use a fixed event name and a bounded status instead. Diagnostic usefulness does not justify sending the entire form payload to telemetry.

Why should notifications have their own state?

Saving an inquiry and sending a notification are different operations. Commit the inquiry and an outbox item together, then let a worker deliver the notification. A temporary notification failure should not erase the saved inquiry or encourage the user to submit it again.

The destination may not support deduplication. If the notification service accepts a message but its acknowledgment is lost, a retry can still duplicate the message. Name that limitation explicitly and choose a reconciliation or receiver-side deduplication strategy where available. An application cannot promise exactly-once effects merely by calling its retry function idempotent.

  • Simultaneous identical requests create one durable inquiry.
  • Same key with changed payload produces a conflict.
  • Lost acknowledgment returns the existing receipt on retry.
  • Notification failure leaves inquiry status truthful.
  • Worker restart does not forget queued or uncertain deliveries.

Record the effect, not just the HTTP status

Inspect durable record counts, receipts and outbox transitions after each fixture. HTTP 200 alone does not prove that only one record exists. A failed request alone does not prove that no record was written. The acceptance evidence should show the actual effect at the receiver and the caller’s recoverable state.

I would keep the unknown-outcome state visible until reconciliation completes. It is less tidy than labeling every timeout failed, but it matches the failure mode the retry design must handle.

Sources

AI-assisted writing and an AI-generated editorial cover. Source links and checked dates are listed below; illustrative examples are labeled in the text.

More on the topic