Отправка сообщения WhatsApp через API: cURL, Node, Python, PHP
Как отправить сообщение WhatsApp через API: рабочие примеры на cURL, Node.js, Python и PHP для шаблонов и сессионных сообщений, отслеживание статусов и ошибки.
Автор: OmniChannels.ai Editorial Team · Проверил: BroadNet Messaging Solutions Team
Интеграции
Короткий ответ
В API OmniChannels.ai каждое сообщение WhatsApp отправляется через один эндпоинт, POST https://api.broadnet.me/whatsapp/message/sendbulk, в виде multipart form data с заголовком Authorization: Bearer. Поле messageType выбирает содержимое: TEMPLATE для одобренных шаблонов, которые нужны, чтобы начать диалог или написать вне 24-часового окна, и TEXT, IMAGE или INTERACTIVE для ответов внутри окна.
На этой странице
- Как отправить сообщение WhatsApp через API?
- Шаблон или сессионное сообщение: что отправлять?
- Как отправить одобренный шаблон?
- Какие поля запроса важнее всего?
- Как отправить шаблон с кнопкой или OTP?
- Как ответить внутри окна обслуживания клиента?
- Как отслеживать доставку?
- Какие ошибки нужно обрабатывать?
- Пример: сервис уведомлений о заказах
- Какие типичные ошибки?
- Как помогает OmniChannels.ai
Главное
- Все сообщения WhatsApp отправляются через POST /whatsapp/message/sendbulk; messageType (TEMPLATE, TEXT, IMAGE, INTERACTIVE и другие) определяет, какие поля обязательны.
- Запрос имеет формат multipart/form-data, а не JSON, поэтому в Python нужен параметр files=, а в PHP — тело в виде массива, чтобы не произошло url-кодирования.
- Вне 24-часового окна обслуживания клиента можно отправить только одобренный шаблон; внутри окна можно отправлять свободный текст, медиа и интерактивные сообщения.
- Вызов ставит сообщения в очередь и сразу возвращает batchId; статусы по каждому получателю приходят на ваш вебхук и доступны через эндпоинт bulk-insights.
- API не принимает ключ идемпотентности, поэтому записывайте каждую отправку до вызова и после тайм-аута проверяйте статус, а не отправляйте повторно.
Это руководство для разработчиков, которые встраивают отправку WhatsApp в приложение. В нём показаны рабочие запросы на cURL, Node.js, Python и PHP, объясняется разница между шаблонными и сессионными сообщениями, а также рассматриваются отслеживание и ошибки. Все эндпоинты и поля взяты из нашего справочника WhatsApp API и спецификации OpenAPI.
Как отправить сообщение WhatsApp через API?
Вы выполняете один аутентифицированный POST на https://api.broadnet.me/whatsapp/message/sendbulk с полями формы. Поле messageType выбирает вид сообщения, а ответ подтверждает, что сообщения поставлены в очередь.
| Параметр | Значение |
|---|---|
| Базовый URL | https://api.broadnet.me (без префикса /v1) |
| Эндпоинт | POST /whatsapp/message/sendbulk |
| Аутентификация | Authorization: Bearer $BROADNET_TOKEN |
| Тип содержимого | multipart/form-data (HTTP-клиент задаёт его автоматически) |
| Ответ | JSON-сводка по пакету с batchId |
Тестовые учётные данные выдаются после краткого запроса доступа, а тестовые отправки идут на ваши собственные подтверждённые номера. См. быстрый старт и страницы об аутентификации. Храните токен только на своём сервере.
Шаблон или сессионное сообщение: что отправлять?
Отправляйте шаблон, чтобы начать диалог или написать вне окна, и сессионное сообщение, если клиент писал вам в последние 24 часа. Это правило задаёт Meta, а не мы.
| Шаблонное сообщение | Сессионное сообщение | |
|---|---|---|
messageType |
TEMPLATE |
TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT, INTERACTIVE и другие |
| Когда разрешено | В любое время после одобрения шаблона | Только пока открыто окно обслуживания клиента |
| Нужно одобрение | Да, со стороны Meta | Нет |
| Типичное применение | Обновления по заказам, OTP, напоминания, рассылки | Ответы поддержки, ответы на вопросы |
Meta берёт плату за каждое доставленное сообщение в зависимости от категории, а её правила для сервисных сообщений и utility-шаблонов изменились 1 октября 2026 года. Механику описывает наше руководство об окне обслуживания, а стоимость — наша страница цен и калькулятор.
Как отправить одобренный шаблон?
Задайте messageType=TEMPLATE, укажите шаблон и код его языка и передайте значения переменных. Чтобы начать новый пакет, задайте batchId=0.
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-400
// Node.js 18+ (встроенные fetch и FormData)
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 сам задаёт границу multipart
body: form,
});
console.log(await res.json());
import os
import requests
fields = {
"batchId": "0",
"campaignName": "order-updates",
"destinationNo": "971500000000",
"messageType": "TEMPLATE",
"templateName": "order_update",
"templateLanguage": "en_US",
"bodyTemplateVariables": "INV-400",
}
# Отправляйте каждое поле как часть multipart (files=), а не с url-кодированием (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
$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")],
// Массив (а не строка) заставляет cURL отправить 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);
Номер назначения — код страны плюс номер, только цифры. Вызов сразу возвращает сводку по пакету:
{
"totalFiles": 1,
"totalSuccess": 1,
"totalFailed": 0,
"totalDuplicates": 0,
"batchId": "2609091405000000001",
"fileResults": [
{
"fileName": "direct",
"totalRecords": 1,
"successCount": 1,
"failedRecipients": [],
"duplicateRecipients": []
}
],
"message": "Messages queued for delivery"
}
Сохраните batchId рядом со своей записью. «В очереди» не означает «доставлено».
Какие поля запроса важнее всего?
Какие поля обязательны, зависит от messageType. Вот самые распространённые.
| Поле | Обязательно | Описание |
|---|---|---|
messageType |
Да | TEXT, IMAGE, VIDEO, AUDIO, DOCUMENT, STICKER, LOCATION, CONTACTS, TEMPLATE, INTERACTIVE, REACTION или FLOW |
destinationNo или destinationFiles |
Одно из двух | Номера через запятую, точку с запятой или перевод строки; либо файл TXT, CSV или XLSX |
batchId |
Нет | 0 начинает новый пакет |
campaignName |
Нет | Ваша метка для отчётности |
allowDuplicate |
Нет | false удаляет повторяющихся получателей |
templateName, templateLanguage |
Для TEMPLATE |
Название одобренного шаблона и код языка, например en_US или ar |
bodyTemplateVariables |
По шаблону | Значение переменной основного текста; именованные параметры передаются как JSON |
buttonsTemplateVariable |
По шаблону | Динамическое значение кнопки: суффикс URL, код купона или OTP-код |
content |
Для TEXT |
Текст сообщения до 4 096 символов либо подпись к медиа |
mediaUrl |
По типу | Публичный HTTPS URL медиафайла |
interactivePayload |
Для INTERACTIVE |
Интерактивный объект Meta в виде JSON-строки |
Для планирования используются scheduled, scheduleTime и gmt, а delay ограничивает скорость доставки. Полный список — в справочнике WhatsApp API.
Как отправить шаблон с кнопкой или OTP?
Передайте значение кнопки в buttonsTemplateVariable. Для шаблона аутентификации с кнопкой копирования кода передайте код и в поле основного текста, и в поле кнопки.
curl -X POST https://api.broadnet.me/whatsapp/message/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F batchId=0 \
-F destinationNo=971500000000 \
-F messageType=TEMPLATE \
-F templateName=login_code \
-F templateLanguage=en_US \
-F bodyTemplateVariables=482913 \
-F buttonsTemplateVariable=482913
Здесь login_code — заглушка для вашего собственного одобренного шаблона аутентификации. Для нескольких кнопок buttonsTemplateVariable принимает JSON-массив. Генерируйте код на сервере, храните его в виде хеша со сроком действия и никогда не логируйте. Доставку в режимах one-tap, zero-tap и copy-code описывает наше руководство по OTP.
Как ответить внутри окна обслуживания клиента?
Используйте тип сессионного сообщения. Для простого текстового ответа нужны messageType=TEXT и content.
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."
Для кнопок ответа отправьте messageType=INTERACTIVE и interactivePayload, соответствующий формату интерактивных сообщений Meta.
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"}}]}}'
Чтобы отметить входящее сообщение прочитанным и при желании показать индикатор набора текста, вызовите POST /whatsapp/message/read с JSON:
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 }'
Рекомендации по оформлению см. в нашем руководстве по интерактивным сообщениям.
Как отслеживать доставку?
Статусы отправляются на вебхук, настроенный в вашем аккаунте, и их также можно читать по пакету. Каждый получатель проходит через SUBMITTED, SENT, DELIVERED и READ либо завершается как FAILED или REJECTED с указанием причины.
curl "https://api.broadnet.me/whatsapp/message/bulk-insights?batch_Id=2609091405000000001&status=FAILED&page=0&size=50" \
-H "Authorization: Bearer $BROADNET_TOKEN"
batch_Id обязателен; status, destination, search, page и size необязательны, а page нумеруется с нуля. Используйте опрос как страховку на случай пропущенных событий и создайте приёмник по описанию в нашем руководстве по вебхукам. Пакеты также можно приостанавливать, возобновлять и прерывать с помощью эндпоинтов пакетов из справочника.
Какие ошибки нужно обрабатывать?
Сначала читайте HTTP-статус, затем тело ошибки. API возвращает ошибки в JSON-теле из пяти полей, а специфические для WhatsApp сбои могут приходить от Meta.
| Статус | Значение | Действие |
|---|---|---|
400 |
Ошибка валидации, например отсутствует поле, обязательное для типа, или неверный адресат | Исправьте запрос; не повторяйте без изменений |
401 / 403 |
Токен отсутствует или недействителен, либо операция не разрешена | Исправьте токен или попросите нас включить операцию |
404 |
Шаблон или отправитель не найден | Проверьте название и язык шаблона |
413 |
Загруженный файл слишком большой | Уменьшите файл |
429 |
Превышен лимит запросов | Подождите значение Retry-After |
502 |
Meta отклонила вызов; в message указан код и текст ошибки Meta |
Прочитайте его; повторяйте только при временной ошибке |
500, 503, 504 |
Проблема на сервере или у вышестоящего узла | Повторяйте с экспоненциальной отсрочкой и jitter |
Распространённые коды Meta объясняет наше руководство по кодам ошибок. Поскольку ключа идемпотентности нет, выполняйте шаги защиты от дублей из раздела ошибки и лимиты: сначала запишите pending-запись, сохраните возвращённый batchId, а после тайм-аута проверьте статус, прежде чем отправлять снова.
Пример: сервис уведомлений о заказах
«Souq Rafiq» (условный пример), розничный продавец из ОАЭ, отправляет шаблон order_update, когда заказ отправлен.
- Сервис заказов записывает строку с ключом
order-8842-shippedи статусомpending. - Он вызывает
sendbulkс шаблоном и номером заказа вbodyTemplateVariables. - При ответе
200он сохраняетbatchIdи отмечает строку какaccepted. - Обработчик вебхука обновляет строку до
DELIVEREDилиREAD, либоFAILEDс указанием причины. - Если клиент отвечает, инструмент поддержки использует сессионное сообщение
TEXTвнутри окна. - Ночное задание читает
bulk-insightsпо вчерашним пакетам и сверяет недостающие статусы.
Какие типичные ошибки?
- Отправка JSON. Эндпоинт отправки WhatsApp принимает multipart;
Content-Type: application/jsonне сработает. data=в Python вместоfiles=, из-за чего форма url-кодируется.- Свободный текст вне окна, который завершается ошибкой. Используйте шаблон.
- Неверный код языка, например
enвместоen_USу шаблона. - Принятие
200за доставку. Он означает «в очереди». - Слепые повторы после тайм-аута, из-за которых клиенту можно написать дважды.
- Токен в клиентском коде. Вызывайте API только из бэкенда.
- Ограничения: детали payload зависят от шаблона. Структуру переменных для вашей первой персонализированной рассылки подтвердите у нашей команды.
Как помогает OmniChannels.ai
BroadNet — официальный Meta Business Solution Provider, а подключение к WhatsApp Business API проходит через Meta Embedded Signup; наша команда вместе с вами готовит регистрацию, а одобрения остаются за Meta. REST API документирован спецификацией OpenAPI 3.1 и TypeScript SDK @broadnet/cpaas-sdk, а статусы доставки приводятся к единому виду во всех каналах. Начните с документации WhatsApp API или быстрого старта.
Часто задаваемые вопросы
Отправлять сообщения WhatsApp как JSON или как form data?
Как form data. Эндпоинт отправки WhatsApp POST /whatsapp/message/sendbulk принимает multipart/form-data, где каждое поле — отдельная часть формы. Одиночная отправка SMS использует JSON, и это частый источник путаницы. В Python передавайте поля через files=, а не data=, а в PHP передавайте массив в качестве тела, чтобы cURL сформировал multipart.
Можно ли отправить сообщение WhatsApp без шаблона?
Только внутри окна обслуживания клиента. Когда клиент пишет вам, открывается 24-часовое окно, и вы можете отвечать текстом, медиа или интерактивными сообщениями. Вне его или чтобы начать диалог, нужно отправить одобренный шаблон. API применяет правила Meta, и свободная отправка при закрытом окне завершится ошибкой.
Как узнать, доставлено ли сообщение?
Вызов отправки лишь подтверждает, что сообщения поставлены в очередь, и возвращает batchId. Статусы по получателям проходят через SUBMITTED, SENT, DELIVERED и READ либо завершаются как FAILED или REJECTED. Они отправляются на ваш вебхук, а также их можно читать по пакету через эндпоинт bulk-insights.
Можно ли отправить нескольким получателям одним запросом?
Да. destinationNo принимает несколько номеров через запятую, точку с запятой или перевод строки, а destinationFiles принимает файл TXT, CSV или XLSX. Для шаблона с разными переменными для каждого получателя загрузите персонализированный файл с одной строкой на получателя. Задайте allowDuplicate значение false, чтобы удалить повторяющиеся номера.
Есть ли ключ идемпотентности для защиты от повторных отправок?
Нет. API не принимает ключ идемпотентности. Если запрос завершился по тайм-ауту, со стороны клиента нельзя понять, был ли он принят. Записывайте каждую отправку в свою базу до вызова, сохраняйте возвращённый batchId и проверяйте статус или список пакетов перед любым повтором.
Нужно ли согласие (opt-in) перед отправкой?
Да. Политика Meta требует, чтобы каждый получатель дал согласие на получение сообщений от вашей компании, а шаблоны должны быть одобрены до использования. API не проверяет согласие за вас. Получайте согласие в источнике, сохраняйте подтверждение и исключайте отписавшихся.
Источники
Читайте дальше
Ещё руководстваИнтеграции
Вебхуки WhatsApp: события, безопасность и повторные доставки
Как работают вебхуки WhatsApp: события сообщений и статусов, надёжный обработчик, проверка подписи Meta, повторы, идемпотентность и пропущенные события.
10 мин чтения
Интеграции
Отправка SMS через API: примеры на Python, Node.js, PHP и cURL
Отправка SMS через API на Python, Node.js, PHP и cURL: одиночные и массовые отправки, Unicode и арабский, подсчёт сегментов, вебхуки статусов доставки и ошибки.
10 мин чтения
WhatsApp
OTP в WhatsApp или по SMS: стоимость, доставка и резервный канал
OTP в WhatsApp и OTP по SMS: модель стоимости, охват и доставка, удобство для пользователя, шаблоны аутентификации, резерв по SMS и защита от фрода.
10 мин чтения
