Skip to content

XHIM 服务端到服务端 REST API

这组 API 供购买方的业务后台调用,解决“业务系统同步用户、签发客户端会话、 管理群组、发送或撤回业务消息、管理登录设备”时还要拼接管理后台接口的成本。它不是管理 后台 API,也不是给 iOS、Android、Web 或桌面客户端直连的接口。

1. 启用与安全边界

  1. 生成与 XHIM_ADMIN_KEY 不同的高强度随机值,至少 32 字节;
  2. 在 XHIM Server 环境中配置 XHIM_BUSINESS_API_KEY
  3. 重启服务。未配置时 /v1/server/* 路由根本不注册,返回 404;
  4. 客户业务后台在每个请求中传入 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_idaccount_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":truemutation_id 标识一次业务意图;同一意图的 网络重试复用原值,换字段却复用 ID 会返回 409 idempotency_conflictexpected_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 字节。

成功响应包括 staterevisionchanged_at_mschangedidempotent_replayrevoked_session_count。停用会撤销当前会话;恢复账号 不会复活旧 Token,购买方身份系统必须重新签发会话。注销是不可恢复的资料 匿名化,不是对聊天记录、群历史或审计记录做物理级联删除。

停用/注销库存使用 JSON body:

json
{
  "app_id":"com.customer.im",
  "after_user_id":"opaque-cursor",
  "limit":100
}

返回 itemsnext_after_user_idhas_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=trueencrypted=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 必须显式为 iosandroidmacoswindowsharmonyosweblinux,不接受模糊的 unknown
  • device_id 是宿主生成并保存在安全存储中的稳定设备标识,不是 APNs/FCM Token;
  • lifetime_seconds 省略时使用服务端默认值,最大 30 天;
  • 响应包含 access_tokenexpires_at_mssession_idplatformevicted_count,并带 Cache-Control: no-store
  • App 只拿 access_token 初始化 SDK;续期仍由客户业务后台重新签发。

4. 好友申请、好友属性和黑名单

4.1 发送与审批好友申请

  • POST /v1/server/friend-requests:send
  • POST /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 必须为 profilecardphoneaccountqr_codegroup;从群成员列表添加时还要传 source_conversation_id。审批请求传 actor_user_id、上一响应的 request_iddecisionacceptedrejected)。发送者不能替接收者审批,黑名单和前置策略仍然生效。

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:delete
  • POST /v1/server/friendships/properties:update

好友属性接口支持三种 action

action字段说明
remarkremark仅当前用户可见的好友备注
pinpinned当前账号私有的星标/置顶关系
application_extensionapplication_extension最大 16 KiB 的客户业务 JSON

属性更新必须传 expected_revision;响应的 friendship.remark_revision 是下次 写入依据。删除好友请求使用 actor_user_idpeer_user_idoperation_idtrace_id,相同操作 ID 可安全重试。

4.4 设置或解除黑名单

POST /v1/server/blocks:update

请求包含 actor_user_idblocked_user_idactivetrue 表示拉黑, 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_friendblocked_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_admintarget_user_id, admin设置/取消管理员
set_mutetarget_user_id, muted_until_ms单成员禁言
set_all_mutemuted_until_ms全员禁言/解除
transfer_ownertarget_user_id转让群主
set_join_policyjoin_approval_required入群审批策略
set_profiletitle, avatar_url, announcement, description群资料
set_member_nicknamemember_nickname发起者修改自己的群昵称
set_access_policymember_profile_visible, member_friend_requests_allowed成员资料与加好友策略
set_new_member_history_visibilitynew_member_history_visible新成员历史消息策略
set_member_application_extensiontarget_user_id, member_application_extension_json设置指定成员在本群的业务扩展

set_member_application_extension 面向 OA 角色、项目分工、成员标签等客户业务数据。 member_application_extension_json 为空字符串时清除;非空时必须是 UTF-8 编码的 JSON 对象,经服务端规范化后不得超过 16 KiB,嵌套深度必须小于 64。 群主可设置任意已入群成员;管理员只能设置普通成员,不能修改群主或其他管理员; 普通成员无此权限。更新继续遵守 expected_revisionoperation_id 的 CAS/幂等规则。

6.3 申请加入和审批

  • POST /v1/server/group-join-requests:send
  • POST /v1/server/group-join-requests:resolve

申请请求传 requester_user_idconversation_idintroductionoperation_idtrace_id。若群已开启审批,响应状态为 pending;若群策略 允许直接加入,响应会同时包含最新 groupmembers。审批请求由群主或 管理员作为 actor_user_id,使用 request_idaccepted/rejected 决策。

6.4 退出群聊

POST /v1/server/groups:leave

请求包含 actor_user_idconversation_idexpected_revisionoperation_idtrace_id。群主必须先转让或解散群聊,不能用普通退群绕过 群主生命周期规则。

6.5 解散群聊

POST /v1/server/groups:dismiss

请求字段为 app_idactor_user_idconversation_idexpected_revisionoperation_idtrace_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_atexpired。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.eventapplication/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_sequenceserver_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_revisionmutation_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。常见错误:

HTTPcode含义
400invalid_request / invalid_argumentJSON 或业务参数无效
401unauthorizedBusiness Key 缺失或错误
403forbidden / policy_rejected发送者不在会话中,或前置策略拒绝
404not_found用户或会话不存在;也可能是功能未启用
409idempotency_conflict同一幂等键对应了不同请求
409revision_conflict对象版本已变,或导入会话不再为空/离线/可提交
503policy_unavailablefail-closed 前置策略暂时不可用

这些接口都调用正式 Application Service,不会从 HTTP 层直接写表。 普通操作继续与客户端通道共用群权限、成员身份、幂等、业务策略、 Webhook Outbox、Push 和跨端 Sync 规则;历史导入则使用上文明确的离线、 零 Push 和聚合 Webhook 事务边界。

XHIM 客户端 SDK 与服务端文档