Skip to content

Send a message

Send a WhatsApp message to a recipient through a connected channel.

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

Authentication

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

Create keys in the dashboard. See Authentication.

Example request

curl "https://api.wazapin.com/v1/messages" \
	--request POST \
	--header "X-Api-Key: $WAZAPIN_API_KEY" \
	--json '{
		"channel_id": "wzp_abc123",
		"to": "6281234567890",
		"type": "text",
		"content": {
				"text": "Hello from Wazapin!"
		}
	}'
const response = await fetch("https://api.wazapin.com/v1/messages", {
  method: "POST",
  body: JSON.stringify({
      "channel_id": "wzp_abc123",
      "to": "6281234567890",
      "type": "text",
      "content": {
        "text": "Hello from Wazapin!"
      }
    }),
});
const data = await response.json();
console.log(data);
import os
import requests

response = requests.post(
    "https://api.wazapin.com/v1/messages",
    headers={
        "X-Api-Key": os.environ["WAZAPIN_API_KEY"],
        "Content-Type": "application/json",
    },
    json={
                "channel_id": "wzp_abc123",
                "to": "6281234567890",
                "type": "text",
                "content": {
                        "text": "Hello from Wazapin!"
                }
        },
    timeout=30,
)
print(response.status_code, response.json())

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.

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

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.
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "text",
  "content": {
    "body": "Hello! Your order #12345 has been shipped."
  }
}

Guide: Send text

Field Required Description
content.media_url Yes Public HTTPS URL (JPEG, PNG). Max 5 MB.
content.caption No Caption under the image.
{
  "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

Field Required Description
content.media_url Yes Public HTTPS URL.
content.caption No Optional caption.
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "video",
  "content": {
    "media_url": "https://cdn.example.com/demo.mp4",
    "caption": "Product demo"
  }
}

Guide: Send video

Field Required Description
content.media_url Yes Public HTTPS URL (AAC, AMR, MP3, M4A, OGG). Max 16 MB.
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "audio",
  "content": {
    "media_url": "https://cdn.example.com/voice-note.ogg"
  }
}

Guide: Send audio

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.
{
  "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

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.

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

{
  "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.

{
  "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.

{
  "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 variableindex is zero-based; sub_type matches the template button type.

{
  "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.

{
  "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 · SDK: templateMessage()

Field Required Description
content.body Yes Message text above buttons.
content.buttons Yes Array of { "id", "title" } (max 3).
{
  "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

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).
{
  "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

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).
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "location",
  "content": {
    "name": "Wazapin Office",
    "address": "Jakarta Selatan",
    "latitude": -6.260697,
    "longitude": 106.781616
  }
}

Guide: Send location

Field Required Description
content.full_name Yes Display name.
content.phone Yes Contact phone number.
content.organization No Company or group name.
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "contact",
  "content": {
    "full_name": "Wazapin Support",
    "phone": "628111111111",
    "organization": "Wazapin"
  }
}

Guide: Send contact

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.
{
  "channel_id": "wzp_abc123",
  "to": "6281234567890",
  "type": "reaction",
  "content": {
    "message_id": "wamid.HBgNMTU1NTE...",
    "reaction": "👍"
  }
}

Guide: Send reaction

Response

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

{
  "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.

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.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close