widget 就是一行 script 标签,但你的场景未必长成网页的样子——自己画的聊天界面、门店自助机、游戏里的客服面板。widget 说的那套 REST API 对你的代码同样开放:十五分钟,从零到一条消息躺进工作台,再加一个能验签的 webhook,对面发生什么你都听得见。
路线是这样:拿一个凭证,两条 curl 打开会话、发出消息,到工作台看它落地,最后配 webhook、验签名。前半程什么都不用部署,后半程也就二十行 Node。
需要什么,不需要什么
进 Applications,选中应用,打开设置里的 Embed tab。那页有两张卡片:
- 绿色的 App ID · public identifier,角上标着 "Safe to share"。今天的教程只需要它,点 Copy App ID。
- 红色的 Secret Key · server-side secret。这篇用不上。访客 API 会发给你一枚按访客签发的 JWT,Secret Key 按卡片上的警示待着就好:只留在服务端,放环境变量或密钥管理器。

这个分工比看起来重要:下面所有请求只用公开标识加一枚临时 token,这段代码哪怕打进移动端 App 也不会泄露任何东西。
第一步:开会话
一条 POST 建(或续上)一个会话,换回 token:
curl -X POST https://api.lane.chat/v1/chat \
-H "Content-Type: application/json" \
-d '{
"appId": "app_YOUR_APP_ID",
"visitor_uuid": "9f2c6a3e-your-stable-uuid",
"fingerprint": "device-fingerprint-string"
}'
返回:
{
"code": 0,
"data": {
"chatId": 99915292,
"clientId": 99890391,
"token": "eyJhbGciOi...",
"visitor_uuid": "9f2c6a3e-...",
"appConfig": { ... }
}
}
两个字段各值一句话。visitor_uuid 是你为每位访客生成并保存的稳定 ID;fingerprint 是设备指纹。两个锚点同时对上,才算老访客回来、取回原会话;只对上一个就按新访客处理——这也是别人抄走一个 UUID 冒充不了老客的原因。这个接口限流 30 次/60 秒,token 拿到手记得缓存,别每条消息都重新开一次门。
第二步:发消息
token 放 Authorization 头:
curl -X POST https://api.lane.chat/v1/send-message \
-H "Authorization: Bearer eyJhbGciOi..." \
-H "Content-Type: application/json" \
-d '{
"chatId": 99915292,
"text": "Update on order #48213: your Ridgeline 38L Pack shipped today. Tracking: 1Z999AA10123456784 (UPS Ground, ETA Thursday)."
}'
回来的是 {"code":0,"msg":"success","data":{"messageId":…,"timestamp":"…"}}。除了 text,还可以带 attachment(字符串,text 和 attachment 至少给一个)、reply_to_id(回复某条早前的消息)、metadata(随消息携带的对象)。限流 60 次/60 秒。
有一点要想明白:这是访客侧 API。服务端会把它收到的每条消息都盖上"访客发的"的戳,payload 里写什么都改不了。所以它适合拿来自建聊天前端,不适合拿来推客服回复——客服的话从工作台、AI 或 flow 里出。
第三步:看它落地
打开控制台的 Conversations。消息就在会话里,站在访客那一侧;在线的客服在你按下回车的那一秒,就已经通过实时连接收到了它。

读这一侧顺带补两句:GET /v1/load-messages 用同一枚 token 拉历史,POST /v1/upload(10 次/60 秒)传文件。
第四步:给服务器配个 webhook
只会发,是半场对话。客服回了、AI 答了、会话关了——想听见这些,就要 webhook。还是那个设置页,Webhook tab:
- Webhook URL:必须 https,输入框旁边就有 Test 按钮。
- 签名密钥:点 Regenerate 生成、复制,留意顶上的提示——"New secret generated — click Save settings to apply"。不保存不生效。
- 事件订阅:一片复选框。第一版勾
message.received、message.ai_response、chat.started、chat.ended就够看清全局;message.sent、chat.assigned、client.online、client.offline这些以后随时加。

每次投递都是一条 POST,Content-Type: application/json,User-Agent: LaneChat-Webhook/1.0,长这样:
{
"event": "message.received",
"app_id": 2000000121,
"app_pub": "app_YOUR_APP_ID",
"timestamp": 1753430000,
"data": { ... }
}
Test 按钮发的是 {"event":"test","timestamp":…,"message":"This is a test webhook from LaneChat"}——先拿它确认端点活着,再等真流量。
第五步:验签
每个请求带一个 X-Webhook-Signature 头:用你的签名密钥对原始 JSON body 做 HMAC-SHA256,hex 编码。Express 二十行:
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/webhooks/lanechat',
express.raw({ type: 'application/json' }),
(req, res) => {
const expected = crypto
.createHmac('sha256', process.env.LANE_WEBHOOK_SECRET)
.update(req.body) // 原始 body,先别 parse
.digest('hex');
if (req.headers['x-webhook-signature'] !== expected) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
console.log(event.event, event.data);
res.sendStatus(200);
});
app.listen(3000);
经典翻车点:对重新序列化过的 body 算 HMAC——JSON.parse 完再 JSON.stringify,键序理论上不变,实践中必变。签原始字节。

你的服务器挂了会怎样
投递规则说清楚,方便你照着设计:2xx 算送达。4xx 会被当作永久性问题——路由不对、鉴权失败——不重试。5xx 或网络错误最多重试 3 次,间隔 2、4、8 秒。每次投递都有日志,端点行为不端会留下完整案底,不会变成悬案。
实际的推论:先秒回 200,再异步处理。handler 里干重活干到超时,你就会吃到重试,把同一个事件处理两遍。先应答,后干活。
到这儿闭环就通了:任何会说 HTTPS 的代码都能往会话里放话,对面一有动静,你的服务器几秒内就听得到。