您可以使用本指南通过 Apps API 构建服务器端聊天集成。完成本教程后,您的集成将能够:
向 Apps API 进行身份验证。
创建或更新最终用户。
为该最终用户发起聊天。
接收并验证来自 Contact Center AI 平台 的网络钩子事件。
在聊天中发送文本消息。
处理可选分支,例如预聊天记录导入、队列选择虚拟代理路由、升级分流和媒体附件。
在对话结束后结束聊天。
本指南面向正在构建后端服务的开发者,该服务可将客户自有的聊天体验连接到 CCAI Platform。它假设您可以在 CCAI 平台中创建 API 凭据、托管 HTTPS webhook 端点、安全地存储密钥,以及从服务器发出 HTTP 请求。
本指南是对 Apps API Chat 端点的补充。请参阅 API 参考文档,了解详尽的请求和响应架构;并参阅本指南,了解建议的端到端实现流程。
术语
以下定义适用于本文档:
客户:在自己的软件中实现聊天集成的 CCAI 平台客户。
使用方:客户自有服务器端应用,用于向 Apps API 发出请求并接收 CCAI 平台 webhook 事件。
最终用户:使用客户的软件开始或继续与客服或虚拟客服对话的人员。
聊天:Apps API 创建的 CCAI 平台对话资源。
Webhook 端点:使用方应用中用于接收来自 CCAI 平台的聊天事件的 HTTPS 端点。
准备工作
在开始之前,请确保满足以下条件:
应用 API 凭据
在 CCAI 平台中创建 API 凭据,依次选择设置 > 开发者设置 > API 凭据。
安全地存储凭据 Secret。请勿在浏览器或移动客户端代码中公开该密钥。
租户网址详细信息
确定您的 CCAI Platform 子网域和网域。
Apps API 的基本网址为:
https://YOUR_SUBDOMAIN.YOUR_DOMAIN/apps/api/v1
网络钩子端点
托管一个可接收来自 CCAI Platform 的 POST 请求的公共 HTTPS 端点。
在 CCAI 平台开发者设置中配置端点。
生成并存储 Webhook 主密钥和辅助密钥。
队列或菜单配置
确定新对话进入的队列或菜单。
如果您使用队列选择虚拟客服,请先配置该虚拟客服并将其分配给入口队列,然后再通过 API 创建对话。
最终用户身份
确定您的系统将为每位最终用户使用哪个稳定标识符。
存储 Apps API 返回的 CCAI Platform 最终用户 ID。
速率限制处理
- CCAI Platform 会对 Apps API 进行速率限制。在集成中构建重试和退避机制,并避免为单个租户发送大量请求。
身份验证和 Webhook 安全性
您的集成使用两条身份验证路径:
针对从您的服务器向 CCAI Platform 发出的请求进行 Apps API 身份验证。
针对从 CCAI Platform 发送到您服务器的请求进行网络钩子签名验证。
对 Apps API 请求进行身份验证
请求使用 HTTP 基本身份验证。在 CCAI 平台中,依次点击设置 > 开发者设置 > API 凭据,创建 API 令牌,并在 password 字段中传递该令牌(推荐)。如果您的租户使用旧版身份验证路径,则可以改为将公司密钥作为用户名传递,并将公司密钥作为密码传递。如需了解完整的身份验证设置,请参阅 Apps API 参考文档。以下示例演示了如何使用基本身份验证对 Apps API 请求进行身份验证:
curl -X GET \
https://YOUR_SUBDOMAIN.YOUR_DOMAIN/apps/api/v1/chats/{chat_id} \
-u "YOUR_SUBDOMAIN:YOUR_API_TOKEN" \
-H "Accept: application/json"
将凭据存储在服务器端密钥存储区中,根据您的安全政策轮换这些凭据,并且绝不在浏览器或移动应用中提供这些凭据。
验证 webhook 请求
CCAI 平台会将聊天事件发送到您的网络钩子端点。每个网络钩子请求都包含:
X-SignatureX-Signature-Timestamp
X-Signature 标头可以包含主签名、辅助签名或同时包含两者:
primary=<primary_signature> secondary=<secondary_signature>
每个签名都是经过 Base64 编码的 HMAC-SHA256 摘要。签名值是时间戳标头与原始 JSON 请求正文的串联:
X-Signature-Timestamp + raw_request_body
在您的网络钩子处理程序中:
请参阅
X-Signature和X-Signature-Timestamp。如果缺少任一标头,则拒绝相应请求。
拒绝过时的时间戳,以降低重放风险。
在解析 JSON 之前读取原始请求正文。
使用每个有效的 Webhook Secret 计算预期签名。
使用恒定时间比较来比较收到的签名和预期签名。
如果任何有效密钥匹配,则接受请求。
以下 Ruby 实现示例演示了如何验证 UJET Webhook 签名:
require "base64"
require "openssl"
require "active_support/security_utils"
def parse_ujet_signature(header)
header.to_s.split(/\s+/).each_with_object({}) do |part, result|
key, value = part.split("=", 2)
result[key] = value if key && value
end
end
def expected_signature(secret, timestamp, raw_body)
Base64.strict_encode64(
OpenSSL::HMAC.digest(
OpenSSL::Digest.new("sha256"),
secret,
"#{timestamp}#{raw_body}"
)
)
end
def secure_match?(received, expected)
return false if received.nil? || expected.nil?
return false unless received.bytesize == expected.bytesize
ActiveSupport::SecurityUtils.secure_compare(received, expected)
end
def verify_ujet_webhook!(request, primary_secret:, secondary_secret:)
signature_header = request.headers["X-Signature"]
timestamp = request.headers["X-Signature-Timestamp"]
return false if signature_header.nil? || timestamp.nil?
# Optional but recommended: reject stale requests.
return false if (Time.now.utc - Time.at(timestamp.to_i).utc).abs > 5.minutes
raw_body = request.body.read
signatures = parse_ujet_signature(signature_header)
expected = [
expected_signature(primary_secret, timestamp, raw_body),
expected_signature(secondary_secret, timestamp, raw_body)
].compact
received = [
signatures["primary"],
signatures["secondary"]
].compact
received.any? do |received_signature|
expected.any? do |expected_signature_value|
secure_match?(received_signature, expected_signature_value)
end
end
end
如果验证成功,请快速返回成功响应,并以幂等方式处理事件。Webhook 传送和 API 响应可能会以不同的顺序到达,因此请构建集成,使其能够容忍多次收到相同的状态更改,而不会创建重复记录。
集成流程
以下流程会创建最终用户、发起聊天、接收 CCAI 平台事件、交换消息并结束聊天。
创建或更新最终用户
目标:确保在创建对话之前,CCAI Platform 具有最终用户记录。
端点
使用以下端点创建或更新最终用户:
POST /apps/api/v1/end_users
示例请求
以下示例展示了用于创建或更新最终用户的请求正文:
{
"identifier": "customer-user-12345",
"email": "customer.user@example.com",
"name": "Customer User",
"phone": "+15551234567"
}
存储内容
将响应中的 CCAI Platform 最终用户 ID 存储在您的系统中。创建对话时,请使用该 ID。
预期用途
如果最终用户不存在,CCAI Platform 会创建一条新记录。
如果已存在具有相同标识符的最终用户,CCAI Platform 会更新相应记录并返回现有最终用户的信息。
创建聊天
目标:为最终用户启动新的 CCAI Platform 对话。
端点
使用以下端点开始新对话:
POST /apps/api/v1/chats
示例请求
以下示例展示了用于创建对话的请求正文:
{
"chat": {
"menu_id": 123,
"end_user_id": 456,
"lang": "en"
}
}
虚拟客服路由的可选上下文
如果您的队列选择虚拟客服需要来自应用的上下文,请在创建对话时添加上下文载荷,如以下示例所示:
{
"chat": {
"menu_id": 123,
"end_user_id": 456,
"lang": "en",
"context": {
"value": {
"customer_tier": "gold",
"issue_type": "billing"
}
}
}
}
虚拟客服可以使用该上下文中的值来决定哪个队列接收聊天。
预期用途
Apps API 会返回聊天资源。
CCAI 平台会向您配置的网络钩子端点发送
chat_created网络钩子事件。API 响应和 webhook 事件可以按任意顺序到达。将两者都视为同一聊天记录(以聊天 ID 为键)的更新。
处理聊天 webhook 事件
目标:使消费类应用与 CCAI Platform 对话状态保持同步。
您的网络钩子端点处理来自 CCAI 平台的聊天生命周期和消息事件。至少存储:
聊天 ID。
活动类型。
事件时间戳。
如果事件包含消息,则为消息发送者、消息类型和消息内容。
当事件描述的是路由行为时,任何升级或分流数据。
建议的行为
在处理事件之前,验证每个 Webhook 签名。
存储已处理的事件 ID 或确定性事件键,以避免重试创建重复项。
在接受事件后返回 2xx 响应。
尽可能异步处理下游副作用。
预期用途
当 CCAI 平台发送聊天创建、传入消息、客服消息、升级变更和聊天完成等事件时,您的应用会更新其聊天状态。
发送短信
目标:从消费类应用向 CCAI Platform 对话发送最终用户消息。
端点
使用以下端点将文本消息发送到聊天中:
POST /apps/api/v1/chats/{chat_id}/message
示例请求
以下示例展示了用于发送短信的请求正文:
{
"from_user_id": 456,
"message": {
"type": "text",
"content": "Hello, I need help with my order."
}
}
预期用途
CCAI 平台接受消息。
该消息会显示在客服人员或虚拟客服人员的对话中。
您的 webhook 端点会收到相应消息的消息事件,包括您自己的应用通过 Apps API 发送的消息。
接收并显示来自 CCAI 平台的消息
目标:在客户拥有的聊天体验中显示客服或虚拟客服消息。
当网络钩子端点收到消息事件时:
验证 webhook 签名。
检查活动是否为新活动。
通过聊天 ID 识别聊天。
标识发件人和消息类型。
在客户拥有的聊天界面中呈现消息。
持久保存事件,以便刷新或重试不会丢失对话历史记录。
预期用途
客户自有聊天界面会按正确的顺序显示客服人员、虚拟客服和最终用户发送的消息。如果事件到达顺序不正确,请使用事件时间戳和您自己的持久层来协调展示顺序。
从虚拟客服升级到人工客服
目标:当最终用户需要客服帮助时,将聊天从虚拟客服处理转为人工客服队列。
如果您的集成使用队列选择虚拟客服,请将虚拟客服配置为将对话转接到目标队列。如果您的服务器直接发起升级,请使用 Apps API 升级端点。
端点
使用以下端点将聊天从虚拟客服上报给人工客服:
POST /apps/api/v1/chats/{chat_id}/escalations
示例请求
以下示例展示了用于升级对话的请求正文:
{
"reason": "by_end_user_ask",
"force_escalate": false
}
预期用途
如果目标队列可用,聊天会移向代理处理。
如果队列因非工作时间或容量过大而无法使用,CCAI Platform 可以通过对话流程返回或发送分流选项。
您的集成会向最终用户呈现可用的分流选项。
记录升级转移选择
目标:告知 CCAI Platform 最终用户选择了哪个分流选项。
当 CCAI 平台提供升级分流选项时,请使用升级更新端点记录最终用户的选择。
端点
使用以下端点更新包含分流选项的升级记录:
PATCH /apps/api/v1/chats/{chat_id}/escalations/{escalation_id}
支持的 deflection_channel 值:
email- 最终用户选择电子邮件改道选项。virtual_agent- 最终用户选择继续与虚拟客服对话。human_agent- 最终用户选择继续等待人工客服。此值仅适用于超容量分流。
示例请求
以下示例展示了用于记录转接选择的请求正文:
{
"deflection_channel": "email"
}
仅向此端点发送受支持的 deflection_channel 值。
external_link 不是升级更新端点的有效值;当最终用户点击外部分流链接时,聊天会结束。
预期用途
CCAI Platform 会根据所选选项更新升级记录并转换对话。
结束聊天
目标:在对话结束后关闭聊天。
端点
使用以下端点结束正在进行的对话:
PATCH /apps/api/v1/chats/{chat_id}/end
示例请求
以下示例展示了用于结束对话的请求正文:
{
"ended_by_user_id": 456
}
预期用途
CCAI Platform 结束聊天。
您的网络钩子端点会收到最终的聊天状态事件。
应用将相应对话标记为已完成,并停止接受该对话的新最终用户消息。
高级流程
以下分支是可选的。仅实现适用于您的集成方案的流程。
导入预聊天转写
如果最终用户在您创建 CCAI 平台聊天之前已在您的系统中进行过对话(例如聊天机器人对话),请使用此流程。
在创建聊天时添加转写载荷。转写内容可为智能体提供上下文,这样最终用户就不必重复提供信息。
Apps API 参考文档包含确切的转写架构。
通过队列选择虚拟客服转接对话
如果您的应用将所有新对话发送到入口队列,并让虚拟客服决定最终的目标队列,请使用此流程。
创建用于选择队列的虚拟客服。
将虚拟客服分配给入口队列。
创建对话时包含上下文。
配置虚拟客服,使其检查上下文并将对话升级到正确的队列。
如果目标队列不可用,则处理转接选项。
发送照片或视频附件
当最终用户通过客户自有聊天界面发送媒体时,使用此流程。
媒体流包含四个阶段。
阶段 1 - 请求预签名上传网址
使用以下端点请求用于上传照片或视频的预签名网址:
POST /apps/api/v1/chats/{chat_id}/photos/upload
POST /apps/api/v1/chats/{chat_id}/videos/upload
阶段 2 - 将文件上传到返回的存储网址
包含文件以及 CCAI 平台在预签名上传响应中返回的任何字段。
第 3 阶段 - 将上传的文件添加到对话中
使用以下端点将上传的照片或视频添加到聊天中:
POST /apps/api/v1/chats/{chat_id}/photos
POST /apps/api/v1/chats/{chat_id}/videos
存储 CCAI 平台返回的 media_id。Chat 消息载荷通过媒体 ID 引用媒体。
第 4 步 - 将媒体作为消息发送
使用以下端点将媒体消息发送到聊天中:
POST /apps/api/v1/chats/{chat_id}/message
示例请求
以下示例展示了用于发送照片附件的请求正文:
{
"from_user_id": 456,
"message": {
"type": "photo",
"content": {
"media_id": 789
}
}
}
对于视频消息,请使用 video 消息类型和视频 media_id。
在聊天期间发送自定义数据
如果您的集成需要将客户定义的上下文附加到活跃对话,请使用以下端点:
POST /apps/api/v1/chats/{chat_id}/custom_data
Apps API 参考文档定义了确切的载荷形状和预留键行为。
在对话期间更新最终用户身份
当最终用户的身份在聊天开始后发生变化或变得已知时,请使用以下端点:
POST /apps/api/v1/chats/{chat_id}/end_user
例如,当匿名最终用户在活跃聊天期间登录时,如果您的集成需要 CCAI Platform 将聊天与更新后的最终用户身份相关联,则可以使用此端点。
收集客户满意度 (CSAT) 或评分数据
如果您的集成负责提供聊天后评分体验,请使用以下聊天 CSAT 和评分端点:
GET /apps/api/v1/chats/{chat_id}/csat
GET /apps/api/v1/chats/{chat_id}/rating
PATCH /apps/api/v1/chats/{chat_id}/rating
如需了解确切的资格条件和评分载荷,请参阅 Apps API 参考文档。