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-SignatureX-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}/testGET 端点是最安全的自动化界面。变更类端点(POST、PATCH、DELETE、test)是受近期登录保护的仪表板会话操作。
重试策略
失败的投递会以指数退避方式最多重试 3 次。
套餐限制
| 套餐 | 最大 Webhook 数 |
|---|---|
| Free | 2 |
| Pro | 10 |
| Team | 50 |
| Enterprise | 无限 |
