WhatsApp 会话如何跨标签交接:安全的 API 操作流程
通过 UnifyPort 将 WhatsApp 会话从一个工作流标签移到另一个,需要两次成员关系操作:先添加目标标签,核实结果,再移除来源标签。公开文档没有承诺原子转移。应保留无关标签,让尚未完成的交接保持可见;不要删除整个标签,也不要把请求超时理解为“肯定没有发生变更”。
要点
action: "remove"只对选定会话移除该标签;删除标签是另一项操作。- 多个标签可以同时存在,不能靠标签强制唯一负责人或唯一工作阶段。
- 先添加再移除有利于保留可见性,但中间会暂时出现两个标签。
- 权威分配记录和交接进度应放在应用中,不能只从标签推断。
定义交接,而不是替换全部标签
WhatsApp 的标签帮助说明介绍了用标签整理聊天,以及为一个聊天添加多个标签。页面也说明标签正逐步转换为列表。这种界面术语变化并不意味着 UnifyPort 的 label_id 字段已改名,也不证明应用内所有列表都有 API 对应项。
以下是一个假设场景:会话已有“待处理”和“VIP”,客服希望将它移到“处理中”。预期变更是添加“处理中”、移除“待处理”,保留“VIP”;不是把全部标签替换成“处理中”。这适用于跨境团队的客服交接,但不是某个客户的实际案例。
六月 API 更新介绍过标签端点。执行交接时,应区分它们操作的对象:
| 意图 | 操作 | 对象 |
|---|---|---|
| 新建或重命名分类 | 创建或更新标签 | 标签定义 |
| 添加目标标签 | 成员关系 action: "add" | 此标签下的选定会话 |
| 移除来源标签 | 成员关系 action: "remove" | 此标签下的选定会话 |
| 停用一个分类 | 删除标签 | 标签本身,而非某个会话的归属 |
不能用删除标签代替从一个会话移除标签;其他会话可能仍在使用该分类。
准备两次操作
在选定消息账号下读取标签目录,取得来源和目标 ID,并完成分页。名称用于展示,不是稳定的身份标识。如果需要创建目标标签,调用创建或更新标签,保存返回的 data.id。
在同一消息账号下确定聊天的 conversation_id。不要用标签名称、联系人显示名或消息 ID 替代会话 ID。先核对渠道操作支持矩阵:这些标签操作目前支持 whatsapp,不支持 LINE、Telegram、Zalo,也不支持独立的 whatsapp-protocol 渠道。
标签成员关系参考规定,请求为 POST /v1/accounts/{account_id}/conversations/labels,包含 label_id、action 和非空 conversation_ids 数组。
下面的服务端 JavaScript 辅助函数只执行一次操作,刻意不自动完成整个交接。传入后端实际选中的 ID 和安全保存的 API key。这是说明性代码,不是已经验证的生产客户端。
async function changeLabelMembership({
apiKey, accountId, labelId, conversationIds, action
}) {
const validId = value => typeof value === 'string' && value.trim() !== '';
if (!validId(accountId) || !validId(labelId) ||
!Array.isArray(conversationIds) || conversationIds.length === 0 ||
!conversationIds.every(validId) || !['add', 'remove'].includes(action)) {
throw new Error('Select an account, label, conversations, and action');
}
const response = await fetch(
`https://api.unifyport.ai/v1/accounts/${encodeURIComponent(accountId)}/conversations/labels`,
{
method: 'POST',
headers: { 'X-Api-Key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({
label_id: labelId,
action,
conversation_ids: [...new Set(conversationIds)]
})
}
);
const result = await response.json();
return { httpStatus: response.status, result };
}
第一次调用使用目标 ID 和 add;后续调用使用来源 ID 和 remove,会话保持相同。在本地拒绝来源与目标 ID 相同的交接。检查 HTTP 状态和响应数据:fetch 正常返回不一定代表操作成功。文档中的成功响应标识受影响的 label_id、action 和 conversation_ids。
在添加与移除之间设置核实关卡
以下是应用设计建议,不是 UnifyPort 的事务保证:
- 在本地保存目标账号、会话、来源及目标标签,确认操作员有权修改该工作流分配。
- 添加目标标签,记录响应和
request_id,不记录 API key。 - 使用显式
label_id调用会话列表,读取目标标签视图。确认选定会话已出现,再移除来源标签。 - 只移除来源成员关系,保留 VIP 等独立标签。
- 复核两个视图;观察结果与预期状态不符时,显示尚未完成的工作。
不要用不带标签筛选的列表验证目标。文档规定,WhatsApp 省略 label_id 时返回特别关注会话。会话缺失与标签筛选排查解释了这一边界和分页规则。第一页没有某个会话,不等于它不存在;读取失败也不是空结果。实时分页查询没有不可变快照保证。
| 观察到的标签归属 | 对本次交接的含义 | 建议处理 |
|---|---|---|
| 只有来源 | 尚未观察到目标 | 保留来源,调查添加操作 |
| 来源和目标都存在 | 中间状态,或其他编辑者的变更 | 确认当前意图,再移除来源 |
| 只有目标 | 已观察到预期归属模式 | 本地分配仍一致时记录完成 |
| 都没有,或读取不完整 | 状态未解决 | 保留本地工作并调查,不猜测完成 |
恢复时不要重放过期意图
在应用中串行处理同一会话的变更,发出请求前拒绝过期任务。如果另一位客服已经更改分配,不要盲目完成旧交接。本地串行化不能锁住所有关联客户端,因此要核对外部变更,而不是声称独占控制。
添加超时后,保留来源,先检查目标再决定下一步。移除结果不确定时,先检查两个视图再考虑重复请求。不要自动移除目标来“回滚”:其他操作员可能已经依赖该标签。
公开参考没有承诺两次调用之间的原子性、整批全成全败、版本前置条件或专门的标签变更 webhook。批量处理时,逐会话保存进度,不要将整批请求当作一个已经完成的分配。set_label_members_failed 表示文档所述的服务端或上游失败;unsupported_by_provider 是能力边界,不是无限重试的理由。
常见问题
添加新标签会自动移除旧标签吗?
不会。API 提供明确的添加和移除动作,应独立处理每个标签成员关系。
标签能保证只有一个客服负责人吗?
此契约不提供这种保证。在应用中保存负责人和权限,让标签作为渠道侧的展示投影。
conversation.updated 会确认交接完成吗?
不要这样假设。该事件记录的设置包括静音、归档、置顶和已读状态,并未承诺标签成员关系回执。
这是 WhatsApp Cloud API 官方操作吗?
不是。这些是面向已连接账号的 UnifyPort 非官方接口端点。WhatsApp 应用帮助只解释用户侧概念,不定义此 API 契约。
下一步与参考资料
从为会话添加或移除标签开始。在受控账号中测试正常交接、目标添加失败、移除超时、并发编辑、分页和无关标签保留,再开放批量操作。这些是建议的验收项,不是已执行的测试结果。
资料核对日期:2026-10-14。
让消息接入变成一条稳定的产品管线。
先用统一 API 跑通发送,再用标准事件把所有入站消息接回业务系统。