WhatsApp Business API для рекламной рассылки предложений
Сценарий отправляет персонализированный маркетинговый шаблон WhatsApp контактам, которые дали согласие, при начале промокампании.
Обзор сценария
Сценарий отправляет персонализированный маркетинговый шаблон WhatsApp подписанным контактам при начале промо-кампании. Сообщение включает имя клиента, рекламируемый продукт или категорию, детали предложения и дату окончания акции. Статическая кнопка URL открывает страницу с предложением.
Пример шаблона
Здравствуйте{{1}}! У нас есть специальное предложение на{{2}}: {{3}}. Акция действует до{{4}}. Нажмите кнопку ниже, чтобы посмотреть предложение и купить сейчас.
- {{1}}имя клиента
- {{2}}продвигаемый товар или категория
- {{3}}подробности рекламного предложения (размер скидки, условия)
- {{4}}предложение действительно до даты
- “Просмотреть предложение”кнопка — исправлено в шаблоне Meta

Когда использовать
Используйте этот сценарий, если у вас уже есть список для WhatsApp-маркетинга и вы хотите объявить скидку или специальное предложение для этой базы, а не для холодных контактов. Он подходит для сезонных распродаж в розничной торговле и электронной коммерции, продвижения обновлений SaaS для существующих подписчиков, а также для агентств, которые проводят промо-рассылки для брендов клиентов из сегментов CRM.
Как это работает
- Триггер
Маркетинг или CRM выбирает сегмент, давший согласие, для рекламной кампании.
event·triggered - Захватить событие
Система определяет номера телефонов и поля персонализации для каждого контакта.
phone:"+…" - Собрать и отправить
Персонализированный рекламный шаблон состоит из четырёх переменных в теле и статической кнопки URL.
POST/sendTemplate - Доставлено
Каждый контакт, давший согласие, получает сообщение с маркетинговым предложением в WhatsApp.
delivered - Статус отслежен
Результаты доставки фиксируются для отслеживания эффективности кампаний и последующих действий.
status:"read"

