撤回、編輯與置頂
從你的服務端修改已經發出的訊息。都按訊息 ID(message_id)指定訊息,會話中能看到這條訊息的人都會同步到變化:線上裝置立即收到,離線裝置下次同步時收到。
| 要做什麼 | 介面 | 常見用途 |
|---|---|---|
| 撤回訊息 | POST /messages/{message_id}/recall | 處理違規內容、撤回發錯的通知 |
| 批次撤回訊息 | POST /messages/batch-recall | 清理某個使用者刷屏的訊息 |
| 編輯訊息 | PATCH /messages/{message_id} | 更新訂單、審批卡片的狀態,更正通知 |
| 置頂訊息、取消置頂 | PUT、DELETE /messages/{message_id}/pin | 群公告、重要通知 |
| 查詢會話中的置頂訊息 | GET /conversations/{conversation_id}/pins | 在你的後臺展示置頂列表 |
例如,撤回一條訊息:
curl -X POST "$IM_API/$ORG/$APP/messages/99583385822822400/recall" \
-H "Authorization: Bearer $APP_TOKEN"可以放心重試:這些介面都返回修改後的訊息物件和 changed。重複操作(撤回已撤回的、置頂已置頂的、編輯為相同的內容)不報錯,changed 為 false,訊息不變。
撤回訊息
撤回一條訊息。服務端可以撤回任何人發的任何訊息,包括以系統身份傳送的訊息和群提示,不受撤回時限的限制:執行策略中的 recall_window_seconds(預設 120 秒)只限制使用者在客戶端撤回自己的訊息。
/{org_name}/{app_name}/messages/{message_id}/recall撤回的內容不能恢復
撤回後服務端也查不到原內容。需要留檔的,請在撤回前匯出。
撤回之後:
| 影響 | 結果 |
|---|---|
| 訊息內容 | body、ext、mentions、reply_to 變為 null;recalled 為 { "by": null, "role": "server", "at": "..." } |
| 表情回應、置頂 | 清除、取消 |
| “有人 @ 我”提醒 | 退回到之前一條仍然有效的 @ |
| 未讀數 | 不變 |
| 會話參與者 | 都同步到撤回(包括髮送者,以及已離開群但能看到這條訊息的人)。客戶端可以按 recalled.role 顯示不同文字,如 server 時顯示“該訊息已被撤回” |
| 已發出的離線推送 | 替換為“一條訊息已被撤回”(部分推送通道不支援);還沒發出的不再發出,見撤回後更新通知 |
| 引用了它的訊息 | 仍保留引用的序號,客戶端顯示“原訊息已撤回”;之後不能再引用、編輯或置頂它 |
| 訊息中的附件 | 不會刪除,保留期內仍能下載。違規的圖片或檔案需要徹底刪除時,撤回前記下檔案地址,再刪除檔案 |
應用處於只讀狀態時也可以撤回。
路徑引數
org_name、app_name 見接入概述。
| 引數 | 型別 | 說明 |
|---|---|---|
message_id | String | 要撤回的訊息 ID |
請求體
可以沒有請求體。
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
reason | String | 否 | 撤回的原因,記入操作日誌,不會通知給使用者 |
請求示例
curl -X POST "$IM_API/$ORG/$APP/messages/99583385822822400/recall" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"reason": "违规内容"
}'響應
成功返回 200 OK:
| 欄位 | 型別 | 說明 |
|---|---|---|
message | Object | 撤回後的訊息物件 |
changed | Boolean | 這次是否撤回了訊息;訊息之前已經被撤回時為 false |
{
"message": {
"conversation_id": "99583031936811008",
"conversation_type": "single",
"seq": 3,
"message_id": "99583385822822400",
"client_msg_id": "d83eb893f17d57d202db27cb79f46ef0",
"sender": "carol",
"sender_type": "user",
"recipient": "dave",
"type": "text",
"body": null,
"ext": null,
"mentions": null,
"reply_to": null,
"need_receipt": false,
"exclude_from_unread": false,
"reactions": null,
"pinned": null,
"edited": null,
"recalled": { "by": null, "role": "server", "at": "2026-10-02T19:08:59.798Z" },
"erased": false,
"via": "openapi",
"created_at": "2026-10-02T19:08:49.350Z",
"message_version": 22
},
"changed": true
}錯誤
| HTTP 狀態碼 | code | 說明 |
|---|---|---|
| 404 | not_found | 訊息不存在、已超過保留期,或 message_id 的格式不對 |
批次撤回訊息
一次撤回多條訊息,最多 100 條,例如撤回某個使用者刷屏的訊息(可以先用查詢使用者發出的訊息找出它們)。每條訊息分別處理,規則與撤回訊息相同,某條失敗不影響其他條。
/{org_name}/{app_name}/messages/batch-recall呼叫一次按訊息條數計入應用每秒的呼叫次數。
路徑引數
org_name、app_name 見接入概述。
請求體
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
message_ids | Array<String> | 是 | 要撤回的訊息 ID,1 到 100 個,每個都必須是字串 |
reason | String | 否 | 撤回的原因,記入操作日誌 |
請求示例
curl -X POST "$IM_API/$ORG/$APP/messages/batch-recall" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message_ids": ["99583386074480640", "99583385822822400", "123", "99583386288390144"],
"reason": "批量清理"
}'響應
成功返回 200 OK,results 中按 message_ids 的順序列出每條訊息的結果:
| 欄位 | 型別 | 說明 |
|---|---|---|
message_id | String | 訊息 ID |
changed | Boolean | 成功時出現:這次是否撤回了訊息,之前已經撤回的為 false |
code | String | 失敗時出現:錯誤碼,如 not_found |
message | String | 失敗時出現:錯誤說明 |
details | Object | 失敗時出現:詳細資訊,可為 null |
{
"results": [
{ "message_id": "99583386074480640", "changed": true },
{ "message_id": "99583385822822400", "changed": false },
{ "message_id": "123", "code": "not_found", "message": "消息不存在", "details": null },
{ "message_id": "99583386288390144", "changed": true }
]
}錯誤
| HTTP 狀態碼 | code | 說明 |
|---|---|---|
| 400 | invalid_argument | message_ids 為空或超過 100 個(details.reason 為 too_many_items,details.max 為 100),或其中有不是字串形式整數的 ID |
編輯訊息
修改一條訊息的內容,用於更新訂單、審批卡片的狀態,或更正服務端發出的通知。只能編輯文字(text)和自定義(custom)訊息,不論是誰發的;其他型別和群提示不能編輯。
/{org_name}/{app_name}/messages/{message_id}- 整體替換:
body和ext至少給一個,給出的欄位整體替換原來的值,沒給的不變;ext為null時清空。mentions、reply_to、need_receipt不能修改。 - 次數:每條訊息最多編輯 100 次,沒有時間限制。內容與當前完全相同時不算一次,
changed為false。 - 顯示:編輯後
edited記下最近一次編輯的時間和累計次數,客戶端通常顯示“已編輯”。 - 只讀狀態:應用處於只讀狀態時不能編輯,返回
403 app_unavailable。
新內容和傳送時一樣要經過檢查,不透過時訊息不變:
| 檢查 | 結果 |
|---|---|
| 型別、格式和大小上限 | 400 invalid_argument、413 payload_too_large |
| 內容安全(預設只按平臺規則,見服務端提交的內容) | 不透過返回 403 message_rejected;命中替換規則的文字被替換為 *,以響應中的訊息為準 |
發訊息前回撥:只在它開啟了“包括編輯”(include_edits)且 vias 包含 openapi 時呼叫,data.edit 為被編輯訊息的 message_id 和 seq | 拒絕時返回 403 message_rejected;改寫時儲存改寫後的內容 |
路徑引數
org_name、app_name 見接入概述。
| 引數 | 型別 | 說明 |
|---|---|---|
message_id | String | 要編輯的訊息 ID |
請求體
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
body | Object | 否 | 新的訊息體,整體替換原來的 body,格式見訊息型別 |
ext | Object | 否 | 新的擴充套件欄位,整體替換原來的 ext;為 null 時清空 |
reason | String | 否 | 編輯的原因,記入操作日誌 |
請求示例
curl -X PATCH "$IM_API/$ORG/$APP/messages/99582642919309312" \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"body": { "card": "order_shipped", "order_id": "8812", "status": "delivered" },
"reason": "订单状态更新"
}'響應
成功返回 200 OK,message 為編輯後的訊息物件,changed 表示內容是否有變化:
{
"message": {
"conversation_id": "99582604130385920",
"conversation_type": "single",
"seq": 7,
"message_id": "99582642919309312",
"client_msg_id": "2ec322dc5f952fd2d05bc6df94a44d9a",
"sender": "bob",
"sender_type": "user",
"recipient": "alice",
"type": "custom",
"body": { "card": "order_shipped", "order_id": "8812", "status": "delivered" },
"ext": { "source": "order-system" },
"mentions": null,
"reply_to": null,
"need_receipt": false,
"exclude_from_unread": false,
"reactions": null,
"pinned": null,
"edited": { "at": "2026-10-02T19:08:27.350Z", "count": 1 },
"recalled": null,
"erased": false,
"via": "openapi",
"created_at": "2026-10-02T19:05:52.228Z",
"message_version": 1
},
"changed": true
}錯誤
| HTTP 狀態碼 | code | 說明 |
|---|---|---|
| 400 | invalid_argument | details.reason 為 empty_edit:body 和 ext 都沒給 |
| 400 | invalid_argument | details.reason 為 invalid_body:新內容不符合要求,details.field 指出欄位 |
| 403 | permission_denied | details.reason 為 not_editable:不是文字或自定義訊息 |
| 403 | permission_denied | details.reason 為 message_recalled:訊息已被撤回或擦除 |
| 403 | message_rejected | 沒透過內容安全或被髮訊息前回撥拒絕,details.reason 的取值同傳送訊息 |
| 404 | not_found | 訊息不存在或已超過保留期 |
| 409 | limit_exceeded | 這條訊息已編輯 100 次,details.reason 為 edit_limit |
| 413 | payload_too_large | 新內容超過應用的大小上限,details.max_bytes 為上限 |
置頂訊息
把一條訊息置頂在會話頂部,會話的全部參與者都能看到,適合公告、重要通知。服務端置頂不檢查角色(客戶端只有單聊雙方、群主和管理員能置頂),pinned.by 為 null。
/{org_name}/{app_name}/messages/{message_id}/pin- 數量上限:每個會話最多
max_pinned_messages_per_conversation條(執行策略,預設 20,可設 1 到 100,見執行策略)。滿了先取消置頂一條。 - 自動移出:已撤回、已擦除的訊息不能置頂;已置頂的被撤回、擦除或超過保留期時自動移出。
- 新成員:看不到入群之前的訊息,也就看不到這些訊息的置頂。
- 只讀狀態:應用處於只讀狀態時不能置頂,返回
403 app_unavailable。
路徑引數
org_name、app_name 見接入概述。
| 引數 | 型別 | 說明 |
|---|---|---|
message_id | String | 要置頂的訊息 ID |
查詢引數
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
reason | String | 否 | 置頂的原因,記入操作日誌 |
請求示例
curl -X PUT "$IM_API/$ORG/$APP/messages/99582672556261376/pin" \
-H "Authorization: Bearer $APP_TOKEN"響應
成功返回 200 OK,message 為置頂後的訊息物件,changed 為這次是否置頂了訊息(已經置頂的為 false):
{
"message": {
"conversation_id": "99582580415791104",
"conversation_type": "group",
"seq": 3,
"message_id": "99582672556261376",
"client_msg_id": "doc-grp-0001",
"sender": "alice",
"sender_type": "user",
"recipient": null,
"type": "text",
"body": { "text": "@bob 请看一下需求文档" },
"ext": null,
"mentions": { "usernames": ["bob"], "all": false },
"reply_to": null,
"need_receipt": false,
"exclude_from_unread": false,
"reactions": null,
"pinned": { "by": null, "at": "2026-10-02T19:08:36.382Z" },
"edited": null,
"recalled": null,
"erased": false,
"via": "openapi",
"created_at": "2026-10-02T19:05:59.294Z",
"message_version": 1
},
"changed": true
}錯誤
| HTTP 狀態碼 | code | 說明 |
|---|---|---|
| 403 | permission_denied | 訊息已被撤回或擦除,details.reason 為 message_recalled |
| 404 | not_found | 訊息不存在或已超過保留期 |
| 409 | limit_exceeded | 會話中置頂的訊息已達上限,details.reason 為 pinned_message_limit |
取消置頂訊息
取消一條訊息的置頂,不論它是誰置頂的。訊息沒有置頂時 changed 為 false。應用處於只讀狀態時也可以取消置頂。
/{org_name}/{app_name}/messages/{message_id}/pin路徑引數
org_name、app_name 見接入概述。
| 引數 | 型別 | 說明 |
|---|---|---|
message_id | String | 要取消置頂的訊息 ID |
查詢引數
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
reason | String | 否 | 取消置頂的原因,記入操作日誌 |
請求示例
curl -X DELETE "$IM_API/$ORG/$APP/messages/99583385617301504/pin" \
-H "Authorization: Bearer $APP_TOKEN"響應
成功返回 200 OK,格式與置頂訊息相同,message.pinned 為 null:
{
"message": {
"conversation_id": "99583031936811008",
"conversation_type": "single",
"seq": 2,
"message_id": "99583385617301504",
"client_msg_id": "7109113ff74e7b0e1e98fc0d29f56f4e",
"sender": "carol",
"sender_type": "user",
"recipient": "dave",
"type": "text",
"body": { "text": "本周值班表已更新" },
"ext": null,
"mentions": null,
"reply_to": null,
"need_receipt": false,
"exclude_from_unread": false,
"reactions": null,
"pinned": null,
"edited": null,
"recalled": null,
"erased": false,
"via": "openapi",
"created_at": "2026-10-02T19:08:49.301Z",
"message_version": 21
},
"changed": true
}錯誤
| HTTP 狀態碼 | code | 說明 |
|---|---|---|
| 404 | not_found | 訊息不存在或已超過保留期 |
查詢會話中的置頂訊息
列出一個會話中全部置頂的訊息,按置頂時間從新到舊排列。以服務端的視角返回,單聊和群聊都可以查詢。
/{org_name}/{app_name}/conversations/{conversation_id}/pins置頂的訊息最多 100 條,通常一次返回全部。一次響應中的訊息合計超過 1 MB 時分頁返回,next_cursor 不為 null 時用它取下一頁。
路徑引數
org_name、app_name 見接入概述。
| 引數 | 型別 | 說明 |
|---|---|---|
conversation_id | String | 會話 ID,群聊為群 ID |
查詢引數
| 引數 | 型別 | 必填 | 說明 |
|---|---|---|---|
cursor | String | 否 | 下一頁的遊標,第一頁不傳 |
請求示例
curl "$IM_API/$ORG/$APP/conversations/99582580415791104/pins" \
-H "Authorization: Bearer $APP_TOKEN"響應
成功返回 200 OK,items 為置頂的訊息物件,next_cursor 為下一頁的遊標,沒有下一頁時為 null:
{
"items": [
{
"conversation_id": "99582580415791104",
"conversation_type": "group",
"seq": 5,
"message_id": "99582672988274688",
"client_msg_id": "doc-grp-0003",
"sender": null,
"sender_type": "system",
"recipient": null,
"type": "text",
"body": { "text": "本群将于今晚 22:00 维护" },
"ext": null,
"mentions": { "usernames": [], "all": true },
"reply_to": null,
"need_receipt": false,
"exclude_from_unread": false,
"reactions": null,
"pinned": { "by": null, "at": "2026-10-02T19:08:36.455Z" },
"edited": null,
"recalled": null,
"erased": false,
"via": "openapi",
"created_at": "2026-10-02T19:05:59.397Z",
"message_version": 2
},
{
"conversation_id": "99582580415791104",
"conversation_type": "group",
"seq": 3,
"message_id": "99582672556261376",
"client_msg_id": "doc-grp-0001",
"sender": "alice",
"sender_type": "user",
"recipient": null,
"type": "text",
"body": { "text": "@bob 请看一下需求文档" },
"ext": null,
"mentions": { "usernames": ["bob"], "all": false },
"reply_to": null,
"need_receipt": false,
"exclude_from_unread": false,
"reactions": null,
"pinned": { "by": null, "at": "2026-10-02T19:08:36.382Z" },
"edited": null,
"recalled": null,
"erased": false,
"via": "openapi",
"created_at": "2026-10-02T19:05:59.294Z",
"message_version": 1
}
],
"next_cursor": null
}錯誤
| HTTP 狀態碼 | code | 說明 |
|---|---|---|
| 400 | invalid_argument | cursor 無效 |
| 404 | not_found | 會話不存在 |
