Webhooks
Agent-TS is Kapso-first for WhatsApp messaging. It keeps a compatibility webhook for external client testing, but production traffic should use Kapso / Meta webhooks.
Endpoints
Compatibility Webhook
POST /api/v1/external/whatsapp
For local testing or temporary bridge scenarios.
Meta Business API Webhook
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,).
Response Format
{
"answer": "Your balance is 5,000 SLE",
"status": "success"
}
Kapso-first messaging
Kapso is the supported WhatsApp transport for:
- free-text conversations
- interactive list menus
- reply buttons for confirmations and navigation
- WhatsApp Flows for structured data capture and PIN/auth steps
- template messages from internal services when approved by WhatsApp
Meta WhatsApp Business API
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?" }
}]
}
}]
}]
}
Meta Developer Console Setup
- 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
messageswebhook field
Payload Fields Reference
| Field | Type | Required | Description |
|---|---|---|---|
event | string | Yes | Event type (always "message") |
from | string | Yes | Sender identifier |
phoneE164 | string | Yes | Phone in E.164 format |
message | string | No | Text message content |
messageType | string | No | Message type (text, image, etc.) |
hasMedia | boolean | No | Whether message has media |
media | object | No | Media data (if hasMedia) |
media.data | string | No | Base64 with data URI prefix |
media.mimetype | string | No | MIME type |
media.filename | string | No | Original filename |
media.size | number | 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": "smartpay-agent",
"version": "1.0.0",
"activeConversations": 5
}
Testing
Test Compatibility 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 Compatibility 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
Input Sanitization
All incoming messages are sanitized to prevent injection attacks.
Rate Limiting
30 requests per minute per phone number by default.
Webhook Verification
Meta webhooks are verified using the configured verify token.
HTTPS Required
Production webhook endpoints must use HTTPS.