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/jsonCreate 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_idstring(required) — Connected WhatsApp channel ID (for examplewzp_abc123). List channels withGET /v1/channels. -
tostring(required) — Recipient phone number in international format without+(for example6281234567890). -
to_phonestring— Alias forto. Prefertoin new integrations. -
conversation_idstring— Optional conversation ID. If omitted, Wazapin resolves or creates the conversation forto. -
typestring(required) — Message type. See the table below for supported values and requiredcontentfields. -
contentobject(required) — Payload for the selectedtype. 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 variable — index 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.
Related
- Sending overview — session vs template window
- Channel support matrix — official vs unofficial behavior
- Webhooks — inbound replies and delivery events
- SDK messages — TypeScript helpers and builders