Chat API 参考文档:会话、消息、已读回执与事件

如何调用 Hibira Chat API:签名请求、会话与成员、消息、补齐漏掉的消息、已读回执与未读数、给浏览器用的用户 Token、附件、推送、内容审核、事件与限制。

开始之前

Chat API 会保存会话与其中的消息,并通过你的 App 已经创建的实时连接送达。由你的服务器决定每个会话有哪些人并调用 API;客户端只需要连接、登录并接收。

  • 目前为 Beta。 账号加入 Beta 或购买 Chat 附加包后即可使用。在此之前,每个请求都会返回 403 the Chat API is not enabled for this app。加入候补名单即可申请。
  • Base URL: https://ws.hibira.io/apps/APP_ID/chat(请使用控制台 App 页面上显示的主机)。
  • 内容为 JSON。错误以 4xx 或 5xx 状态码返回 {"error": "..."}。
  • 用户 ID 由你决定:使用与客户端传给 pusher.signin() 相同的 ID。

工作原理

  1. 你的服务器创建会话及其成员(PUT /conversations/{id})。
  2. 每个客户端用 pusher-js 连接,并以自己的用户身份调用 pusher.signin()。这会订阅 #server-to-user-<user_id>,所以成员不需要为每个会话做频道授权。
  3. 你的服务器代替用户发送消息(POST /conversations/{id}/messages)。Hibira 会以会话的下一个序号(seq)保存,接着把 chat.message 送给每位成员,并调用你的 chat_message Webhook。
  4. 离线过的客户端用 GET /conversations/{id}/messages?after=<手上最后的 seq> 读回漏掉的消息。

每个变更都会先保存,再送出事件。如果 App 超过每日消息额度,变更仍会保存但不会送出事件,客户端可以读取补齐。

签名请求

来自你服务器的请求,与 Pusher HTTP API 的其他请求一样以 App key 与 secret 签名:查询字符串带有 auth_key、auth_timestamp、auth_version=1.0、body_md5(有内容时)与 auth_signature,也就是对 METHOD\nPATH\nQUERY(查询参数依名称排序)计算的 HMAC-SHA256。任何 Pusher 服务器库的签名代码都能用;以下是 Node.js 的完整写法:

import crypto from 'node:crypto';

const { PUSHER_APP_ID: id, PUSHER_APP_KEY: key, PUSHER_APP_SECRET: secret } = process.env;

export async function chat(method, path, body) {
  const fullPath = `/apps/${id}/chat${path}`;
  const json = body === undefined ? '' : JSON.stringify(body);
  const params = { auth_key: key, auth_timestamp: Math.floor(Date.now() / 1000), auth_version: '1.0' };
  if (json) params.body_md5 = crypto.createHash('md5').update(json).digest('hex');
  const query = Object.keys(params).sort().map((k) => `${k}=${params[k]}`).join('&');
  const signature = crypto.createHmac('sha256', secret).update(`${method}\n${fullPath}\n${query}`).digest('hex');
  const res = await fetch(`https://ws.hibira.io${fullPath}?${query}&auth_signature=${signature}`, {
    method, body: json || undefined, headers: { 'Content-Type': 'application/json' },
  });
  if (!res.ok) throw new Error(`${res.status} ${(await res.json()).error}`);
  return res.json();
}

await chat('PUT', '/conversations/order-1042', { name: 'Order #1042', members: ['ann', 'support-bob'] });
await chat('POST', '/conversations/order-1042/messages', { user_id: 'ann', name: 'Ann', text: 'Where is my parcel?' });

GET 的查询参数(例如 after 或 limit)也包含在签名的查询字符串中。

会话与成员

会话:

{"id": "order-1042", "name": "Order #1042", "metadata": {"kind": "support"},
 "members": [{"user_id": "ann", "read_seq": 3}, {"user_id": "support-bob", "read_seq": 1, "role": "agent"}],
 "last_seq": 3, "created_at": "...", "updated_at": "..."}

角色与内部备注。 带有 visible_to(最多 5 个角色)的消息只会送到拥有其中一个角色的成员:其他人不会收到、读到,也不会算入未读。Watchers 是会收到该会话所有事件的 private 或 presence 频道,让团队收件匣用一个订阅追踪所有会话。请只授权你的员工订阅。

消息

消息:

{"id": "...", "seq": 3, "sender": "ann", "name": "Ann", "kind": "text",
 "text": "hi", "metadata": {"reply_to": 2}, "reactions": {"👍": ["support-bob"]}, "created_at": "..."}

