From Missing dingtalkid to One‑Click Agent: Building DingTalk Robot Jump Links
This guide explains how to capture the hidden $:LWCP_v1:$‑prefixed dingtalkid from DingTalk robot callbacks using a minimal Python HTTP service, then construct a dingtalk://jump‑robot link that pre‑fills a query, enabling users to launch an internal Agent with a single click.
To expose an internal enterprise Agent as a DingTalk robot, the author first describes the desired user experience: a single link that opens a robot conversation and pre‑fills the input box with a question, allowing the Agent to answer without the user opening a web page or logging in.
01 What the link does
The link
dingtalk://dingtalkclient/action/jumprobot?dingtalkid=xxxx&content=xxxxxlaunches a specific robot in the DingTalk client and fills the content parameter into the input field.
02 What dingtalkid really is
It is not the AppId, agentId, appKey, userId, or unionId. The correct value starts with the prefix $:LWCP_v1:$ and represents the robot’s internal identifier, which is not shown in the console.
03 Why it cannot be found
The console only shows application‑level parameters.
The API documentation lists a different robot identifier.
No existing field in the system matches the required format.
Therefore the author decides to let DingTalk return the ID via a callback.
04 Solution: Deploy a minimal callback service
Run a tiny HTTP server locally to receive DingTalk robot callbacks.
Expose the service to the public Internet with a tunneling tool (e.g., ngrok).
Configure the robot’s message‑receive URL to point to the public address.
Send a private message to the robot in DingTalk.
Extract the chatbotUserId field from the callback; its value is the $:LWCP_v1:$ ‑prefixed dingtalkId.
The following Python script implements the service (zero external dependencies):
# -*- coding: utf-8 -*-
"""
DingTalk robot callback capture service – extracts chatbotUserId (the encrypted dingtalkId).
"""
import argparse, json, sys
from datetime import datetime
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
SAVE_FILE = "dingtalk_callback_capture.json"
class Handler(BaseHTTPRequestHandler):
def _respond(self, code=200, body=None):
payload = json.dumps(body or {}).encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
def do_POST(self):
length = int(self.headers.get("Content-Length", 0))
raw = self.rfile.read(length) if length else b""
text = raw.decode("utf-8", errors="replace")
try:
data = json.loads(text)
except json.JSONDecodeError:
data = {}
bot_id = data.get("chatbotUserId")
if bot_id:
print("
>>> Robot dingtalkId (chatbotUserId):", bot_id)
with open(SAVE_FILE, "w", encoding="utf-8") as f:
json.dump({"chatbotUserId": bot_id, "raw": data}, f, ensure_ascii=False, indent=2)
print(f">>> Saved to {SAVE_FILE}")
self._respond(200, {})
def do_GET(self):
self._respond(200, {"status": "ok", "hint": "POST DingTalk callback here"})
def log_message(self, *args):
pass
def main():
p = argparse.ArgumentParser(description="DingTalk robot callback capture")
p.add_argument("--port", type=int, default=8080, help="Listening port (default 8080)")
p.add_argument("--host", default="0.0.0.0", help="Listening address (default 0.0.0.0)")
args = p.parse_args()
server = ThreadingHTTPServer((args.host, args.port), Handler)
print(f"[info] Service started: http://{args.host}:{args.port}/")
print("[info] Send a private message to the robot; chatbotUserId will be printed and saved.")
try:
server.serve_forever()
except KeyboardInterrupt:
print("
[info] Stopped")
server.shutdown()
if __name__ == "__main__":
main()Start the service with: python dingtalk_callback.py --port 8080 Expose the local port via ngrok (or similar): ngrok http 8080 Copy the public URL (e.g., https://xxxx.ngrok-free.app) into the robot’s callback configuration as https://xxxx.ngrok-free.app/callback. After sending a test message, the console prints the full JSON payload and extracts the chatbotUserId, which is the required $:LWCP_v1:$ ‑prefixed ID.
05 Building the jump link
With the ID, construct the link:
dingtalk://dingtalkclient/action/jumprobot?dingtalkid=$:LWCP_v1:$xxxxxxxx&content=你好,帮我查一下最近一周的订单If content contains Chinese characters or spaces, URL‑encode it:
from urllib.parse import quote
dingtalkid = "$:LWCP_v1:$xxxxxxxx"
content = "你好,帮我查一下最近一周的订单"
url = f"dingtalk://dingtalkclient/action/jumprobot?dingtalkid={dingtalkid}&content={quote(content)}"
print(url)Paste the link into any DingTalk chat; clicking it opens the robot conversation with the pre‑filled question.
06 Pitfalls Checklist
dingtalkidis the $:LWCP_v1:$ internal identifier, not AppId, agentId, userId, or appKey.
The callback URL must be publicly reachable; localhost cannot be accessed by DingTalk.
During capture you may skip signature verification, but production use must verify timestamp and sign.
The dingtalk:// scheme only works inside the DingTalk client; it cannot be launched from regular web browsers.
Only enterprise members can invoke the link; external users see no effect.
Keep content short; the link length is limited.
If the link does nothing, verify the completeness of dingtalkid, ensure the URL was not truncated by the chat client, and confirm the robot’s callback configuration.
07 Summary
The key takeaway is to avoid guessing the robot identifier in documentation; instead, let a real callback return the $:LWCP_v1:$ ‑prefixed ID. A minimal Python callback service plus a test message quickly reveals the correct dingtalkid, after which the jump link can be assembled to provide a seamless one‑click entry to the internal Agent.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Mala Tang
Read source code together, write articles together, and enjoy spicy hot pot together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
