Documentation Index
Fetch the complete documentation index at: https://docs.getsupervisor.ai/llms.txt
Use this file to discover all available pages before exploring further.
Los webhooks permiten recibir notificaciones HTTP en tiempo real cuando
ocurren eventos en tu workspace. Solo se entregan eventos para agentes y
suscripciones activas.
Requiere permisos webhooks:read para consultas y
webhooks:write para crear, actualizar o eliminar. Todos los
endpoints requieren el header X-Workspace-Id.
Eventos disponibles
Los webhooks pueden suscribirse a los siguientes eventos:
- tool.invoked: Se ejecuta cuando un agente termina de invocar una herramienta y recibe una respuesta.
Eventos de llamadas (Calls)
- call.callStarted: Una llamada de voz comenzó para uno de los agentes del workspace.
- call.callEnded: Una llamada de voz finalizó (incluye URLs de grabación y transcripción).
- call.callMissed: Una llamada no fue contestada antes del timeout.
- call.voicemailReceived: Un interlocutor dejó un mensaje de buzón de voz.
Eventos de WhatsApp
- whatsapp.messageReceived: Un usuario de WhatsApp envió un mensaje entrante al workspace.
- whatsapp.messageDelivered: Un mensaje saliente de WhatsApp llegó al dispositivo del destinatario.
- whatsapp.messageFailed: Un mensaje saliente de WhatsApp falló al entregarse.
- whatsapp.conversationStarted: La plataforma abrió una nueva sesión de conversación de WhatsApp.
- whatsapp.conversationEnded: Una sesión de conversación de WhatsApp existente se cerró.
Seguridad
Todas las entregas de webhook incluyen:
- HTTPS requerido: Solo se aceptan URLs HTTPS.
- Firma HMAC-SHA256: Cada request incluye el header
x-signature con la firma del payload.
- Timestamp: Header
x-timestamp con marca temporal ISO-8601.
- Secreto: El secreto se genera automáticamente (mínimo 16 caracteres) o puedes proveer el tuyo.
Verificación de firmas
Para validar que el webhook proviene de Agents Studio, verifica la firma HMAC-SHA256:
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
timestamp: string,
secret: string,
): boolean {
const message = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(message)
.digest('base64');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
);
}
// Uso en un endpoint Express
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-signature'] as string;
const timestamp = req.headers['x-timestamp'] as string;
const payload = req.body.toString('utf-8');
if (
!verifyWebhookSignature(
payload,
signature,
timestamp,
process.env.WEBHOOK_SECRET!,
)
) {
return res.status(401).send('Invalid signature');
}
// Procesar el evento
const event = JSON.parse(payload);
console.log('Event received:', event.event);
res.status(200).send('OK');
});
Gestión de webhooks
Crear webhook
import { createClient } from '@getsupervisor/agents-studio-sdk';
const client = createClient({
baseUrl: process.env.API_BASE_URL!,
workspaceId: process.env.WORKSPACE_ID!,
apiKey: process.env.API_KEY!,
});
const webhook = await client.webhooks.create({
url: 'https://mi-servidor.com/webhooks/agents-studio',
agentId: '00000000-0000-4000-8000-000000000301', // Opcional: eventos solo de este agente
description: 'Webhook para notificaciones de llamadas',
method: 'POST', // 'GET' o 'POST' (por defecto: 'POST')
isActive: true,
headers: {
'X-Custom-Header': 'valor',
},
// secret: 'mi-secreto-de-16-caracteres-o-mas', // Opcional: se genera automáticamente si se omite
});
console.log('Webhook creado:', webhook.id);
console.log('Secreto (preview):', webhook.secretPreview);
curl -X POST "$API_BASE_URL/v1/webhooks" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"url": "https://mi-servidor.com/webhooks/agents-studio",
"description": "Webhook para notificaciones de llamadas",
"method": "POST",
"isActive": true,
"headers": {
"X-Custom-Header": "valor"
}
}'
Listar webhooks
import { createClient } from '@getsupervisor/agents-studio-sdk';
const client = createClient({
baseUrl: process.env.API_BASE_URL!,
workspaceId: process.env.WORKSPACE_ID!,
apiKey: process.env.API_KEY!,
});
const page1 = await client.webhooks.list({
page: 1,
limit: 20,
sort: '-createdAt',
});
console.log(`Total: ${page1.meta.total} webhooks`);
for (const webhook of page1.data) {
console.log(`- ${webhook.url} (${webhook.isActive ? 'activo' : 'inactivo'})`);
}
// Paginación automática
if (page1.meta.hasNext) {
const page2 = await page1.next();
console.log('Siguiente página:', page2?.meta.page);
}
curl "$API_BASE_URL/v1/webhooks?page=1&limit=20&sort=-createdAt" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Obtener webhook
const webhook = await client.webhooks.get('webhook-id-uuid');
console.log('URL:', webhook.url);
console.log('Método:', webhook.method);
console.log('Entregas exitosas:', webhook.successCount);
console.log('Entregas fallidas:', webhook.failureCount);
console.log('Última entrega:', webhook.lastDeliveryAt);
console.log('Headers personalizados:', webhook.headers);
curl "$API_BASE_URL/v1/webhooks/WEBHOOK_ID" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Deliveries (historial de entregas)
Los deliveries representan cada intento de entrega de un evento a tu endpoint (incluye status, intentos, error y response status).
Listar deliveries de un webhook
const deliveries = await client.webhooks.listDeliveries('WEBHOOK_ID', {
page: 1,
limit: 50,
// Ejemplo: filtrar por fallidos de un evento específico
filter: 'and(eq(status,"failed"),eq(eventKey,"call.callEnded"))',
});
for (const d of deliveries.data) {
console.log(d.id, d.status, d.responseStatus, d.lastError);
}
curl "$API_BASE_URL/v1/webhooks/WEBHOOK_ID/deliveries?page=1&limit=50&filter=and(eq(status,\"failed\"),eq(eventKey,\"call.callEnded\"))" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Obtener un delivery por ID
const delivery = await client.webhooks.getDelivery('WEBHOOK_ID', 'DELIVERY_ID');
console.log('Status:', delivery.status);
console.log('Response status:', delivery.responseStatus);
console.log('Last error:', delivery.lastError);
curl "$API_BASE_URL/v1/webhooks/WEBHOOK_ID/deliveries/DELIVERY_ID" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Actualizar webhook
const updated = await client.webhooks.update('webhook-id-uuid', {
url: 'https://nuevo-servidor.com/webhook',
isActive: false,
method: 'GET',
description: 'Webhook actualizado',
});
console.log('Webhook actualizado:', updated.id);
curl -X PATCH "$API_BASE_URL/v1/webhooks/WEBHOOK_ID" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"isActive": false,
"description": "Webhook actualizado"
}'
Eliminar webhook
await client.webhooks.delete('webhook-id-uuid');
console.log('Webhook eliminado');
curl -X DELETE "$API_BASE_URL/v1/webhooks/WEBHOOK_ID" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Gestión de suscripciones
Crear suscripción a evento
const subscription = await client.webhooks.createSubscription(
'webhook-id-uuid',
{
eventKey: 'call.callEnded',
isActive: true,
},
);
console.log('Suscripción creada:', subscription.id);
console.log('Evento:', subscription.eventKey);
curl -X POST "$API_BASE_URL/v1/webhooks/WEBHOOK_ID/subscriptions" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"eventKey": "call.callEnded",
"isActive": true
}'
Listar suscripciones
const subs = await client.webhooks.listSubscriptions('webhook-id-uuid', {
page: 1,
limit: 50,
});
for (const sub of subs.data) {
console.log(`- ${sub.eventKey} (${sub.isActive ? 'activo' : 'inactivo'})`);
}
curl "$API_BASE_URL/v1/webhooks/WEBHOOK_ID/subscriptions?page=1&limit=50" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Obtener suscripción
const sub = await client.webhooks.getSubscription(
'webhook-id-uuid',
'subscription-id-uuid',
);
console.log('Evento:', sub.eventKey);
console.log('Activa:', sub.isActive);
curl "$API_BASE_URL/v1/webhooks/WEBHOOK_ID/subscriptions/SUBSCRIPTION_ID" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Actualizar suscripción
const updated = await client.webhooks.updateSubscription(
'webhook-id-uuid',
'subscription-id-uuid',
{
isActive: false,
},
);
console.log('Suscripción actualizada');
curl -X PATCH "$API_BASE_URL/v1/webhooks/WEBHOOK_ID/subscriptions/SUBSCRIPTION_ID" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"isActive": false
}'
Eliminar suscripción
await client.webhooks.deleteSubscription(
'webhook-id-uuid',
'subscription-id-uuid',
);
console.log('Suscripción eliminada');
curl -X DELETE "$API_BASE_URL/v1/webhooks/WEBHOOK_ID/subscriptions/SUBSCRIPTION_ID" \
-H "X-API-Key: $API_KEY" \
-H "X-Workspace-Id: $WORKSPACE_ID"
Estructura de payloads
Evento: call.callEnded
Se dispara cuando una llamada de voz finaliza. Incluye información del objetivo alcanzado, URLs de grabación y transcripción.
{
"event": "call.callEnded",
"timestamp": "2025-01-18T10:30:00.000Z",
"workspaceId": "00000000-0000-4000-8000-000000000100",
"agentId": "00000000-0000-4000-8000-000000000301",
"callId": "call-uuid-12345",
"status": "ended",
"sipCode": 180,
"endReason": "dial_no_answer",
"durationMs": 45000,
"result": {
"goal": {
"achieved": true,
"reason": "Customer inquiry resolved successfully"
},
"recordingRoute": "https://storage.example.com/recordings/call-uuid-12345.mp3",
"transcriptionRoute": "https://storage.example.com/transcriptions/call-uuid-12345.json"
},
"metadata": {
"toNumber": "+1234567890",
"createdAt": "2025-01-18T10:29:15.000Z",
"executionId": "execution-uuid-67890"
}
}
Evento: call.callStarted
Se dispara cuando una llamada de voz comienza.
{
"event": "call.callStarted",
"timestamp": "2025-01-18T10:29:15.000Z",
"workspaceId": "00000000-0000-4000-8000-000000000100",
"agentId": "00000000-0000-4000-8000-000000000301",
"callId": "call-uuid-12345",
"status": "started",
"metadata": {
"toNumber": "+1234567890",
"direction": "outbound",
"executionId": "execution-uuid-67890"
}
}
Se dispara cuando un agente termina de ejecutar una herramienta.
{
"event": "tool.invoked",
"timestamp": "2025-01-18T10:30:00.000Z",
"workspaceId": "00000000-0000-4000-8000-000000000100",
"agentId": "00000000-0000-4000-8000-000000000301",
"toolId": "tool-uuid-54321",
"toolIdentifier": "search_knowledge_base",
"executionId": "execution-uuid-67890",
"result": {
"success": true,
"output": "Query results here..."
}
}
Evento: whatsapp.messageReceived
Se dispara cuando se recibe un mensaje entrante de WhatsApp.
{
"event": "whatsapp.messageReceived",
"timestamp": "2025-01-18T10:30:00.000Z",
"workspaceId": "00000000-0000-4000-8000-000000000100",
"agentId": "00000000-0000-4000-8000-000000000301",
"messageId": "wamid.12345",
"from": "+1234567890",
"message": {
"type": "text",
"text": {
"body": "Hola, necesito ayuda"
}
},
"metadata": {
"conversationId": "conversation-uuid-11111"
}
}
Mejores prácticas
Los webhooks pueden ser reenviados en caso de fallos. Asegúrate de que tu endpoint pueda procesar el mismo evento múltiples veces sin efectos secundarios:
const processedEvents = new Set<string>();
app.post('/webhook', async (req, res) => {
const event = req.body;
const eventId = `${event.event}-${event.timestamp}-${event.callId || event.messageId}`;
if (processedEvents.has(eventId)) {
console.log('Event already processed:', eventId);
return res.status(200).send('OK');
}
// Procesar el evento
await processEvent(event);
processedEvents.add(eventId);
res.status(200).send('OK');
});
2. Responder rápidamente
Responde con 2xx lo antes posible y procesa el evento de forma asíncrona:
app.post('/webhook', async (req, res) => {
const event = req.body;
// Responder inmediatamente
res.status(200).send('OK');
// Procesar asíncronamente
setImmediate(async () => {
try {
await processEvent(event);
} catch (error) {
console.error('Error processing event:', error);
}
});
});
3. Reintentos automáticos
El sistema reintenta entregas fallidas hasta 5 veces con backoff exponencial:
- Intento 1: inmediato
- Intento 2: ~1 segundo después
- Intento 3: ~2 segundos después
- Intento 4: ~4 segundos después
- Intento 5: ~8 segundos después
Solo respuestas con código 2xx se consideran exitosas.
4. Monitorear entregas
Usa los campos successCount, failureCount y lastDeliveryAt del webhook para monitorear la salud:
const webhook = await client.webhooks.get('webhook-id');
if (webhook.failureCount > 10) {
console.warn(`Webhook ${webhook.id} has ${webhook.failureCount} failures`);
}
if (webhook.lastDeliveryAt) {
const hoursSinceLastDelivery =
(Date.now() - new Date(webhook.lastDeliveryAt).getTime()) /
(1000 * 60 * 60);
if (hoursSinceLastDelivery > 24) {
console.warn(`No deliveries in ${hoursSinceLastDelivery.toFixed(1)} hours`);
}
}
5. Filtrar por agente
Si solo necesitas eventos de un agente específico, configura agentId al crear el webhook:
const webhook = await client.webhooks.create({
url: 'https://mi-servidor.com/webhook',
agentId: 'agent-uuid-12345', // Solo eventos de este agente
// ...
});