A webhook can wake an agent while no person is watching. That makes the receiver more than an integration endpoint: it is a boundary between internet input and software that can choose tools. Review the whole path before enabling production traffic.
Use this checklist for RelayLink and adapt the principles to other signed, at-least-once event sources.
Draw the boundaries first
The safe path has distinct stages:
signed envelope
-> authenticated receiver
-> durable inbox and queue
-> account-authorized package fetch
-> policy-bounded agent
-> human review
-> approved effect
Combining stages also combines credentials and failure modes. Keep each transition visible in storage and logs without retaining the correspondence itself.
Receiver and replay checklist
Separate the secrets. The RelayLink
whsec_secret authenticates webhook POSTs. A user's OAuth token or named API key authorizes MCP package access. Store, rotate, scope, and audit them independently. Never let the webhook secret stand in for the user. An opaque route token may select a receiver record, but it does not replace HMAC verification.Verify the raw body before parsing. Read the exact bytes, build
"{timestamp}.{body}", compute HMAC-SHA256, encode lowercase hex aftersha256=, and compare in constant time. Re-serializing parsed JSON changes the signed bytes.Enforce a replay window. Parse
X-RelayLink-Timestampas Unix seconds only after the MAC is valid, then reject requests too far from your synchronized clock. Because the timestamp is signed, a captured body cannot be paired with a fresh timestamp.Match headers to the authenticated body. Require
X-RelayLink-EventandX-RelayLink-Deliveryto equal the parsedeventanddelivery_id. Reject ambiguity instead of deciding which copy is authoritative.Deduplicate durably. Put the delivery id under a unique constraint. Commit the accepted inbox row and work item together. A duplicate with the same authenticated body returns success without creating a second job. An in-memory cache is not recovery.
Queue, then return 2xx. RelayLink's current attempt budget is five seconds. Do not fetch the package, call a model, or post to another system inside the callback. Return success only after the handoff is durable; otherwise let RelayLink retry.
Content and identity checklist
Carry the least content. The webhook envelope needs package and thread ids, timing, reply state,
mcp_url, and limited sender metadata. It does not need the note, TL;DR, ask, or brief. Do not enlarge it in your internal event bus merely because storage is convenient.Treat envelope fields as untrusted information. Sender and topic are outside words. They must not select a tool, credential, tenant, workflow, system prompt, or destination. Bound and escape them for display, or omit them.
Fetch under the intended user. The registered receiver route selects an internal account context before signature verification. Pin that account's expected RelayLink MCP endpoint and require
mcp_urlto match it before attaching a credential. The worker resolves only that account's credential and callsget_packagewith the package id. Never try several users' credentials until one succeeds or send one to a body-selected URL.Keep fetched content out of authority.
get_packageproves provenance and access, not truth or safety. The note, ask, and brief remain third-party content. Put them in a labelled input field, not the system role, tool description, memory policy, or code template.Require a person for consequential sends. The agent may summarize, classify, or prepare a draft. Webhook receipt is not consent to reply. A framework approval pause can support review, but a RelayLink send still goes through RelayLink's preview and explicit confirmation. Do not add a Slack reaction, Teams card, or webhook callback that bypasses it.
Side-effect and logging checklist
Give every downstream effect its own operation id. Webhook deduplication prevents two accepted jobs for one delivery, but a worker can still crash after an external API accepts a call and before completion is recorded. Use provider idempotency where available or reconcile uncertain results before retrying.
Log delivery id, internal receiver id, event type, status category, duration, attempt, and workflow id. Do not log the signing secret, signature, OAuth token, API key, authorization header, full endpoint URL, raw body, sender-chosen text, or fetched package. A URL path may itself contain a token.
Bound model input, tool arguments, runtime, retries, and concurrency. Use an allowlist of tools for the triggered workflow rather than exposing every capability the agent has in an interactive session. A timeout should end in a recoverable queue state, not an automatic “approve to make progress.”
Disable and recovery checklist
Monitor the account page's latest status, error, and failure time. If repeated failures switch the subscription off, its disabled row also shows the consecutive-failure count. Test the public signature route after changing DNS, TLS, proxies, request middleware, or secrets.
RelayLink currently makes up to six attempts for one failed delivery over about an hour. Thirty consecutive failed attempts across deliveries automatically disable the subscription and abandon its queued deliveries; any 2xx resets that health counter.
After repair, re-enable and send a new test. Re-enabling clears the counter and allows new events, but it does not replay abandoned deliveries. Reconcile with check_inbox, fetch missing packages under the user credential, and claim them through the same package-level work store.
What this checklist does not prove
Passing these checks does not make an autonomous agent safe for every tool or decision. It establishes a narrow authenticated trigger, recoverable processing, tenant isolation, and an approval boundary.
The pattern fits an agent whose work can start asynchronously and whose operator can run a public receiver, queue, secret store, and review surface. If you cannot operate those controls, poll check_inbox from a private worker or keep the workflow manual. Lower latency is not worth turning outside correspondence into unattended authority.