Webhooks
Webhook entrypoint to wake and isolate agent runs.
The gateway can expose a small HTTP Webhook endpoint for external triggers.
Enable
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
},
}Notes:
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.token'</code>' is required when '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.enabled=true'</code>'.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.path'</code>' defaults to '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'/hooks'</code>'.
Authorization
Each request must include the hook token. Preferred headers:
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'Authorization: Bearer <token>'</code>' (recommended)
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'x-openclaw-token: <token>'</code>'
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'?token=<token>'</code>' (deprecated; logs warning and will be removed in a future major version)
Endpoints
#
`POST /hooks/wake`
Payload:
{ "text": "System line", "mode": "now" }- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'text'</code>' '<strong>'required'</strong>' (string): Description of the event (e.g., "New email received").
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'mode'</code>' optional ('<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'now'</code>' | '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'next-heartbeat'</code>'): Whether to trigger heartbeat immediately (default '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'now'</code>') or wait for next periodic check.
Effects:
- Queues a system event into the <strong>main</strong> session
- If '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'mode=now'</code>', triggers heartbeat immediately
#
`POST /hooks/agent`
Payload:
{
"message": "Run this",
"name": "Email",
"sessionKey": "hook:email:msg-123",
"wakeMode": "now",
"deliver": true,
"channel": "last",
"to": "+15551234567",
"model": "openai/gpt-5.2-mini",
"thinking": "low",
"timeoutSeconds": 120
}- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'message'</code>' '<strong>'required'</strong>' (string): The prompt or message for the agent to process.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'name'</code>' optional (string): Human-readable hook name (e.g., "GitHub"), used as a prefix in the session summary.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'sessionKey'</code>' optional (string): Key to identify the agent session. Defaults to a random '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hook:<uuid>'</code>'. Using a consistent key allows multi-turn conversations within the hook context.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'wakeMode'</code>' optional ('<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'now'</code>' | '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'next-heartbeat'</code>'): Whether to trigger heartbeat immediately (default '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'now'</code>') or wait for next periodic check.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'deliver'</code>' optional (boolean): If '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'true'</code>', the agent's response will be sent to the messaging channel. Defaults to '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'true'</code>'. Responses that are only heartbeat acknowledgments will be automatically skipped.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'channel'</code>' optional (string): Messaging channel for delivery. One of: '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'last'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'whatsapp'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'telegram'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'discord'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'slack'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'mattermost'</code>' (plugin), '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'signal'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'imessage'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'msteams'</code>'. Defaults to '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'last'</code>'.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'to'</code>' optional (string): Recipient identifier for the channel (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack/Mattermost (plugin), conversation ID for MS Teams). Defaults to the last recipient in the main session.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'model'</code>' optional (string): Model override (e.g., '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'anthropic/claude-3-5-sonnet'</code>' or alias). Must be in the allowed models list if restricted.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'thinking'</code>' optional (string): Thinking level override (e.g., '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'low'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'medium'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'high'</code>').
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'timeoutSeconds'</code>' optional (number): Maximum duration for the agent run in seconds.
Effects:
- Runs an <strong>isolated</strong> agent turn (its own session key)
- Always publishes a summary into the <strong>main</strong> session
- If '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'wakeMode=now'</code>', triggers heartbeat immediately
#
`POST /hooks/<name>` (mapped)
Custom hook names are resolved via '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.mappings'</code>' (see config). Mappings can
use optional templates or transform arbitrary payloads into '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'wake'</code>' or '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'agent'</code>' actions
via code transforms.
Mapping options (summary):
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.presets: ["gmail"]'</code>' enables built-in Gmail mapping.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.mappings'</code>' allows you to define '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'match'</code>', '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'action'</code>' and templates in config.json.
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.transformsDir'</code>' + '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'transform.module'</code>' loads JS/TS modules for custom logic.
- Use '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'match.source'</code>' to keep a generic ingest endpoint (payload-driven routing).
- TS transforms require a TS loader (e.g., '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'bun'</code>' or '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'tsx'</code>') or pre-compile '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'.js'</code>' at runtime.
- Set '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'deliver: true'</code>' + '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'channel'</code>'/'<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'to'</code>' on a mapping to route replies to chat interface
('<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'channel'</code>' defaults to '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'last'</code>' and falls back to WhatsApp).
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'allowUnsafeExternalContent: true'</code>' disables the external content safety wrapper for that hook
(dangerous; only for trusted internal sources).
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'openclaw webhooks gmail setup'</code>' writes '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'hooks.gmail'</code>' config for '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'openclaw webhooks gmail run'</code>'.
See '<a href="/automation/gmail-pubsub" className="text-emerald-400 hover:text-emerald-300 transition-colors">'Gmail Pub/Sub'</a>' for the full Gmail watch flow.
Responses
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'200'</code>' for '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'/hooks/wake'</code>'
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'202'</code>' for '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'/hooks/agent'</code>' (async run started)
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'401'</code>' authorization failed
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'400'</code>' invalid payload
- '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'413'</code>' payload too large
Examples
curl -X POST http://127.0.0.1:18789/hooks/wake -H 'Authorization: Bearer SECRET' -H 'Content-Type: application/json' -d '{"text":"New email received","mode":"now"}'curl -X POST http://127.0.0.1:18789/hooks/agent -H 'x-openclaw-token: SECRET' -H 'Content-Type: application/json' -d '{"message":"Summarize inbox","name":"Email","wakeMode":"next-heartbeat"}'#
Using a different model
Add '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'model'</code>' to the agent payload (or mapping) to override the model for that run:
curl -X POST http://127.0.0.1:18789/hooks/agent -H 'x-openclaw-token: SECRET' -H 'Content-Type: application/json' -d '{"message":"Summarize inbox","name":"Email","model":"openai/gpt-5.2-mini"}'If you enforce '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'agents.defaults.models'</code>', ensure it includes the override model.
curl -X POST http://127.0.0.1:18789/hooks/gmail -H 'Authorization: Bearer SECRET' -H 'Content-Type: application/json' -d '{"source":"gmail","messages":[{"from":"Ada","subject":"Hello","snippet":"Hi"}]}'Security
- Keep the hook endpoint behind loopback, Tailscale, or a trusted reverse proxy.
- Use a dedicated hook token; don't reuse the gateway auth token.
- Avoid including sensitive raw payloads in Webhook logs.
- By default, hook payloads are treated as untrusted and wrapped in safety boundaries.
If you must disable this for a specific hook, set '<code className="bg-white/10 px-1.5 py-0.5 rounded text-emerald-300 text-sm">'allowUnsafeExternalContent: true'</code>'
in that hook's mapping (dangerous).