When users send messages to your WhatsApp Business number, Chirp delivers them to your application via webhooks.
Message Types
You can receive various types of incoming messages:
| Type | Description |
|---|
text | Plain text messages |
image | Images with optional captions |
video | Video files |
audio | Voice messages and audio files |
document | PDF, Word, and other document files |
sticker | WhatsApp stickers |
location | Shared location pins |
contacts | Shared contact cards (vCard format) |
reaction | Emoji reactions to your messages |
interactive | Button/list selection responses |
order | Product orders (WhatsApp Commerce) |
system | System events (number changes, identity updates) |
Webhook Payload Structure
When you receive a message webhook, the payload follows this structure:
{
"event": "messages.whatsapp.received",
"timestamp": "2024-01-15T12:00:00.000Z",
"data": {
"message": {
"id": "msg_wa_2DbBs7GWhGvVNJGrDXr5RG0mBWI",
"from": "+15559876543",
"to": "+15551234567",
"type": "text",
"direction": "inbound",
"text": "Hello, I have a question about my order",
"receivedAt": "2024-01-15T12:00:00.000Z",
"conversationWindow": {
"id": "wacw_abc123",
"originType": "user_initiated",
"expiresAt": "2024-01-16T12:00:00.000Z"
}
},
"app": {
"id": "app_2DbBs7GWhGvVNJGrDXr5RG0mBWI"
}
}
}
Message Type Payloads
Text Messages
{
"type": "text",
"text": "Hello, I need help!",
"context": null
}
{
"type": "image",
"text": null,
"media": {
"url": "https://cdn.buildwithchirp.com/media/abc123.jpg",
"id": "med_2DbBs7GWhGvVNJGrDXr5RG0mBWI",
"mimeType": "image/jpeg",
"caption": "Here's a photo of the issue",
"fileSize": 102400,
"filename": null,
"sha256": "a1b2c3d4..."
}
}
Audio Messages
Audio messages have additional voice note detection:
{
"type": "audio",
"audio": {
"url": "https://cdn.buildwithchirp.com/media/voice123.opus",
"id": "med_audio123",
"mimeType": "audio/ogg; codecs=opus",
"fileSize": 51200,
"isVoiceNote": true,
"sha256": "x1y2z3..."
}
}
Sticker Messages
{
"type": "sticker",
"sticker": {
"url": "https://cdn.buildwithchirp.com/media/sticker123.webp",
"id": "med_sticker123",
"mimeType": "image/webp",
"fileSize": 25600,
"isAnimated": false,
"sha256": "s1t2u3..."
}
}
Location Messages
{
"type": "location",
"location": {
"latitude": "37.7749",
"longitude": "-122.4194",
"name": "My Store",
"address": "123 Main St, San Francisco, CA"
}
}
Contacts are shared in vCard format with full structured data:
{
"type": "contacts",
"contacts": {
"contacts": [
{
"name": {
"formattedName": "John Doe",
"firstName": "John",
"lastName": "Doe",
"middleName": null,
"prefix": null,
"suffix": null
},
"phones": [
{ "phone": "+15551234567", "type": "CELL", "waId": "15551234567" },
{ "phone": "+15559999999", "type": "WORK", "waId": null }
],
"emails": [
{ "email": "[email protected]", "type": "WORK" }
],
"addresses": [
{
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "United States",
"countryCode": "US",
"type": "WORK"
}
],
"org": {
"company": "Acme Inc",
"department": "Engineering",
"title": "Senior Engineer"
},
"urls": [
{ "url": "https://example.com", "type": "WORK" }
],
"birthday": "1990-01-15"
}
]
}
}
Reaction Messages
{
"type": "reaction",
"reaction": {
"messageId": "wamid.HBgLMTUwMzMwNzk5NzQ...",
"emoji": "👍"
}
}
An empty emoji string indicates the user removed their reaction.
Interactive Responses
When users click buttons or select list items:
{
"type": "interactive",
"interactive": {
"type": "button_reply",
"buttonReply": {
"id": "support_tech",
"title": "Technical Help"
},
"listReply": null
}
}
{
"type": "interactive",
"interactive": {
"type": "list_reply",
"buttonReply": null,
"listReply": {
"id": "row_option_1",
"title": "Option 1",
"description": "First option description"
}
}
}
Order Messages (WhatsApp Commerce)
{
"type": "order",
"order": {
"catalogId": "1234567890",
"text": "Please ship to my home address",
"productItems": [
{
"productRetailerId": "sku_123",
"quantity": "2",
"itemPrice": "1999",
"currency": "USD"
}
]
}
}
System Messages
System messages notify you of account-level events:
{
"type": "system",
"system": {
"body": "User's phone number has changed",
"type": "customer_changed_number",
"identity": null,
"waId": "15551234567",
"newWaId": "15559876543"
}
}
Reply Context
When a user replies to a specific message:
{
"type": "text",
"text": "Yes, that works for me",
"context": {
"replyToMessageId": "wamid.HBgLMTUwMzMwNzk5NzQ...",
"from": "+15551234567"
}
}
Message Handler Example
function handleWebhook(payload) {
const { message } = payload.data;
switch (message.type) {
case "text":
handleTextMessage(message);
break;
case "image":
case "video":
case "audio":
case "document":
handleMediaMessage(message);
break;
case "sticker":
handleStickerMessage(message);
break;
case "location":
handleLocationMessage(message);
break;
case "contacts":
handleContactsMessage(message);
break;
case "reaction":
handleReaction(message);
break;
case "interactive":
handleInteractiveResponse(message);
break;
case "order":
handleOrder(message);
break;
}
}
function handleTextMessage(message) {
console.log(`Received from ${message.from}: ${message.text}`);
}
function handleMediaMessage(message) {
console.log(`Received ${message.type} from ${message.from}`);
// Download media from message.media.url
}
function handleInteractiveResponse(message) {
const { interactive } = message;
if (interactive.type === "button_reply") {
console.log(`Button selected: ${interactive.buttonReply.id}`);
} else if (interactive.type === "list_reply") {
console.log(`List item selected: ${interactive.listReply.id}`);
}
}
function handleReaction(message) {
const { reaction } = message;
if (reaction.emoji) {
console.log(`User reacted with ${reaction.emoji}`);
} else {
console.log(`User removed their reaction`);
}
}
Replying to Messages
To reply to an incoming message, use the sender’s number as the to field and optionally include replyTo with the message ID:
curl -X POST https://api.buildwithchirp.com/v1/whatsapp/messages \
-H "Authorization: Bearer YOUR_APP_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "+15551234567",
"to": "+15559876543",
"type": "text",
"text": {
"body": "Thanks for reaching out! How can I help?"
},
"replyTo": "msg_wa_2DbBs7GWhGvVNJGrDXr5RG0mBWI"
}'
24-Hour Messaging Window
When a user messages you, you have a 24-hour window to respond with any message type. After this window closes, you must use Template Messages to re-initiate contact.
The conversationWindow object in the webhook payload tells you:
expiresAt - When the window closes
originType - How the conversation started (user_initiated, business_initiated, referral_conversion)
Best Practices
- Respond quickly - Users expect fast responses on WhatsApp
- Acknowledge receipt - Send a quick reply or reaction to let users know you received their message
- Handle all types - Gracefully handle message types you don’t expect
- Download media promptly - Media URLs may expire; download files you need to keep
- Track conversations - Store conversation history for context
- Use reactions - React to messages with emoji for quick acknowledgment