Skip to content

Webhook

已冻结的界面(仪表板 UI)

云端仪表板中的专用 webhook 页面目前因一项明确的产品决定(2026-07-04,参见仓库中的 docs/FROZEN_SURFACES.md)而处于冻结状态:侧边栏条目已隐藏,/webhooks 会重定向到 /dashboard。下面描述的 webhook API 和投递管道仍然在线并保留,但在冻结解除之前不会计划任何新的 webhook 功能工作。请通过此处记录的 REST API 界面管理 webhook。

仅 Cloud

Webhook 仅在 Cloud 版本中可用。

Webhook 让 ADHDev Cloud 把机器和代理事件推送到你自己的 HTTP 端点。

当前重要行为:

  • webhook API 在线
  • 专用的 /webhooks 仪表板页面目前不是一个稳定的标准导航界面
  • 请使用 REST API 参考作为负载、事件和投递历史结构的权威契约
  • webhook 的创建 / 切换 / 删除 / 测试操作是受近期登录保护的云账户操作,需要一个新鲜的仪表板会话 JWT,而非 API 密钥

创建 Webhook

从当前已登录的云端仪表板会话中创建 webhook。底层请求体为:

json
{
  "url": "https://example.com/hooks/adhdev",
  "events": ["agent:generating_completed", "agent:waiting_approval"]
}

如果你从一个陈旧的浏览器会话或用 API 密钥调用变更类 webhook 端点,服务器会返回 403 RECENT_LOGIN_REQUIRED 而不是配置该 hook。

创建响应返回:

  • webhook 记录
  • 一个只显示一次的 secret,用于签名验证

events 中使用 ["*"] 可订阅每一个受支持的事件。

事件

事件触发条件
agent:generating_started代理开始生成响应
agent:generating_completed代理完成生成
agent:waiting_approval代理正在等待批准
agent:error代理遇到错误
webhook:test显式请求了测试投递

负载格式

json
{
  "event": "agent:generating_completed",
  "payload": {
    "chatTitle": "Claude Code · myproject",
    "ideType": "claude-code",
    "duration": 42,
    "timestamp": 1714000000000
  },
  "timestamp": 1714000000000
}

签名验证

每个 webhook 请求包含:

  • X-ADHDev-Signature
  • X-ADHDev-Event

签名格式为:

text
X-ADHDev-Signature: t=1714000000000,v1=a3f9c2b1...

请针对原始请求体进行验证。

js
import crypto from 'crypto'

function verifySignature(body, signature, secret) {
  const parts = Object.fromEntries(signature.split(',').map((p) => p.split('=')))
  const timestamp = parts.t
  const received = parts.v1
  const payload = `${timestamp}.${body}`
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(received),
    Buffer.from(expected)
  )
}

// Express 示例
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-adhdev-signature']
  const body = req.body.toString('utf8')
  if (!verifySignature(body, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature')
  }
  const event = JSON.parse(body)
  // 处理事件...
  res.sendStatus(200)
})
python
import hmac
import hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    parts = dict(p.split('=', 1) for p in signature.split(','))
    timestamp = parts.get('t', '')
    received = parts.get('v1', '')
    payload = f"{timestamp}.{body.decode('utf-8')}".encode()
    expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(received, expected)

# Flask 示例
@app.route('/webhook', methods=['POST'])
def webhook():
    sig = request.headers.get('X-ADHDev-Signature', '')
    if not verify_signature(request.data, sig, WEBHOOK_SECRET):
        return 'Invalid signature', 401
    event = request.get_json()
    # 处理事件...
    return '', 200

你的 webhook secret 在你创建 webhook 时只显示一次 —— 请妥善保管。

管理端点

text
GET    /api/v1/webhooks
POST   /api/v1/webhooks
PATCH  /api/v1/webhooks/{webhookId}
DELETE /api/v1/webhooks/{webhookId}
GET    /api/v1/webhooks/{webhookId}/deliveries?limit=20
POST   /api/v1/webhooks/{webhookId}/test

GET 端点是最安全的自动化界面。变更类端点(POSTPATCHDELETEtest)是受近期登录保护的仪表板会话操作。

重试策略

失败的投递会以指数退避方式最多重试 3 次。

套餐限制

套餐最大 Webhook 数
Free2
Pro10
Team50
Enterprise无限

托管云端文档在此。开源与自托管文档位于 OSS 仓库。