Webhooks Inteligencia en tiempo real

Webhooks

ConvoAI envia notificaciones de eventos en tiempo real a tu servidor via solicitudes HTTP POST cada vez que algo sucede — un nuevo mensaje, una actualizacion de contacto, una solicitud de transferencia. Registra un endpoint de webhook una vez y recibe cada evento automaticamente.

Cómo Funciona

Cuando un evento ocurre en ConvoAI, la plataforma envia una solicitud HTTP POST a la URL de tu endpoint registrado con un cuerpo JSON describiendo el evento. Tu servidor debe responder con un 2xx estado dentro de En menos de 2 segundos. Sin documentos de conocimiento

Plataforma ConvoAI
HTTP POST + HMAC Signature
Tu sala de
Resp. Promedio

Webhooks

Registra un endpoint de webhook para un agente especifico. Cada agente puede tener multiples registros de webhook, cada uno escuchando diferentes subconjuntos de eventos.

POST /v1/agents/{agent_id}/webhooks/ Webhooks

Solicitar eliminación

ParámetroTipoRequeridoSuscripción
urlstringOpt-in requerido:URL de endpoint HTTPS para recibir eventos. Debe ser accesible publicamente.
eventsarrayOpt-in requerido:Lista de tipos de eventos a los que suscribirse. Usa ["*"] para recibir todos los eventos.
secretstringopcionalUna cadena secreta usada para firmar payloads (HMAC-SHA256). Se genera automaticamente si se omite.
descriptionstringopcionalEtiqueta legible para este registro de webhook.
activebooleanopcionalSi se debe activar la entrega inmediatamente. Predeterminado true.
Registrar un webhook — curl
curl -X POST https://app.convoai.cloud/api/v1/agents/ag_01J8X…/webhooks/ \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-server.com/webhooks/convoai", "events": ["message.received", "conversation.created", "handoff.requested"], "description": "Production webhook", "secret": "your-webhook-secret-min-32-chars" }'
Tiempo de respuesta
{ "id": "wh_01J9K2P…", "agent_id": "ag_01J8X…", "url": "https://your-server.com/webhooks/convoai", "events": ["message.received", "conversation.created", "handoff.requested"], "secret": "your-webhook-secret-min-32-chars", "description": "Production webhook", "active": true, "created_at": "2026-05-11T14:30:00Z" }

Endpoints de Gestion de Webhooks

GET/v1/agents/{agent_id}/webhooks/Webhooks
GET/v1/agents/{agent_id}/webhooks/{webhook_id}/Webhooks
PATCH/v1/agents/{agent_id}/webhooks/{webhook_id}/Webhooks
DELETE/v1/agents/{agent_id}/webhooks/{webhook_id}/Webhooks
POST/v1/agents/{agent_id}/webhooks/{webhook_id}/test/Enviar enlace de restablecimiento

Documentación

Cada payload de evento comparte un sobre comun. El data Notificaciones de eventos en tiempo real.

