Chat 平台 API 集成指南

请按照本指南,使用 Apps API 构建服务器端聊天集成。最后,您的集成将能够:

  • 向 Apps API 进行身份验证。

  • 创建或更新最终用户。

  • 为该最终用户发起对话。

  • 接收并验证来自 Contact Center AI 平台的 webhook 事件。

  • 在聊天中发送短信。

  • 处理可选分支,例如聊天前转写文本导入、队列选择虚拟客服路由、上报偏转和媒体附件。

  • 对话完成后,结束聊天。

本指南适用于正在构建后端服务的开发者,该服务可将客户拥有的聊天体验连接到 CCAI Platform。本教程假定您可以在 CCAI Platform 中创建 API 凭据、托管 HTTPS 网络钩子端点、安全地存储密钥,以及从服务器发出 HTTP 请求。

本指南是对 Apps API Chat 端点的补充。如需了解详尽的请求和响应 schema,请参阅 API 参考文档;如需了解推荐的端到端实现流程,请参阅本指南。

术语

以下定义适用于本文档:

  • 客户:在其自己的软件中实现聊天集成的 CCAI Platform 客户。

  • 使用方:客户拥有的服务器端应用,用于向 Apps API 发出请求并接收 CCAI 平台 webhook 事件。

  • 最终用户:使用客户的软件与客服人员或虚拟客服人员开始或继续对话的人员。

  • Chat:Apps API 创建的 CCAI Platform 对话资源。

  • Webhook 端点:使用方应用中用于接收来自 CCAI Platform 的聊天事件的 HTTPS 端点。

准备工作

在开始之前,请确保满足以下条件:

  • 应用 API 凭证

    • 在 CCAI Platform 中,依次前往设置 > 开发者设置 > API 凭证,创建 API 凭证。

    • 安全地存储凭证密钥。请勿在浏览器或移动客户端代码中公开它。

  • 租户网址详情

    • 确定您的 CCAI Platform 子网域和网域。

    • Apps API 基础网址为: https://YOUR_SUBDOMAIN.YOUR_DOMAIN/apps/api/v1

  • Webhook 端点

    • 托管可接收来自 CCAI Platform 的 POST 请求的公共 HTTPS 端点。

    • 在 CCAI 平台开发者设置中配置端点。

    • 生成并存储 webhook 的主要和次要密钥。

  • 队列或菜单配置

    • 确定新对话进入的队列或菜单。

    • 如果您使用队列选择虚拟客服,请先配置该虚拟客服并将其分配给入口队列,然后再通过 API 创建聊天。

  • 最终用户身份

    • 确定系统将为每位最终用户使用哪个稳定标识符。

    • 存储 Apps API 返回的 CCAI Platform 最终用户 ID。

  • 速率限制处理

    • CCAI Platform 会对 Apps API 进行速率限制。在集成中构建重试和退避机制,并避免针对单个租户发送突发请求。

身份验证和 webhook 安全性

您的集成使用两条身份验证路径:

  • 针对从您的服务器向 CCAI Platform 发出的请求进行 Apps API 身份验证。

  • 针对从 CCAI Platform 发送到您服务器的请求进行 Webhook 签名验证。

对 Apps API 请求进行身份验证

请求使用 HTTP 基本身份验证。在 CCAI Platform 的设置 > 开发者设置 > API 凭证下创建一个 API 令牌,并将其传入密码字段(推荐)。如果您的租户使用旧版身份验证路径,您可以改为将公司密钥作为用户名传递,并将公司密钥作为密码传递。如需了解完整的身份验证设置,请参阅 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"

将凭据存储在服务器端 Secret 存储区中,根据您的安全政策轮替凭据,并且切勿在浏览器或移动应用中传送凭据。

验证 webhook 请求

CCAI 平台会将聊天事件发送到您的 webhook 端点。每个 webhook 请求都包含:

  • X-Signature

  • X-Signature-Timestamp

X-Signature 标头可以包含主要签名、次要签名或两者:

primary=<primary_signature> secondary=<secondary_signature>

每个签名都是一个 Base64 编码的 HMAC-SHA256 摘要。签名值是时间戳标头与原始 JSON 请求正文串联而成的:

X-Signature-Timestamp + raw_request_body

在 webhook 处理程序中:

  1. 阅读X-Signature和X-Signature-Timestamp。

  2. 如果缺少任一标头,则拒绝请求。

  3. 拒绝过时的时间戳,以降低重放风险。

  4. 在解析 JSON 之前,先读取原始请求正文。

  5. 使用每个活跃的 webhook 密钥计算预期签名。

  6. 使用恒定时间比较法比较收到的签名和预期签名。

  7. 如果任何有效 Secret 匹配,则接受请求。

