Skip to content

事件订阅

组织发生成员变动、你的应用被安装/卸载/调整授权时,机灵兔会主动 POST 通知你的 后端(webhook)——不用轮询通讯录,也不用让管理员手动同步。

1. 配置回调地址

在客户端「组织」→「应用」→ 应用设置 →「事件订阅」里填回调地址(https,本地 调试可用 http://localhost:*)并勾选关心的事件,点「保存并验证回调地址」。

保存前平台会向该地址发一次验证请求

json
// POST 你的回调地址
{ "type": "url_verification", "challenge": "a1b2c3…" }

你的端点必须返回 HTTP 200 + 原样回显 challenge:

json
{ "challenge": "a1b2c3…" }

回显一致才保存成功。留空回调地址即关闭订阅。

2. 事件推送格式

json
// POST 你的回调地址
{
  "event_id": "e8f0…",
  "type": "org.member.joined",
  "occurred_at": "2026-08-25T10:00:00+00:00",
  "data": { "org_id": "org_1", "user_id": "u_123", "nickname": "张三" }
}

请求头:

Header说明
X-Jlt-Event事件类型(见下表)
X-Jlt-Event-Id事件 id(幂等去重用,同一事件重试时不变)
X-Jlt-Timestamp发送时间(unix 秒)
X-Jlt-Signaturehex(HmacSHA256(app_secret, "<timestamp>." + 请求体原文))

验签(务必做):用 app_secret 对 <X-Jlt-Timestamp> 的字符串值 + "." + 请求体 原始字节(不要 re-serialize 过的 JSON)复算 HmacSHA256,与 X-Jlt-Signature 常量时间比对;并拒绝时间戳过老(如超过 1 小时)的请求防重放。

返回 2xx 表示接收成功;非 2xx 或超时(10s)会自动重试,退避间隔 30s → 2m → 10m → 1h,共 5 次尝试后标记失败(终态)。同一事件重试时 X-Jlt-Event-Id 不变——建议按它做幂等。

3. 可订阅事件

事件data 字段说明
org.member.joinedorg_id, user_id, nickname成员加入你的组织
org.member.leftorg_id, user_id成员离开(被移除或主动退出)
app.installedorg_id, granted_scopes你的应用被某组织安装(市场应用)
app.uninstalledorg_id你的应用被某组织卸载
app.scopes.grantedorg_id, granted_scopes安装方组织调整了授予的能力

4. 接收端示例(Node.js / Express)

js
import crypto from "node:crypto";
import express from "express";

const APP_SECRET = process.env.JLT_APP_SECRET;

const app = express();
// 注意:验签要的是请求体原始字节,先拿 raw body 再 parse。
app.use("/jlt/events", express.raw({ type: "application/json" }), (req, res) => {
  const ts = req.header("X-Jlt-Timestamp") || "";
  const sig = req.header("X-Jlt-Signature") || "";
  const expected = crypto
    .createHmac("sha256", APP_SECRET)
    .update(ts + "." + req.body.toString("utf8"))
    .digest("hex");
  if (!sig || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return res.status(401).json({ error: "bad signature" });
  }
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 3600) {
    return res.status(401).json({ error: "stale" });
  }

  const event = JSON.parse(req.body.toString("utf8"));

  // ① 配置回调时的验证请求:原样回显 challenge
  if (event.type === "url_verification") {
    return res.json({ challenge: event.challenge });
  }

  // ② 按 event_id 幂等处理事件
  console.log("收到事件", event.type, event.data);

  // ③ 尽快 2xx;耗时逻辑丢队列异步做,超时(10s)会被判定失败重试
  res.json({ ok: true });
});

app.listen(3000);

5. 常见问题

  • 保存时报「回调地址验证失败」:端点没有回显 challenge,或返回了非 200; 先用 curl 自测(见下)。
  • 收不到事件:在应用设置里看「待投递 N · 失败 N」概况;失败通常是端点 5xx/超时。修好端点后点**「重投失败事件」**,已终态失败的投递会重新排队 (成功过的不重发)。
  • 密钥轮换后:旧签名验不过是预期——新事件用新 secret 签名,接收端记得同步。
bash
# 本地自测回调端点
curl -s http://localhost:3000/jlt/events \
  -H 'Content-Type: application/json' -H 'X-Jlt-Timestamp: 1700000000' -H 'X-Jlt-Signature: x' \
  -d '{"type":"url_verification","challenge":"abc"}'
# → {"challenge":"abc"}

机灵兔开放平台 · 组织应用开发者文档