WhatsApp Bot Development
Run your own instance of the GAIA WhatsApp bot. The bot receives messages through Kapso, a proxy for the Meta WhatsApp Cloud API, via a signed webhook, so you need a publicly reachable URL.Looking to use GAIA on WhatsApp as a user? See Using GAIA on WhatsApp.
Prerequisites
- Node.js 18+ and pnpm
- A Kapso account with a connected WhatsApp Business number
- GAIA API running (see Self-Hosting Guide)
- A publicly reachable URL for the webhook (or a tunnel like ngrok during development)
Step 1: Set up Kapso
- Create a Kapso project and connect your WhatsApp Business phone number
- Copy your API key and Phone Number ID
- Configure a webhook pointing to your bot’s
/webhookendpoint (the bot listens on port3203by default; override withBOT_SERVER_PORT) - Copy the webhook secret: Kapso signs every request with HMAC-SHA256, and the bot rejects requests with invalid signatures
Step 2: Configure environment variables
Create a.env file in apps/bots/whatsapp/ (or use the shared apps/bots/.env):
GAIA_BOT_API_KEY must match the BOT_API_KEY configured in your GAIA API. Generate BOT_LOG_HASH_SECRET with openssl rand -hex 32.
Step 3: Start the bot
Platform behavior
A few WhatsApp-specific constraints, handled automatically by the adapter:- No streaming or edits, responses are sent as complete messages once generation finishes
- Commands are matched by text prefix (e.g.
/gaia ...), there is no native slash-command registration like Discord or Telegram - Media: voice notes are transcribed (up to 25 MB), images and documents are uploaded (up to 10 MB), videos and stickers get a polite “not supported” reply
- Message cap: 4,096 characters; longer responses are split
Troubleshooting
| Problem | Solution |
|---|---|
| Webhook returns 401/403 | Verify KAPSO_WEBHOOK_SECRET matches the secret configured in Kapso |
| Bot doesn’t respond | Check the webhook URL is publicly reachable and points to /webhook on the bot’s port |
| Authentication fails | Ensure GAIA_BOT_API_KEY matches the API’s BOT_API_KEY and the API is reachable |

