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。
工作原理
- 你的服务器创建会话及其成员(
PUT /conversations/{id})。 - 每个客户端用 pusher-js 连接,并以自己的用户身份调用
pusher.signin()。这会订阅#server-to-user-<user_id>,所以成员不需要为每个会话做频道授权。 - 你的服务器代替用户发送消息(
POST /conversations/{id}/messages)。Hibira 会以会话的下一个序号(seq)保存,接着把chat.message送给每位成员,并调用你的chat_messageWebhook。 - 离线过的客户端用
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)也包含在签名的查询字符串中。
会话与成员
| 方法与路径 | 内容或查询参数 | 返回 |
|---|---|---|
PUT /conversations/{id} | name、metadata、members(用户 ID;省略则保留现有成员)、roles({user_id: role})、watchers(最多 10 个 private 或 presence 频道) | 新建 201、取代 200,{conversation} |
GET /conversations/{id} | {conversation} | |
DELETE /conversations/{id} | {};一并删除成员、消息与文件 | |
POST /conversations/{id}/members | user_ids | {conversation} |
PUT /conversations/{id}/members/{user_id} | role(1–32 个 a-z 0-9 _ -;"" 表示无) | {conversation} |
DELETE /conversations/{id}/members/{user_id} | {conversation} | |
GET /users/{user_id}/conversations | limit(1–100)、before(上一页的 next) | {conversations, next},依最新动态排序,每笔含 unread |
会话:
{"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 频道,让团队收件匣用一个订阅追踪所有会话。请只授权你的员工订阅。
消息
| 方法与路径 | 内容或查询参数 | 返回 |
|---|---|---|
POST /conversations/{id}/messages | user_id(成员)、name、text、metadata、push、push_title、push_url、visible_to、attachments | 201 {message, push} |
GET /conversations/{id}/messages | after(seq)与 limit,或 last(最新 n 则) | {messages, last_seq},由旧到新 |
GET /conversations/{id}/search | q(1–100 字符)、before、limit(1–200) | {messages, next},由新到旧 |
PUT /conversations/{id}/messages/{seq} | user_id(发送者)、text、metadata | 含 edited_at 的 {message} |
DELETE /conversations/{id}/messages/{seq} | 查询参数 user_id(发送者);省略则以 App 身份删除 | 含 deleted_at 的 {message} |
POST /conversations/{id}/messages/{seq}/reactions | user_id、emoji | {message} |
DELETE /conversations/{id}/messages/{seq}/reactions | 查询参数 user_id、emoji | {message} |
POST /conversations/{id}/system | text、metadata(例如 {"event": "transferred", "to": "bob"}) | kind: "system" 的 201 {message} |
POST /conversations/{id}/typing | user_id、typing(true 或 false) | {};不会保存 |
消息:
{"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),才能取得编辑、删除与表情回应。搜索会匹配字词的任何部分,忽略大小写与全角,所以中文、日文、韩文都适用。系统消息说明发生了什么(加入、转接、结案):只有你的服务器能发送,不会推送,也不会算入未读。
已读回执与未读数
| 方法与路径 | 内容 | 返回 |
|---|---|---|
POST /conversations/{id}/read | user_id、seq | {read_seq} |
成员的已读位置只会前进,且不超过 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 产生:
| 方法与路径 | 内容 | 返回 |
|---|---|---|
POST /tokens | user_id、conversation_id(只限该会话)、join(第一次使用时把用户加入会话)、ttl(60–86400 秒) | 201 {token, expires_at} |
有了 Token,客户端可以列出自己的会话(GET /me/conversations)、读取、搜索、发送、编辑与删除自己的消息、加上表情回应、标记已读与送出输入中状态。用户一律是 Token 的 sub。客户端不能创建会话、变更成员、设置推送选项、禁言或封禁:这些都留在你的服务器。每位用户每秒最多写入 2 次,突发最多 10 次。带 conversation_id 与 join 的 Token 适合客服聊天的匿名访客:你的服务器创建会话、验证访客,再为自定义的 ID 发出短效 Token。
附件
- 你的服务器申请上传:
POST /conversations/{id}/attachments,带user_id、name、content_type与size(最大 25 MB)。返回内容含attachment与upload(url、method、headers)。 - 客户端在 15 分钟内,用完全相同的标头把文件直接上传到
upload.url。 - 你的服务器发送带
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 会说明推送了几位成员,或为什么没有推送。
内容审核
| 方法与路径 | 内容或查询参数 | 返回 |
|---|---|---|
PUT /moderation/filter | words(最多 500 个)、action(mask 或 reject) | {filter} |
POST /users/{user_id}/ban | reason、until(RFC 3339;省略表示无期限) | {user_id, banned, until} |
DELETE /users/{user_id}/ban | {user_id, banned} | |
POST /conversations/{id}/members/{user_id}/mute | reason、until | {conversation_id, user_id, muted, until} |
DELETE /conversations/{id}/members/{user_id}/mute | {conversation_id, user_id, muted} | |
POST /conversations/{id}/messages/{seq}/reports | user_id、reason | 201 {report} |
GET /moderation/reports | limit(1–200) | {reports},由新到旧 |
词语过滤会检查每条新的或编辑过的消息,匹配字词内部并忽略大小写与全角,所以中文与日文也能拦截。mask 把每个符合处换成*;reject 返回 422 且不保存。被封禁的用户在 App 的所有会话中发送、编辑、表情回应与输入中都会得到 403;被禁言的成员在该会话中发送与输入中会得到 403,但仍可读取。
事件与 Webhook
事件会送到相关成员各自的 #server-to-user-<user_id>,以及会话的 watchers。每个事件算一则消息额度。
| 事件 | 对象 | 数据 |
|---|---|---|
chat.conversation | 每位成员,于创建、变更或添加成员时 | {conversation} |
chat.message | 每位成员 | {conversation_id, message} |
chat.message_updated | 每位成员,于编辑、删除或表情回应之后 | {conversation_id, message} |
chat.typing | 输入者以外的每位成员 | {conversation_id, user_id, typing} |
chat.read | 每位成员 | {conversation_id, user_id, read_seq} |
chat.left | 被移除的成员 | {conversation_id} |
chat.deleted | 被删除会话的每位成员 | {conversation_id} |
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 即可接收。
限制
| 会话 ID | 1–128 个 A-Z a-z 0-9 _ - = @ , . ; |
| 用户 ID | 与 pusher.signin 用户 ID 规则相同 |
| 每个会话的成员 | 100 |
| 消息文字 | 4,000 字符,不可空白 |
| 会话或发送者名称 | 100 字符 |
metadata | JSON 对象,最多 4,096 bytes |
| 每次读取的消息 | 200 |
| 表情回应 | 每个 1–32 bytes,每条消息最多 20 种 |
| 附件 | 每个 25 MB,每条消息 10 个 |
写入共用 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。