Message Types
All WhatsApp message types delivered over the WebSocket connection.
Every incoming WhatsApp message arrives as a whatsapp_message event. The data.type field determines the shape of the payload.
Which messages you receive depends on your connection scope:
- User-scoped connections receive all messages from all your activated pool numbers
- Conversation-scoped connections receive only messages matching the specified conversationId
Envelope structure
All messages share this common structure:
{
"type": "whatsapp_message",
"data": {
"messageId": "msg_abc123",
"conversationId": "conv_xyz789",
"poolNumberId": "pool_123",
"poolNumberPhoneNumber": "+15551234567",
"fromNumber": "+15559876543",
"fromName": "Jane Doe",
"text": "Message text or caption",
"timestamp": "2025-01-15T10:30:00.000Z",
"label": "support",
"type": "text",
"body": "Message text or caption"
}
}Common fields
| Field | Type | Description |
|---|---|---|
messageId | string | Unique message identifier |
conversationId | string | Pipes.bot conversation ID (use this for replies) |
poolNumberId | string | Pool number that received the message |
poolNumberPhoneNumber | string | Pool number's phone number |
fromNumber | string | Sender's WhatsApp number |
fromName | string? | Sender's WhatsApp profile name |
text | string | Text content or caption |
timestamp | string | ISO 8601 timestamp |
label | string? | Activation label, if one was set |
type | string | Message type (see sections below) |
body | string | Same as text — the message body |
Additional fields appear depending on the message type.
Text
{
"type": "text",
"body": "Hello from WhatsApp!",
"text": "Hello from WhatsApp!"
}The simplest message type. text and body both contain the message content.
Image
{
"type": "image",
"body": "Check this out",
"text": "Check this out",
"media": {
"mediaId": "aBcDeFgHiJkLmNoPqRs1t",
"downloadUrl": "/v1/media/download/aBcDeFgHiJkLmNoPqRs1t",
"mimeType": "image/jpeg",
"byteSize": 245120
}
}Images include an optional caption in body/text. See Media Handling for download details.
Audio
{
"type": "audio",
"media": {
"mediaId": "aBcDeFgHiJkLmNoPqRs1t",
"downloadUrl": "/v1/media/download/aBcDeFgHiJkLmNoPqRs1t",
"mimeType": "audio/ogg",
"byteSize": 52480
}
}Includes voice notes and audio file attachments.
Video
{
"type": "video",
"body": "Watch this",
"text": "Watch this",
"media": {
"mediaId": "aBcDeFgHiJkLmNoPqRs1t",
"downloadUrl": "/v1/media/download/aBcDeFgHiJkLmNoPqRs1t",
"mimeType": "video/mp4",
"byteSize": 1048576
}
}Videos include an optional caption.
Document
{
"type": "document",
"body": "Here's the invoice",
"text": "Here's the invoice",
"media": {
"mediaId": "aBcDeFgHiJkLmNoPqRs1t",
"downloadUrl": "/v1/media/download/aBcDeFgHiJkLmNoPqRs1t",
"mimeType": "application/pdf",
"fileName": "invoice.pdf",
"byteSize": 102400
}
}Documents include the original fileName and an optional caption.
Sticker
{
"type": "sticker",
"media": {
"mediaId": "aBcDeFgHiJkLmNoPqRs1t",
"downloadUrl": "/v1/media/download/aBcDeFgHiJkLmNoPqRs1t",
"mimeType": "image/webp",
"byteSize": 25600
}
}Location
{
"type": "location",
"location": {
"latitude": 37.7749,
"longitude": -122.4194,
"name": "San Francisco",
"address": "San Francisco, CA, USA"
}
}| Field | Type | Description |
|---|---|---|
latitude | number | Latitude coordinate |
longitude | number | Longitude coordinate |
name | string? | Location name |
address | string? | Full address string |
Location messages do not include a media object.
Contacts
{
"type": "contacts",
"contacts": [
{
"name": { "formatted_name": "Jane Doe" },
"phones": [{ "phone": "+15559876543", "type": "CELL" }]
}
]
}The contacts array follows WhatsApp's native contact structure. Each contact may include name, phones, emails, addresses, org, and urls fields.
Reaction
{
"type": "reaction",
"reaction": {
"messageId": "msg_original123",
"emoji": "👍"
}
}| Field | Type | Description |
|---|---|---|
messageId | string | ID of the message being reacted to |
emoji | string? | The emoji. Absent when the reaction has been removed |
Unsupported
Messages that don't match any recognized type are delivered as type: "unsupported". The original WhatsApp payload is preserved so you can inspect it if needed.
Handling all types
ws.on("message", (raw) => {
const event = JSON.parse(raw.toString());
if (event.type !== "whatsapp_message") return;
const msg = event.data;
switch (msg.type) {
case "text":
console.log("Text:", msg.body);
break;
case "image":
case "audio":
case "video":
case "document":
case "sticker":
if (msg.media?.unavailable) {
console.log("Media unavailable");
} else {
console.log("Download:", msg.media.downloadUrl);
console.log("MIME:", msg.media.mimeType);
console.log("Size:", msg.media.byteSize, "bytes");
}
if (msg.body) console.log("Caption:", msg.body);
break;
case "location":
console.log("Location:", msg.location.latitude, msg.location.longitude);
break;
case "contacts":
console.log("Contacts:", msg.contacts.length);
break;
case "reaction":
if (msg.reaction.emoji) {
console.log("Reaction:", msg.reaction.emoji, "on", msg.reaction.messageId);
} else {
console.log("Reaction removed on", msg.reaction.messageId);
}
break;
default:
console.log("Unsupported type:", msg.type);
}
});