Skip to main content
Use @kapso/openclaw-whatsapp when an OpenClaw agent needs to send and receive WhatsApp messages through Kapso. Use @kapso/whatsapp-cloud-api directly when you are building your own app and do not need OpenClaw channel routing.

Installation

Requires OpenClaw >= 2026.5.27.
openclaw plugins install clawhub:@kapso/openclaw-whatsapp
openclaw config set 'plugins.allow' '["codex","kapso-whatsapp"]' --strict-json
openclaw config set 'channels["kapso-whatsapp"].enabled' true --strict-json
If plugins.allow already has entries, keep them and add kapso-whatsapp. OpenClaw resolves the plugin from ClawHub and installs runtime dependencies, including @kapso/whatsapp-cloud-api and @kapso/cli. No separate npm install is needed. The Kapso CLI is bundled with the plugin:
openclaw kapso-whatsapp cli login
openclaw kapso-whatsapp cli status

Setup

Requires:
RequirementUse
Kapso API keySends messages, resolves numbers, and registers webhooks.
WhatsApp sender numberThe Kapso-connected number your agent sends from.
Public HTTPS gateway URLReceives Kapso webhooks.
Webhook secretVerifies inbound webhook signatures.
After your OpenClaw gateway has a public URL, let the plugin resolve the number, register the webhook, and write config:
openclaw kapso-whatsapp setup \
  --api-key "kapso_..." \
  --phone-number "+15551234567" \
  --webhook-url "https://your-machine.your-tailnet.ts.net/kapso/webhook" \
  --webhook-secret "$(openssl rand -hex 32)" \
  --default-to "+15551234567" \
  --register-webhook \
  --write-config
If you already know the Kapso/Meta phone_number_id, use --phone-number-id:
openclaw kapso-whatsapp setup \
  --api-key "kapso_..." \
  --phone-number-id "1234567890" \
  --webhook-url "https://your-openclaw-host.example.com/kapso/webhook" \
  --webhook-secret "$(openssl rand -hex 32)" \
  --register-webhook \
  --write-config
Useful setup flags:
FlagUse
--default-toDefault outbound WhatsApp recipient.
--allow-fromComma-separated inbound allowlist.
--dm-securityallowlist, open, or disabled.
--dry-runPrint actions without writing config or registering webhooks.
--jsonPrint machine-readable setup output.
Omit --write-config to print the openclaw config set commands without applying them.

Agent setup

The plugin ships an OpenClaw skill named kapso-whatsapp-setup. Check that OpenClaw sees it:
openclaw skills list | grep kapso
openclaw skills info kapso-whatsapp-setup
Before asking the agent to finish setup, do the interactive and secret-bearing steps yourself:
openclaw kapso-whatsapp cli login
openclaw kapso-whatsapp cli status
openclaw config set 'channels["kapso-whatsapp"].apiKey' '"kapso_..."' --strict-json
Use the browser dashboard for setup visibility:
openclaw dashboard
If you prefer the terminal UI, start openclaw tui and send:
/verbose full
/trace on
/tools verbose
Then paste something like this into OpenClaw:
Use the kapso-whatsapp-setup skill to finish my Kapso WhatsApp OpenClaw setup.

I already configured my Kapso API key on this server.
I already ran openclaw kapso-whatsapp cli login successfully.
Default outbound recipient: +15551234567
Public gateway: discover it if possible. I am using Tailscale Funnel on this machine.
Kapso WhatsApp sender number: discover it with the Kapso CLI. If there is more than one available number, ask me which one to use.

Please verify the Kapso CLI and @kapso/openclaw-whatsapp plugin are installed, but do not run interactive login. Discover the public webhook URL, resolve the phone_number_id, generate a webhook secret, register the Kapso phone-number webhook for whatsapp.message.received, write the OpenClaw channel config, set the default outbound recipient, run diagnostics, and tell me exactly what remains manual.
If you already know the public webhook URL, replace the gateway line with:
Public gateway webhook URL: https://your-openclaw-host.example.com/kapso/webhook
If you use another public tunnel or reverse proxy, name it:
Public gateway: discover it if possible. I am using ngrok on this machine.
If you already know the Kapso/Meta number ID, replace the sender-number line with:
Kapso/Meta phone_number_id: 1234567890
If the Kapso CLI is not logged in, the agent should stop and ask you to run openclaw kapso-whatsapp cli login in your terminal. It should not run interactive login for you.

Public gateway

Kapso Cloud must reach your OpenClaw webhook over the public internet. A private tailnet URL is not enough. For Tailscale, use Funnel:
mkdir -p ~/.openclaw
openssl rand -base64 32 > ~/.openclaw/gateway-password
chmod 600 ~/.openclaw/gateway-password

openclaw gateway run \
  --bind loopback \
  --auth password \
  --password-file ~/.openclaw/gateway-password \
  --tailscale funnel \
  --force
Use --password-file instead of passing a password directly in process arguments. The Kapso webhook URL should use your Funnel hostname plus /kapso/webhook:
https://your-machine.your-tailnet.ts.net/kapso/webhook
Discover an existing Funnel URL from the machine running OpenClaw:
tailscale funnel status --json

Configure manually

