Agent workflows
Design Agent Webhooks for Duplicate Deliveries and Uncertain Outcomes
Separate incoming event receipts from downstream actions so webhook redelivery or a timed-out tool call does not silently create duplicate tasks and messages.
An issue arrives, an agent creates a task, and the network connection drops before the task service's response reaches your worker. Retrying feels sensible. It may also create a second task.
A different path reaches the same visible problem: the webhook is delivered again and your receiver launches a second agent run. Preventing repeated incoming work does not automatically prevent repeated outgoing effects.
For teams building event-triggered agents, the useful design unit is not just a webhook handler. It is a delivery receipt connected to a durable record of each intended action.
Methodology: compare contracts at two boundaries
We reviewed GitHub's webhook guidance, Slack's Events API documentation, and the AWS Builders' Library discussion of idempotent APIs. They address different services; their retry behavior is not interchangeable.
GitHub requests a 2XX response within ten seconds, recommends asynchronous processing, and preserves X-GitHub-Delivery when a redelivery is requested. GitHub webhook best practices.
Slack asks for acknowledgment within three seconds and documents three retries after a failed delivery attempt. Its event_id identifies the event. Slack Events API.
Those are incoming delivery contracts. Neither establishes that a separate task-creation API will suppress duplicates. The example below is a proposed architecture, not an implementation we have benchmarked or deployed.
Give the fictional workflow one precise intent
Suppose an authorized integration creates one triage task when a new issue is opened in a selected repository. The task belongs in a specific tracker project. Issue edits should update neither its destination nor the meaning of the original creation request without an explicit policy.
Define the business identity as the source installation, repository and issue identity, destination project, and operation type. This is more useful than hashing the generated task description: the model might phrase the same task differently on a second run.
Also record the incoming delivery identity. Several transport receipts may relate to one business object, while one event may legitimately request several different operations. Keep those relationships explicit instead of using one generic processed flag everywhere.
For this particular policy, another event concerning the same issue must not accidentally create a new initial-triage task. If the product needs one task per reopening, model that as a separate lifecycle rule before enabling it.
Acknowledge durable receipt, not agent completion
Authenticate the provider request using its documented verification procedure and check whether the event and action are in scope. Untrusted issue text can inform the task description; it must not change integration permissions or select a new destination.
Our proposed receiver then persists a receipt and pending work in one database transaction, using a uniqueness constraint for the receipt identity. An outbox dispatcher can move committed work into an external queue. If persistence fails, do not report successful acceptance merely because the payload briefly existed in memory.
The receiver's successful acknowledgment means the event is durably accepted under this design. It does not mean the agent has reasoned about the issue or that the tracker task exists. Keep that distinction visible in operational dashboards.
Provider deadlines make long model calls inside the receiver a poor fit. However, putting work in a queue is only the beginning: queue workers can also repeat work.
Track the intended action through uncertainty
For the fictional tracker operation, maintain an action record with the normalized request, destination, state, attempt reference, and confirmed result identifier. Use an atomic claim so two workers cannot both decide from an old read that the action is unclaimed.
A claim or lease helps coordinate workers; it is not proof that the external action happened only once. If a worker disappears after sending the request, an expired lease must not automatically mean “safe to send again.”
Use explicit states:
- Pending: the action has not been dispatched.
- Claimed: a worker is responsible for an attempt.
- Confirmed: the result identifier has been recorded.
- Needs reconciliation: dispatch may have succeeded, but the outcome is unknown.
AWS explains why caller-provided request identifiers can express intent more reliably than treating identical parameters as duplicates. It also emphasizes atomic handling of the identifier and mutation inside a service that offers that contract. AWS on idempotent APIs.
If the target supports an idempotency key, follow its actual scope and retention rules and reuse the key for the same intent. If it does not, a locally stored key alone cannot force the remote API to become idempotent.
Reconcile before repeating an uncertain side effect
After a timeout, use a supported result lookup or a reliable correlation field to search for the task created by that attempt. A matching title alone is weak evidence: users can legitimately create similar tasks.
If the target cannot establish whether the first attempt succeeded, route the action for review. Record what is uncertain and what another attempt could duplicate. The agent should not translate a missing response into a confident claim of failure.
A proposed acceptance exercise has three parts. Request redelivery of an already accepted event and check that it does not add a second intended action. Start concurrent workers and inspect which one acquired the claim. Finally, interrupt the response after downstream acceptance and verify that recovery enters reconciliation rather than blind creation.
These are expected behaviors to test with controlled fixtures, not results we observed.
Limitations and selection criteria
This design does not promise exactly-once effects across arbitrary services. Deduplication retention, event ordering, worker leases, and the target's consistency model all require explicit decisions. A provider event identifier also does not tell you whether a later business event supersedes an earlier one.
When choosing an automation skill from the skills directory, look for a documented action identity and recovery path, not merely the word retry. A skill can help implement the design; its instructions are not themselves a delivery guarantee.
Our calendar change-contract guide illustrates the related need to identify the exact object being changed. For webhooks, add one more question: when the response is missing, what evidence will distinguish a completed action from one that still needs to happen?