Как вьетнамская команда поддержки объединила Zalo, WhatsApp и LINE в одну входящую очередь
В понедельник в 8:12 утра, когда руководитель поддержки в Хошимине еще не открыла ноутбук, пришло первое сообщение в Zalo. Покупатель хотел изменить адрес доставки до приезда курьера. Через четыре минуты поставщик из Бангкока написал в LINE, что одна коробка задержится. В 8:27 постоянный клиент из Сингапура спросил в WhatsApp про условия гарантии.
Ни одно из этих сообщений не было необычным. Проблема была в том, что они попали в три разных канала, на три разных телефона, к трем разным людям. К 9 утра команда уже отправила два скриншота в Slack, переслала одно сообщение менеджеру по продажам и забыла, какой именно клиент спрашивал про гарантию.
Это была операционная команда из пяти человек, обслуживающая трансграничные заказы небольшого beauty-бренда. Клиенты были в основном во Вьетнаме, Таиланде и Сингапуре, поэтому набор каналов был естественным: Zalo для местных покупателей, LINE для тайских партнеров, WhatsApp для поставщиков и международных постоянных клиентов. Команде не нужны были кампании, массовые рассылки, шаблоны или новая CRM. Ей нужно было превращать входящие сообщения в рабочие задачи.
Три inbox, один SLA
До изменений процесс был ручным, но привычным. Zalo оставался на телефоне руководителя поддержки во Вьетнаме. LINE был у координатора, который вел тайских поставщиков. WhatsApp оставался у основателя, потому что старые клиенты все еще писали на его номер. Каждое утро команда открывала Slack и публиковала туда скриншоты того, что выглядело срочным.
Пока объем был небольшим, это работало. Потом пропущенная смена адреса до забора посылки стала возвратом денег. Сообщение поставщика в LINE о задержке увидели уже после того, как склад пообещал отправку в тот же день. Вопрос по гарантии в WhatsApp остался непрочитанным, потому что основатель был в поездке.
За две недели команда зафиксировала 31 поздний ответ. В большинстве случаев это не были технические сбои. Это были сбои видимости. Кто-то видел сообщение, но остальные нет. Или нужный человек был недоступен, а остальные даже не знали, что сообщение пришло.
Официальные продукты платформ решали более широкие задачи. Zalo Official Account полезен для официального аккаунта и шаблонных уведомлений. LINE Official Account полезен для rich menu, рассылок и брендированного присутствия. WhatsApp Cloud API полезен для масштабной шаблонной бизнес-коммуникации. Но эта команда не инициировала массовые разговоры. Важные диалоги начинались с клиента или поставщика.
Так требование изменилось. Проект был не про внедрение трех официальных стеков business messaging. Он был про то, чтобы каждое входящее сообщение становилось надежной задачей в очереди.
Webhook, который был действительно нужен
Команда создала один backend endpoint:
POST /inbound/messages
Затем через UnifyPort она подключила по одному аккаунту Zalo, WhatsApp и LINE. Все три аккаунта доставляли события в один webhook endpoint. Каждая доставка имела одну и ту же оболочку: id, type, provider, account_id, occurred_at и data.
Нормализованное сообщение Zalo выглядело так:
{
"id": "evt_7f4c20a91d",
"type": "message.received",
"provider": "zalo",
"account_id": "acc_vn_support",
"occurred_at": "2026-07-02T01:12:16Z",
"data": {
"conversation": { "id": "zalo_user_1942", "type": "user", "title": "Minh Anh" },
"sender": { "id": "zalo_user_1942", "type": "user", "name": "Minh Anh" },
"message": {
"id": "zalo_msg_8831",
"type": "text",
"text": "Em muốn đổi địa chỉ giao hàng trước 11h được không?",
"direction": "inbound",
"sent_at": "2026-07-02T01:12:15Z"
},
"event": { "kind": "message_received" }
}
}
Тот же handler обрабатывал WhatsApp и LINE. Менялся provider, но команде не нужен был отдельный parser под каждую платформу.
Перед тем как доверять payload, endpoint проверял подпись доставки. У endpoint был задан signing_secret, поэтому каждый request содержал X-Device-Timestamp и X-Device-Signature. Подпись — это hex digest HMAC-SHA256 от timestamp, точки и raw request body.
import crypto from 'crypto';
import express from 'express';
const app = express();
const secret = process.env.WEBHOOK_SIGNING_SECRET;
app.post('/inbound/messages', express.raw({ type: 'application/json' }), async (req, res) => {
const timestamp = req.get('X-Device-Timestamp');
const signature = req.get('X-Device-Signature');
const hmac = crypto.createHmac('sha256', secret);
hmac.update(timestamp + '.');
hmac.update(req.body);
const expected = hmac.digest('hex');
const valid = signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) {
res.status(401).end();
return;
}
const event = JSON.parse(req.body.toString('utf8'));
await enqueueInboundMessage(event);
res.status(200).end();
});
В очередь попадали пять полей, которые были важны команде: платформа, имя клиента, текст сообщения, conversation ID и account ID. Остальное сохранялось как raw event metadata для аудита.
Правила маршрутизации, которые команда могла объяснить
Первая версия routing была намеренно простой.
Zalo шел в очередь поддержки во Вьетнаме. LINE шел в очередь поставщиков. WhatsApp шел в очередь международных клиентов. Сообщение с номером заказа создавало заметку в CRM. Сообщение с ключевыми словами про доставку попадало в Slack-канал #fulfillment.
В первую неделю не было AI classifier. Команде нужна была система, которую можно отладить прямо во время движения заказов. Они использовали понятные rule names: delivery_change, supplier_delay, warranty_question. Каждый item показывал, какое правило сработало.
На второй неделе добавили только один вспомогательный слой: draft suggestions для трех типовых вопросов. Для смены адреса система готовила ответ с просьбой прислать новый адрес и номер заказа. Для гарантии — текст с условиями и требованиями к фото. Для задержки поставщика — уточнение нового времени передачи. Черновики публиковались в Slack и не отправлялись автоматически.
Это было важной границей. Команда не хотела, чтобы автоматизация разговаривала с клиентами. Она хотела, чтобы следующее человеческое действие было очевидным.
Что изменилось через четыре недели
Самое заметное изменение было простым: никто больше не следил за тремя телефонами. Руководитель поддержки открывала утром одну очередь и видела Zalo, WhatsApp и LINE в одном списке. Команда могла фильтровать по provider, но обычный вид был временной лентой входящих задач.
Медианное время первого ответа снизилось с 2 часов 18 минут до 26 минут. Сообщения старше четырех часов упали с 31 за две недели до 3 за следующие две недели. Еще важнее: скриншоты перестали быть операционными записями. У каждого inbound event появились стабильный event ID, provider, account ID и timestamp.
CRM тоже стала полезнее. Раньше история Zalo жила на одном телефоне, а контекст LINE поставщика был только у одного координатора. После изменений CRM показывала последнее сообщение независимо от канала. Когда основатель готовился к звонку о продлении контракта, ему больше не нужно было просить команду искать переписку. Он открывал contact record.
Клиентов никуда не переводили. Они продолжили писать в те же Zalo, WhatsApp и LINE аккаунты. Изменился только backend path после прихода сообщения.
Паттерн
Для небольших команд главное — разделить inbound support и outbound campaign infrastructure. Официальные business-продукты полезны, когда нужны верифицированное присутствие, рассылки, шаблоны, rich menu или кампании. Но если текущая боль — “клиенты пишут первыми, а команда пропускает сообщения”, первым нужен inbound routing layer.
С UnifyPort команда рассматривала все платформы как один event stream. message.received стал общим контрактом. HMAC-SHA256 verification сделала доставки доверенными. Поле provider показывало, откуда пришло сообщение, а остальной support workflow остался platform-neutral.
Для команды из пяти человек этого оказалось достаточно: один webhook, одна очередь и инструменты, которыми они уже пользовались.