Техническая реализация
Предварительные условия
- Ключ API 1MSG · Как получить ключ API
- Аккаунт WhatsApp Business · Как подключить WABA
- Шаблон WhatsApp · Как одобрить шаблон WABA
- Подписка клиента · Как управлять согласием клиентов
Примеры кода
#!/usr/bin/env bash
set -euo pipefail
# === Configuration (replace "___" placeholders) ===
API_BASE_URL="https://api.1msg.io" # production 1MSG API base URL
CHANNEL_ID="___" # channel ID from 1MSG dashboard
API_TOKEN="___" # channel JWT token (Bearer)
TEMPLATE_NAME="___" # approved template name
TEMPLATE_NAMESPACE="___" # template namespace (required — send fails without it)
TEMPLATE_LANGUAGE="___" # template language code, e.g. "en"
# === Test data ===
TEST_PHONE="___" # client phone in international format
TEST_CUSTOMERNAME="___" # {{1}} customer name
TEST_PRODUCTORSERVICENAME="___" # {{2}} product or service name
TEST_DETAILS="___" # {{3}} change details
TEST_DATE="___" # {{4}} date or time
PHONE_NORM="$(printf '%s' "$TEST_PHONE" | tr -cd '0-9')"
for pair in "CHANNEL_ID=$CHANNEL_ID" "API_TOKEN=$API_TOKEN" \
"TEMPLATE_NAME=$TEMPLATE_NAME" "TEMPLATE_NAMESPACE=$TEMPLATE_NAMESPACE" \
"TEMPLATE_LANGUAGE=$TEMPLATE_LANGUAGE" "TEST_PHONE=$TEST_PHONE" \
"TEST_CUSTOMERNAME=$TEST_CUSTOMERNAME" \
"TEST_PRODUCTORSERVICENAME=$TEST_PRODUCTORSERVICENAME" \
"TEST_DETAILS=$TEST_DETAILS" \
"TEST_DATE=$TEST_DATE"; do
val="${pair#*=}"
if [ -z "$val" ] || [ "$val" = "___" ]; then
echo "Missing configuration value: ${pair%%=*}" >&2
exit 1
fi
done
if [ -z "$PHONE_NORM" ]; then
echo "Error: phone number has no digits after normalization" >&2
exit 1
fi
URL="${API_BASE_URL%/}/${CHANNEL_ID}/sendTemplate"
# params carries body ONLY. Button text is fixed in the Meta template — no button param.
# {{1}} customer name → ${TEST_CUSTOMERNAME}
# {{2}} product or service name → ${TEST_PRODUCTORSERVICENAME}
# {{3}} change details → ${TEST_DETAILS}
# {{4}} date or time → ${TEST_DATE}
read -r -d '' PAYLOAD <<JSON || true
{
"phone": "${PHONE_NORM}",
"template": "${TEMPLATE_NAME}",
"namespace": "${TEMPLATE_NAMESPACE}",
"language": { "policy": "deterministic", "code": "${TEMPLATE_LANGUAGE}" },
"params": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "${TEST_CUSTOMERNAME}" },
{ "type": "text", "text": "${TEST_PRODUCTORSERVICENAME}" },
{ "type": "text", "text": "${TEST_DETAILS}" },
{ "type": "text", "text": "${TEST_DATE}" }
]
}
]
}
JSON
RESPONSE="$(curl -s -w '\n%{http_code}' -X POST "$URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${API_TOKEN}" \
-d "$PAYLOAD")"
HTTP_CODE="$(printf '%s' "$RESPONSE" | tail -n1)"
BODY="$(printf '%s' "$RESPONSE" | sed '$d')"
case "$BODY" in
*'"sent":true'*) ok=1 ;;
*) ok=0 ;;
esac
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ] && [ "$ok" -eq 1 ]; then
echo "Message sent to client."
echo "API response: $BODY"
else
echo "Send failed. HTTP status: $HTTP_CODE" >&2
echo "$BODY" >&2
exit 1
fi
Статус ответа и доставки
HTTP 2xx и JSON "sent": true обозначают, что 1MSG приняла сообщение для отправки — это не значит, что оно уже доставлено на телефон клиента. Сохранитеid поле (выглядит как wamid.…), чтобы сопоставить обратные вызовы доставки.
{
"sent": true,
"id": "wamid.HBgLMzgwNjM5...",
"message": "Message accepted for delivery"
}sentПринято для отправки — ещё не доставлено на телефон клиента
idСохраните это; обратные вызовы доставки и
hookInfoпривязаны к этому
Доставка поступает позже в виде отдельного обратного вызова. Зарегистрируйте POST …/webhookвебхук, и 1MSG POSTs статус обновления на ваш HTTPS-эндпоинт в hooks[]основной полезной нагрузке
{
"hooks": [
{
"id": "gBGGeSaGViBfAgnlzOSHEwK9O6F",
"type": "message",
"status": "sent",
"timestamp": "1654864094",
"recipient_id": "556123122026"
}
]
}statussent,delivered,read— или статус сбоя, если применимоidСвязывает callback с
id, возвращённым вызовом sendtimestampUnix-время в виде строки
Если вы предпочитаете не получать обратные вызовы, вместо этого используйте опрос. GET {base}/{channel}/hookInfo?messageId=<id>На практике доставка часто завершается за считанные секунды — но контракт API этого не гарантирует, так что никогда не блокируйте поток, ожидая её.
Типичные ошибки
| Статус | Ответ | Причина |
|---|---|---|
| 200 | Message was not sent: template is not defined | namespace, template or language missing from the request body. |
| 200 | template name (…) does not exist in <language> | The template is approved in a different language than the one requested. |
| 200 | Message was not sent: provide chatId, phone, bsuid, or username | No recipient the channel could resolve. |
| 403 | access denied | The token is wrong, or belongs to a different channel than the URL. |
| 429 | too many requests. please try later | The channel is over its send rate. |

