Skip to main content
This guide shows how to create a WhatsApp assistant with Timbal that can:
  • Respond to messages in real time via webhooks
  • Persist conversations using Timbal Knowledge Bases or JSONL
  • Handle text, audio, images, and documents

Setup Requirements

1

Create or open your WhatsApp App

Go to the Meta console and open your app’s WhatsApp section: Meta WhatsApp App Dashboard.
2

Set Callback URL and Verify Token

  1. Set the Callback URL to your endpoint, e.g., https://yourdomain.com/
  2. Set a Verify Token (keep it secret)
  3. In your app code, use the same token (see WHATSAPP_VERIFY_TOKEN in the example below)
  4. Click Verify and Save
3

Subscribe to fields

In the WhatsApp product settings, subscribe to message-related events (e.g., messages, statuses).
Create a .env file and set:
.env
The included whatsapp_tools.send_whatsapp_message currently references WHATSAPP_ACCESS_TOKEN/WHATSAPP_PHONE_NUMBER_ID. Ensure these are set. If you see mismatched names in your local template (e.g., WHATSAPP_TOKEN, PHONE_NUMBER_ID), set both pairs to be safe.

Minimal Webhook App (FastAPI)

Use a simple FastAPI app to receive the webhook and pass the full payload to the agent. The agent will process everything in pre_hook and send responses in post_hook.
app.py
Expose locally (optional):
Then set the ngrok HTTPS URL as the Callback URL in the Meta console.

Creating the Agent

agent.py
The pre_hook runs before invoking the agent. It extracts metadata from the webhook, builds a prompt from the incoming message, persists the inbound event, and sets values in span.input for the agent to use during inference.
agent.py
The post_hook runs after obtaining the agent’s response. It sends the text via WhatsApp and persists the outbound event to JSONL.
agent.py

Optional: system prompt example

Optional: Timbal KB persistence and history

If you prefer using Timbal Knowledge Bases instead of JSONL, you can import and query records with the platform helpers. These are async functions; adapt their usage to your environment accordingly.

Media Handling Notes

  • Images: The webhook includes an image id. Use the Graph API to fetch the media URL or content using your WHATSAPP_ACCESS_TOKEN. Provide the URL or a File object to the agent if needed.
  • Audio: The webhook includes an audio id. You can fetch and transcribe audio, then pass the transcript as the prompt.
  • Documents: You receive basic metadata (e.g., filename). Decide whether to fetch and analyze content or treat as a reference.
Keep the pre_hook minimal: extract the message type, create a concise prompt, and persist the event.

Complete implementation

For a full, end-to-end reference (webhook app, helpers, and utilities), see the official WhatsApp examples in the Timbal repository:

Possible Improvements

Each WhatsApp webhook creates a separate agent instance. Persist every inbound message immediately (JSONL or KB), then wait a short debounce window. After that window, if storage shows a newer message for the same user, bail this run so the most recent instance answers with full context. No in-memory buffer is required.
  • Why it helps: avoids replying to partial context when the user is still typing or sending media.
  • Trade-offs: adds small latency; choose a short window (e.g., 800–1500 ms).
  • Example approach (JSONL):
Use the WhatsApp Cloud API to mark messages as read. This improves UX and clears unread indicators for the user.
Call mark_as_read(message_id) right after parsing the webhook.
You can mark the message as read and briefly show a typing indicator while the agent thinks or uses tools. Optionally send a short acknowledgement, then follow up with the final answer.
  • Call this right after parsing the webhook and before long operations.
  • Keep interim notifications minimal to avoid noise.
Persist metadata for images, audio, videos, and documents so the agent can reuse them as context later (e.g., to re-attach an image or analyze it again).Suggested fields to store: id, user_phone, direction, message_type, media_id, media_url (or object store key), mime_type, caption, timestamp.
Later, query recent media by user_phone or conversation_id and inject URLs or file handles into the agent context as needed.

Running

Expose with ngrok, then verify the webhook in the Meta console. New messages should invoke your agent and send responses via WhatsApp.

Troubleshooting

  • Verify token: Ensure the value in Meta matches VERIFY_TOKEN in your app
  • Public URL: Your callback must be publicly reachable (use ngrok)
  • HTTPS: Required in production
  • Tokens: Confirm WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID are set
  • Phone ID: Ensure your app uses the correct phone number ID
  • Permissions: Check token permissions in Access Token Debugger
  • Check ORG_ID and KB_ID if using Timbal KB
  • If using JSONL, verify the JSONL file path and write permissions