编辑过的消息保留 seq 并加上 edited_at。删除的消息保留位置、带 deleted_at 且没有文字,之后不能再编辑或表情回应(409)。after 只返回更新的消息,所以回到在线的客户端要重新读取界面上的消息(last=n),才能取得编辑、删除与表情回应。搜索会匹配字词的任何部分,忽略大小写与全角,所以中文、日文、韩文都适用。系统消息说明发生了什么(加入、转接、结案):只有你的服务器能发送,不会推送,也不会算入未读。

已读回执与未读数

成员的已读位置只会前进,且不超过 last_seq;消息的发送者视为已读。用户会话列表中的 unread 计算 read_seq 之后、非系统消息的数量。有人的 read_seq 前进时,每位成员都会收到 chat.read。

浏览器与 App:用户 Token

客户端可以自己在 /apps/APP_ID/chat/me 底下调用 Chat API,带上 Authorization: Bearer <user token>,你的服务器不需要代理。Token 是用 App secret 签署的 JWT(HS256、{sub, exp}、最长 30 天);你的服务器可用任何 JWT 库或 POST /tokens 产生:

有了 Token,客户端可以列出自己的会话(GET /me/conversations)、读取、搜索、发送、编辑与删除自己的消息、加上表情回应、标记已读与送出输入中状态。用户一律是 Token 的 sub。客户端不能创建会话、变更成员、设置推送选项、禁言或封禁:这些都留在你的服务器。每位用户每秒最多写入 2 次,突发最多 10 次。带 conversation_id 与 join 的 Token 适合客服聊天的匿名访客:你的服务器创建会话、验证访客,再为自定义的 ID 发出短效 Token。

附件

  1. 你的服务器申请上传:POST /conversations/{id}/attachments,带 user_id、name、content_type 与 size(最大 25 MB)。返回内容含 attachment 与 upload(url、method、headers)。
  2. 客户端在 15 分钟内,用完全相同的标头把文件直接上传到 upload.url。
  3. 你的服务器发送带 attachments 的消息:第 1 步取得的 id、name 与 content_type(每条最多 10 个;此时 text 可以留空)。

从 API 或事件读到的每个附件都带有一小时内可下载的 url。一天内没有送出的上传会被移除,删除消息或会话也会删除其文件。

推送给离线成员

新消息会通过 App 的 Hibira Push 推送给未连接的成员(push: "offline",默认)、除发送者外的所有成员("always"),或不推送("never")。标题是发送者与会话名称(或 push_title),正文是文字的前 180 个字符,点击时打开 push_url。设备以兼容 Beams 的推送 SDK 用相同的用户 ID 注册。返回的 push 会说明推送了几位成员,或为什么没有推送。

内容审核

词语过滤会检查每条新的或编辑过的消息,匹配字词内部并忽略大小写与全角,所以中文与日文也能拦截。mask 把每个符合处换成*;reject 返回 422 且不保存。被封禁的用户在 App 的所有会话中发送、编辑、表情回应与输入中都会得到 403;被禁言的成员在该会话中发送与输入中会得到 403,但仍可读取。

事件与 Webhook

事件会送到相关成员各自的 #server-to-user-<user_id>,以及会话的 watchers。每个事件算一则消息额度。

const pusher = new Pusher('APP_KEY', { cluster: 'hkg', wsHost: 'ws.hibira.io', userAuthentication: { endpoint: '/pusher/user-auth' } });
pusher.signin();
pusher.user.bind('chat.message', ({ conversation_id, message }) => showMessage(conversation_id, message));

每条新消息也会调用你服务器的 chat_message Webhook:channel 是会话 ID,user_id 是发送者,data 是 JSON {"conversation_id", "message"}。在 App 的某个 Webhook 事件中加入 chat_message 即可接收。

限制

写入共用 App 的 HTTP API 速率限制(超过时返回 429)。

每月活跃用户与保存期间

用户在某个月(UTC)发送消息或标记会话已读,即为该月的活跃用户,账号下所有 App 只计算一次。每个账号每月包含 500 位;Chat 附加包可提高到 1,000、5,000、20,000 或 60,000 位(套餐)。当月额满后,尚未计入的用户在发送与已读时会得到 429,直到下个月;已计入的用户不受影响。GET /usage 返回 {month, active_users, limit}。

消息在 30 天内可完整编辑。之后,付费套餐或有 Chat 附加包的账号会将消息保存在封存中,API 仍会返回(唯读);没有附加包的 免费套餐则会删除。

从 AI 代理使用

Hibira MCP 服务器提供 Chat API 工具,让 AI 编程代理帮你设置与测试聊天:create_conversation、send_message、list_conversations 与 list_messages。它们作用在你自己的 App 上,检查与签名请求相同,且账号需要已开通 Chat API。适用 Claude Code、Codex、Cursor 等工具的代理插件提供一个技能,依照这些步骤把 Chat API 接入项目。若需要 React 或纯 JavaScript 的现成会话界面,请见 @hibira-io/chat-ui。