MINARA

DingTalk (钉钉)

DingTalk custom group robot with HMAC-SHA256 signed URL. Stream Mode (WebSocket) is the planned next enhancement.

🟡 Outbound-ready via custom robot, inbound via outgoing webhook. The lowest-friction setup is DingTalk's "custom group robot" with a signed URL. Enterprise self-built apps and Stream Mode (DingTalk's recommended WebSocket transport for new builds) are a future enhancement.

What you get

  • Outbound text via POST https://oapi.dingtalk.com/robot/send with HMAC-SHA256 URL signing (timestamp + secret).
  • Inbound outgoing webhook at /webhooks/dingtalk for the custom-robot mention flow. DingTalk POSTs the user's message back to us with a fresh timestamp + sign pair Minara verifies the same way it signed outbound.
  • Text only. Markdown, actionCard, and feedCard message types are deferred.
  • 5000-char limit per text message.

Setup

1. Create a custom group robot

  1. Open the target DingTalk group on desktop or mobile
  2. Group settings → "Group Assistant" → "Add Robot" → "Custom"
  3. Pick a name and avatar; for "Security settings" select "Signature" (the signed-URL option, not "Custom keyword" or "IP whitelist")
  4. Save. Copy two strings:
    • Webhook URL (the https://oapi.dingtalk.com/robot/send?access_token=... URL)
    • Signing secret (starts with SEC)

2. Configure Minara

minara auth messaging add
# pick `dingtalk`; paste the webhook URL + SECxxxx secret.

Or set env vars directly:

DINGTALK_WEBHOOK_URL=https://oapi.dingtalk.com/robot/send?access_token=<token>
DINGTALK_WEBHOOK_SECRET=SECxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

3. Test

minara auth messaging test dingtalk

The target group receives "✅ Minara gateway test ping" within a second or two.

How outbound signing works

DingTalk requires every POST to carry an HMAC of the millisecond timestamp and the secret, URL-encoded into the request URL:

sign = base64( HmacSHA256( "<timestamp>\n<secret>", secret ) )
url  = <webhook URL>?timestamp=<ts>&sign=<urlencoded sig>

DingTalk rejects timestamps more than 1 hour from its server clock, so out-of-sync hosts will see "签名错误" / "sign error" responses.

Inbound webhook (outgoing-message mode)

In the custom-robot outgoing-message flow, DingTalk POSTs to:

https://<your-host>/webhooks/dingtalk

The body carries the user's message plus a fresh timestamp and sign pair in the headers. Minara recomputes the signature using the same DINGTALK_WEBHOOK_SECRET and rejects mismatches with 401.

Inbound payload shape:

{
  "msgtype": "text",
  "text": { "content": "@bot hello world" },
  "senderId": "user-staff-id",
  "senderNick": "Alice",
  "conversationId": "cidxxxx",
  "msgId": "<unique>",
  "createAt": 1700000000000
}

Only msgtype === "text" events are forwarded. Other types (image, audio, markdown, actionCard) are dropped.

Limits & caveats

  • Clock skew matters. DingTalk enforces a 1-hour window on outbound timestamp. If your host's clock drifts, signing fails.
  • Outgoing webhook fires on @mention only. The group robot does not see every message, only mentions of itself. This is a DingTalk design choice, not a Minara filter.
  • Stream Mode is the future-recommended path. DingTalk officially recommends Stream Mode (WebSocket) for new applications. Minara ships the legacy custom-robot path first; Stream Mode lands in a follow-up.

Troubleshooting

"签名错误" (signature error) on outbound

  • Server clock drift. Run chronyc tracking or timedatectl status and confirm offset is well under 1 second.
  • Typo in DINGTALK_WEBHOOK_SECRET, the SECxxxx string is the full secret, including the SEC prefix.

"Inbound webhook returns 401"

  • The receiving signature does not match. Most common cause: the group's "Security settings" was set to "Signature" but the secret was rotated and the new value is not in ~/.minara/credentials.json.

"Bot does not respond to mention in group"

  • Confirm "Outgoing webhook" is enabled on the robot detail page (separate toggle from the inbound URL). Without it, the robot is push-only.

Reference

On this page