Webhooks
Frozen surface (dashboard UI)
The dedicated webhooks page in the cloud dashboard is currently frozen by an explicit product decision (2026-07-04, see docs/FROZEN_SURFACES.md in the repository): the sidebar entry is hidden and /webhooks redirects to /dashboard. The webhook API and delivery pipeline described below remain live and retained, but no new webhook feature work is planned until the freeze is lifted. Manage webhooks via the REST API surface documented here.
Cloud Only
Webhooks are available in the Cloud version only.
Webhooks let ADHDev Cloud push machine and agent events to your own HTTP endpoint.
Important current behavior:
- the webhook API is live
- the dedicated
/webhooksdashboard page is not currently a stable standard navigation surface - use the REST API reference as the canonical contract for payloads, events, and delivery-history shapes
- webhook create / toggle / delete / test actions are recent-login protected cloud account operations and require a fresh dashboard session JWT, not an API key
Creating a Webhook
Create the webhook from a currently signed-in cloud dashboard session. The underlying request body is:
{
"url": "https://example.com/hooks/adhdev",
"events": ["agent:generating_completed", "agent:waiting_approval"]
}If you call the mutating webhook endpoints from a stale browser session or with an API key, the server returns 403 RECENT_LOGIN_REQUIRED instead of provisioning the hook.
The create response returns:
- the webhook record
- a
secretshown once for signature verification
Use ["*"] in events to subscribe to every supported event.
Events
| Event | Trigger |
|---|---|
agent:generating_started | Agent started generating a response |
agent:generating_completed | Agent finished generating |
agent:waiting_approval | Agent is waiting for approval |
agent:error | Agent encountered an error |
webhook:test | Test delivery requested explicitly |
Payload Format
{
"event": "agent:generating_completed",
"payload": {
"chatTitle": "Claude Code · myproject",
"ideType": "claude-code",
"duration": 42,
"timestamp": 1714000000000
},
"timestamp": 1714000000000
}Signature Verification
Each webhook request includes:
X-ADHDev-SignatureX-ADHDev-Event
The signature format is:
X-ADHDev-Signature: t=1714000000000,v1=a3f9c2b1...Verify it against the raw request body.
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 example
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)
// handle event...
res.sendStatus(200)
})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 example
@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()
# handle event...
return '', 200Your webhook secret is shown once when you create the webhook — store it securely.
Management Endpoints
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 endpoints are the safest automation surface. The mutating endpoints (POST, PATCH, DELETE, test) are recent-login protected dashboard-session operations.
Retry Policy
Failed deliveries are retried up to 3 times with exponential backoff.
Plan Limits
| Plan | Max Webhooks |
|---|---|
| Free | 2 |
| Pro | 10 |
| Team | 50 |
| Enterprise | Unlimited |
