WhatsApp Webhooks
Agent-TS supports two webhook endpoints for receiving WhatsApp messages: one for Meta’s WhatsApp Business API and one for external clients like whatsapp-web.js.
Endpoints
External Client Webhook
POST /api/v1/external/whatsapp
For external WhatsApp clients (whatsapp-web.js, Baileys, etc.).
GET /api/v1/webhooks/whatsapp - Verification
POST /api/v1/webhooks/whatsapp - Message handling
For Meta’s official WhatsApp Business API.
External Client Integration
Text Message Payload
{
"event" : "message" ,
"message" : "What is my balance?" ,
"from" : "23279648205@c.us" ,
"phoneE164" : "+23279648205"
}
Image Message Payload
{
"event" : "message" ,
"messageType" : "image" ,
"hasMedia" : true ,
"phoneE164" : "+23279372497" ,
"message" : "Here is my ID" ,
"media" : {
"data" : "data:image/jpeg;base64,/9j/4AAQSkZJRg..." ,
"mimetype" : "image/jpeg" ,
"filename" : "id_card_front.jpg" ,
"size" : 52643
}
}
The media.data field must include the data URI prefix (data:image/jpeg;base64,).
{
"answer" : "Your balance is 5,000 SLE" ,
"status" : "success"
}
whatsapp-web.js Integration
Example client implementation:
const { Client , LocalAuth } = require ( 'whatsapp-web.js' );
const axios = require ( 'axios' );
const client = new Client ({
authStrategy: new LocalAuth ()
});
const AGENT_URL = 'http://localhost:8000/api/v1/external/whatsapp' ;
client . on ( 'message' , async ( msg ) => {
// Skip group messages
if ( msg . from . includes ( '@g.us' )) return ;
const payload = {
event: 'message' ,
from: msg . from ,
phoneE164: `+ ${ msg . from . split ( '@' )[ 0 ] } ` ,
message: msg . body ,
messageType: msg . type ,
hasMedia: msg . hasMedia
};
// Handle media messages
if ( msg . hasMedia ) {
const media = await msg . downloadMedia ();
payload . media = {
data: `data: ${ media . mimetype } ;base64, ${ media . data } ` ,
mimetype: media . mimetype ,
filename: media . filename ,
size: Buffer . from ( media . data , 'base64' ). length
};
}
try {
const response = await axios . post ( AGENT_URL , payload );
if ( response . data . answer ) {
await msg . reply ( response . data . answer );
}
} catch ( error ) {
console . error ( 'Agent error:' , error );
await msg . reply ( 'Sorry, I encountered an error. Please try again.' );
}
});
client . initialize ();
Webhook Verification
Meta sends a GET request to verify your webhook:
GET /api/v1/webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=YOUR_TOKEN&hub.challenge=CHALLENGE
The agent responds with the challenge if the verify token matches WHATSAPP_VERIFY_TOKEN.
Incoming Message Payload
{
"object" : "whatsapp_business_account" ,
"entry" : [{
"id" : "WHATSAPP_BUSINESS_ACCOUNT_ID" ,
"changes" : [{
"value" : {
"messaging_product" : "whatsapp" ,
"metadata" : {
"display_phone_number" : "15551234567" ,
"phone_number_id" : "PHONE_NUMBER_ID"
},
"contacts" : [{
"profile" : { "name" : "John Doe" },
"wa_id" : "23279648205"
}],
"messages" : [{
"from" : "23279648205" ,
"id" : "wamid.xxx" ,
"timestamp" : "1234567890" ,
"type" : "text" ,
"text" : { "body" : "What is my balance?" }
}]
}
}]
}]
}
Go to Meta for Developers
Create or select your app
Add WhatsApp product
Configure webhook URL: https://your-domain.com/api/v1/webhooks/whatsapp
Set verify token: Same as WHATSAPP_VERIFY_TOKEN
Subscribe to messages webhook field
Payload Fields Reference
Field Type Required Description eventstring Yes Event type (always “message”) fromstring Yes Sender identifier phoneE164string Yes Phone in E.164 format messagestring No Text message content messageTypestring No Message type (text, image, etc.) hasMediaboolean No Whether message has media mediaobject No Media data (if hasMedia) media.datastring No Base64 with data URI prefix media.mimetypestring No MIME type media.filenamestring No Original filename media.sizenumber No File size in bytes
Error Responses
Validation Error
{
"error" : "Missing required field: phoneE164" ,
"status" : "error"
}
Agent Error
{
"error" : "Internal agent error" ,
"status" : "error"
}
Rate Limited
{
"error" : "Too many requests" ,
"status" : "error" ,
"retryAfter" : 60
}
Health Check
Verify the agent is running:
curl http://localhost:8000/health
Response:
{
"status" : "healthy" ,
"service" : "olive-agent" ,
"version" : "1.0.0" ,
"activeConversations" : 5
}
Testing
Test Text Message
curl -X POST http://localhost:8000/api/v1/external/whatsapp \
-H "Content-Type: application/json" \
-d '{
"event": "message",
"message": "what is my balance?",
"from": "23279648205@c.us",
"phoneE164": "+23279648205"
}'
Test Transfer
curl -X POST http://localhost:8000/api/v1/external/whatsapp \
-H "Content-Type: application/json" \
-d '{
"event": "message",
"message": "send 5000 to CARD0001",
"from": "23279648205@c.us",
"phoneE164": "+23279648205"
}'
Security Considerations
All incoming messages are sanitized to prevent injection attacks.
30 requests per minute per phone number by default.
Meta webhooks are verified using the configured verify token.
Production webhook endpoints must use HTTPS.
Next Steps