---
title: "Send a message"
description: "Send a WhatsApp message to a recipient through a connected channel."
---

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

# Send a message

Send a WhatsApp message to a recipient. Pick a `type`, then fill `content` with the fields for that message shape.

Unlike Meta's Cloud API (`messaging_product`, `phone_number_id`), Wazapin uses a channel-centric body: `channel_id`, `to`, `type`, and `content`.

## Supported message types

- **text** — Plain text with optional quoted reply
- **image** — Images with optional caption
- **video** — Videos with optional caption
- **audio** — Audio files and voice notes
- **document** — PDFs and files with optional caption and filename
- **sticker** — WebP stickers
- **location** — Map pin with coordinates
- **location_request** — Ask the user to share their location
- **contact** — Contact card (vCard-style)
- **template** — Approved Meta templates (body, header, buttons)
- **buttons** — Interactive quick-reply buttons (up to 3)
- **list** — Interactive list picker
- **reaction** — Emoji reaction on an existing message
- **mark_as_read** — Mark a provider message as read

:::info
Outside the 24-hour customer service window on official channels, business-initiated outreach must use an approved **template**. See [Send template](/getting-started/send-template).
:::

## Authentication

```http
X-Api-Key: YOUR_API_KEY
Content-Type: application/json
```

Create keys in the [dashboard](https://app.wazapin.com). See [Authentication](/api/authentication).

## Example request

## Request body

- **`channel_id`** `string` *(required)* — Connected WhatsApp channel ID (for example `wzp_abc123`). List channels with `GET /v1/channels`.

- **`to`** `string` *(required)* — Recipient phone number in international format without `+` (for example `6281234567890`).

- **`to_phone`** `string` — Alias for `to`. Prefer `to` in new integrations.

- **`conversation_id`** `string` — Optional conversation ID. If omitted, Wazapin resolves or creates the conversation for `to`.

- **`type`** `string` *(required)* — Message type. See the table below for supported values and required `content` fields.

- **`content`** `object` *(required)* — Payload for the selected `type`. Shape changes per message type — see [Content by type](#content-by-type).

## Supported `type` values

| `type` | Required `content` fields | Notes |
| --- | --- | --- |
| `text` | `body` or `text` | `content.text` can be a string or `{ "body": "..." }`. Optional `reply_to.id`. |
| `image` | `media_url` | Optional `caption`. |
| `video` | `media_url` | Optional `caption`. |
| `audio` | `media_url` | AAC, AMR, MP3, M4A, OGG. Max 16 MB. |
| `document` | `media_url` | Optional `caption`, `file_name`. Max 100 MB. |
| `sticker` | `sticker_url` or `media_url` | WebP. Static ≤ 100 KB, animated ≤ 500 KB. |
| `template` | `template.name`, `template.language.code` | Often needs `template.components` for variables. |
| `buttons` | `body`, `buttons` | Up to 3 `{ id, title }` buttons. |
| `list` | `body`, `button_text`, `sections` | Sections contain `rows` with `id`, `title`, optional `description`. |
| `location` | `name`, `address`, `latitude`, `longitude` | Decimal degrees. |
| `location_request` | `body` or `text` | Prompts the user to share location. |
| `contact` | `full_name`, `phone` | Optional `organization`. |
| `reaction` | `message_id`, `reaction` | Optional `from_me`, `participant`. |
| `mark_as_read` | `message_id` | Marks a provider message as read. |

## Media field aliases

For `image`, `video`, `audio`, and `document`, Wazapin accepts compatibility aliases:

| Canonical field | Accepted aliases | Notes |
| --- | --- | --- |
| `media_url` | `media`, `url` | Prefer `media_url` in public integrations. |
| `media_type` | `mediaType`, `type`, `mediatype` | Usually optional — inferred from top-level `type`. |
| `file_name` | `fileName` | Mainly for `document`. |

## Content by type

### Text

| Field | Required | Description |
| --- | --- | --- |
| `content.body` | Yes* | Message text. Prefer for new integrations. |
| `content.text` | Yes* | String or `{ "body": "..." }` — compatibility alias. |
| `content.reply_to.id` | No | Provider message ID to quote-reply. |
| `content.reply_to.participant` | No | Required in some group or multi-device contexts. |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "text",
  "content": {
"body": "Hello! Your order #12345 has been shipped."
  }
}
```

Guide: [Send text](/getting-started/send-text)

### Image

| Field | Required | Description |
| --- | --- | --- |
| `content.media_url` | Yes | Public HTTPS URL (JPEG, PNG). Max 5 MB. |
| `content.caption` | No | Caption under the image. |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "image",
  "content": {
"media_url": "https://cdn.example.com/product.jpg",
"caption": "Check out our new product!"
  }
}
```

Guide: [Send image](/getting-started/send-image)

### Video

| Field | Required | Description |
| --- | --- | --- |
| `content.media_url` | Yes | Public HTTPS URL. |
| `content.caption` | No | Optional caption. |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "video",
  "content": {
"media_url": "https://cdn.example.com/demo.mp4",
"caption": "Product demo"
  }
}
```

Guide: [Send video](/getting-started/send-video)

### Audio

| Field | Required | Description |
| --- | --- | --- |
| `content.media_url` | Yes | Public HTTPS URL (AAC, AMR, MP3, M4A, OGG). Max 16 MB. |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "audio",
  "content": {
"media_url": "https://cdn.example.com/voice-note.ogg"
  }
}
```

