@hitheo/whatsapp package connects Theo to the WhatsApp Cloud API. Customers message your WhatsApp number and the adapter forwards each message to POST /api/v1/completions, then replies via the Cloud API.
Theo never hosts your WhatsApp number or tokens. You own the WhatsApp Business Account in Meta Business Suite, hold the access token, and deploy this adapter on your infrastructure; Theo only processes the completion traffic the adapter forwards. Your customers message your brand, not ours.
Prerequisites
- A WhatsApp Business API account (via Meta Business Suite)
- A Theo API key (
theo_sk_...) - A webhook URL for receiving incoming messages
- The Meta App Secret (used to verify webhook signatures)
Install
Quick Setup
The package exports three functions:createWhatsAppHandler(config)— returns an async function that processes a WhatsApp webhook payload.verifyWebhook(params)— validates the Meta GET challenge during webhook registration.verifySignature(rawBody, headerSig, appSecret)— HMAC-SHA256 signature check for the inbound POST. You MUST pass the raw request body (bytes), not a re-parsed JSON object.
How It Works
- Customer sends a WhatsApp message
- Meta POSTs the webhook payload with an
X-Hub-Signature-256header - Your HTTP layer verifies the signature via
verifySignature - The adapter iterates over messages, forwards text and media to Theo (with a per-sender
conversation_id), and sends Theo’s response back via the Cloud API - Status events (
delivered,read) are acknowledged silently — no duplicate replies
Handler Config
| Option | Type | Required | Description |
|---|---|---|---|
theoApiKey | string | ✅ | Your theo_sk_... API key |
whatsappToken | string | ✅ | WhatsApp Cloud API access token |
phoneNumberId | string | ✅ | Your WhatsApp phone number ID |
appSecret | string | Meta App Secret. Store it in your environment and pass it to verifySignature; the adapter itself never reads network traffic. | |
theoBaseUrl | string | API base URL (default: https://hitheo.ai) | |
mode | ChatMode | Default mode | |
persona | PersonaInput | Custom persona or "theo" / "none" | |
skills | string[] | Skill slugs to activate on every message | |
conversationStore | ConversationStore | Map sender numbers → Theo conversation IDs. Defaults to an in-process Map. | |
enableVoice | boolean | Transcribe voice/audio with Theo speech-to-text and append the transcript to the prompt. Off by default (STT adds cost). | |
maxChunkChars | number | Max chars per outbound message. Default 4000. |
Attachments
Image messages (andimage/* documents) are downloaded inside the adapter and forwarded to Theo as base64 image data. The adapter presents the Meta access token to the Graph API to fetch the bytes, but never passes the tokenized Graph CDN URL onwards — so no credentials leave your deployment. Captions become the prompt text.
Voice and audio messages are ignored unless you set enableVoice: true. When enabled, the adapter downloads the clip, transcribes it with Theo speech-to-text, and appends the transcript to the prompt as text.
Conversation Persistence Across Instances
The defaultConversationStore is in-process. For serverless or multi-instance deployments, plug in Redis or your database:
