Related docs: Tech overview · Architecture · Configuration guide
Astromesh supports WhatsApp as a messaging channel through the Meta Cloud API. Incoming WhatsApp messages are received via a webhook, routed to a configured agent for processing, and the agent's response is sent back to the user through the WhatsApp Business API.
The integration consists of three components:
- WhatsApp Client (
astromesh/channels/whatsapp.py) — Handles webhook verification, signature validation, payload parsing, and outbound message delivery via the Meta Graph API. - Webhook Route (
astromesh/api/routes/whatsapp.py) — FastAPI endpoints that receive Meta webhook events and dispatch messages to the agent runtime in the background. - Channel Configuration (
config/channels.yaml) — Declares credentials, the default agent, and rate limiting settings.
WhatsApp User
|
v
Meta Cloud API
|
v (POST /v1/channels/whatsapp/webhook)
Astromesh Webhook Route
|
v
Agent Runtime (whatsapp-assistant)
|
v (Graph API)
Meta Cloud API
|
v
WhatsApp User
- A Meta Business Account (free at business.facebook.com)
- A Meta Developer App with the WhatsApp product added (developers.facebook.com)
- A phone number registered with WhatsApp Business
- Astromesh platform running (local or deployed)
- Python 3.12+ and uv
-
Install Astromesh (if not already done):
uv sync
-
Set environment variables with your Meta app credentials:
export WHATSAPP_VERIFY_TOKEN="my-secret-verify-token" export WHATSAPP_ACCESS_TOKEN="EAAx..." export WHATSAPP_PHONE_NUMBER_ID="106..." export WHATSAPP_APP_SECRET="abc123..." # optional but recommended
-
Start the Astromesh server:
uv run uvicorn astromesh.api.main:app --host 0.0.0.0 --port 8000
-
Expose your local server using ngrok (for development):
ngrok http 8000
-
Configure the webhook in Meta Developer Dashboard:
- Go to your app > WhatsApp > Configuration
- Set the Callback URL to
https://<your-ngrok-url>/v1/channels/whatsapp/webhook - Set the Verify Token to the same value as
WHATSAPP_VERIFY_TOKEN - Subscribe to the
messageswebhook field
-
Send a test message from your WhatsApp phone to the registered business number. The
whatsapp-assistantagent will process it and reply.
| Variable | Required | Description |
|---|---|---|
WHATSAPP_VERIFY_TOKEN |
Yes | A secret token you create for Meta webhook verification. Must match the value entered in the Meta Developer Dashboard. |
WHATSAPP_ACCESS_TOKEN |
Yes | Access token from Meta for calling the WhatsApp Cloud API. Found under your app's WhatsApp > API Setup page. |
WHATSAPP_PHONE_NUMBER_ID |
Yes | The phone number ID assigned to your WhatsApp Business number. Found under WhatsApp > API Setup. |
WHATSAPP_APP_SECRET |
No | Your Meta app's secret key. Used to validate X-Hub-Signature-256 headers on incoming webhooks. Strongly recommended for production. |
WhatsApp messages are routed to the agent specified in config/channels.yaml (default: whatsapp-assistant). The sample agent definition lives at config/agents/whatsapp-assistant.agent.yaml:
apiVersion: astromesh/v1
kind: Agent
metadata:
name: whatsapp-assistant
version: "1.0.0"
namespace: messaging
labels:
channel: whatsapp
tier: production
spec:
identity:
display_name: "WhatsApp Assistant"
description: "Conversational assistant optimized for WhatsApp messaging"
model:
primary:
provider: ollama
model: "llava:7b"
endpoint: "http://ollama:11434"
parameters:
temperature: 0.4
max_tokens: 1024
fallback:
provider: ollama
model: "llama3.1:8b"
endpoint: "http://ollama:11434"
parameters:
temperature: 0.4
max_tokens: 1024
routing:
strategy: capability_match
prompts:
system: |
You are a friendly and helpful WhatsApp assistant. Keep your responses concise
and conversational — max 1-2 short paragraphs. Use a warm, approachable tone.
You can receive images and will describe or answer questions about them.
When you receive non-image media (audio, video, documents), acknowledge
the attachment and let the user know what you can help with.
Guidelines:
- Be direct and get to the point quickly
- Use simple language; avoid jargon
- If you don't know something, say so and offer alternatives
- Never share sensitive information in chat
orchestration:
pattern: react
max_iterations: 5
timeout_seconds: 30
memory:
conversational:
backend: sqlite
strategy: sliding_window
max_turns: 30
ttl: 86400
guardrails:
input:
- type: pii_detection
action: redact
- type: max_length
max_chars: 2000
output:
- type: pii_detection
action: redactKey considerations for WhatsApp agents:
- Keep
max_tokenslow (1024 or less). WhatsApp messages display best when short. - Set
timeout_seconds. WhatsApp users expect fast replies; 30 seconds is a reasonable upper bound. - Enable PII guardrails. WhatsApp messages often contain personal data. The
pii_detectionguardrail withredactaction prevents the agent from echoing sensitive information.
To use a different agent, update the default_agent field in config/channels.yaml.
-
Install ngrok and authenticate:
ngrok config add-authtoken <your-ngrok-token>
-
Start Astromesh on port 8000, then start ngrok:
ngrok http 8000
-
Copy the
https://forwarding URL from ngrok output (e.g.,https://a1b2c3d4.ngrok-free.app). -
In the Meta Developer Dashboard under WhatsApp > Configuration:
- Callback URL:
https://a1b2c3d4.ngrok-free.app/v1/channels/whatsapp/webhook - Verify Token: the value of your
WHATSAPP_VERIFY_TOKENenvironment variable - Click Verify and Save
- Callback URL:
-
Under Webhook Fields, subscribe to messages.
Note: ngrok URLs change each time you restart. Update the callback URL in the Meta dashboard after each restart, or use a paid ngrok plan with a stable subdomain.
-
Deploy Astromesh behind a reverse proxy (nginx, Caddy, etc.) with a valid TLS certificate. Meta requires HTTPS for webhooks.
-
Set the Callback URL in the Meta Developer Dashboard to your production endpoint:
https://api.yourdomain.com/v1/channels/whatsapp/webhook -
Set the Verify Token to match your
WHATSAPP_VERIFY_TOKENenvironment variable and click Verify and Save. -
Subscribe to the messages webhook field.
-
Set
WHATSAPP_APP_SECRETto enable signature validation on incoming webhook requests (see Security Considerations).
1. User sends a WhatsApp message
2. Meta Cloud API delivers a POST to /v1/channels/whatsapp/webhook
3. Webhook route validates X-Hub-Signature-256 (if app secret is configured)
4. Webhook route parses the payload and extracts text messages
5. Each message is dispatched to the agent runtime as a background task
6. Agent runtime runs the configured agent (whatsapp-assistant)
- Loads conversation memory (keyed by wa_<phone_number>)
- Renders the system prompt
- Executes the orchestration pattern (ReAct)
- Applies guardrails on input and output
7. Agent response is sent back via Meta Graph API POST to /<phone_number_id>/messages
8. User receives the reply in WhatsApp
Channel settings are defined in config/channels.yaml:
channels:
whatsapp:
# WhatsApp Business Cloud API settings
verify_token: "${WHATSAPP_VERIFY_TOKEN}"
access_token: "${WHATSAPP_ACCESS_TOKEN}"
phone_number_id: "${WHATSAPP_PHONE_NUMBER_ID}"
app_secret: "${WHATSAPP_APP_SECRET}"
# Which agent handles WhatsApp conversations
default_agent: "whatsapp-assistant"
# Rate limiting for outgoing messages
rate_limit:
window_seconds: 60
max_messages: 30| Field | Description |
|---|---|
verify_token |
Token for Meta webhook verification handshake |
access_token |
Bearer token for outbound Graph API calls |
phone_number_id |
Phone number ID for sending messages |
app_secret |
App secret for webhook signature validation |
default_agent |
Name of the agent YAML (must exist in config/agents/) |
rate_limit.window_seconds |
Rolling window duration for rate limiting |
rate_limit.max_messages |
Maximum outbound messages per window |
Credential values use the ${VAR_NAME} syntax and are resolved from environment variables at startup.
The WhatsApp integration supports receiving multimedia messages including images, audio, video, and documents. Media is downloaded from Meta's servers, processed in-memory, and discarded after the agent responds — no media is stored on disk.
| Type | Behavior | LLM Format |
|---|---|---|
| Image | Downloaded and base64-encoded | Sent as image_url content part to vision-capable models |
| Audio | Downloaded | Text description passed to agent (e.g., [Attached audio: audio/ogg, 15234 bytes]) |
| Video | Downloaded | Text description passed to agent |
| Document | Downloaded | Text description with filename passed to agent |
- User sends a photo (or other media) via WhatsApp
- Meta webhook delivers the message with a media ID
- Astromesh downloads the media bytes via the Graph API (
GET /{media_id}→ download URL → bytes) - The
build_multimodal_query()utility converts the message:- Images → base64 data URL in OpenAI
image_urlformat - Other media → text description for the agent
- Images → base64 data URL in OpenAI
- The
ModelRouterauto-detects image content and routes to a vision-capable model (e.g.,llava:7b) - Agent processes the query and responds as text via WhatsApp
To handle images, configure a vision-capable model as primary in the agent YAML:
spec:
model:
primary:
provider: ollama
model: "llava:7b"
fallback:
provider: ollama
model: "llama3.1:8b"
routing:
strategy: capability_matchThe capability_match routing strategy ensures image queries go to vision models, while text-only queries can use any available model.
WhatsApp image messages can include a caption. When present, the caption is included as the text part of the multimodal query alongside the image, so the agent can answer questions about the image directly.
- Confirm that
WHATSAPP_VERIFY_TOKENmatches the value entered in the Meta Developer Dashboard. - Verify the callback URL ends with
/v1/channels/whatsapp/webhookand is reachable over HTTPS. - Check Astromesh server logs for the incoming verification request.
- Confirm the messages webhook field is subscribed in the Meta dashboard.
- Check that your ngrok tunnel (or production endpoint) is running and accessible.
- Look at Meta's webhook test tool in the dashboard to see delivery status.
- Verify the phone number is registered and the WhatsApp Business account is active.
- Check that the agent specified in
config/channels.yaml(default_agent) has a matching YAML file inconfig/agents/. - Verify the LLM provider endpoint is reachable (e.g., Ollama is running).
- Review Astromesh logs for errors in background task processing.
- Confirm
WHATSAPP_ACCESS_TOKENis valid and has not expired.
- Meta enforces rate limits on the WhatsApp Cloud API. If you receive 429 responses, reduce message throughput.
- The
rate_limitsection inconfig/channels.yamlcontrols Astromesh's outbound rate. Adjustmax_messagesandwindow_secondsas needed.
- Ensure
WHATSAPP_APP_SECRETmatches your app's secret in the Meta Developer Dashboard (under App Settings > Basic). - If you do not need signature validation during development, leave
WHATSAPP_APP_SECRETunset. The client will skip validation when no secret is configured.
- Always use HTTPS in production. Meta requires a valid TLS certificate for webhook URLs. Use a reverse proxy with automatic certificate management (e.g., Caddy, Certbot with nginx).
- Enable signature validation. Set
WHATSAPP_APP_SECRETto your Meta app secret. The webhook route validates theX-Hub-Signature-256header on every incoming request, rejecting payloads with an invalid or missing signature. - Enable PII guardrails. WhatsApp messages frequently contain phone numbers, names, and other personal data. Configure
pii_detectionguardrails on both input and output to redact sensitive information before it reaches the LLM or is stored in memory. - Apply rate limiting. Use the
rate_limitconfiguration inconfig/channels.yamlto cap outbound messages per time window. This protects against runaway loops and excessive API usage. - Rotate access tokens. Meta access tokens can expire. Use System User tokens for long-lived production deployments and rotate them periodically.
- Restrict network access. In production, restrict inbound traffic to Meta's webhook IP ranges where possible.