#Whatsapp webhook
1 messages Β· Page 1 of 1 (latest)
My webhooks are configured but it's not sending
when i post /hooks/agent i get back:
{
"ok": true,
"runId": "782bb3fa-13b4-4817-a6cf-9ee89277663a"
}
but it's not running the command/message
When you say βwebhooksβ, do you mean OpenClaw Hooks (hooks.enabled β POST /hooks/...) that should trigger a WhatsApp send, or a WhatsApp Cloud/Twilio webhook? (OpenClawβs built-in WhatsApp channel is WhatsApp Web (Baileys) β there isnβt a separate Twilio/Cloud βwebhook channelβ.)
Either way, the fastest way to pinpoint βconfigured but not sendingβ is:
- Version + baseline health
openclaw --version
openclaw status --all
openclaw channels status --probe
Paste the outputs (tokens are redacted in --all).
- Confirm the webhook request is actually accepted
If youβre using OpenClaw Hooks, hit it with a known-good request and check the HTTP code:
curl -i -X POST http://127.0.0.1:18789/hooks/agent \
-H 'x-openclaw-token: YOUR_HOOK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"message":"test webhook -> whatsapp",
"deliver": true,
"channel":"whatsapp",
"to":"+15551234567"
}'
Notes:
- Query-string tokens are rejected (
?token=...wonβt work). UseAuthorization: Bearer ...orx-openclaw-token. - If youβre behind a reverse proxy, it may drop the
Authorizationheader βx-openclaw-tokenusually survives.
- Watch logs while you fire the webhook
openclaw logs --follow
Common log signatures:
- 401/429 β token/header/auth problem.
- 202 accepted but no message β usually one of:
- no recipient info (fix by setting
"channel"+"to"explicitly like above) - WhatsApp not actually connected (look for βno active listener when sendingβ; fix with
openclaw channels login --channel whatsapp+ ensure gateway is running)
- no recipient info (fix by setting
Docs refs:
If you paste: (a) your openclaw --version, (b) the HTTP status you get from the curl, and (c) the relevant log lines right after the request, I can tell you exactly which branch youβre in.
That { ok: true, runId: ... } response is just the ack β /hooks/agent is async (it wonβt return the agentβs reply in the HTTP response). The actual run either:
- is running but delivering βnowhereβ, or
- is failing immediately (models/auth/channel), and the only place youβll see why is the logs.
1) Most common gotcha: channel/to defaults donβt work for βwebhooks-onlyβ setups
If you donβt set them, delivery defaults to:
channel: "last"to: <last recipient in main session>
If your βmain sessionβ has never chatted on any surface, there is no βlast recipientβ, so the run can complete but nothing gets sent.
Try forcing delivery explicitly:
curl -i -X POST http://127.0.0.1:18789/hooks/agent \
-H 'x-openclaw-token: YOUR_HOOK_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"name":"hook test",
"message":"Reply with exactly: webhook works",
"wakeMode":"now",
"deliver": true,
"channel":"whatsapp",
"to":"+15551234567"
}'
(If you set wakeMode: "next-heartbeat" it may βdo nothingβ until the next heartbeat tick.)
2) Tail logs by runId while firing the webhook
In one terminal:
openclaw logs --follow
Then fire the webhook and look for that runId. Youβll usually see one of these:
- model/auth error (
All models failed,No credentials found, 429s, etc.) - WhatsApp not connected / no listener
- delivery target missing/invalid
3) Quick health checks to paste here
These three outputs usually make it obvious whatβs wrong:
openclaw --version
openclaw models status
openclaw channels status --probe
If you can paste the exact JSON payload youβre POSTing to /hooks/agent (redact token) + the log lines around that runId, I can tell you precisely whether itβs a delivery-default issue, a model/auth failure, or WhatsApp connectivity.
Agents
ββββββββββββββ¬ββββββββββββ¬βββββββββββ¬βββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Agent β Bootstrap β Sessions β Active β Store β
ββββββββββββββΌββββββββββββΌβββββββββββΌβββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β main β PENDING β 13 β 1m ago β ~/.openclaw/agents/main/sessions/sessions.json β
ββββββββββββββ΄ββββββββββββ΄βββββββββββ΄βββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Diagnosis (read-only)
Gateway connection details:
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /root/.openclaw/openclaw.json
Bind: loopback
β Config: /root/.openclaw/openclaw.json
β Restart sentinel: none
! Port 18789
Port 18789 is already in use.
- pid 72450 root: openclaw-gateway (127.0.0.1:18789)
- Gateway already running locally. Stop it (openclaw gateway stop) or use a different port.
β Tailscale: off Β· Running Β· sandbox-1.tail067cba.ts.net
ips: 100.97.18.42, fd7a:115c:a1e0::8d3b:122a
β Skills: 8 eligible Β· 0 missing Β· /root/.openclaw/workspace
! Channel issues skipped (gateway query failed)
Gateway health:
Error: gateway closed (1008): pairing required
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /root/.openclaw/openclaw.json
Bind: loopback
Pasteable debug report. Auth tokens redacted.
Troubleshooting: https://docs.openclaw.ai/troubleshooting
β
root@sandbox-1:~#
Whats causing this?
logs on gateway:
f durationMs=6242 active=0 queued=0
02:48:03 warn gateway {"subsystem":"gateway"} security audit: device access upgrade requested reason=scope-upgrade device=55d0f223fecf3ebb0245f7b8ec151e288cba2d11e179c699a4761ce2c0b8540a ip=unknown-ip auth=token roleFrom=operator roleTo=operator scopesFrom=operator.admin,operator.approvals,operator.pairing scopesTo=operator.write client=gateway-client conn=73eeeeab-a7c2-4c3e-acad-08236276f8c2
02:48:03 error gateway connect failed: Error: pairing required
02:48:03 error gateway connect failed: Error: pairing required
02:48:03 error Subagent completion direct announce failed for run 5f70fa28-12b3-4ed6-b45b-55b4adb61f14:b51a79cc-1a6f-49e2-9e2f-1638db334791: gateway closed (1008): pairing required
Gateway target: ws://127.0.0.1:18789
Source: local loopback
Config: /root/.openclaw/openclaw.json
Bind: loopback
02:48:03 warn gateway/ws {"subsystem":"gateway/ws"} {"cause":"pairing-required","handshake":"failed","durationMs":13,"lastFrameType":"req","lastFrameMethod":"connect","lastFrameId":"c4b801fb-2d00-42e2-b91f-c3a6730922eb","host":"127.0.0.1:18789","deviceId":"55d0f223fecf3ebb0245f7b8ec151e288cba2d11e179c699a4761ce2c0b8540a","requestId":"641db701-ca37-4630-8d3c-4050e2f7f35e","reason":"scope-upgrade"} closed before connect conn=73eeeeab-a7c2-4c3e-acad-08236276f8c2 remote=127.0.0.1 fwd=n/a origin=n/a host=127.0.0.1:18789 ua=n/a code=1008 reason=pairing required
That status output is the smoking gun:
Gateway health: Error: gateway closed (1008): pairing required
Channel issues skipped (gateway query failed)
That means your operator device (CLI / Control UI) is not approved, so the gateway is rejecting the WebSocket control connection. Until thatβs fixed, you wonβt be able to reliably inspect what /hooks/agent is doing (and the dashboard/CLI canβt show you channel status, session output, etc.).
Fix the 1008 βpairing requiredβ first (device approval)
Run on the gateway host (same machine where openclaw-gateway is running):
openclaw devices list
openclaw devices approve <requestId>
Then re-check:
openclaw status --deep
openclaw channels status --probe
Docs: https://docs.openclaw.ai/web/control-ui#device-pairing-first-connection
Second: your agent is still bootstrapping
Your table shows:
main Bootstrap: PENDING
So even if the hook run starts, the agent may be stuck doing the bootstrapping Q&A ritual instead of executing your βrealβ message. Finish bootstrapping by chatting with it in Control UI (once pairing is approved) and answering the questions until BOOTSTRAP.md is removed.
Docs: https://docs.openclaw.ai/start/bootstrapping
Third: make sure your hook run has an explicit delivery target
If your payload doesnβt set channel + to, it defaults to channel:"last" and can end up delivering nowhere.
Use something like:
{
"message": "say: webhook works",
"deliver": true,
"channel": "whatsapp",
"to": "+15551234567",
"wakeMode": "now"
}
If it still βacks but doesnβt runβ
Tail logs and search for your runId:
openclaw logs --follow
Paste the log lines around 782bb3fa-13b4-4817-a6cf-9ee89277663a and the output of openclaw devices list (requestIds are fine to share). That will tell us if itβs failing on bootstrapping, model auth, WhatsApp connectivity, or delivery routing.
That log is not a WhatsApp/webhook problem yet β itβs the Gateway refusing a WebSocket βdeviceβ connection because it needs a scope upgrade and hasnβt been approved.
Key lines:
security audit: device access upgrade requested reason=scope-upgrade ... scopesTo=operator.writehandshake failed ... reason":"scope-upgrade" ... code=1008 reason=pairing required
Meaning: a client identified as deviceId 55d0f2... is trying to connect with a token that previously had limited operator scopes, and the gateway now wants to grant it operator.write (needed for βwriteβ actions like sending/chat/etc). Until you approve that upgrade, the gateway closes the WS handshake with 1008 pairing required.
Fix
On the gateway host, as the same user/state dir the gateway is running under (yours shows /root/.openclaw/...):
openclaw devices list
openclaw devices approve 641db701-ca37-4630-8d3c-4050e2f7f35e
Then retry:
openclaw status --deep
If devices list shows nothing
Youβre probably running the CLI against a different state directory than the gateway (common when the gateway is running as root in docker/systemd, but you run CLI as a normal user). Make sure the CLI is pointed at /root/.openclaw (or just run the commands as root in the same environment where the gateway runs).
Once that device approval is done, your /hooks/agent runs should start actually executing + delivering again, and channels status --probe will work too.
When I use https://docs.openclaw.ai/tools/agent-send
openclaw agent , does it uses tokens?
Yeah β openclaw agent ... does use LLM tokens, because itβs literally running an agent turn (same as if a chat message came in), so it will call whatever model you have configured and youβll pay prompt+completion tokens.
Two related but different βtokensβ people mean:
- LLM tokens (cost/usage)
- Used by:
openclaw agent - Not used by:
openclaw message send(it just sends a message; no AI call)
- Gateway auth token (access control to the Gateway / Control UI / remote CLI)
openclaw agentgoes through the Gateway by default (per the doc), so if your Gateway is protected (remote mode / non-local), the CLI may need the Gateway token/password to talk to it. Thatβs auth, not LLM usage.
If your webhook just needs βreceive HTTP β send a WhatsApp messageβ, you probably want no AI at all, so use the messaging CLI:
openclaw message send --channel whatsapp --target +15555550123 --message "Hello from webhook"
Docs:
- Agent Send: https://docs.openclaw.ai/tools/agent-send
- Message CLI: https://docs.openclaw.ai/cli/message
If you tell me whether by βtokensβ you mean LLM cost or Gateway auth token, and whether youβre running the webhook on the same machine as the Gateway, I can suggest the cleanest wiring.
How can I trigger openclaw message send from a webhook?
Youβve basically got two options:
Option A (no LLM tokens): webhook server β run openclaw message send
This is the most direct way to βtrigger openclaw message send from a webhookβ, and it doesnβt call a model.
Minimal Node/Express example (safe-ish spawning, no shell):
// server.js
import express from "express";
import { spawn } from "node:child_process";
const app = express();
app.use(express.json({ limit: "64kb" }));
const WEBHOOK_TOKEN = process.env.WEBHOOK_TOKEN; // your shared secret
app.post("/send-whatsapp", (req, res) => {
// 1) auth
if (!WEBHOOK_TOKEN || req.header("x-webhook-token") !== WEBHOOK_TOKEN) {
return res.status(401).send("unauthorized");
}
// 2) input
const { to, message } = req.body ?? {};
if (typeof to !== "string" || typeof message !== "string") {
return res.status(400).send("expected JSON: { to, message }");
}
// 3) run OpenClaw CLI
const args = [
"message",
"send",
"--channel",
"whatsapp",
"--target",
to,
"--message",
message,
];
const p = spawn("openclaw", args, { stdio: ["ignore", "pipe", "pipe"] });
let out = "";
let err = "";
p.stdout.on("data", (d) => (out += d));
p.stderr.on("data", (d) => (err += d));
p.on("close", (code) => {
if (code === 0) return res.status(200).send("ok");
res.status(500).json({ code, err: err.trim(), out: out.trim() });
});
});
app.listen(3001, () => console.log("listening on :3001"));
Run it:
npm i express
node server.js
Trigger it:
curl -X POST http://localhost:3001/send-whatsapp \
-H "Content-Type: application/json" \
-H "x-webhook-token: YOUR_SECRET" \
-d '{"to":"+15555550123","message":"hello from webhook"}'
Notes that matter in practice
- Run this on the same machine as your OpenClaw Gateway (simplest), so it uses the same
~/.openclaw/openclaw.jsonconfig + WhatsApp channel setup. - Donβt use
exec("openclaw ... " + userInput)(command injection risk).spawn([...args])like above is the right pattern. - Add rate limiting + strict validation if this is internet-exposed.