主题
XHIM 服务端到服务端 REST API
这组 API 供购买方的业务后台调用,解决“业务系统同步用户、签发客户端会话、 管理群组、发送或撤回业务消息、管理登录设备”时还要拼接管理后台接口的成本。它不是管理 后台 API,也不是给 iOS、Android、Web 或桌面客户端直连的接口。
1. 启用与安全边界
- 生成与
XHIM_ADMIN_KEY不同的高强度随机值,至少 32 字节; - 在 XHIM Server 环境中配置
XHIM_BUSINESS_API_KEY; - 重启服务。未配置时
/v1/server/*路由根本不注册,返回 404; - 客户业务后台在每个请求中传入
X-XHIM-Business-Key。
生产环境还必须:
- 只允许 HTTPS;
- 在 Ingress/API Gateway 上对
/v1/server/*做业务后台出口 IP、mTLS 或私有网络限制; - 不把凭证写入源码、日志、URL、客户端包或浏览器存储;
- 轮换时更新 Secret 并滚动重启。当前版本不同时接受新旧两个 Business Key,因此由网关维持短时间双凭证过渡。
bash
export XHIM_BASE_URL='https://im.customer.example'
export XHIM_BUSINESS_KEY='REPLACE_FROM_SECRET_MANAGER'2. 创建或更新用户
POST /v1/server/users:upsert
bash
curl --fail-with-body \
-H 'Content-Type: application/json' \
-H "X-XHIM-Business-Key: ${XHIM_BUSINESS_KEY}" \
--data '{
"app_id":"com.customer.im",
"user_id":"employee-10001",
"display_name":"林晨曦",
"avatar_url":"https://cdn.customer.example/avatar/10001.png",
"department_id":"sales-east",
"department_name":"华东销售部",
"application_extension_json":{
"crm.label":"gold",
"workflow.enabled":true
}
}' \
"${XHIM_BASE_URL}/v1/server/users:upsert"user_id 是业务后台的稳定路由标识。响应同时包含适合前端展示/复制的 public_user_id。account_id 可省略,首次创建时由服务端生成;不要向普通 用户展示这两个内部标识。
application_extension_json 是应用拥有的公开业务扩展,非空时必须 是 UTF-8 JSON object,服务端规范化后最大 16 KiB、嵌套最大 64 层。 它在陌生人的精确资料查询中也会返回,严禁放入手机、邮箱、实名、 密码、Token、会话/认证状态、加密密钥或任何其他个人敏感/凭证数据。 回执中的 application_extension_json 是服务端规范化后的权威值。
2.1 批量读取用户资料
POST /v1/server/users:get
json
{
"app_id":"com.customer.im",
"requester_user_id":"employee-10001",
"user_ids":["employee-10002","employee-10003"],
"context_conversation_id":"grp_optional_context"
}这不是管理员绕过隐私的全库查询。requester_user_id 是真实查看者:好友或 同群语境可返回允许的详细资料,陌生人只返回昵称、头像等公开字段。返回 application_extension_json 也始终属于公开字段;missing_user_ids 方便业务后台处理已删除或不存在的账号。
2.2 创建或获取直聊会话
POST /v1/server/conversations/direct:get-or-create
json
{
"app_id":"com.customer.im",
"actor_user_id":"employee-10001",
"peer_user_id":"employee-10002",
"operation_id":"direct-10001-10002-1",
"trace_id":"trace-20260807-0011"
}相同 App 内同一对用户始终返回同一个确定性会话;首次响应 created=true,后续重试为 false。服务端仍会执行用户存在性、直聊 参与者和资料隐私规则。
2.3 企业账号停用、恢复与注销
购买方 HR、OA、IAM 或 SSO 后台可以提交账号生命周期变更:
http
POST /v1/server/users:suspend
POST /v1/server/users:unsuspend
POST /v1/server/users:unregister
POST /v1/server/users/disabled:list四个路由继续使用本页统一的 X-XHIM-Business-Key,不要新增另一套 X-Server-Key。停用、恢复与注销请求为:
json
{
"app_id":"com.customer.im",
"user_id":"employee-10001",
"mutation_id":"oa-user-lifecycle-2026-0088",
"expected_revision":7,
"reason":"离职流程单 OA-2026-0088"
}注销必须再传 "confirm":true。mutation_id 标识一次业务意图;同一意图的 网络重试复用原值,换字段却复用 ID 会返回 409 idempotency_conflict。 expected_revision 来自上次权威读取;409 revision_conflict 后必须重新读取, 不能在本地递增版本后盲目覆盖。
unregistered 是终态。只有原 mutation_id 且内容完全一致的重放会返回原成功 回执;新的注销、恢复或其他生命周期 mutation 均返回 403 account_unregistered,不会返回 changed=false 伪装成功。
expected_revision 必须是正整数;reason trim 后必须为 1–256 个 UTF-8 字节。调用方必须按 UTF-8 字节数校验,不能把 256 个中文字符误当成 256 字节。
成功响应包括 state、revision、changed_at_ms、changed、 idempotent_replay 和 revoked_session_count。停用会撤销当前会话;恢复账号 不会复活旧 Token,购买方身份系统必须重新签发会话。注销是不可恢复的资料 匿名化,不是对聊天记录、群历史或审计记录做物理级联删除。
停用/注销库存使用 JSON body:
json
{
"app_id":"com.customer.im",
"after_user_id":"opaque-cursor",
"limit":100
}返回 items、next_after_user_id 和 has_more;条目以 display_name/public_user_id 供管理界面展示,内部 user_id 只作为治理路由键 和审计辅助。reason_summary 只给受控管理端,不会下发客户端。完整状态语义、 管理台流程、旧 Server 兼容和迁移回滚见 企业账号治理。
2.4 通知服务账号与业务通知
企业 OA、订单、运维和运营系统必须使用专用通知服务账号,不能让真人账号或 普通 /messages:send 冒充系统通知。Business 路由为:
text
POST /v1/server/notification-accounts:create
POST /v1/server/notification-accounts:update
POST /v1/server/notification-accounts:list
POST /v1/server/notifications:send
POST /v1/server/notifications:notify
POST /v1/server/notification-jobs:submit
POST /v1/server/notification-jobs:get
POST /v1/server/notification-jobs:list
POST /v1/server/notification-jobs/recipients:list
POST /v1/server/notification-jobs:cancel所有路由继续使用 X-XHIM-Business-Key。单用户/群通知固定 server_visible=true、encrypted=false;指定用户 fanout 最多 1000 人, 全量发送必须传 confirm_all_active_humans:true,并在事务中冻结当时的活跃 真人收件人快照。任务异步执行,submit 成功不等于立即送达。任务查询不返回 title/body/data,只保留 payload hash、字节数、统计、稳定错误码与 server_message_id。
账号模型、全部 JSON、worker 租约、取消线性化、迁移与回滚见 业务通知、服务账号与异步任务。
3. 签发客户端会话
POST /v1/server/sessions:issue
购买方登录系统验证自己的账号/密码、短信、SSO 或 OA 身份后,由业务后台调用 此接口换取 XHIM 客户端会话。Business Key 只能留在业务后台,不能放进 App。
bash
curl --fail-with-body \
-H 'Content-Type: application/json' \
-H "X-XHIM-Business-Key: ${XHIM_BUSINESS_KEY}" \
--data '{
"app_id":"com.customer.im",
"user_id":"employee-10001",
"device_id":"ios-DEVICE-STABLE-ID",
"platform":"ios",
"lifetime_seconds":86400
}' \
"${XHIM_BASE_URL}/v1/server/sessions:issue"platform必须显式为ios、android、macos、windows、harmonyos、web或linux,不接受模糊的unknown;device_id是宿主生成并保存在安全存储中的稳定设备标识,不是 APNs/FCM Token;lifetime_seconds省略时使用服务端默认值,最大 30 天;- 响应包含
access_token、expires_at_ms、session_id、platform和evicted_count,并带Cache-Control: no-store; - App 只拿
access_token初始化 SDK;续期仍由客户业务后台重新签发。
4. 好友申请、好友属性和黑名单
4.1 发送与审批好友申请
POST /v1/server/friend-requests:sendPOST /v1/server/friend-requests:resolve
发送示例:
json
{
"app_id":"com.customer.im",
"from_user_id":"employee-10001",
"to_user_id":"employee-10002",
"introduction":"我是华东销售部林晨曦",
"source":"phone",
"operation_id":"friend-request-10001-10002-1",
"trace_id":"trace-20260807-0010"
}source 必须为 profile、card、phone、account、qr_code 或 group;从群成员列表添加时还要传 source_conversation_id。审批请求传 actor_user_id、上一响应的 request_id 和 decision(accepted 或 rejected)。发送者不能替接收者审批,黑名单和前置策略仍然生效。
4.2 可审计的好友关系导入
POST /v1/server/friendships:import
该路由只用于受信业务后台迁移已有好友关系,不经过终端好友申请。 管理台的同构路由是 POST /v1/admin/friendships:import;浏览器管理台仍只 使用同源 Admin Session/CSRF,绝不接触 Business Key。
json
{
"app_id":"com.customer.im",
"owner_user_id":"employee-10001",
"friend_user_ids":["employee-10002","employee-10003"],
"operation_id":"migration-friends-10001-0001",
"trace_id":"trace-migration-20260809-0001"
}friend_user_ids必须是 1..1000 个原始唯一、严格 UTF-8 的用户 ID, 返回items严格保持输入顺序;该受鉴权批量路由的 JSON 请求体上限是 4 MiB;- 整批在一个事务中校验并写入:任一用户不存在、已注销、为通知服务 账号,或任一目标与 owner 存在活动拉黑关系时,整批零写入;
- suspended human 可保留迁移关系,但仍不能登录;unregistered 终态不可导入;
- 相同
operation_id和相同有序载荷返回首次的完整结果且idempotent_replay=true;复用操作 ID 但改变 owner、目标或顺序会返回409 idempotency_conflict; - 只有首次创建或重新激活的关系会产生双向 Sync 事件;已激活关系不 会重复事件或实时 Sync Hint。重新激活时会像正常重新加好友一样清空双方旧的 备注、置顶和业务扩展属性;
items[].changed表示该目标是否在本次首次创建或重新激活,items[].friendship是 owner 视角的最终服务端投影;内部事件接收账号不会出现在响应;- 审计只保存 actor、owner、数量和 operation ID;聚合 after-webhook
friendships.imported只携带 owner、实际变更数量、operation/trace,二者都不保存或 投递完整目标数组。
普通 SDK 没有该写入能力,客户端仍必须使用好友申请/审批流程。
4.3 删除好友和设置好友属性
POST /v1/server/friendships:deletePOST /v1/server/friendships/properties:update
好友属性接口支持三种 action:
action | 字段 | 说明 |
|---|---|---|
remark | remark | 仅当前用户可见的好友备注 |
pin | pinned | 当前账号私有的星标/置顶关系 |
application_extension | application_extension | 最大 16 KiB 的客户业务 JSON |
属性更新必须传 expected_revision;响应的 friendship.remark_revision 是下次 写入依据。删除好友请求使用 actor_user_id、peer_user_id、operation_id 和 trace_id,相同操作 ID 可安全重试。
4.4 设置或解除黑名单
POST /v1/server/blocks:update
请求包含 actor_user_id、blocked_user_id 和 active;true 表示拉黑, false 表示解除。该接口不伪装成管理员强制操作,仍以 actor_user_id 的 个人关系链提交并向其其他设备同步。
4.5 批量检查好友与黑名单状态
POST /v1/server/relationships:check
json
{
"app_id":"com.customer.im",
"requester_user_id":"employee-10001",
"user_ids":["employee-10002","employee-10003"]
}响应按请求顺序返回 is_friend 和 blocked_by_me。不存在的用户与非好友 保持不可区分,防止该接口变成用户目录枚举器。
5. 创建群组
POST /v1/server/groups:create
bash
curl --fail-with-body \
-H 'Content-Type: application/json' \
-H "X-XHIM-Business-Key: ${XHIM_BUSINESS_KEY}" \
--data '{
"app_id":"com.customer.im",
"owner_user_id":"employee-10001",
"client_group_id":"oa-project-2026-0088",
"title":"项目 0088 协作群",
"member_user_ids":["employee-10002","employee-10003"],
"administrator_user_ids":["employee-10002"],
"operation_id":"oa-project-2026-0088-create",
"trace_id":"trace-20260807-0001"
}' \
"${XHIM_BASE_URL}/v1/server/groups:create"owner_user_id不需重复放进member_user_ids;- 管理员必须同时在
member_user_ids中; client_group_id是幂等键;省略时使用operation_id;- 相同幂等键重试返回同一个群,不会重复建群;字段变更的同键请求 返回
409 idempotency_conflict。
响应中 group.conversation_id 是后续发消息的会话 ID。
6. 更新群成员、治理、入群审批和群生命周期
所有群变更都要求 expected_revision。客户端或业务后台先保存上次响应中的 group.revision,冲突时重新读取群资料再提交,不能盲目覆盖。
6.1 增删成员
POST /v1/server/groups/members:update
json
{
"app_id":"com.customer.im",
"actor_user_id":"employee-10001",
"conversation_id":"grp_xxx",
"expected_revision":3,
"add_user_ids":["employee-10004"],
"remove_user_ids":["employee-10003"],
"operation_id":"oa-project-0088-members-4",
"trace_id":"trace-20260807-0004"
}actor_user_id 是此次操作的真实发起者。群主、管理员和普通成员的能力继续受 同一套群权限策略约束;Business Key 不会绕过群权限。
6.2 群治理
POST /v1/server/groups/governance:update
json
{
"app_id":"com.customer.im",
"actor_user_id":"employee-10001",
"conversation_id":"grp_xxx",
"expected_revision":4,
"action":"set_admin",
"target_user_id":"employee-10002",
"admin":true,
"operation_id":"oa-project-0088-admin-1",
"trace_id":"trace-20260807-0005"
}支持的 action 与字段:
action | 主要字段 | 用途 |
|---|---|---|
set_admin | target_user_id, admin | 设置/取消管理员 |
set_mute | target_user_id, muted_until_ms | 单成员禁言 |
set_all_mute | muted_until_ms | 全员禁言/解除 |
transfer_owner | target_user_id | 转让群主 |
set_join_policy | join_approval_required | 入群审批策略 |
set_profile | title, avatar_url, announcement, description | 群资料 |
set_member_nickname | member_nickname | 发起者修改自己的群昵称 |
set_access_policy | member_profile_visible, member_friend_requests_allowed | 成员资料与加好友策略 |
set_new_member_history_visibility | new_member_history_visible | 新成员历史消息策略 |
set_member_application_extension | target_user_id, member_application_extension_json | 设置指定成员在本群的业务扩展 |
set_member_application_extension 面向 OA 角色、项目分工、成员标签等客户业务数据。 member_application_extension_json 为空字符串时清除;非空时必须是 UTF-8 编码的 JSON 对象,经服务端规范化后不得超过 16 KiB,嵌套深度必须小于 64。 群主可设置任意已入群成员;管理员只能设置普通成员,不能修改群主或其他管理员; 普通成员无此权限。更新继续遵守 expected_revision 和 operation_id 的 CAS/幂等规则。
6.3 申请加入和审批
POST /v1/server/group-join-requests:sendPOST /v1/server/group-join-requests:resolve
申请请求传 requester_user_id、conversation_id、introduction、 operation_id 和 trace_id。若群已开启审批,响应状态为 pending;若群策略 允许直接加入,响应会同时包含最新 group 和 members。审批请求由群主或 管理员作为 actor_user_id,使用 request_id 和 accepted/rejected 决策。
6.4 退出群聊
POST /v1/server/groups:leave
请求包含 actor_user_id、conversation_id、expected_revision、 operation_id 和 trace_id。群主必须先转让或解散群聊,不能用普通退群绕过 群主生命周期规则。
6.5 解散群聊
POST /v1/server/groups:dismiss
请求字段为 app_id、actor_user_id、conversation_id、 expected_revision、operation_id 和 trace_id。只有群主可解散;相同 operation_id 重试是幂等回放。
7. 代发文字消息
POST /v1/server/messages:send
bash
curl --fail-with-body \
-H 'Content-Type: application/json' \
-H "X-XHIM-Business-Key: ${XHIM_BUSINESS_KEY}" \
--data '{
"app_id":"com.customer.im",
"sender_user_id":"employee-10001",
"conversation_id":"grp_xxx",
"text":"OA 审批已通过",
"operation_id":"oa-approval-9001-notice-1",
"trace_id":"trace-20260807-0002"
}' \
"${XHIM_BASE_URL}/v1/server/messages:send"text 是开箱即用的简化字段,服务端会生成 text/plain v1 信封。 client_message_id 可省略,默认使用 operation_id。超时后必须使用原值重试, 返回的 idempotent_replay=true 说明本次是已提交消息的幂等回放。
可选的 burn_after_read_seconds 启用阅后即焚;默认为持久消息。它仍受 XHIM Server 配置的最大时长限制。
8. 代发自定义/媒体信封
这个入口不负责上传附件。先按 SDK 的媒体流程完成上传与审核,再传递 与客户端 Renderer 共享的版本化 payload。
bash
PAYLOAD_BASE64="$(printf '%s' '{"approval_id":"9001","state":"approved"}' | base64)"
curl --fail-with-body \
-H 'Content-Type: application/json' \
-H "X-XHIM-Business-Key: ${XHIM_BUSINESS_KEY}" \
--data "{
\"app_id\":\"com.customer.im\",
\"sender_user_id\":\"employee-10001\",
\"conversation_id\":\"grp_xxx\",
\"content_type\":\"application/vnd.customer.oa-approval+json\",
\"content_version\":1,
\"payload_base64\":\"${PAYLOAD_BASE64}\",
\"fallback_text\":\"OA 审批已通过\",
\"operation_id\":\"oa-approval-9001-card-1\",
\"trace_id\":\"trace-20260807-0003\"
}" \
"${XHIM_BASE_URL}/v1/server/messages:send"text 模式和高级信封字段不能混用。system/* 保留给 XHIM Server 自身的 群事件,业务 API 不能伪造系统消息。
9. 历史消息离线导入
历史消息导入是一条受信控制面流程,用于把旧系统的时间线一次性 迁入一个已存在但从未发过消息的 XHIM 会话。它不是终端发消息的 快速通道,普通 SDK Token 没有这些路由。
Business 路由为:
text
POST /v1/server/message-import-jobs:begin
POST /v1/server/message-import-jobs:upload
POST /v1/server/message-import-jobs:get
POST /v1/server/message-import-jobs/active:get
POST /v1/server/message-import-jobs:finalize
POST /v1/server/message-import-jobs:cancel管理面提供完全同构的 /v1/admin/message-import-jobs:* 路由。Business 路由使用 X-XHIM-Business-Key;Admin 路由使用 Admin Key,或已登录的同源 Admin Session/CSRF。不要把任何一种控制面凭证放入客户端。
9.1 建立导入作业
json
{
"app_id":"com.customer.im",
"conversation_id":"grp_existing_empty",
"source_system":"legacy-oa-im",
"operation_id":"history-begin-20260809-0001",
"trace_id":"trace-history-20260809-0001"
}begin 只接受序列号为 0 的现有会话,并且同时只能有一个 staging 作业。所有会话成员必须是 human user:已停用但未注销的 human 可以迁移,通知服务账号和已注销账号不可以。建立和最终提交时都会 检查全部成员没有活动 Device Session;任一成员在线都会返回 409 revision_conflict。
作业处于 staging 期间,该会话的客户端发送、受信系统发送和业务通知 发送都被同一会话锁拒绝,避免导入序列与新消息竞争。
begin 响应包含 expires_at 和 expired。staging 租约为 24 小时;每个 成功接受的新 upload 块都会从服务端当前时间续租 24 小时。编排器应在每次 响应后持久化最新 expires_at,不要使用客户端时钟推算。
9.2 分块上传
json
{
"app_id":"com.customer.im",
"job_id":"mimp_...",
"chunk_id":"legacy-page-0001",
"trace_id":"trace-history-upload-0001",
"messages":[
{
"source_message_id":"legacy-msg-90001",
"sender_user_id":"employee-10001",
"source_sent_at_ms":1786215600000,
"content_type":"text/plain",
"content_version":1,
"payload":"5Y6G5Y+y5raI5oGv",
"fallback_text":"历史消息"
}
]
}payload 是标准 JSON Base64 字符串。每块必须有 1..1000 条消息,HTTP Body 上限为 16 MiB,解码后的本块 payload 安全上限为 11 MiB。这是两个 同时生效的上限:Base64、fallback 和其他 JSON 元数据也占用 HTTP Body, 因此元数据较大时传输上限会更早触发;调用方应以实际序列化后的字节数 分块,不要把 11 MiB 视为所有元数据组合都保证可达的配额。
单条普通消息 payload 最大 1 MiB,MLS E2EE 信封最大 2 MiB。单作业 最多 100,000 条,累计解码 payload 最大 256 MiB。历史导入的 fallback_text 上限为 1024 个 UTF-8 字节(不改变日常消息的全局上限), 防止 fallback 在按成员扩展时绕过批次容量边界。由于每条消息需要向 每个当前会话成员写入一条 MessageUpsert,还必须同时满足:
text
staged_message_count * current_participant_count <= 200000
staged_payload_bytes * current_participant_count <= 512 MiB这使双人直聊仍可达 100,000 条/256 MiB,同时避免万人群在单个事务中 扩展成数亿事件。上传和最终提交都会使用当前成员数重新校验。
source_message_id在(app_id, source_system)中永久唯一;- sender 必须是该会话中的 human user;
source_sent_at_ms必须是 Unix 毫秒,不早于 epoch,不晚于服务端当前时间;- 业务自定义类型可保留,但不允许伪造
xhim.system.event或application/vnd.xhim.business-notification+json; - MLS 类型必须是 v1,且 fallback 必须为 XHIM 的固定加密占位文本;
- 请求严格拒绝重复 JSON key、字段名大小写别名、未知字段和尾随的 第二个 JSON 值。
chunk_id 是块级幂等键。超时重试必须原样复用 chunk ID 和有序载荷; 同键改变条目、顺序或正文会返回 409 idempotency_conflict。
9.3 Manifest 与原子提交
上传和 get 响应的 job.staged_manifest_sha256 是服务端权威值,可直接 用于 finalize。它与分块边界和上传顺序无关,规范化算法为:
text
field(s) = uint64_be(len(utf8(s))) || utf8(s)
item_hash = SHA256(
field(source_message_id) || field(sender_user_id) ||
uint64_be(source_sent_at_ms) || field(content_type) ||
uint64_be(content_version) || SHA256(payload) || field(fallback_text)
)
ordered = sort(items, source_sent_at_ms ASC, raw source_message_id bytes ASC)
manifest = hex_lower(SHA256(uint64_be(len(ordered)) || concat(ordered.item_hash)))json
{
"app_id":"com.customer.im",
"job_id":"mimp_...",
"expected_message_count":42000,
"manifest_sha256":"64-lowercase-hex-characters...",
"operation_id":"history-finalize-20260809-0001",
"trace_id":"trace-history-finalize-0001"
}finalize 会再次锁定空会话、核对全员离线、数量和 manifest,然后在一个 事务中按 (source_sent_at_ms, source_message_id) 排序写入 1..N 的 server_sequence。server_sent_at 保留源毫秒时间;源消息映射持久化, 防止之后重复迁移。任一校验或写入失败都是零消息提交。
提交后会产生每条 MessageUpsert 和每成员 ConversationRead Sync 事件, 全员已读游标直接置为 N,因此导入历史不会制造未读。导入不生成 Push, 也不生成逐消息 message.sent Webhook;只在整批事务中追加一条聚合审计和 一个 message.history.imported after-webhook。二者都不含 payload、fallback、 sender 列表或 source message ID。
这是有界但可能耗时的事务:导入 upload 路由的读取、处理和响应总时限 为 5 分钟,finalize 路由会把服务端事务/写响应时限从全局 30 秒延长到 15 分钟。这是保护性上限,不是性能 SLA;上线前必须用客户实际群规模 和数据做试迁移。Ingress/ API Gateway 的 body 和 upstream timeout 也要至少覆盖这两个路由边界。如果 finalize 客户端超时而结果不明,先调用 get,仍为 staging 时再使用原 operation_id 和原 manifest 重放;绝不能换新 operation ID 盲目再提交。
operation_id 用于 begin/finalize/cancel 的精确重放;同一键改变参数会 返回 409 idempotency_conflict。对 staging 作业调用 cancel 会清除全部 暂存正文并释放会话发送锁。若编排器丢失 job_id,可用 message-import-jobs/active:get 和 {app_id,conversation_id} 恢复当前活跃作业; 它只返回控制面元数据,不返回正文。超过 expires_at 的 staging 作业会在 下一次 get/recover/begin/upload/finalize/cancel 或该会话发送事务中原子标为 state=canceled, expired=true,清除全部暂存正文并释放发送锁;审计只记录 job、source、数量和字节数。已接受 chunk 的精确幂等重放仍返回原响应快照, 不能据此延长已过期租约。调用方仍应在已知失败路径主动 cancel,不要把 自动过期当作正常控制流。 已 finalized 的作业不支持通过 API 倒退; 生产迁移前必须做 PostgreSQL 备份和试迁移,回滚时使用经过验证的整库/ 定点恢复流程,不要手工删除消息行。
10. 编辑或撤回消息
POST /v1/server/messages:mutate
编辑文字:
json
{
"app_id":"com.customer.im",
"actor_user_id":"employee-10001",
"conversation_id":"grp_xxx",
"server_message_id":"msg_xxx",
"kind":"edit_text",
"expected_revision":0,
"text":"更新后的审批结果",
"operation_id":"oa-message-9001-edit-1",
"trace_id":"trace-20260807-0006"
}撤回时把 kind 改为 recall、删除 text,并使用上次响应的 mutation_revision 作为 expected_revision。mutation_id 可省略,默认使用 operation_id。原发送者、撤回时限、群管理权限和 CAS revision 均由正式消息 策略执行,Business Key 不会无条件撤回任意消息。
11. 查询和撤销设备会话
查询:POST /v1/server/device-sessions:list
json
{
"app_id":"com.customer.im",
"user_id":"employee-10001",
"limit":50
}响应只返回会话 ID、设备 ID、平台、时间、状态和撤销原因,不返回 Token ID 或 Access Token。可用 account_id 代替 user_id 查询。
撤销:POST /v1/server/device-sessions:revoke
json
{
"app_id":"com.customer.im",
"user_id":"employee-10001",
"session_id":"SESSION-ID-FROM-LIST",
"mutation_id":"security-kick-20260807-1",
"reason":"账号密码已重置",
"confirmed":true
}撤销会立即使该 Access Token 失效,并通过实时链路通知在线设备退出; confirmed:true 是防止客户后台误调用的强制字段。
12. 路由总表
| 路由 | 用途 |
|---|---|
POST /v1/server/users:upsert | 创建或更新用户 |
POST /v1/server/users:get | 按真实查看者权限批量读取资料 |
POST /v1/server/sessions:issue | 签发指定平台的客户端会话 |
POST /v1/server/conversations/direct:get-or-create | 幂等获取或创建直聊会话 |
POST /v1/server/relationships:check | 批量检查好友与黑名单状态 |
POST /v1/server/friend-requests:send | 发送带来源的好友申请 |
POST /v1/server/friend-requests:resolve | 接受或拒绝好友申请 |
POST /v1/server/friendships:import | 原子、幂等导入已有好友关系 |
POST /v1/server/friendships:delete | 删除好友关系 |
POST /v1/server/friendships/properties:update | 设置备注、置顶或业务扩展 |
POST /v1/server/blocks:update | 设置或解除黑名单 |
POST /v1/server/groups:create | 幂等创建群聊 |
POST /v1/server/groups/members:update | 增删群成员 |
POST /v1/server/groups/governance:update | 管理员、禁言、群资料等治理 |
POST /v1/server/group-join-requests:send | 申请加入群聊 |
POST /v1/server/group-join-requests:resolve | 审批入群申请 |
POST /v1/server/groups:leave | 退出群聊 |
POST /v1/server/groups:dismiss | 群主解散群聊 |
POST /v1/server/messages:send | 代发文字、自定义或媒体信封 |
POST /v1/server/messages:mutate | 编辑或撤回消息 |
POST /v1/server/message-import-jobs:begin | 为空会话建立历史导入作业 |
POST /v1/server/message-import-jobs:upload | 幂等上传历史消息分块 |
POST /v1/server/message-import-jobs:get | 查询导入控制面元数据 |
POST /v1/server/message-import-jobs/active:get | 按会话恢复当前活跃导入作业 |
POST /v1/server/message-import-jobs:finalize | 原子提交并锁定导入时间线 |
POST /v1/server/message-import-jobs:cancel | 清理暂存并释放导入锁 |
POST /v1/server/device-sessions:list | 查询用户登录设备 |
POST /v1/server/device-sessions:revoke | 踢下线并使 Token 失效 |
13. 响应和错误
所有响应为 application/json。常见错误:
| HTTP | code | 含义 |
|---|---|---|
| 400 | invalid_request / invalid_argument | JSON 或业务参数无效 |
| 401 | unauthorized | Business Key 缺失或错误 |
| 403 | forbidden / policy_rejected | 发送者不在会话中,或前置策略拒绝 |
| 404 | not_found | 用户或会话不存在;也可能是功能未启用 |
| 409 | idempotency_conflict | 同一幂等键对应了不同请求 |
| 409 | revision_conflict | 对象版本已变,或导入会话不再为空/离线/可提交 |
| 503 | policy_unavailable | fail-closed 前置策略暂时不可用 |
这些接口都调用正式 Application Service,不会从 HTTP 层直接写表。 普通操作继续与客户端通道共用群权限、成员身份、幂等、业务策略、 Webhook Outbox、Push 和跨端 Sync 规则;历史导入则使用上文明确的离线、 零 Push 和聚合 Webhook 事务边界。