- Register webhooks via POST /api/v1/webhooks with a URL, event list, channel ID, and signing secret
- OpenClaw signs every payload with HMAC-SHA256 — always verify the X-OpenClaw-Signature header before processing
- Four event types: message.sent, task.completed, agent.error, agent.status
- Failed deliveries retry up to 5 times using exponential backoff over 6 hours — design your endpoint for idempotent processing
- Return HTTP 200 immediately and process the payload asynchronously — endpoints that are slow trigger retry logic
Here's what I've seen consistently across dozens of OpenClaw integrations: teams that poll the message history API spend twice as long debugging race conditions and missed responses as teams that use webhooks. Webhooks eliminate both problems. The setup takes under ten minutes. Let me walk you through the exact implementation that works in production.
What OpenClaw Webhooks Do
A webhook is a URL on your server that OpenClaw calls when a specific event occurs. Instead of your system repeatedly asking "did anything happen?" — OpenClaw tells you the moment it does. This is the push model, and it's always more efficient than polling at scale.
OpenClaw supports webhooks at the channel level. Each webhook registration targets a specific channel and listens for specific event types. When a matching event occurs on that channel, the gateway constructs a JSON payload and POSTs it to your registered URL. Your server acknowledges receipt with a 200, and OpenClaw moves on.
The four event types you can subscribe to:
- message.sent — fires when an agent sends a response to any message on the subscribed channel
- task.completed — fires when a multi-step agent task finishes, including the final output
- agent.error — fires when an agent encounters an unhandled error, including the error details
- agent.status — fires when an agent comes online or goes offline on the channel
You can subscribe to multiple event types per webhook, or create separate webhooks for different event types to route them to different handlers. Most integrations subscribe to message.sent and task.completed — that covers the vast majority of use cases.
Registering a Webhook
Webhooks are registered via the REST API. One POST request is all it takes.
curl -X POST https://your-domain.com/api/v1/webhooks \
-H "Authorization: Bearer your-gateway-token" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/openclaw",
"events": ["message.sent", "task.completed"],
"channel_id": "research-agent-01",
"secret": "your-webhook-signing-secret-here"
}'
The response includes a webhook id — save it. You'll need it to update or delete this webhook later. The registration is active immediately — no restart required.
Your local server isn't reachable by OpenClaw unless you expose it. Run ngrok http 3000 to get a public HTTPS URL that tunnels to localhost:3000. Use that ngrok URL as your webhook endpoint during development. ngrok also shows you the raw HTTP traffic — invaluable for debugging payload formats.
Webhook Payload Format
Every webhook payload follows the same JSON envelope regardless of event type. The event field tells you what happened; the data field contains the event-specific payload.
{
"id": "evt_01HXYZ789ABC",
"event": "message.sent",
"channel_id": "research-agent-01",
"timestamp": "2025-01-16T14:23:41Z",
"data": {
"message_id": "msg_01HXYZ456DEF",
"content": "Here are the top 5 findings from the research task...",
"agent_name": "ResearchAgent",
"role": "assistant",
"metadata": {
"task_id": "task-99",
"source": "cron-scheduler"
}
}
}
The id field is a unique event ID. Store it and use it for idempotency — if OpenClaw retries a delivery after a transient failure on your end, you may receive the same event twice. Checking the event ID prevents duplicate processing.
HMAC Signature Verification
Every incoming webhook includes an X-OpenClaw-Signature header. Always verify it before processing the payload. This is not optional in production — without verification, anyone can POST fake data to your endpoint.
The signature is computed as sha256=HMAC-SHA256(raw_request_body, your_signing_secret). Here's how to verify it in Python and Node.js:
# Python verification
import hmac, hashlib
def verify_signature(payload_bytes, header_sig, secret):
expected = "sha256=" + hmac.new(
secret.encode(), payload_bytes, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header_sig)
# Usage in Flask
@app.route('/webhooks/openclaw', methods=['POST'])
def handle_webhook():
sig = request.headers.get('X-OpenClaw-Signature', '')
if not verify_signature(request.get_data(), sig, WEBHOOK_SECRET):
return 'Unauthorized', 401
payload = request.json
# queue for async processing
queue.enqueue(process_event, payload)
return '', 200
Always use a constant-time comparison function like hmac.compare_digest() in Python or crypto.timingSafeEqual() in Node.js when comparing signatures. Regular string equality (=== or ==) is vulnerable to timing attacks that allow signature forgery. This is a real attack — don't skip it.
Retry Behavior and Idempotency
OpenClaw considers a webhook delivery failed when your endpoint returns a non-2xx HTTP status or does not respond within 10 seconds. Failed deliveries are retried automatically using exponential backoff.
The retry schedule:
- Attempt 1: Immediate (the original delivery)
- Attempt 2: 30 seconds after failure
- Attempt 3: 2 minutes after failure
- Attempt 4: 10 minutes after failure
- Attempt 5: 1 hour after failure
- Attempt 6 (final): 6 hours after failure
After 6 failed attempts, the event is dropped and logged in the gateway. You can retrieve missed events via the message history API if needed.
Because of retry logic, your endpoint must handle duplicate event deliveries gracefully. Store the event id in a database and check for it before processing. If you've already processed an event ID, return 200 immediately without reprocessing.
Common Webhook Mistakes
- Processing synchronously before returning 200 — if your handler takes 15 seconds to process a payload, OpenClaw logs it as a failed delivery and starts retrying. Always queue the payload immediately and return 200 before any processing begins.
- Not verifying HMAC signatures — an unprotected webhook endpoint is a direct injection point into your system. Verify every payload, no exceptions.
- Not handling duplicate deliveries — retry logic means the same event can arrive twice. Store event IDs and deduplicate before processing.
- Subscribing to all events instead of specific ones — each event type adds overhead. Subscribe only to the events your integration actually needs.
- Using the same signing secret for all webhooks — use unique secrets per webhook registration. If one endpoint is compromised, the damage is contained to that integration.
Frequently Asked Questions
What events can OpenClaw webhooks listen for?
Four event types: message.sent (agent sent a response), task.completed (multi-step task finished), agent.error (unhandled error), and agent.status (agent came online or went offline). Subscribe to only the events you need — each adds overhead to gateway processing.
How do I verify OpenClaw webhook signatures?
OpenClaw signs every payload with HMAC-SHA256 using your registration secret. The signature is in the X-OpenClaw-Signature header as sha256=<hex>. Recompute the HMAC on the raw request body with your secret and compare using a constant-time comparison function. Reject payloads that don't match.
Does OpenClaw retry failed webhook deliveries?
Yes — up to 5 retries using exponential backoff: 30 seconds, 2 minutes, 10 minutes, 1 hour, and 6 hours after the initial failure. A delivery fails if your endpoint returns non-2xx or doesn't respond within 10 seconds. Design your endpoint for idempotent processing to handle retries safely.
Can I register multiple webhooks for the same channel?
Yes. A channel can have multiple independent webhook registrations — each with its own URL, secret, and event filter. Use this to send different event types to different handlers: agent errors to PagerDuty, completed tasks to your dashboard, messages to Slack.
What should my webhook endpoint return?
Return HTTP 200 immediately — ideally within 2 seconds. Queue the payload for async processing before responding. Long response times cause OpenClaw to consider the delivery failed, triggering unnecessary retries and potential duplicate processing.
How do I debug webhook delivery failures?
Check gateway logs first — they record every delivery attempt, the response status received, and the response time. If your endpoint returns non-2xx codes, check your server logs. In development, use ngrok to expose a local endpoint and inspect raw payloads in ngrok's web UI at http://localhost:4040.
A. Larsen has built webhook integrations between OpenClaw and dozens of external platforms including Slack, Notion, Airtable, and custom internal systems. Specializes in event-driven architectures and async processing patterns for AI agent pipelines.