Guide: [Send audio](/getting-started/send-audio)

### Document

| Field | Required | Description |
| --- | --- | --- |
| `content.media_url` | Yes | Public HTTPS URL. Max 100 MB. |
| `content.file_name` | No | Display name in chat (recommended). |
| `content.caption` | No | Optional caption. |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "document",
  "content": {
"media_url": "https://cdn.example.com/invoice.pdf",
"file_name": "invoice-2026-001.pdf",
"caption": "Your invoice"
  }
}
```

Guide: [Send document](/getting-started/send-document)

### Template

Templates use a nested `content.template` object (Meta-compatible). You must pass **components** that match the approved template structure — not just `name` and `language`.

| Field | Required | Description |
| --- | --- | --- |
| `content.template.name` | Yes | Approved template name. |
| `content.template.language.code` | Yes | Locale (`en_US`, `id`, …). Must match exactly. |
| `content.template.components` | Often | `header`, `body`, and `button` parameters. |

:::warning
Do **not** use flat `content.template_name`. The API requires `content.template.name` and `content.template.language.code`.
:::

**Body variables only** — template body: `Hi {{1}}, your order {{2}} is on the way.`

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "order_confirmation",
  "language": { "code": "en_US" },
  "components": [
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Jessica" },
        { "type": "text", "text": "SKBUP2-4CPIG9" }
      ]
    }
  ]
}
  }
}
```

**Header image + body** — use a public HTTPS image URL Meta can fetch.

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "seasonal_promotion",
  "language": { "code": "en_US" },
  "components": [
    {
      "type": "header",
      "parameters": [
        {
          "type": "image",
          "image": { "link": "https://cdn.example.com/summer-sale.jpg" }
        }
      ]
    },
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Summer Sale" },
        { "type": "text", "text": "SUMMER25" },
        { "type": "text", "text": "25%" }
      ]
    }
  ]
}
  }
}
```

**Header text variable** — template header `Hello {{1}}`, nested inside `content.template.components`.

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "greeting_header",
  "language": { "code": "en_US" },
  "components": [
    {
      "type": "header",
      "parameters": [
        { "type": "text", "text": "Black Friday Sale" }
      ]
    },
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "November 30th" }
      ]
    }
  ]
}
  }
}
```

**URL button variable** — `index` is zero-based; `sub_type` matches the template button type.

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "limited_time_offer",
  "language": { "code": "en_US" },
  "components": [
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Mark" },
        { "type": "text", "text": "Premium Package" }
      ]
    },
    {
      "type": "button",
      "sub_type": "url",
      "index": "0",
      "parameters": [
        { "type": "text", "text": "summer2024" }
      ]
    }
  ]
}
  }
}
```

**Quick reply buttons** — use `sub_type: "quick_reply"` and `payload` parameters.

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "template",
  "content": {
"template": {
  "name": "customer_feedback",
  "language": { "code": "en_US" },
  "components": [
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Sarah" }
      ]
    },
    {
      "type": "button",
      "sub_type": "quick_reply",
      "index": "0",
      "parameters": [
        { "type": "payload", "payload": "yes_helpful" }
      ]
    },
    {
      "type": "button",
      "sub_type": "quick_reply",
      "index": "1",
      "parameters": [
        { "type": "payload", "payload": "no_not_helpful" }
      ]
    }
  ]
}
  }
}
```

