Choose this pattern deliberately
Relay’s public contract supports more than one integration shape. Pick the narrowest one that fits.
A channel plugin is the right shape only when the host runtime, not Relay,
decides which local agent handles a turn.
Pick a transport
terminated_by_other_consumer.
Scope every inbound turn to an owner
CallGET /v1/agents/me once at startup and pin the returned owner user id.
Drop any inbound message whose sender is not the owner (or an explicit
allowlist you maintain) before your plugin interprets its content. This is
the same rule the released OpenClaw and coding-agent integrations enforce:
default-deny, not default-allow.
Map events to your runtime’s turns
Your plugin owns the mapping from a Relayconversation_id to your host’s
own session or thread state; Relay does not prescribe one. Persist that
mapping durably, and persist the long-poll next_cursor (or nothing, for
webhooks, since Relay’s outbox already retries) in the same atomic write as
any locally queued work. A crash must never be able to acknowledge an event
your runtime never actually queued.
Deduplicate on event_id regardless of transport: both webhooks and
long-poll redeliver at least once. If your host processes a rapid burst of
messages as separate turns, consider a short debounce window before starting
work, the way Relay’s own released plugins coalesce a fast sequence of
messages into one turn instead of running the agent once per message.
Reply idempotently
Idempotency-Key from the triggering event_id. A
retried turn, whether from your host’s own crash recovery or a Relay
redelivery, then replays the original send instead of posting twice. Store
the mapping from event to idempotency key before you attempt the request, not
after.
Handle failure without losing state
Follow the recovery rule: conversation history is the source of truth for what a thread contains. After a crash, a401, or any ambiguous response, reconcile from
conversation history rather than
reconstructing state from your own retry attempts.
Next steps
- Delivery model for the full transport and idempotency contract
- Webhooks for registration, verification, and rotation
- Text your coding agent for OpenClaw, a shipped channel plugin
- Developer data access for what a plugin may read and store