Authoritative Tutorial on DingTalk Department Query API Field Manual: From listsub Single-Level Traversal to Multi-System Org Sync
What This Interface Solves
DingTalk's topapi/v2/department/listsub returns the direct sub-departments under a given parent department and is the primary entry point for syncing DingTalk's org tree to ERP (e.g., Kingdee Cloud Cosmic), approval flows, and permission systems. The API only returns one level at a time and does not recurse, so the engineering side must implement a traverse-and-aggregate loop. It is commonly used for org sync, employee-department mapping, and approver filtering.
Interface Capability Overview
- Authentication: DingTalk Open Platform AppKey/AppSecret with an access_token; legacy corpId/corpSecret is still supported. The access_token typically expires in 7200 seconds and must be cached and refreshed.
- Request: HTTP POST with
Content-Type: application/json. The body includesdept_id(parent department ID, default1for the root) and optionallanguage. - Response:
errcode/errmsgplus aresultarray; each element is a department object (dept_id,name,parent_id,create_dept_group,auto_add_user, etc.). - Pagination / Incremental Mode: No pagination fields are returned; in practice the entire direct-child list under the queried parent is returned at once. There is no cursor/offset or official incremental timestamp. Incremental sync usually relies on the downstream ERP's
lastModifiedTimeor master-data change logs. - Rate Limits & Boundaries: Calls are subject to enterprise-level QPS limits. Large traversals require queuing and backoff. The most important design trait is that this is a single-level, non-recursive API.
Typical Field Mapping
| Field | Type | Meaning | Practical Notes |
|---|---|---|---|
| dept_id | string | Unique department primary key; used as the join key with Kingdee departments | Keep as a string across systems; do not coerce to numeric. In Qeasy metadata it is usually mapped to id. |
| name | string | Display name of the department | Often contains full-width spaces, line breaks, or emoji. Trim and normalize encoding before persisting. Mapped to number (business identifier) in metadata. |
| parent_id | string | Parent department ID; the root department is always 1 | This is the only clue for rebuilding the org tree. Strictly distinguish it from the request parameter dept_id to avoid parent-child confusion. |
| create_dept_group | string | Whether a department group is created | Historical responses mix boolean and string forms; map to 0/1 or an enum on the Kingdee side, with compatibility handling. |
| auto_add_user | string | Whether new members are auto-added to the department group | Same as above: normalize to boolean to avoid schema drift in downstream ES/DB. |
How to Configure on Qeasy
On the Qeasy Data Integration Platform, this interface is typically packaged as a "DingTalk adapter + field mapper":
- Adapter Selection: Choose "DingTalk - Department Query" as the source; the target depends on the scenario, such as Kingdee Cloud Cosmic's "Department" entity or Qeasy's internal master-data store. The platform already includes access_token caching and refresh logic.
- Request Parameters: Expose
dept_idas a strategy parameter with the default value1. For multi-level traversal, configure a "loop caller" on the Qeasy platform that pushes the child department IDs from the previous result into the next request. - Field Mapping: The field mapper auto-recognizes
dept_id → idandname → number, and lets you bindparent_idto Kingdee's "Parent Department" field. Across multiple customer projects we consistently normalizecreate_dept_groupandauto_add_userto boolean before sending downstream. - Scheduling: The typical cron is
1 1 1 * *(01:01 on the 1st of each month); the adapter pushes the full org snapshot to Kingdee, while day-to-day changes are handled by a separate change-detection strategy.
Cross-Project Best Practices
- Always do single-level traversal plus in-memory aggregation: listsub is not recursive. Across multiple customer projects we adopt a "queue + recursive call" pattern, collecting child IDs per level and issuing the next round until
resultis empty. - The default root is
1, but allow a custom root: Some customers start their DingTalk org from a non-1 sub-department (common in sub-groups or branches). Thedept_idparameter must be configurable rather than hard-coded to1. - Reconstruct the org tree on the downstream side: DingTalk only returns flat parent-child relations. Tree expansion, sorting, and level numbering belong on the Kingdee or Qeasy side to keep the DingTalk interface loosely coupled.
- Prefer
dept_idovercodeas the join key with Kingdee: Codes can collide during cross-org mergers or restructuring, whereasdept_idis globally unique in DingTalk. Qeasy's mapping table module automatically usesdept_idas the match key. - Normalize the department-group boolean fields: Historical API values are unstable, so the mapper must handle both
true/falseand1/0bidirectionally. This is a battle-tested default action. - Run full + incremental schedules side by side: Run a monthly full sync to reconcile the org tree, and use DingTalk event callbacks or source polling for incremental changes. Qeasy's scheduler supports both modes simultaneously.
Pitfall Recap
- Pitfall 1: Treating a single-level API as recursive and only fetching the first level. In real projects, calling
dept_id=1once only returns level-1 departments; subsequent branches and employees are not attached. The safe approach is to configure a "recursive traverser" in Qeasy that issues requests level by level until an empty array is returned. - Pitfall 2: Confusing
parent_idwith the request parameterdept_id. In one project, the returnedparent_idwas mistakenly fed into the next request asdept_id, causing infinite loops or lost paths. This is where things go wrong easily—always use the level's returneddept_idas the next starting point. - Pitfall 3: Triggering rate limits due to unthrottled full traversal. A single full traversal can fire too many requests at once and hit DingTalk's enterprise-level QPS cap. The safe approach is to let Qeasy's rate limiter and retry/backoff mechanism control the pace.
- Pitfall 4: Boolean field type mismatch causing DB errors. For the same
create_dept_groupfield, different tenants/historical versions returntrue/falseor1/0. Schema type mismatch will fail the insert. Centralize normalization in the field mapper. - Pitfall 5: Mixing read and write in a single pipeline. DingTalk's side is a read-only QUERY strategy; the write into Kingdee must be handled by a separate write strategy. Bundling create/update on top of the query interface makes failures impossible to roll back.
When to Use
Suitable when you need to periodically or in real time sync DingTalk's org tree to ERP, HR, approval, or data warehouse systems, and when the org hierarchy is clear and changes are controllable. Not suitable for "only sync a few designated departments with high-frequency real-time changes"—that case should use DingTalk's event callback stream instead of polling listsub.