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 仍會回傳(唯讀);沒有加購方案的 Free 方案則會刪除。
從 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。