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

Когда использовать
Используйте этот сценарий, когда коммерческое предложение готово, но электронная почта или ссылка в CRM сами по себе не приводят к его открытию — у потенциального клиента нет активной переписки в WhatsApp, поэтому только утверждённый шаблон может послужить первым исходящим сообщением. Подходит для B2B команд продаж, отправляющих предложения сразу после их создания, агентств, запускающих WhatsApp, когда PDF или веб-предложение публикуется в CRM, и сервисных компаний, отправляющих ценовые пакеты квалифицированным лидам.
Как это работает
- Триггер
CRM или система предложений создаёт событие готовности предложения.
event·triggered - Зафиксировать событие
Система определяет номер телефона потенциального клиента и поля предложения.
phone:"+…" - Собрать и отправить
Персонализированное сообщение по шаблону создаётся с тремя переменными в теле и статической кнопкой URL.
POST/sendTemplate - Доставлено
Потенциальный клиент получает сообщение в WhatsApp с ссылкой на предложение.
delivered - «Статус отслеживается»
Результат доставки фиксируется в CRM; отдел продаж может продолжить работу с учётом вовлечённости.
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_NEXTSTEP="___" # {{3}} next step
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_NEXTSTEP=$TEST_NEXTSTEP"; 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}} next step → ${TEST_NEXTSTEP}
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_NEXTSTEP}" }
]
}
]
}
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 будет отправлять обновления статусов на ваш HTTPS-эндпоинт в корневомhooks[] payload.
{
"hooks": [
{
"id": "gBGGeSaGViBfAgnlzOSHEwK9O6F",
"type": "message",
"status": "sent",
"timestamp": "1654864094",
"recipient_id": "556123122026"
}
]
}statussent,delivered,read— или статус ошибки, если применимоidСоотносит обратный вызов с
idрезультатом вызова sendtimestampUnix-секунды в виде строки
If you would rather not receive callbacks, poll GET {base}/{channel}/hookInfo?messageId=<id> instead. In practice delivery often completes within seconds — but the API contract does not guarantee it, so never block a flow waiting on it.
Распространённые ошибки
| Статус | Ответ | Причина |
|---|---|---|
| 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. |

