All WhatsApp messages go through one endpoint, POST /whatsapp/message/sendbulk, where messageType selects the payload. This page shows how to send approved templates and session messages, manage templates, and track delivery per recipient.
Before you send#
- Connect a WhatsApp Business Account. Onboarding uses Meta Embedded Signup.
POST /whatsapp/generate-linkreturns an encrypted signup link (HTTP201) for your account; the business admin opens it and completes Meta's flow (business account, phone number verification and display name). Our team supports you through Meta's review. - Have an approved template for any message that starts a conversation or is sent outside the customer service window.
- Hold opt-in from each recipient, as Meta's policies require.
The customer service window#
When a customer messages you, a 24-hour customer service window opens. Inside it you can reply with any message type (text, media, interactive). Outside it, you can only send an approved template. Meta sets these rules and its pricing; check Meta's current documentation and rate card for the categories and markets you send to.
Send a template#
Set messageType=TEMPLATE, name the template and its language, and pass the body variable.
cURL
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F campaignName=order-updates \
-F destinationNo=971500000000 \
-F messageType=TEMPLATE \
-F templateName=order_update \
-F templateLanguage=en_US \
-F bodyTemplateVariables=INV-400Node.js
const form = new FormData();
form.append("batchId", "0");
form.append("campaignName", "order-updates");
form.append("destinationNo", "971500000000");
form.append("messageType", "TEMPLATE");
form.append("templateName", "order_update");
form.append("templateLanguage", "en_US");
form.append("bodyTemplateVariables", "INV-400");
const res = await fetch("https://api.broadnet.me/whatsapp/message/sendbulk", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.BROADNET_TOKEN}` }, // fetch sets the multipart boundary
body: form,
});
console.log(await res.json());Python
import os
import requests
fields = {
"batchId": "0",
"campaignName": "order-updates",
"destinationNo": "971500000000",
"messageType": "TEMPLATE",
"templateName": "order_update",
"templateLanguage": "en_US",
"bodyTemplateVariables": "INV-400",
}
# Send every field as a multipart part (files=) rather than url-encoded (data=).
r = requests.post(
"https://api.broadnet.me/whatsapp/message/sendbulk",
headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
files={k: (None, v) for k, v in fields.items()},
timeout=30,
)
print(r.json())PHP
<?php
$ch = curl_init("https://api.broadnet.me/whatsapp/message/sendbulk");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("BROADNET_TOKEN")],
// An array (not a string) makes cURL send multipart/form-data.
CURLOPT_POSTFIELDS => [
"batchId" => "0",
"campaignName" => "order-updates",
"destinationNo" => "971500000000",
"messageType" => "TEMPLATE",
"templateName" => "order_update",
"templateLanguage" => "en_US",
"bodyTemplateVariables" => "INV-400",
],
]);
echo curl_exec($ch);
curl_close($ch);The call queues the messages and returns at once with a batch summary:
Response
{
"totalFiles": 1,
"totalSuccess": 1,
"totalFailed": 0,
"totalDuplicates": 0,
"batchId": "2609091405000000001",
"fileResults": [
{
"fileName": "direct",
"totalRecords": 1,
"successCount": 1,
"failedRecipients": [],
"duplicateRecipients": []
}
],
"message": "Messages queued for delivery"
}Store batchId. Per-recipient statuses arrive on your webhook and can be read from the bulk insights endpoint.
Request fields#
Which fields are required depends on messageType. These are the common ones:
| Field | Type | Required | Description |
|---|---|---|---|
messageType |
string | Yes | TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT, STICKER, LOCATION, CONTACTS, TEMPLATE, INTERACTIVE, REACTION or FLOW. |
destinationNo |
string | One of these two | One or more numbers with country code, separated by commas, semicolons or new lines. |
destinationFiles |
file | One of these two | A TXT, CSV or XLSX file of recipients. For personalised templates, one row per recipient with its variables. |
batchId |
string | No | 0 starts a new batch. |
campaignName |
string | No | Your label for reporting. |
allowDuplicate |
string | No | false removes duplicate recipients. |
content |
string | Per type | Text body (TEXT, up to 4,096 characters) or caption for media. |
mediaUrl |
string | Per type | Public HTTPS URL of the media. |
mediaFile |
file | Per type | Upload the media instead of linking it. |
templateName |
string | TEMPLATE |
Approved template name. |
templateLanguage |
string | TEMPLATE |
Template language code, for example en_US or ar. |
bodyTemplateVariables |
string | Per template | Body variable value. Named parameters, currency and date-time values are passed as JSON. |
headerTemplateVariables |
string | Per template | Header variable (text, or product details for product headers). |
buttonsTemplateVariable |
string | Per template | Dynamic button value: URL suffix, coupon code or OTP code. |
interactivePayload |
string (JSON) | INTERACTIVE |
Meta's interactive object: buttons, list, CTA URL, location request and more. |
contactsPayload |
string (JSON) | CONTACTS |
Array of contact cards. |
latitude, longitude, locationName, locationAddress |
string | LOCATION |
Location pin. |
replyToMessageId |
string | No | WhatsApp message ID (wamid) to quote-reply to, or to react to with REACTION. |
emoji |
string | REACTION |
The reaction emoji. |
delay |
string | No | Throttle between messages. |
scheduled, scheduleTime, gmt |
string | No | Schedule the batch, for example scheduleTime=2026-11-01 10:00:00.000, gmt=+04:00. |
recurrence, repeatCount, recurrenceEndTime |
string | No | Recurring sends, for example DAILY. |
For templates with several variables per recipient, upload a personalised destinationFiles file with one row per recipient. Confirm the column layout with our team for your first campaign.
Template variations#
| Template feature | Fields to add |
|---|---|
| Header image, video or document | mediaUrl (overrides the header media) |
| Header text variable | headerTemplateVariables |
| Dynamic URL button | buttonsTemplateVariable=summer2026 |
| Authentication (OTP) with copy-code button | bodyTemplateVariables=482913 and buttonsTemplateVariable=482913 |
| Coupon (copy code) | buttonsTemplateVariable=SAVE20 |
| Several buttons | buttonsTemplateVariable as a JSON array with index, sub_type and value |
| Carousel | carouselSendCards[0].bodyVariables[0] and similar per-card fields |
| Named parameter | bodyTemplateVariables={"parameter_name":"order_id","text":"ORD-12345"} |
Send session messages#
Inside the service window you can send free-form messages. Two examples:
Text
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F destinationNo=971500000000 \
-F messageType=TEXT \
-F campaignName=support \
-F "content=Your order has shipped. Reply here if you need anything."Reply buttons
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F destinationNo=971500000000 \
-F messageType=INTERACTIVE \
-F campaignName=support \
-F 'interactivePayload={"type":"button","body":{"text":"Confirm your delivery slot?"},"action":{"buttons":[{"type":"reply","reply":{"id":"slot_ok","title":"Confirm"}},{"type":"reply","reply":{"id":"slot_change","title":"Change"}}]}}'Interactive payloads follow Meta's interactive message format, which also covers lists, CTA URL buttons, location requests, product and catalog messages, and Flows.
Mark a message as read#
POST /whatsapp/message/read marks an inbound message as read. Add "typing": true to show a typing indicator while you prepare the reply.
cURL
curl -X POST https://api.broadnet.me/whatsapp/message/read \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "messageId": "wamid.HBgMOTcxNTAwMDAwMDAwFQIAEhgg", "typing": true }'Track delivery#
Each recipient moves through SUBMITTED, SENT, DELIVERED and READ, or ends as FAILED or REJECTED with a reason. Statuses are pushed to your account's webhook. You can also read them per batch:
cURL
curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&status=FAILED&page=0&size=50" \
-H "Authorization: Bearer $BROADNET_TOKEN"| Parameter | Required | Description |
|---|---|---|
batch_Id |
Yes | Batch ID returned by the send. |
status |
No | Filter, for example DELIVERED, READ or FAILED. |
destination |
No | Filter to one recipient. |
search |
No | Free-text search. |
page, size |
No | Zero-based page index and page size (default 0 and 10). |
Manage batches#
| Method | Path | Description |
|---|---|---|
| POST | /whatsapp/message/all-bulk |
List batches with filters (multipart form fields such as page, size, status, campaignName). |
| GET | /whatsapp/message/edit?batch_Id=… |
Batch details. |
| GET | /whatsapp/message/pause?batch_Id=… |
Pause a batch. |
| GET | /whatsapp/message/resume?batch_Id=… |
Resume a paused batch. |
| DELETE | /whatsapp/message/abort?batch_Id=… |
Abort a batch. |
Manage templates#
Templates are created through the API (or the console), reviewed by Meta, and then sent by name.
| Method | Path | Description |
|---|---|---|
| POST | /whatsapp/template |
Create a template (multipart). |
| GET | /whatsapp/template/list |
List templates. Filters: name, status (APPROVED, PENDING, REJECTED, PAUSED), language, category, page, size. |
| GET | /whatsapp/template?name=… |
Template details by name. |
| GET | /whatsapp/template/grouped-by-category |
Templates grouped by category. |
| PUT | /whatsapp/template/update/{WA_TEMPLATE_ID} |
Edit a template. |
| DELETE | /whatsapp/template?name=… |
Delete a template by name. |
| POST | /whatsapp/template/refresh?name=… |
Re-sync status and category from Meta. |
| POST | /whatsapp/template/library |
Create a template from Meta's template library. |
Main fields for POST /whatsapp/template:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Lowercase name, unique in your account. |
languageType |
string | Yes | Language code, for example en_US. |
category |
string | Yes | MARKETING, UTILITY or AUTHENTICATION. |
templateType |
string | Yes | For example DEFAULT, UTILITY_DEFAULT, OTP, CATALOG or CALLING_PERMISSION. |
headerFormat |
string | No | NONE, TEXT, IMAGE, VIDEO, DOCUMENT, LOCATION or PRODUCT. |
headerText, headerFile |
string, file | Per header | Header text, or a sample media file. |
bodyText |
string | Yes (except OTP) | Body with numbered placeholders. |
bodyExample |
string (JSON) | With variables | Sample values, for example [["Sara","ORD-12345"]]. |
footerText |
string | No | Footer text. |
buttonRequest |
string | No | Button definition, for example QUICK_REPLY:Track order or URL:Track|https://example.com/track/{{1}}|ORD123. |
deliveryMode |
string | OTP | COPY_CODE, ONE_TAP or ZERO_TAP for authentication templates. |
codeExpirationMinutes |
string | No | OTP expiry shown to the user. |
New templates start as PENDING until Meta approves them. If you suspect a missed status update, call the refresh endpoint.
Other WhatsApp features in the API#
The WhatsApp section of the API also covers:
- WhatsApp Flows: create, upload, publish and measure Flows (for example
POST /whatsapp/flow/create,POST /whatsapp/flow/{WA_FLOW_ID}/publish,GET /whatsapp/flow/{WA_FLOW_ID}/metrics). - Catalogs: manage products and product sets on a connected catalog (for example
POST /whatsapp/catalog/{WA_CATALOG_ID}/product,POST /whatsapp/catalog/connect) and readGET /whatsapp/message/catalog-insights. - QR links: create and manage QR codes that open a chat (
/whatsapp/qr-links). - Business profile: read and update the profile (
/whatsapp/business-profile) and upload a profile photo (POST /whatsapp/profile-photo). - Blocked users: list, block and unblock users (
/whatsapp/block-users).
The full field reference for each is in the OpenAPI spec, available to customers on request. See SDKs and OpenAPI.
Next steps#
- Webhooks: handle
DELIVERED,READandFAILEDevents. - RCS: reach Android users with rich cards, with SMS fallback.
- Errors and limits: handle
502errors that carry Meta's error code. - SDKs and OpenAPI: send WhatsApp messages with the TypeScript SDK.
Ready to send your first message?
Request test credentials. An engineer sets up your account, and test sends go to your own verified numbers.
Request test access