Guide: [Send template](/getting-started/send-template) · SDK: [templateMessage()](/sdk/templates)

### Buttons

| Field | Required | Description |
| --- | --- | --- |
| `content.body` | Yes | Message text above buttons. |
| `content.buttons` | Yes | Array of `{ "id", "title" }` (max 3). |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "buttons",
  "content": {
"body": "Would you like to proceed with your order?",
"buttons": [
  { "id": "btn_yes", "title": "Yes" },
  { "id": "btn_no", "title": "No" }
]
  }
}
```

Guide: [Send buttons](/getting-started/send-buttons)

### List

| Field | Required | Description |
| --- | --- | --- |
| `content.body` | Yes | Message text. |
| `content.button_text` | Yes | Label on the list open button. |
| `content.sections` | Yes | Sections with `title` and `rows` (`id`, `title`, optional `description`). |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "list",
  "content": {
"body": "Choose an option",
"button_text": "View menu",
"sections": [
  {
    "title": "Support",
    "rows": [
      { "id": "track", "title": "Track order", "description": "Check status" },
      { "id": "help", "title": "Contact CS", "description": "Get help" }
    ]
  }
]
  }
}
```

Guide: [Send list](/getting-started/send-list)

### Location

| Field | Required | Description |
| --- | --- | --- |
| `content.name` | Yes | Place name. |
| `content.address` | Yes | Address label. |
| `content.latitude` | Yes | Latitude (decimal degrees). |
| `content.longitude` | Yes | Longitude (decimal degrees). |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "location",
  "content": {
"name": "Wazapin Office",
"address": "Jakarta Selatan",
"latitude": -6.260697,
"longitude": 106.781616
  }
}
```

Guide: [Send location](/getting-started/send-location)

### Contact

| Field | Required | Description |
| --- | --- | --- |
| `content.full_name` | Yes | Display name. |
| `content.phone` | Yes | Contact phone number. |
| `content.organization` | No | Company or group name. |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "contact",
  "content": {
"full_name": "Wazapin Support",
"phone": "628111111111",
"organization": "Wazapin"
  }
}
```

Guide: [Send contact](/getting-started/send-contact)

### Reaction

| Field | Required | Description |
| --- | --- | --- |
| `content.message_id` | Yes | Provider message ID from webhook or `GET /v1/messages/{id}`. |
| `content.reaction` | Yes | Emoji (empty string may remove reaction on some providers). |
| `content.from_me` | No | Defaults to `true`. |
| `content.participant` | No | Required in some group contexts. |

```json
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "reaction",
  "content": {
"message_id": "wamid.HBgNMTU1NTE...",
"reaction": "👍"
  }
}
```

Guide: [Send reaction](/getting-started/send-reaction)

## Response

On success the API returns `201 Created` with a queued message record:

```json
{
  "data": {
"id": "9f1fd66d-c37a-4b50-a8c2-b4dca523f9c8",
"channel_id": "wzp_abc123",
"to_phone": "6281234567890",
"type": "text",
"status": "queued",
"created_at": "2026-03-04T06:20:10Z",
"updated_at": "2026-03-04T06:20:10Z"
  }
}
```

Poll delivery with `GET /v1/messages/{messageID}/status`. See [Message lifecycle](/api/message-lifecycle-idempotency).

## Advanced types

The API also supports `cta_url`, `media_carousel`, `product_carousel`, `single_product`, `multi_product`, `catalog`, `link`, `poll`, `address`, `presence`, `edit_message`, and `delete_message`. These are available in the implementation but not yet covered by getting-started guides.

## Related

- [Sending overview](/getting-started/sending-overview) — session vs template window
- [Channel support matrix](/api/channel-support) — official vs unofficial behavior
- [Webhooks](/api/webhooks) — inbound replies and delivery events
- [SDK messages](/sdk/messages) — TypeScript helpers and builders

Source: https://docs.wazapin.id/api-reference/messages/send-a-message/index.mdx
