一个 LINE Webhook 搞定全团队:福冈初创公司如何将 LINE 消息路由到 Slack、CRM 和 AI
周二早上 9:47,大阪的一位客户通过 LINE 给客户经理发了条消息:“CSV 导出一直失败,这是已知问题吗?“客户经理正在开会,消息在她手机上躺了三个小时。等她回复时,客户已经通过官网提交了工单——还在 X 上 @了公司,问到底有没有人在看他们的 LINE 频道。
这不是一个关于平台能力不足的故事。LINE 作为即时通讯工具本身很好用。问题在于,福冈办公室的六个人各自用自己的 LINE 账号接收客户消息,各自看各自的手机,完全没有一个共享视图来追踪谁在回复谁。这家公司——一支 B2B SaaS 团队,向日本和东南亚的仓库销售物流规划工具——已经过了”每个人看自己 LINE 就行”的阶段。但所有替代方案似乎都要求客户离开 LINE,而在日本,这根本行不通。
那个不存在的消息队列
2026 年初,这支团队客服体系是这样的:三名销售和两名技术支持各有一个 LINE 账号,客户加他们为好友。消息直接到个人手机上。没有共享收件箱,没有回复追踪,没有 SLA。谁请了病假,他手机上的消息就没人看,直到回来上班。客户晚上 7 点以后发消息,就只能等到下一个工作日——不是团队不想帮忙,而是根本没人知道消息来了。
公司考虑过 LINE 官方账号(OA)方案,它提供共享收件箱和部分自动化能力。但 OA 申请审核最多需要 60 个工作日,未认证(Unverified)等级的好友上限是 500 人——对于客户群已接近 800 人的团队来说,这是硬性天花板。认证(Verified)和高级(Premium)等级可以解锁更高上限,但伴随月费和互动窗口规则——这些规则限制的是商家主动发起联系的时机。但这支团队根本不主动联系客户,他们只做回复。OA 那套群发广播机制,解决的是他们没有的问题。
与此同时,维持现状的运营代价在不断累积。2026 年第一季度,团队记录了 23 次客户消息超过四小时未回复的情况,其中 7 次导致客户通过其他渠道升级投诉,2 次演变为 X 上的公开抱怨。客服负责人 Kenji 开了个表格追踪漏回的消息,第六周就放弃了更新。
一个 Webhook,三个去向
转折点并非某次危机事件——而是 CTO 在 Slack 里发的一条消息。他一直在研究基于 webhook 的消息路由方案。思路很简单:与其让每个人各自盯自己的 LINE,不如把六个 LINE 账号统一接入一个中间层,把收到的消息转发到团队已经在用的工具里。Slack 负责实时可见,CRM 负责记录和跟进,而针对常规问题,用 AI 先起草回复,团队审核后再发送。
团队通过扫码认证(QR code authentication)将 LINE 账号接入了 UnifyPort——这是 LINE 在该平台上唯一支持的认证方式。每个账号扫码大约 30 秒,不到一小时,六个账号全部上线,一个 webhook 端点就能以 message.received 事件的形式接收所有 LINE 消息。
webhook 的 payload 结构如下:
{
"id": "evt_9a3f7c2e01",
"type": "message.received",
"provider": "line",
"account_id": "acc_4b82d1",
"occurred_at": "2026-05-12T01:47:23Z",
"data": {
"conversation": {
"id": "U4af2c891...",
"type": "user",
"title": "Tanaka-san"
},
"sender": {
"id": "U4af2c891...",
"type": "user",
"name": "Tanaka-san"
},
"message": {
"id": "msg_18420...",
"type": "text",
"text": "The CSV export keeps failing — is this a known issue?",
"direction": "inbound",
"sent_at": "2026-05-12T01:47:22Z"
},
"event": {
"kind": "message_received"
}
}
}
每次推送都带有 X-Device-Signature 请求头——这是用 webhook 的 signing_secret 对时间戳拼接原始请求体做 HMAC-SHA256 签名后的十六进制摘要。团队的 Express 处理器在处理任何逻辑之前先验证签名:
import crypto from 'crypto';
import express from 'express';
const app = express();
const SECRET = process.env.WEBHOOK_SIGNING_SECRET;
app.post('/webhook', 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');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString('utf8'));
if (event.type === 'message.received') {
await routeMessage(event);
}
res.status(200).end();
});
有意思的部分不是 webhook 处理器本身——每个集成都有这个。真正有意思的是验证通过后执行的 routeMessage 函数。它不是把所有消息一股脑扔进一个队列,而是根据消息内容和会话上下文来决定消息该往哪里去。
路由逻辑:Slack、CRM、AI——或者三个都去
团队的路由规则在头两周不断迭代,最终稳定为三个层级:
第一层——Slack 通知(所有消息)。 每条收到的 LINE 消息都会推送到 Slack 的 #support-line 频道,附带发送者名称、消息预览和 account_id,这样团队就知道消息是发到哪个销售的账号上的。光这一层就解决了”消息躺在某人手机上没人看”的问题。如果账号主人在开会,团队里其他任何人都能在 Slack 里看到这条消息,然后通过 UnifyPort 的 POST /v1/messages 端点,用原始消息中的 reply_to.reply_token 来回复客户。
第二层——CRM 归档(所有消息)。 一个并行调用会在 CRM 中创建或更新联系人记录,写入完整消息文本、时间戳和会话 ID。在此之前,客户在 LINE 上的互动对 CRM 来说是不可见的——它们只存在于个人手机的聊天记录里。现在,每一段 LINE 对话都有了完整的审计轨迹,和邮件、官网工单记录并列在一起。
第三层——AI 起草(仅常规问题)。 一个简单的关键词分类器会检查收到的消息是否匹配常见主题:CSV 导出、账单、登录问题、API 文档请求。如果命中,消息会被转发到内部的 LLM 端点,系统提示词中包含了相关文档段落。AI 起草的回复会发到 #support-drafts Slack 频道,附带一个”审核并发送”按钮。没有任何回复会在未经人工确认的情况下发出——但草稿几秒内就准备好了,销售只需点一下。
async function routeMessage(event) {
const { conversation, sender, message } = event.data;
const accountId = event.account_id;
// Tier 1: Always notify Slack
await postToSlack('#support-line', {
account: accountId,
sender: sender.name,
preview: message.text?.slice(0, 120),
reply_token: message.reply_token,
});
// Tier 2: Always log to CRM
await crm.upsertContact({
external_id: sender.id,
platform: 'line',
name: sender.name,
last_message: message.text,
last_message_at: message.sent_at,
});
// Tier 3: AI draft for routine questions
const topic = classifyTopic(message.text);
if (topic) {
const draft = await generateDraft(topic, message.text, sender.name);
await postToSlack('#support-drafts', {
sender: sender.name,
topic,
draft,
account_id: accountId,
reply_token: message.reply_token,
});
}
}
classifyTopic 函数刻意保持简单——它是一个关键词映射表,不是神经网络分类器。“CSV”、“export”、“download”映射到导出文档主题;“請求”、“料金”、“支払い”映射到账单主题。保持基于规则的逻辑,意味着团队可以在几分钟内而不是几小时内排查路由错误。
会话标签实现团队分工
第二周,团队开始使用 UnifyPort 的会话标签(conversation labels)功能。通过 POST /v1/conversations/labels 端点,他们为每个成员创建了标签:kenji、yuki、hiro、sales。消息到达时,路由函数会给会话打上账号主人的标签。如果账号主人不在办公室(通过一个简单的日历集成来检查),会话标签会切换为值班人员。
这意味着,任何时候,任何团队成员都可以调用 GET /v1/conversations/labels,准确看到哪些会话分配给了自己——横跨全部六个 LINE 账号。共享收件箱的问题,不是通过创建一个新收件箱来解决的,而是通过给现有收件箱中的会话打标签来实现的。
标签还驱动了一份周报:一个定时任务(cron job)拉取每个成员标签下的所有会话,统计响应时间(从 message.received 的 occurred_at 到第一次出站 POST /v1/messages 调用的时间差),每周一早上把汇总发到 #support-metrics 频道。
第一周 vs 第四周
数据说明了一切。webhook 上线前一周,团队在 LINE 上的中位响应时间是 3 小时 40 分钟,11 条消息超过四小时未回复。第四周,中位响应时间降至 22 分钟,零条消息超过四小时未回复——最慢的一次是全公司外出团建时的 1 小时 15 分钟。
AI 起草层处理了约 35% 的收到消息。团队对其中约 80% 的草稿直接审核发送,无需修改。剩余 20% 需要小幅调整——通常是因为客户的问题有关键词分类器没捕捉到的细微之处。即便在这些情况下,草稿也给了销售一个起点,把撰写回复的时间从几分钟缩短到几秒。
CRM 集成带来了一个意想不到的副作用:销售团队开始用 LINE 会话记录来准备续约电话。webhook 上线之前,销售去开续约会议时,完全看不到客户过去在 LINE 上的客服互动记录。现在他们能看到每一条消息、每一次响应时间和每一个解决方案——全部自动归档。
团队没有改变的东西
客户什么变化都没感知到。他们还是给用了几个月的那些 LINE 账号发消息,还是从同样的人那里收到回复。唯一的区别是回复变快了,而且如果平时对接的销售没空,会有其他人接上——因为消息对全团队在 Slack 里可见,不再埋在某一个人的手机里。
团队也继续保留 LINE 个人账号,没有切换到 OA 账号。UnifyPort 的 webhook 无论账号类型如何都能接收消息——OA 的认证排队和互动窗口限制针对的是主动群发场景,而这支团队的工作流完全是被动的(接收回复)。如果他们将来决定做主动营销(季节性促销、产品公告),OA 方案会有意义。在那之前,那只是一层不需要的行政审批。
通用模式
这不仅仅是一个 LINE 的故事。任何在多个个人账号上接收客户消息的团队——无论平台是 WhatsApp、Telegram、X 还是 Zalo——都面临同样的路由问题。webhook 不关心消息来自哪个平台;provider 字段会变,但 payload 结构、HMAC-SHA256 验证和路由逻辑完全一致。这支福冈团队已经在计划把他们的 WhatsApp 账号(东南亚仓库合作伙伴在用)接入同一套路由系统。一个处理器,一套标签,一条数据管线。
如果你团队的客服策略还是”各看各的消息”,问题不在于你是否需要换一个更好的平台——而在于你的消息是否需要一个更好的路由层。一个 webhook 端点、20 行路由逻辑、加上你已经在用的工具,可能就够了。