以下 Ruby 实现示例演示了如何验证 UJET 网络钩子签名:

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 Platform 事件、交换消息并结束聊天。

创建或更新最终用户

目标:确保 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 平台会向您配置的 webhook 端点发送 chat_created webhook 事件。

  • API 响应和 webhook 事件可以按任意顺序到达。将这两者都视为对同一聊天记录的更新,并以聊天 ID 作为键。

处理 Chat webhook 事件

目标:使消费者应用与 CCAI Platform 聊天状态保持同步。

您的 webhook 端点会处理来自 CCAI Platform 的聊天生命周期和消息事件。至少存储:

  • Chat 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 Platform 接受该消息。

  • 该消息会显示在客服人员或虚拟客服对话中。

  • 您的 webhook 端点会收到该消息的消息事件,包括您自己的应用通过 Apps API 发送的消息。

接收并显示来自 CCAI Platform 的消息

目标:在客户拥有的聊天体验中显示客服人员或虚拟客服消息。

当您的 webhook 端点收到消息事件时:

  1. 验证 webhook 签名。

  2. 检查事件是否为新事件。

  3. 通过聊天 ID 识别聊天。

  4. 识别发件人和消息类型。

  5. 在客户拥有的聊天界面中呈现消息。

  6. 持久保存事件,以便刷新或重试时不会丢失对话记录。

预期用途

客户拥有的聊天界面会按正确的顺序显示客服人员、虚拟客服和最终用户发送的消息。如果事件到达顺序不正确,请使用事件时间戳和您自己的持久层来协调显示顺序。

从虚拟客服升级到人工客服

目标:当最终用户需要客服帮助时,将聊天从虚拟客服处理移至人工队列。

如果您的集成使用队列选择虚拟客服,请将虚拟代理配置为将对话转接到目标队列。如果您的服务器直接发起上报,请使用 Apps API 上报端点。

端点

使用以下端点将聊天从虚拟客服升级到人工客服:

POST /apps/api/v1/chats/{chat_id}/escalations

示例请求

以下示例展示了用于上报对话的请求正文:

{
  "reason": "by_end_user_ask",
  "force_escalate": false
}

预期用途

  • 如果目标队列可用,聊天会转由客服人员处理。

  • 如果队列因非工作时间或容量过大而不可用,CCAI Platform 可以通过聊天流程返回或发送偏转选项。

  • 您的集成会向最终用户呈现可用的转移选项。

记录升级转移选择

目标:告知 CCAI Platform 最终用户选择了哪个分流选项。

当 CCAI Platform 提供升级偏转选项时,请使用升级更新端点记录最终用户的选择。

端点

使用以下端点,通过转移选项更新上报记录:

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 会结束聊天。

  • 您的 webhook 端点会收到最终的聊天状态事件。

  • 您的应用会将聊天标记为已完成,并停止接受该聊天的新的最终用户消息。

高级流程

以下分支是可选的。仅实现适用于您的集成的流程。

导入聊天前转写内容

如果最终用户在您创建 CCAI Platform 聊天之前已经在您的系统中进行了对话(例如聊天机器人对话),请使用此流程。

在创建对话时添加转写内容载荷。转写内容为智能体提供了上下文,因此最终用户无需重复信息。

Apps API 参考文档中包含确切的转录内容架构。

使用队列选择虚拟客服路由聊天

当您的应用将所有新聊天发送到条目队列并让虚拟客服决定最终目标队列时,请使用此流程。

  1. 创建用于队列选择的虚拟客服。

  2. 将虚拟客服分配给入口队列。

  3. 在创建对话时提供背景信息。

  4. 配置虚拟客服以检查上下文并将聊天上报到正确的队列。

  5. 如果目标队列不可用,则处理偏转选项。

发送照片或视频附件

当最终用户从客户拥有的聊天界面发送媒体内容时,请使用此流程。

媒体流包含四个阶段。

第 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": "Photo of the damaged item",
    "media_id": 789
  }
}

端点接受 type、content 和 media_id 作为平面值;content 中提供的嵌套对象会被舍弃,媒体引用会丢失。对视频消息使用 video 消息类型和视频 media_id。media_id 是 content 的同级。

在对话期间发送自定义数据

当您的集成需要将客户定义的上下文附加到活跃聊天时,请使用以下端点:

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 参考文档。