Sobre comun de eventos
{ "id": "evt_01J9K…", // Unique event ID — safe to deduplicate on "type": "message.received", // Event type string "created_at": "2026-05-11T14:35:22Z", "agent_id": "ag_01J8X…", "livemode": true, // false when sent from /test/ endpoint "data": { /* event-specific payload */ } }
message.received Punto de entrada de mensajes
Se dispara inmediatamente al recibir
"data": { "message_id": "msg_01J9…", "conversation_id": "conv_01J9…", "contact_id": "con_01J9…", "direction": "inbound", "type": "text", // text | image | audio | video | document | sticker | location | reaction "text": "Hi, I need help with my order", "media_url": null, "timestamp": "2026-05-11T14:35:22Z", "whatsapp_message_id": "wamid.HBgL…" }
message.sent Punto de entrada de mensajes
Se dispara al envio exitoso por API
"data": { "message_id": "msg_01J9…", "conversation_id": "conv_01J9…", "direction": "outbound", "type": "text", "text": "Of course! Could you share your order number?", "source": "ai_agent", // ai_agent | human_agent | api | template_campaign "timestamp": "2026-05-11T14:35:24Z" }
message.delivered message.read Actualizacion de estado de entrega de WhatsApp
"data": { "message_id": "msg_01J9…", "conversation_id": "conv_01J9…", "status": "delivered", // sent | delivered | read | failed "timestamp": "2026-05-11T14:35:26Z" }
conversation.created Conversaciones
"data": { "conversation_id": "conv_01J9…", "contact_id": "con_01J9…", "contact_phone": "+15551234567", "contact_name": "Maria Garcia", "channel": "whatsapp", "status": "open", "created_at": "2026-05-11T14:35:22Z" }
conversation.closed Conversaciones
"data": { "conversation_id": "conv_01J9…", "closed_by": "ai_agent", // ai_agent | human_agent | api | auto_timeout "duration_seconds": 184, "message_count": 7, "resolution": "resolved", // resolved | unresolved | abandoned "closed_at": "2026-05-11T14:38:26Z" }
contact.created contact.updated Contactos
"data": { "contact_id": "con_01J9…", "phone": "+15551234567", "name": "Maria Garcia", "email": "maria@example.com", "stage": "qualified", // new | contacted | qualified | customer | churned "tags": ["vip", "spanish"], "custom_fields": { "plan": "enterprise" }, "changed_fields": ["stage", "tags"] // only present on contact.updated }
handoff.requested Agente de IA escalando a humano
Accion requerida — asignar un agente humano
"data": { "conversation_id": "conv_01J9…", "contact_id": "con_01J9…", "reason": "customer_requested", // customer_requested | ai_confidence_low | rule_triggered "summary": "Customer asking about a refund for order #4821. Frustrated tone.", "priority": "high", // low | normal | high | urgent "requested_at": "2026-05-11T14:36:10Z" }
agent.limit_reached Límite mensual alcanzado.
"data": { "agent_id": "ag_01J8X…", "limit_type": "monthly_messages", "current_usage": 1000, "limit": 1000, "reset_at": "2026-06-01T00:00:00Z" }

Todos los tipos

EventoSuscripciónPreguntas
message.receivedPunto de entrada de mensajesMensajes IA
message.sentPunto de entrada de mensajesMensajes IA
message.deliveredEntrega de WhatsApp confirmadaActualizacion de estado por mensaje
message.readCosto por mensajePor acuse de lectura
message.failedEntregada al instante por WhatsAppEn caso de fallo
conversation.createdDisparador: Inicia nueva conversaciónNombre
conversation.closedConversacionesAl cerrar
contact.createdAún no hay facturas.Nombre
contact.updatedCampos de contacto modificadosAl actualizar CRM
handoff.requestedEscalada a humanosDisparadores de comportamiento
agent.limit_reachedLimite de uso del plan alcanzadoen línea
agent.updated3.2 Datos de Configuración del AgenteConfiguración del Agente

Notificaciones por correo

Cada entrega de webhook incluye un X-ConvoAI-Signature encabezado. Este es un digest hexadecimal HMAC-SHA256 del cuerpo crudo de la solicitud, firmado con tu secreto de webhook.

Siempre verifica la firma antes de procesar un payload de webhook. Rechaza solicitudes donde la firma este ausente o sea invalida.
X-ConvoAI-Signature: sha256=7b3f1a8c2e9d4f0b6a5c8e1f2d4a7b9c3e6f0a2b5d8e1c4f7a0b3d6e9f2c5a
Verificar firma — Python
import hmac import hashlib from django.http import HttpResponse, HttpResponseForbidden WEBHOOK_SECRET = "your-webhook-secret" def convoai_webhook(request): # 1. Get the signature from the header sig_header = request.headers.get("X-ConvoAI-Signature", "") if not sig_header.startswith("sha256="): return HttpResponseForbidden() received_sig = sig_header[7:] # strip "sha256=" prefix # 2. Compute expected signature from raw body expected_sig = hmac.new( WEBHOOK_SECRET.encode("utf-8"), request.body, # use the RAW bytes, not decoded string hashlib.sha256 ).hexdigest() # 3. Compare — use compare_digest to prevent timing attacks if not hmac.compare_digest(expected_sig, received_sig): return HttpResponseForbidden() # 4. Safe to parse and process import json payload = json.loads(request.body) handle_event(payload) return HttpResponse(status=200)
Verificar firma — Node.js (Express)
const crypto = require('crypto'); const express = require('express'); const app = express(); const WEBHOOK_SECRET = process.env.CONVOAI_WEBHOOK_SECRET; // Use express.raw() to preserve the raw body for signature checking app.post('/webhooks/convoai', express.raw({ type: 'application/json' }), (req, res) => { const sigHeader = req.headers['x-convoai-signature'] || ''; if (!sigHeader.startsWith('sha256=')) { return res.status(403).send('Missing signature'); } const receivedSig = sigHeader.slice(7); const expectedSig = crypto .createHmac('sha256', WEBHOOK_SECRET) .update(req.body) // req.body is a Buffer here .digest('hex'); const valid = crypto.timingSafeEqual( Buffer.from(expectedSig, 'hex'), Buffer.from(receivedSig, 'hex') ); if (!valid) return res.status(403).send('Invalid signature'); const payload = JSON.parse(req.body.toString()); handleEvent(payload); res.sendStatus(200); });

Política de Privacidad

Si tu endpoint retorna un estado diferente a2xx o expira el tiempo (5s), ConvoAI reintenta la entrega con backoff exponencial. Cada reintento lleva el mismo id en el sobre para que puedas deduplicar.

Intentos MáximosRetraso despues del fallo anteriorTiempo acumulado
1 (original)0s
2En menos de 2 segundos.~30s
3~2 minutos~5m
4~2 minutos~35m
5 (final)dentro de las 72 horas~2h 35m

Despues de 5 intentos fallidos, el evento se marca como Fallido y la entrega se detiene. Puedes ver los logs de entrega y reproducir eventos manualmente desde el dashboard de ConvoAI en Configuracion → Webhooks.

Calificación de Prospectos Siempre procesa los eventos de webhook de forma idempotente. Almacena el evento id y omite el procesamiento si ya lo has visto antes. Esto protege contra entregas duplicadas durante reintentos o cualquier fallo transitorio de nuestro lado.

Solicitar eliminación

Ve aSuscripción
Content-TypeSiempre Decir application/json; charset=utf-8
X-ConvoAI-SignatureFirma HMAC-SHA256: sha256={hex_digest}
X-ConvoAI-EventLa cadena de tipo de evento, ej. message.received
X-ConvoAI-DeliveryID unico de intento de entrega — diferente del ID del evento
User-AgentConvoAI-Webhook/1.0

Webhooks

Usa el endpoint de prueba para enviar un evento sintetico a tu URL registrada sin necesitar una conversacion real.

POST /v1/agents/{agent_id}/webhooks/{webhook_id}/test/ Enviar enlace de restablecimiento
Enviar evento de prueba — curl
curl -X POST https://app.convoai.cloud/api/v1/agents/ag_01J8X…/webhooks/wh_01J9K…/test/ \ -H "Authorization: Bearer ck_live_…" \ -H "Content-Type: application/json" \ -d '{"event_type": "message.received"}'

Panel de Prueba del Agente "livemode": false en el sobre. Los IDs usan un test_ prefijo. Para desarrollo local, usa una herramienta de tunel como ngrok Tu Tunnelmole para exponer tu servidor local.