Pipes.bot

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
  • Pool-number-scoped connections receive only messages from the specified pool number
  • 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

FieldTypeDescription
messageIdstringUnique message identifier
conversationIdstringPipes.bot conversation ID (use this for replies)
poolNumberIdstringPool number that received the message
poolNumberPhoneNumberstringPool number's phone number
fromNumberstringSender's WhatsApp number
fromNamestring?Sender's WhatsApp profile name
textstringText content or caption
timestampstringISO 8601 timestamp
labelstring?Activation label, if one was set
typestringMessage type (see sections below)
bodystringSame 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"
  }
}
FieldTypeDescription
latitudenumberLatitude coordinate
longitudenumberLongitude coordinate
namestring?Location name
addressstring?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": "👍"
  }
}
FieldTypeDescription
messageIdstringID of the message being reacted to
emojistring?The emoji. Absent when the reaction has been removed

Unrecognized types

Messages that don't match a modeled type are delivered with data.type set to the raw WhatsApp type string (e.g. "order", "system") and empty text/body. The original WhatsApp payload is not forwarded over the WebSocket — only the type and empty text arrive. Switch on data.type and treat any unknown value as an unmodeled message.

Button

Template quick-reply taps arrive as type: "button" with an interactive object. The button's payload is delivered as interactive.id and its label as interactive.title (and body/text):

{
  "type": "button",
  "body": "Confirm",
  "text": "Confirm",
  "interactive": {
    "type": "button_reply",
    "id": "confirm_order",
    "title": "Confirm"
  }
}

Interactive

Replies to interactive messages (button_reply, list_reply, nfm_reply) arrive as type: "interactive" with an interactive object:

{
  "type": "interactive",
  "interactive": {
    "type": "button_reply",
    "id": "confirm",
    "title": "Confirm"
  }
}
FieldTypeDescription
typestringbutton_reply, list_reply, nfm_reply, or the raw subtype for unknown interactive messages
idstring?Selected option ID (or the button payload for type: "button" messages)
titlestring?Selected option title
descriptionstring?Selected list-row description
payloadstring?Template button payload
responseJsonstring?Raw JSON response for Flows (nfm_reply)

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;

    case "button":
    case "interactive":
      console.log("Interactive:", msg.interactive.type, msg.interactive.title ?? msg.interactive.payload);
      break;

    default:
      console.log("Unrecognized type:", msg.type); // raw WhatsApp type, empty text
  }
});

On this page