AI agent webhook idempotency means a channel event may arrive more than once but must not make an agent act more than once. A provider can retry after a timeout even when your process accepted the first request, and a process can crash after it starts work but before it records completion.

For a channel plugin, duplicate work can mean two visible replies, two approval actions, two tickets, or a repeated configuration write. OpenClaw’s experimental durable-ingress guidance gives plugin authors a concrete boundary: record the raw event durably, then let dispatch adopt it through one serialized lane per conversation. It does not promise exactly-once delivery. It makes at-least-once delivery safe to operate.

This guide is for people building or maintaining OpenClaw channel plugins. If you are deciding where a self-hosted agent should run, start with how OpenClaw works. For the broader security boundary around agent-owned channels and tools, read why OpenClaw is self-hostable. If you are connecting an existing browser session rather than building an inbound transport, see OpenClaw browser extension tab access.

Table of contents

What AI agent webhook idempotency protects

Webhook providers commonly use at-least-once delivery. A sender retries when it sees a timeout, a dropped connection, or a non-success response. The sender has no reliable way to know whether your handler completed a side effect before that failure. An agent channel can turn that into two visible replies, two approval actions, two tickets, or a repeated configuration write. The receiving system needs a stable event identity and a durable record of its outcome.

Failure pointWhat the provider seesSafe receiving behavior
The request never reaches the pluginNo acknowledgementRetry the event
The event is durably recorded, then the process stopsNo completed response or a closed connectionRe-read the stored event and continue safely
Work starts, then a response times outA failed deliveryDetect the same event identity instead of starting a second action
A non-idempotent action runs before the worker records completionAn ambiguous failureProtect the action with its own stable effect key

Hookdeck’s idempotency guide makes the general case clear: retry and idempotency belong together. Its practical recommendation is to store a unique event identifier before triggering the side effect, not after it. Stripe gives the same operational advice: return a successful response promptly and design handlers for retries and duplicate events.

How OpenClaw durable ingress handles duplicate delivery

OpenClaw’s channel-plugin SDK recommends createChannelIngressMonitor for channels that adopt durable ingress. The plugin still owns its transport, authorization facts, session grammar, and native side effects.

The documented sequence has four useful boundaries:

  1. Receive the raw transport envelope at one chokepoint. Do not normalize it into a different identity before it enters the queue. A dedupe key only works if all deliveries of the same platform event remain recognizably the same event.
  2. Append before acknowledging a webhook. The transport acknowledgement follows the durable append. If the process dies before the append, the provider can retry. If it dies after the append, the stored event survives the process.
  3. Serialize per conversation. A channel should not let two events in the same conversation race through independent workers simply because they arrived close together. The monitor derives one lane per conversation.
  4. Mark completion when dispatch adopts the event. The durable queue distinguishes an accepted event from an event that has finished its lifecycle.

The queue’s primary key is documented as (queue_name, event_id). Completion tombstones the row instead of deleting it, so a late re-delivery with the same event_id can be rejected for the tombstone-retention window. That is a better default than a process-local Set that disappears on restart.

OpenClaw’s channel plugin SDK guide also warns against inventing a second dedupe layer with the same key. Keep an additional replay guard only when it has a different identity or a longer retention window. Telegram is a useful example from the docs: a logical chat_id:message_id can remain relevant when a debounce path produces a fresh transport update_id.

The boundary that still needs an effect key

Durable ingress protects admission and replay of an inbound event. It cannot erase the classic crash window between a side effect and its completion record.

Imagine a channel plugin dispatching an approval that changes configuration. The worker calls the external system, succeeds, then crashes before it reaches the queue’s completion tombstone. On restart, the stored ingress event can be replayed. That is correct from an at-least-once delivery perspective, but it can repeat an unsafe write.

OpenClaw documents createIngressEffectOnce(...) for this kind of work. Use the stable ingress eventId plus an action-specific key. The action key matters. One inbound message might legitimately produce a durable reply and a separate audit write; those are different effects and should not accidentally block each other.

A useful rule of thumb is simple:

  • Let durable ingress own duplicate transport delivery.
  • Let an effect-once record own an external action that cannot safely run twice.
  • Keep the effect key stable across retries, restarts, and handoff to another worker.
  • Do not use a user-controlled message body as the idempotency key.

The last item is both a reliability and security issue. A message body can be edited, normalized differently, or collide with another message. Platform event IDs and plugin-owned action names are narrower, auditable inputs. That fits the broader ownership model described in OpenClaw’s plugin documentation: a channel plugin owns platform-specific configuration, pairing, outbound behavior, and transport identity, while core owns shared dispatch and session plumbing.

A practical implementation checklist

Before moving a channel to durable ingress, answer these questions in the plugin’s own terms:

1. What is the source event ID?

Use the provider’s stable delivery ID where available. Record where the ID comes from and whether a provider can issue a fresh delivery ID for the same logical user message. If it can, define a separate logical-message key only for the cases that need it.

2. When can the platform be acknowledged?

For webhook transports, do not send a success acknowledgement merely because the request parsed. The durable append is the meaningful acknowledgment point. This leaves the provider free to retry if the agent has not accepted responsibility for the event.

3. Which effects are safe to replay?

Drafting an internal response may be safe. Sending a payment request, changing configuration, or posting an approval is a different class of operation. Give each non-idempotent effect an explicit effect-once guard and test the crash case, not only the happy path.

4. How long should dedupe state live?

The queue tombstone retention must cover the provider’s retry horizon. If a platform can retry later than the tombstone lasts, a late duplicate can become a new event. Longer-lived logical replay guards should be deliberate and scoped, not a blanket cache that hides legitimate messages.

5. Can you prove the plugin loaded the intended code?

After installing or updating a plugin, inspect its live runtime registration. OpenClaw documents openclaw plugins inspect <plugin-id> --runtime --json for this check.

Test the ugly path, not only the demo

A useful test plan injects failure at each handoff:

  • deliver the same event twice before the first handler finishes
  • stop the process after durable append but before dispatch
  • stop it after the external side effect but before completion
  • replay the event after restart
  • send two different messages in one conversation and confirm their order
  • send the same logical message under a fresh transport delivery ID, if the platform permits it

The expected outcome should be written down. Some effects may run twice by design when the target itself is idempotent. Others must never be duplicated, and those are the operations that need a stable effect key. Pretending every API call is safe to replay is how a minor retry becomes an operator incident.

FAQ

Is AI agent webhook idempotency the same as exactly-once delivery?

No. Webhook systems generally provide at-least-once delivery, so duplicates remain possible. Idempotency makes repeated delivery produce one safe outcome for the action you care about.

Should every channel plugin use createChannelIngressMonitor?

OpenClaw recommends it for channels adopting durable ingress unless the plugin has a materially different admission or pumping contract. A plugin with different transport requirements should still preserve the same explicit ownership of event identity, durable acceptance, ordering, and completion.

Why are queue tombstones better than deleting completed events?

A tombstone retains enough identity to reject a late duplicate. Deleting a completed row immediately removes the evidence needed to distinguish a legitimate new delivery from a retry of the old one.

When do I need createIngressEffectOnce(...)?

Use it around a non-idempotent side effect that could be repeated if a process crashes after the effect succeeds but before the ingress event reaches durable completion. Give it the stable ingress event ID and an action-specific key.

Sources