组织目录同步
阅读指引:本篇是《开放平台服务端 API 指南》的第 10 部分。接口基础地址、通用约定与各接口请求限制,请先阅读《服务端 API 概览》。
13. 组织目录读取与可靠同步
组织接口使用 App access token,需要 contact:organization:read,并始终受发布范围约束。组织变更事件是尽量推送;应用必须使用同步状态接口弥补事件丢失,不能每次调用都直接做全量同步。
正在查找组织变化推送?事件类型、请求结构、签名、幂等和失败补偿流程见 第 10.8 节:组织目录变更事件。Webhook 只负责加速发现变化,sync-state 才是判断是否需要同步的可靠依据。
13.1 接口与通用约定
| Method | 路径 | 用途 |
|---|---|---|
| GET | /openapi/v1/organization/sync-state |
比较上次同步水位,判断是否需要同步 |
| POST | /openapi/v1/organization/sync-complete |
确认同一快照已经完整读取 |
| GET | /openapi/v1/organization/departments |
一次读取范围内全量组织,或指定节点自身及其完整子树 |
| GET | /openapi/v1/organization/department-detail |
读取指定部门详情 |
| GET | /openapi/v1/organization/department-users |
分页读取指定部门直接任职用户 |
| GET | /openapi/v1/organization/user-detail |
读取发布范围内用户及其可见任职 |
- 所有 ID 在 JSON 中使用十进制字符串;不要转换成 JavaScript Number。
- 四个目录读取接口拒绝未知或重复 query 参数;同步状态和完成接口也只应提交本文规定的参数。
- 成功响应结构为
code/message/data/request_id;排障时记录request_id。 - 全量读取期间,四个目录读取接口应带当前
X-Matrix-Directory-Sync-Token;sync-complete则在 JSON body 提交同一个 Token。 - 组织接口当前没有额外的接口级限流,但仍共同受来源 IP 每分钟 300 次的全局限制;禁止按部门树逐节点递归高频读取。
13.2 检查同步状态
GET /openapi/v1/organization/sync-state
curl "${OPENAPI_BASE_URL}/openapi/v1/organization/sync-state" \
-H "Authorization: Bearer ${APP_ACCESS_TOKEN}"
curl "${OPENAPI_BASE_URL}/openapi/v1/organization/sync-state" \
-H "Authorization: Bearer ${APP_ACCESS_TOKEN}" \
-H 'If-None-Match: "PREVIOUS_SYNC_TOKEN"'
ETag: "CURRENT_SYNC_TOKEN"
{
"code": 200,
"message": "",
"data": {
"need_sync": true,
"sync_token": "CURRENT_SYNC_TOKEN",
"reason": "ORGANIZATION_CHANGED"
},
"request_id": "request_xxx"
}
reason |
含义 | 处理 |
|---|---|---|
INITIAL_SYNC |
未提供上次 Token | 执行完整同步 |
PUBLICATION_SCOPE_CHANGED |
管理员修改了应用可读边界 | 完整同步;新快照中删除已越界数据 |
ORGANIZATION_CHANGED |
组织、人员或任职数据变化 | 完整同步 |
BOTH_CHANGED |
发布范围与组织目录均变化 | 完整同步 |
TOKEN_EXPIRED |
Token 非法、过期、属于其他应用或无法识别 | 不要修补 Token,执行完整同步 |
目录与发布范围均未变化时返回 HTTP 304 和当前 ETag,无 JSON body。sync_token 是经过认证的不透明字符串,不要解析、比较内容或自行构造;应用只保存并原样回传。
13.3 完整同步算法
sync-state → 建立临时快照 → 遍历部门 → 分页读取部门用户 → 按需读取用户详情 → sync-complete → 原子替换正式快照
- 调用
sync-state。200 且need_sync=true时记录响应 Token 为“进行中 Token”,不要立刻覆盖“上次完成 Token”。 - 创建新的临时表或带批次号的临时快照。不要边同步边删除线上旧数据。
- 不带
id调用 departments,一次获取当前发布范围内的全量组织架构。响应已经包含完整可见子树,不得再按返回 ID 逐层调用 departments。请求带进行中 Token。 - 对每个部门分页读取直接任职用户;按业务需要补充读取 user-detail。
- 任一读取返回 HTTP 409 /
170703时,说明同步过程中目录或发布范围又发生变化:丢弃整个临时批次,重新从 sync-state 开始。 - 全部读取成功后调用 sync-complete。仅当返回
completed=true才在本地事务中用临时快照替换正式快照,并保存该 Token 为上次完成 Token。
为什么必须调用 sync-completeToken 表示候选快照水位,不表示应用真的读完。complete 会再次检查目录和发布范围是否变化;变化时本轮不能提交。它不锁定后续目录变化,完成同步后仍须继续轮询。
13.4 部门列表
GET /openapi/v1/organization/departments[?id={department_id}]
curl "${OPENAPI_BASE_URL}/openapi/v1/organization/departments" \
-H "Authorization: Bearer ${APP_ACCESS_TOKEN}" \
-H "X-Matrix-Directory-Sync-Token: ${CURRENT_SYNC_TOKEN}"
{
"code": 200,
"message": "",
"data": {
"departments": [{
"id": "20001",
"parent": "10000",
"name": "研发中心",
"name_en": "R&D Center",
"leader_user_ids": ["10001"],
"order": 10,
"has_children": true,
"updated_at": 1788537600000
}]
},
"request_id": "request_xxx"
}
不传 id 时一次返回应用发布范围内的全量组织架构;传正整数 id 时返回该部门自身及其全部 active 后代。响应是按组织顺序排列的扁平数组,应用通过 id/parent 在本地重建树,禁止对返回的每个部门再次递归调用本接口。部分组织范围的入口节点 parent 可以是范围外 ID,用于保留原始层级;应用拿该范围外 ID 查询详情仍会得到 404。
13.5 部门详情
GET /openapi/v1/organization/department-detail?id={department_id}
id 必填且只能出现一次,必须是正整数。成功时 data.department 包含 id/parent/name/name_en/leader_user_ids/order/updated_at;详情不返回 has_children,需要通过列表关系判断。部门 parent 可为 null(无父节点);非空时为十进制字符串。
13.6 部门用户
GET /openapi/v1/organization/department-users?id={id}&page={page}&page_size={size}
curl "${OPENAPI_BASE_URL}/openapi/v1/organization/department-users?id=20001&page=1&page_size=100" \
-H "Authorization: Bearer ${APP_ACCESS_TOKEN}" \
-H "X-Matrix-Directory-Sync-Token: ${CURRENT_SYNC_TOKEN}"
{
"code": 200,
"message": "",
"data": {
"users": [{
"user_id": "10001",
"name": "张三",
"employee_no": "E10001",
"contact_hint": null,
"assignments_in_department": [{
"assignment_type": "primary",
"assignment_label_code": null,
"assignment_label_name": null,
"is_key_contact": false,
"is_leader": true
}],
"updated_at": 1788537600000
}],
"page": 1,
"page_size": 100,
"has_more": false
},
"request_id": "request_xxx"
}
| 参数 | 必填 | 规则 |
|---|---|---|
id |
是 | 范围内部门正整数 ID |
page |
否 | 省略时按 1;显式提供时必须为正整数,page=0 返回参数错误 |
page_size |
否 | 省略或低于 100 按 100;高于 500 按 500 |
只返回该部门的直接任职关系,不自动把子部门成员混入。应用按 has_more 继续翻页;同一轮所有页必须使用同一个同步 Token。
13.7 用户详情
GET /openapi/v1/organization/user-detail?user_id={user_id}
{
"code": 200,
"message": "",
"data": {
"user": {
"user_id": "10001",
"name": "张三",
"employee_no": "E10001",
"contact_hint": null,
"assignments": [{
"department_id": "20001",
"assignment_type": "primary",
"assignment_label_code": null,
"assignment_label_name": null,
"is_key_contact": false,
"is_leader": true
}],
"updated_at": 1788537600000
}
},
"request_id": "request_xxx"
}
user_id 必填且必须是正整数。只有有效用户集合内的用户可读取;返回的 assignments 也受可见组织边界约束。employee_no/contact_hint/name_en/assignment_label_* 可能为 JSON null,调用方必须按可空字段处理。
部门用户分页只用于枚举组织成员,不是应用全部可见用户列表。若发布范围包含不属于任何可见部门的指定用户,遍历部门不会发现这些用户;当前没有枚举全部可见用户的 OpenAPI,只有已知 user_id 时可查询 user-detail。不要把部门枚举结果当作所有可触达用户。
13.8 确认同步完成
POST /openapi/v1/organization/sync-complete
curl -X POST "${OPENAPI_BASE_URL}/openapi/v1/organization/sync-complete" \
-H "Authorization: Bearer ${APP_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"sync_token":"CURRENT_SYNC_TOKEN"}'
{
"code": 200,
"message": "",
"data": {"completed": true},
"request_id": "request_xxx"
}
13.9 HTTP 状态与错误码
| HTTP | code | message | 处理 |
|---|---|---|---|
| 400 | 170700 |
invalid_request |
修正 query、分页、JSON 或 Token 参数,不重试原请求 |
| 401/403 | Token/Scope 相关码 | 按鉴权响应 | 重新取得 App Token或联系管理员授权 contact:organization:read |
| 404 | 170701 |
organization_resource_not_found |
目标不存在、停用或越出发布范围;从本地快照删除/忽略 |
| 409 | 170703 |
organization_sync_version_changed |
丢弃本轮临时快照,从 sync-state 重新开始 |
| 503 | 170702 |
organization_service_unavailable |
有限次数指数退避,保留上次完成快照 |