Qeasy Cloud
Get Started

Authoritative Tutorial on DingTalk Department Query API Field Manual: From listsub Single-Level Traversal to Multi-System Org Sync

· 吕修远· Engineering Best Practices· 10 views· 5 min read

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 includes dept_id (parent department ID, default 1 for the root) and optional language.
  • Response: errcode/errmsg plus a result array; 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 lastModifiedTime or 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

FieldTypeMeaningPractical Notes
dept_idstringUnique department primary key; used as the join key with Kingdee departmentsKeep as a string across systems; do not coerce to numeric. In Qeasy metadata it is usually mapped to id.
namestringDisplay name of the departmentOften contains full-width spaces, line breaks, or emoji. Trim and normalize encoding before persisting. Mapped to number (business identifier) in metadata.
parent_idstringParent department ID; the root department is always 1This 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_groupstringWhether a department group is createdHistorical responses mix boolean and string forms; map to 0/1 or an enum on the Kingdee side, with compatibility handling.
auto_add_userstringWhether new members are auto-added to the department groupSame 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_id as a strategy parameter with the default value 1. 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 → id and name → number, and lets you bind parent_id to Kingdee's "Parent Department" field. Across multiple customer projects we consistently normalize create_dept_group and auto_add_user to 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

  1. 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 result is empty.
  2. 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). The dept_id parameter must be configurable rather than hard-coded to 1.
  3. 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.
  4. Prefer dept_id over code as the join key with Kingdee: Codes can collide during cross-org mergers or restructuring, whereas dept_id is globally unique in DingTalk. Qeasy's mapping table module automatically uses dept_id as the match key.
  5. Normalize the department-group boolean fields: Historical API values are unstable, so the mapper must handle both true/false and 1/0 bidirectionally. This is a battle-tested default action.
  6. 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=1 once 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_id with the request parameter dept_id. In one project, the returned parent_id was mistakenly fed into the next request as dept_id, causing infinite loops or lost paths. This is where things go wrong easily—always use the level's returned dept_id as 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_group field, different tenants/historical versions return true/false or 1/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.

Original content. Please credit the source when reposting: https://www.qeasy.cloud/insights/engineering/hb-p2-261-7538

Comments