openclaw config set 'channels["kapso-whatsapp"].apiKey' '"kapso_..."' --strict-json
openclaw config set 'channels["kapso-whatsapp"].phoneNumberId' '"1234567890"' --strict-json
openclaw config set 'channels["kapso-whatsapp"].webhookSecret' '"shared webhook secret"' --strict-json
Use JSON strings for IDs and secrets, especially digit-only values. Current plugin versions coerce numeric phoneNumberId and defaultTo values, but JSON strings avoid precision loss for large IDs. Optional settings:
openclaw config set 'channels["kapso-whatsapp"].baseUrl' '"https://api.kapso.ai/meta/whatsapp"' --strict-json
openclaw config set 'channels["kapso-whatsapp"].webhookPath' '"/kapso/webhook"' --strict-json
openclaw config set 'channels["kapso-whatsapp"].defaultTo' '"+15551234567"' --strict-json
openclaw config set 'channels["kapso-whatsapp"].dmSecurity' '"allowlist"' --strict-json
openclaw config set 'channels["kapso-whatsapp"].allowFrom' '["+15551234567","15551234567"]' --strict-json
Kapso webhook sender IDs often arrive as digits-only values, while users usually type E.164 numbers with +. Current plugin versions compare both forms as the same sender. Including both is harmless for older installs or raw config edits. You can also use environment variables:
export KAPSO_API_KEY="kapso_..."
export KAPSO_PHONE_NUMBER_ID="1234567890"
export KAPSO_WEBHOOK_SECRET="shared webhook secret"
export KAPSO_BASE_URL="https://api.kapso.ai/meta/whatsapp"
export KAPSO_WEBHOOK_PATH="/kapso/webhook"
export KAPSO_BOT_USERNAME="support"
export KAPSO_DEFAULT_TO="+15551234567"

Kapso webhook

Create a phone-number scoped Kapso webhook, not a project webhook.
SettingValue
MethodPOST
URLhttps://your-openclaw-host.example.com/kapso/webhook
Eventswhatsapp.message.received
SecretSame value as channels["kapso-whatsapp"].webhookSecret or KAPSO_WEBHOOK_SECRET
Payload versionv2 recommended
The setup command can register this webhook with --register-webhook. You can also run the bundled Kapso CLI through OpenClaw:
openclaw kapso-whatsapp cli login
openclaw kapso-whatsapp cli status
openclaw kapso-whatsapp cli whatsapp numbers list --output json
openclaw kapso-whatsapp cli whatsapp numbers resolve "+15551234567" --output json
openclaw kapso-whatsapp cli whatsapp webhooks new \
  --phone-number-id "1234567890" \
  --url "https://your-openclaw-host.example.com/kapso/webhook" \
  --event whatsapp.message.received \
  --active \
  --output json
The setup command uses the bundled CLI internally for number resolution, project access checks, and webhook registration.

Channel targets

The plugin accepts WhatsApp phone-number targets with these prefixes:
kapso:+15551234567
kapso-whatsapp:+15551234567
whatsapp:+15551234567
wa:+15551234567
Plain E.164 or digits-only phone numbers also work. Set a default recipient when you want OpenClaw to send through this channel without an explicit target:
openclaw config set 'channels["kapso-whatsapp"].defaultTo' '"+15551234567"' --strict-json

Diagnostics

Check config and webhook registration:
openclaw kapso-whatsapp setup --help
openclaw kapso-whatsapp doctor
openclaw kapso-whatsapp doctor --json
openclaw config get 'channels["kapso-whatsapp"]'
openclaw channels status --channel kapso-whatsapp --probe
Tail OpenClaw logs:
openclaw logs --follow --plain --limit 500
openclaw channels logs --lines 500 | grep -Ei 'kapso|whatsapp|media|image|audio|document|webhook|signature'
If the gateway prints a log path, tail it directly:
tail -f /tmp/openclaw/openclaw-YYYY-MM-DD.log

Media

Images, videos, and documents can be forwarded when Kapso includes a downloadable media URL, such as mediaUrl, media_data.url, media_data.downloadUrl, or a direct media link or url. The selected OpenClaw model/runtime must support that media type. If Kapso sends only a media ID and no URL, the plugin records that media exists but cannot pass the file bytes to the model. Logs warn about id but no URL. Voice notes and audio messages need a transcription provider. Without one, the agent may only receive audio metadata. Hosted options include OpenAI gpt-4o-transcribe, gpt-4o-mini-transcribe, or whisper-1. Self-hosted ASR can work if your OpenClaw deployment exposes a compatible transcription provider.

Troubleshooting

Config values rejected as “must be string”

Use strict JSON strings:
openclaw config set 'channels["kapso-whatsapp"].phoneNumberId' '"1234567890"' --strict-json
openclaw config set 'channels["kapso-whatsapp"].defaultTo' '"+15551234567"' --strict-json

Webhook returns 401

The Kapso webhook secret and channels["kapso-whatsapp"].webhookSecret do not match, or the request is missing the Kapso signature header.

Webhook is reachable but no messages arrive

Confirm the webhook is phone-number scoped and subscribed to whatsapp.message.received. Project webhooks do not receive WhatsApp message events.

Webhook dispatches but no agent session or reply

Check allowFrom. Kapso commonly sends inbound sender IDs without a leading +, even if your allowlist was configured with E.164 values. Current plugin versions compare those forms as the same sender. On older installs, include both variants and restart the gateway:
openclaw config set 'channels["kapso-whatsapp"].allowFrom' '["+56975746426","56975746426"]' --strict-json

Images sometimes work and sometimes do not

Check logs for media=image/url=yes. If logs say url=no or warn that the event had an ID but no URL, the model did not receive the actual image bytes.

Source

GitHub repository

Source code, setup skill, and issue tracker for @kapso/openclaw-whatsapp.