DingTalk
DingTalk integration is notification-only today (push fired alerts into a DingTalk group). The IM-bridge two-way mode is planned but not shipped; the validator accepts dingtalk in stream/webhook mode for forward compatibility.
| Mode | Status |
|---|---|
| Notification | Supported. Custom group bot, signed. |
| IM bridge | Scaffold present; production support deferred. |
Payload
DingTalk's custom bot accepts the standard text-message shape:
{
"msgtype": "text",
"text": {"content": "[CRITICAL] node-01 swap_high\nswap_in_pages > 1000 for 5m\nsource: alert\ndedupe: alert:swap_high:device=7"}
}The body itself is unsigned. DingTalk authenticates the request via URL query parameters instead — see signing.
This is the same flat-text formatter the Feishu and WeCom senders use, produced by formatText.
Signing
When the custom bot has 加签 (signed) turned on, DingTalk requires the request to carry a timestamp and a sign URL query parameter:
timestamp = milliseconds since epoch
stringToSign = timestamp + "\n" + secret
sign = base64(HMAC-SHA256(key=secret, message=stringToSign))The signing key is the secret only (unlike Feishu, where the secret is part of the string-to-sign). The signDingTalkURL helper appends both query params:
POST https://oapi.dingtalk.com/robot/send?access_token=…×tamp=1717012345000&sign=…Body stays plain JSON. The signed URL plus the body is what DingTalk authenticates against.
Use millisecond timestamps
DingTalk's signing expects milliseconds, not seconds. The signer uses time.Now().UnixMilli(). A second-precision timestamp gives a 310000 "sign invalid" response.
Setup
- In the target DingTalk group → 群设置 → 智能群助手 → 添加机器人 → 自定义. Give it a name and avatar.
- Pick 安全设置 → 加签 and copy the secret. (You can also pick IP allowlist or keyword filter instead; the Ongrid sender supports any of them but signed mode is recommended.)
- Copy the webhook URL — looks like
https://oapi.dingtalk.com/robot/send?access_token=<token>. - In Ongrid: Settings → Channels → New → Provider =
dingtalk→ Endpoint = the webhook URL → Secret = the signing secret from step 2 (skip if you picked keyword or IP allowlist mode).
Test message must match the group's "safety" rule
If you picked keyword filter, the test message body MUST contain that keyword or DingTalk rejects it. The default Ongrid test message is [INFO] Ongrid test. Either configure a keyword that matches (Ongrid works) or pick signed mode.
Quirks
at is not supported
The current sender ships pure msgtype: text. Mentioning a specific user (atMobiles / atUserIds / isAtAll) is not in the payload builder yet. Add it via the generic webhook if you need it today; native support is a future enhancement.
Retry on 4xx is intentional
DingTalk returns HTTP 200 even for protocol-level failures (signing mismatch, message too long, bot rate-limited), with an errcode in the JSON body. The sender treats any 2xx as success and surfaces only non-2xx HTTP status codes. If the bot silently stops delivering, check the manager log for dingtalk: send_text: code=… — Feishu and DingTalk share the same pattern of "200 OK + errcode in body".
DingTalk message rate-limit
DingTalk caps custom bot messages at 20 per minute per bot. The alert pipeline's dampening (default 5m per dedupe key) usually stays well under this; high-cardinality clusters may need a higher dampening window or per-rule channel routing.
Comparison with Feishu
| Aspect | DingTalk | Feishu |
|---|---|---|
| Signed query / body | URL query (timestamp, sign) | Body fields (timestamp, sign) |
| Timestamp unit | Milliseconds | Seconds |
| Signing key | secret | timestamp + "\n" + secret |
| HMAC algorithm | SHA-256 | SHA-256 |
| Empty body signed | No (sign is over stringToSign) | Yes (HMAC message is empty) |
Two superficially similar protocols, three real differences. They exist in separate Senders for exactly this reason.
Related
- Webhook — for protocols Ongrid doesn't speak natively (Wecom Mass app, Microsoft Teams, Mattermost, Rocket Chat, OpsGenie, PagerDuty…).
- Channels overview — the unified
notify.Messageshape every Sender consumes.