Webhooks
Superficie congelada (UI del panel)
La página dedicada de webhooks en el panel de la nube está actualmente congelada por una decisión de producto explícita (2026-07-04, consulta docs/FROZEN_SURFACES.md en el repositorio): la entrada de la barra lateral está oculta y /webhooks redirige a /dashboard. La API de webhooks y el pipeline de entrega descritos a continuación siguen activos y se mantienen, pero no hay planeado trabajo de nuevas funciones de webhook hasta que se levante la congelación. Gestiona los webhooks a través de la superficie de la REST API documentada aquí.
Solo en la nube
Los webhooks están disponibles solo en la versión Cloud.
Los webhooks permiten que ADHDev Cloud envíe eventos de máquina y agente a tu propio endpoint HTTP.
Comportamiento actual importante:
- la API de webhooks está activa
- la página dedicada
/webhooksdel panel no es actualmente una superficie de navegación estándar estable - usa la referencia de la REST API como el contrato canónico para las cargas útiles, los eventos y las formas del historial de entrega
- las acciones de crear / alternar / eliminar / probar webhook son operaciones de cuenta de la nube protegidas por inicio de sesión reciente y requieren un JWT de sesión de panel nuevo, no una clave de API
Crear un webhook
Crea el webhook desde una sesión de panel de la nube con la sesión actualmente iniciada. El cuerpo de la solicitud subyacente es:
{
"url": "https://example.com/hooks/adhdev",
"events": ["agent:generating_completed", "agent:waiting_approval"]
}Si llamas a los endpoints de webhook que mutan desde una sesión de navegador obsoleta o con una clave de API, el servidor devuelve 403 RECENT_LOGIN_REQUIRED en lugar de aprovisionar el hook.
La respuesta de creación devuelve:
- el registro del webhook
- un
secretmostrado una vez para la verificación de firma
Usa ["*"] en events para suscribirte a todos los eventos compatibles.
Eventos
| Evento | Disparador |
|---|---|
agent:generating_started | El agente empezó a generar una respuesta |
agent:generating_completed | El agente terminó de generar |
agent:waiting_approval | El agente está esperando aprobación |
agent:error | El agente encontró un error |
webhook:test | Entrega de prueba solicitada explícitamente |
Formato de la carga útil
{
"event": "agent:generating_completed",
"payload": {
"chatTitle": "Claude Code · myproject",
"ideType": "claude-code",
"duration": 42,
"timestamp": 1714000000000
},
"timestamp": 1714000000000
}Verificación de firma
Cada solicitud de webhook incluye:
X-ADHDev-SignatureX-ADHDev-Event
El formato de la firma es:
X-ADHDev-Signature: t=1714000000000,v1=a3f9c2b1...Verifícala contra el cuerpo de la solicitud en bruto.
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 '', 200Tu secreto de webhook se muestra una vez cuando creas el webhook — guárdalo de forma segura.
Endpoints de gestión
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}/testLos endpoints GET son la superficie de automatización más segura. Los endpoints que mutan (POST, PATCH, DELETE, test) son operaciones de sesión de panel protegidas por inicio de sesión reciente.
Política de reintentos
Las entregas fallidas se reintentan hasta 3 veces con backoff exponencial.
Límites de plan
| Plan | Máx. webhooks |
|---|---|
| Free | 2 |
| Pro | 10 |
| Team | 50 |
| Enterprise | Ilimitado |
