Сценарий отправляет клиенту персонализированный шаблон WhatsApp после завершения поддержки с просьбой оценить качество обслуживания. Сообщение включает имя клиента, номер тикета и краткий запрос на участие в опросе. Кнопка со статичным URL открывает форму CSAT или страницу отзывов.
Пример шаблона
Здравствуйте, {{1}}! Запрос #{{2}} закрыт. {{3}} Оцените качество поддержки — это займёт около минуты.
Оценить поддержку
{{1}}
имя клиента
{{2}}
номер тикета или дела
{{3}}
Оцените свой опыт по шкале от 1 до 5 (это займёт 1 минуту)
“Оценить поддержку”
кнопка — исправлено в шаблоне Meta
Когда использовать это
Используйте этот сценарий, когда обращение в поддержку решено или закрыто, и команде нужна структурированная оценка удовлетворённости в WhatsApp, а не сообщение вручную от агента или опрос только по электронной почте. Подходит для служб поддержки, собирающих отзывы от 1 до 5 звёзд или NPS после закрытия тикета, контакт-центров, проводящих стандартизированные опросы после решения случаев, и агентств, подключающих триггеры CSAT из Zendesk, Freshdesk или пользовательских CRM Как это работает.
Более высокий процент ответов на опросы
Запрос на оценку CSAT приходит в WhatsApp с именем, номером тикета и приглашением к участию в опросе в виде трёх переменных в теле, чтобы просьба об оценке находилась в канале, который клиент уже использует, а не в виде опроса по электронной почте, который он никогда не открывает.
Сообщение после закрытия
Служба поддержки отправляет утверждённый шаблон, когда случай подходит, поэтому запрос CSAT дойдёт до клиента, даже если нет открытого разговора и свободный текст ответов не разрешён.
Опросы для агентов вручную отключены
Триггер после разрешения подставляет поля с номером телефона и опросом получателя, формирует шаблон с тремя переменными в теле и кнопкой с постоянной ссылкой, чтобы сотрудники не переписывали контекст тикета и не забывали отправить ссылку для оценки.
Контекст тикета перед тем, как поставить оценку
Имя клиента и номер запроса появляются в теле сообщения, чтобы клиент знал, какой закрытый случай он оценивает, перед тем как нажать кнопку «Оценить поддержку», которая открывает форму опроса.
Доставка зафиксирована для дальнейшего анализа
Результат доставки записывается обратно в службу поддержки или CRM, завершение опроса отслеживается на связанной форме, чтобы обратная связь по CSAT оставалась привязанной к той же записи тикета.
Как это работает
Создать и отправить
Служба поддержки запускает триггер CSAT после решения, когда случай подходит.
POST/sendTemplate
Фиксация события
Система определяет номер телефона получателя и поля опроса.
phone: "+…"
Персонализированный шаблон сообщения
Персонализированный шаблон сообщения создаётся с тремя переменными в теле и кнопкой со статичной ссылкой.
POST/sendTemplate
Доставлено
Клиент получает запрос на WhatsApp CSAT с контекстом тикета.
delivered
Статус отслеживается
Результат доставки фиксируется, завершение опроса отслеживается за пределами WhatsApp.
#!/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_TICKETNUMBER="___" # {{2}} ticket number
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_TICKETNUMBER=$TEST_TICKETNUMBER" \
"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}} ticket number → ${TEST_TICKETNUMBER}
# {{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_TICKETNUMBER}" },
{ "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.…), чтобы сопоставить с уведомлениями о доставке.
Принято для отправки — ещё не доставлено на телефон клиента
id
Сохраните это; обратные вызовы доставки и hookInfo привязываются к этому
Delivery itself arrives later, as a separate callback. Register a webhook (POST …/webhook) and 1MSG POSTs status updates to your HTTPS endpoint in a top-level hooks[] payload.
200 OKВебхук статуса доставки — данные, которые вы получаете
sent, delivered, read — или статус ошибки, если это уместно
id
Соотносит callback с id, возвращённым вызовом send
timestamp
Unix-секунды в виде строки
Если вы предпочитаете не получать обратные вызовы, используйте 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.
ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ (FAQ)
Да, для холодного старта сообщений в WhatsApp требуется одобренный Meta шаблон.