Skip to main content

DingTalk Integration Configuration

This guide explains how to configure DingTalk integration for:

  • imported-session user and group name resolution
  • local org sync of DingTalk departments and users into Open ACE teams and users
  • alert delivery to DingTalk custom robot webhooks

Overview​

Open ACE integrates with DingTalk to:

  • Display real DingTalk user names instead of raw userId values
  • Display DingTalk group names instead of raw chatId metadata when available
  • Sync DingTalk departments into Open ACE collaboration teams
  • Sync DingTalk users into local users + team memberships
  • Send alert center notifications to DingTalk group robots

Current scope:

  • OpenClaw message import path
  • DingTalk user name resolution
  • DingTalk group name resolution when session metadata contains a DingTalk chatId
  • Manual and optionally scheduled DingTalk organization sync
  • DingTalk custom robot webhook notification payloads

Prerequisites​

  1. A DingTalk developer account
  2. An internal enterprise application with API access
  3. An AppKey and AppSecret for that application
  4. Optional: a DingTalk custom robot webhook URL for alert delivery

Setup​

1. Create a DingTalk app​

  1. Visit the DingTalk Open Platform
  2. Create an internal enterprise application
  3. Save the AppKey and AppSecret

2. Configure Open ACE​

Edit ~/.open-ace/config.json:

{
"dingtalk": {
"app_key": "dingxxxxxxxxxxxxxx",
"app_secret": "your_app_secret_here",
"org_sync_enabled": true,
"org_sync_tenant_id": 1,
"org_sync_interval_minutes": 60,
"org_sync_root_dept_id": "1"
}
}

3. Test name-resolution configuration​

# Test user name lookup
python3 scripts/shared/dingtalk_user_cache.py test manager123 <app_key> <app_secret>

# Test group name lookup
python3 scripts/shared/dingtalk_group_cache.py test chatabcd1234 <app_key> <app_secret>

4. Sync the organization structure​

After saving the config:

  1. Restart Open ACE if you changed config.json
  2. Call POST /api/admin/dingtalk/sync as an administrator, optionally with {"tenant_id": 1}

When dingtalk.org_sync_enabled=true, the background data-fetch scheduler also performs periodic sync based on dingtalk.org_sync_interval_minutes.

Current sync behavior:

  • departments are mirrored into collaboration teams
  • users are provisioned into local users
  • DingTalk identities are linked through sso_identities
  • team memberships are reconciled for DingTalk-managed teams

Current non-goals:

  • disabling or deleting local users when they disappear from DingTalk
  • removing DingTalk-managed teams automatically when departments disappear
  • DingTalk SSO login flow
  • inbound DingTalk chatbot commands

config.json vs the dingtalk_settings Table​

There are two configuration channels; know which one you are using:

config.json (dingtalk section)dingtalk_settings table
Managed byServer operators, by editing the fileSystem administrators, via the admin UI backed by /api/management/dingtalk-config (admin-only GET/PUT/DELETE plus POST .../test)
Secret storagePlaintext app_secret (and alerts.dingtalk_webhook_secret) in the fileEncrypted at rest (app_secret_enc, fallback_webhook_secret_enc, Fernet); the API only returns masked "configured" indicators, never the secrets
Change flowEdit file + restartUI save + test connection; no restart needed
Fieldsapp_key, app_secret, org_sync_*, org_sync_root_dept_idapp_key, app_secret, fallback_webhook_secret, sync_enabled, target_tenant_id, interval_minutes, root_dept_id, max_runtime_seconds, auto_recovery

Behavior at runtime (verified against app/services/dingtalk_org_sync.py and app/repositories/notification_settings_repository.py):

  • One-time legacy import: the first time the settings are read, if no dingtalk_settings row exists yet, the dingtalk section of config.json (plus the global alerts.dingtalk_webhook_secret) is imported into the table once and recorded in config_import_state. From then on config.json is not re-imported, and later edits to the file have no effect on stored settings.
  • Priority: when both exist, the database row wins field by field; config.json only supplies values while no database row exists.
  • Deleting via the admin API removes the row and writes a tombstone that prevents the legacy config.json values from being silently re-imported.

Security recommendation: prefer the management-UI (database) channel, especially in production — it keeps the AppSecret and the robot signing secret out of plaintext config files. After the one-time import has run (or once you have saved credentials through the UI), remove the plaintext secrets from config.json. If the instance encryption key is later rotated, the stored secrets become undecryptable and the connection test fails with a secret-decryption error; re-enter and save them to fix it.

5. Configure DingTalk robot alerts​

In Manage -> Quota Alerts -> Notification Preferences, set the webhook URL to a DingTalk custom robot URL such as:

https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx

Open ACE sends DingTalk-compatible text payloads for alert notifications. If the DingTalk robot requires signing, the signing secret is resolved by priority: (1) a per-user secret — save openace_dingtalk_secret=<secret> in the webhook URL under your alert preferences and Open ACE encrypts it (Fernet/AES) into that user's preferences, so each tenant/user signs with their own key, isolated from others; (2) the global alerts.dingtalk_webhook_secret in config.json (shared across all users); (3) openace_dingtalk_secret=<secret> carried in the saved URL (legacy fallback). Open ACE strips that parameter before sending and adds DingTalk's timestamp / sign parameters. The per-user secret is stored only as ciphertext and decrypted solely at signing time; it is never persisted in plaintext or echoed back.

Cache management​

CommandDescription
python3 scripts/shared/dingtalk_user_cache.py listList cached users
python3 scripts/shared/dingtalk_user_cache.py clearClear user cache
python3 scripts/shared/dingtalk_group_cache.py listList cached groups
python3 scripts/shared/dingtalk_group_cache.py clearClear group cache

Cache files:

  • ~/.open-ace/dingtalk_users.json
  • ~/.open-ace/dingtalk_groups.json

Troubleshooting​

User names are not resolving​

  • Check that the DingTalk app credentials are correct
  • Verify the app can call user-detail APIs
  • Confirm the imported session metadata contains a DingTalk sender_id

Group names are not resolving​

  • Confirm the imported session metadata contains a DingTalk chatId
  • Check that the app can call group-info APIs
  • Verify the cached label includes a chat... identifier

Disabling integration​

To disable DingTalk integration, remove the dingtalk section from your config file. To keep name resolution but stop scheduled org sync, set dingtalk.org_sync_enabled=false.