Keep one identity for the intended operation across attempts, record completed steps, and use the destination's supported idempotency or deduplication mechanism. Treat uncertain outcomes as something to inspect before replaying an external action.
In this guide
Distinguish the operation from the run attempt
An operation is the business change you intend, such as sending the approved reply version to a recipient. An attempt is one execution trying to complete it. Assign a stable operation ID based on the actual task/version, and give each attempt its own run record.
A new random run ID for every retry does not establish that the business operation is new. Keep status and destination references beside the operation. In concurrent systems, the record or destination must enforce uniqueness atomically; a separate lookup followed by creation can still race.
- Identify which steps only prepare data and which create external side effects.
- Choose the stable operation identity and record the input or approved version it represents.
- Check whether the destination supports idempotency keys, unique keys, or another documented duplicate-protection mechanism.
- Save confirmed destination IDs and completed-step status after successful actions.
- When a response is lost, inspect destination records or the supported request status before deciding what to replay.
- Retry the smallest incomplete unit under the destination's documented rules, preserving the same intended operation identity.
Which replay is safe after a lost acknowledgment?
REPLY-A-v2 stays the same operation during the lookup. A new attempt ID is a log entry, not permission to repeat an external action. If lookup cannot establish the outcome, keep the hold rather than writing failed and retrying automatically. Stripe's idempotency rules illustrate a service-specific mechanism; an email destination needs its own documented support.
| Operation / attempt | Observed result | Destination check | Next step |
|---|---|---|---|
| REPLY-A-v2 / attempt-1 | Timeout; acceptance unknown | No saved message reference | Hold for lookup or manual inspection |
| REPLY-A-v2 / review-1 | Message M-103 found with matching recipient and body | Confirmed in this training scenario | Record success; do not resend |
| REPLY-B-v1 / attempt-1 | Validation rejected before acceptance | Provider says invalid recipient | Correct input; create a reviewed new attempt |
Which guarantees does the destination provide?
Stripe documents idempotent requests and duplicate webhook deliveries as concrete examples of service-specific behavior. Those rules should not be assumed to apply to an email, spreadsheet, or other API. Check key scope, request matching, retention, and error behavior in the actual destination's documentation.
Receiving a repeated event and retrying an outbound action are also different problems. Track the source event ID and the intended downstream operation so a duplicate delivery does not automatically create a second business action.
Keep a replay decision record
operation_id: [same intended action across attempts] input_or_approved_version: [reference] attempt_id: [current run] last_confirmed_step: [state] destination_reference: [if known] result: [succeeded/failed/uncertain] deduplication_support: [documented destination mechanism] replay_decision_and_reason: [record] Preserve operation identity while checking whether the external action already succeeded. Reuse a destination idempotency key only under that service's documented scope and parameter rules.
What happens when this input changes?
Simulate two workers receiving the same event. A lookup followed by creation in each worker can still race. Verify the actual destination key or atomic uniqueness control before enabling concurrent execution; a spreadsheet note by itself is not a concurrency guarantee.
Test the uncertain path before a real incident
Use nonproduction cases to inspect timeouts, repeated input events, changed parameters, and concurrent attempts. Keep the observed results and recovery decision with the workflow. Duplicate prevention belongs in the execution design rather than a request for the model to remember what it did.
