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
userIdvalues - Display DingTalk group names instead of raw
chatIdmetadata 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
- A DingTalk developer account
- An internal enterprise application with API access
- An AppKey and AppSecret for that application
- Optional: a DingTalk custom robot webhook URL for alert delivery
Setup
1. Create a DingTalk app
- Visit the DingTalk Open Platform
- Create an internal enterprise application
- 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:
- Restart Open ACE if you changed
config.json - Call
POST /api/admin/dingtalk/syncas 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 by | Server operators, by editing the file | System administrators, via the admin UI backed by /api/management/dingtalk-config (admin-only GET/PUT/DELETE plus POST .../test) |
| Secret storage | Plaintext app_secret (and alerts.dingtalk_webhook_secret) in the file | Encrypted at rest (app_secret_enc, fallback_webhook_secret_enc, Fernet); the API only returns masked "configured" indicators, never the secrets |
| Change flow | Edit file + restart | UI save + test connection; no restart needed |
| Fields | app_key, app_secret, org_sync_*, org_sync_root_dept_id | app_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_settingsrow exists yet, thedingtalksection ofconfig.json(plus the globalalerts.dingtalk_webhook_secret) is imported into the table once and recorded inconfig_import_state. From then onconfig.jsonis 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.jsononly supplies values while no database row exists. - Deleting via the admin API removes the row and writes a tombstone that prevents the legacy
config.jsonvalues 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
| Command | Description |
|---|---|
python3 scripts/shared/dingtalk_user_cache.py list | List cached users |
python3 scripts/shared/dingtalk_user_cache.py clear | Clear user cache |
python3 scripts/shared/dingtalk_group_cache.py list | List cached groups |
python3 scripts/shared/dingtalk_group_cache.py clear | Clear 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.