> ## Documentation Index
> Fetch the complete documentation index at: https://docs.buildwithchirp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Receiving WhatsApp Messages

> Handle incoming WhatsApp messages from your customers

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:

```json icon="json" title="Webhook Payload" theme={null}
{
  "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",
      "occurredAt": "2024-01-15T12:00:00.000Z",
      "source": "cloud_api",
      "isHistoricalImport": false,
      "conversationWindow": {
        "id": "wacw_abc123",
        "originType": "user_initiated",
        "expiresAt": "2024-01-16T12:00:00.000Z"
      }
    },
    "app": {
      "id": "app_2DbBs7GWhGvVNJGrDXr5RG0mBWI"
    }
  }
}
```

### Message Fields Reference

| Field                | Type    | Description                                                      |
| -------------------- | ------- | ---------------------------------------------------------------- |
| `id`                 | string  | Unique Chirp message identifier                                  |
| `from`               | string  | Sender phone number (E.164 format)                               |
| `to`                 | string  | Recipient phone number (E.164 format)                            |
| `type`               | string  | Message type (see [Message Types](#message-types))               |
| `direction`          | string  | `inbound` or `outbound`                                          |
| `text`               | string  | Message text content (for text messages)                         |
| `receivedAt`         | string  | ISO 8601 timestamp when received                                 |
| `occurredAt`         | string  | Canonical timestamp when the message event occurred              |
| `source`             | string  | Message source: `cloud_api`, `business_app`, or `history_import` |
| `isHistoricalImport` | boolean | Whether this is a historical import from coexistence mode        |
| `conversationWindow` | object  | 24-hour messaging window context                                 |

<Info>
  The `source` field indicates how the message was created:

  * `cloud_api` - Real-time message via WhatsApp Cloud API
  * `business_app` - Message sent via WhatsApp Business App (coexistence mode)
  * `history_import` - Historical message imported during coexistence onboarding (up to 6 months)
</Info>

## Message Type Payloads

### Text Messages

```json icon="json" title="Text Message" theme={null}
{
  "type": "text",
  "text": "Hello, I need help!",
  "context": null
}
```

### Media Messages (Image, Video, Audio, Document)

```json icon="json" title="Image Message" theme={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:

```json icon="json" title="Voice Note" theme={null}
{
  "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

```json icon="json" title="Sticker Message" theme={null}
{
  "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

```json icon="json" title="Location Message" theme={null}
{
  "type": "location",
  "location": {
    "latitude": "37.7749",
    "longitude": "-122.4194",
    "name": "My Store",
    "address": "123 Main St, San Francisco, CA"
  }
}
```

### Contact Messages

Contacts are shared in vCard format with full structured data:

```json icon="json" title="Contacts Message" theme={null}
{
  "type": "contacts",
  "contacts": {
    "contacts": [
      {
        "name": {
          "formattedName": "John Doe",
          "firstName": "John",
          "lastName": "Doe",
          "middleName": null,
          "prefix": null,
          "suffix": null
        },
        "phones": [
          { "phone": "+15551234567", "type": "CELL" },
          { "phone": "+15559999999", "type": "WORK" }
        ],
        "emails": [
          { "email": "john@example.com", "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

```json icon="json" title="Reaction Message" theme={null}
{
  "type": "reaction",
  "reaction": {
    "messageId": "wamid.HBgLMTUwMzMwNzk5NzQ...",
    "emoji": "👍"
  }
}
```

<Note>
  An empty `emoji` string indicates the user removed their reaction.
</Note>

### Interactive Responses

When users click buttons or select list items:

```json icon="json" title="Button Reply" theme={null}
{
  "type": "interactive",
  "interactive": {
    "type": "button_reply",
    "buttonReply": {
      "id": "support_tech",
      "title": "Technical Help"
    },
    "listReply": null
  }
}
```

```json icon="json" title="List Reply" theme={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)

```json icon="json" title="Order Message" theme={null}
{
  "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:

```json icon="json" title="Number Change Event" theme={null}
{
  "type": "system",
  "system": {
    "body": "User's phone number has changed",
    "type": "customer_changed_number",
    "identity": null
  }
}
```

### Reply Context

When a user replies to a specific message:

```json icon="json" title="Reply with Context" theme={null}
{
  "type": "text",
  "text": "Yes, that works for me",
  "context": {
    "replyToMessageId": "wamid.HBgLMTUwMzMwNzk5NzQ...",
    "from": "+15551234567"
  }
}
```

## Message Handler Example

```javascript icon="javascript" title="Message Handler" theme={null}
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:

```bash icon="terminal" title="Reply to Message" theme={null}
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

<Info>
  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](/whatsapp/sending-messages/templates) to re-initiate contact.
</Info>

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`)

## Testing Incoming Messages

You have two options for testing incoming WhatsApp messages without sending a real one:

* **Browser-based [Playground](/concepts/playground)** -- open the Playground in your dashboard, pick a "User Phone Number", and send. Your webhook fires in real-time.
* **API-based simulation** -- `POST /v1/playground/whatsapp/inbound` with a test API key. Useful for end-to-end tests, CI, and local development. Today this supports text messages only. See [Simulating Inbound Messages](/concepts/simulating-inbound-messages).

## Best Practices

1. **Respond quickly** - Users expect fast responses on WhatsApp
2. **Acknowledge receipt** - Send a quick reply or reaction to let users know you received their message
3. **Handle all types** - Gracefully handle message types you don't expect
4. **Download media promptly** - Media URLs may expire; download files you need to keep
5. **Track conversations** - Store conversation history for context
6. **Use reactions** - React to messages with emoji for quick acknowledgment
