Webhooks: eventos a su servidor
Webhooks firmados y REST hooks de reactive.chat: eventos, cabeceras, cuerpo, verificación de firmas con HMAC-SHA256, reintentos, tiempos de espera, desactivación y ejemplos de verificación en PHP, Node y Python.
Los webhooks comunican a su servidor lo que ocurre en el chat: una conversación nueva, un mensaje nuevo, una transferencia al equipo o una valoración. Hay dos vías:
- Webhook firmado – una dirección de destino por espacio de trabajo, configurada en su cuenta, con cada entrega firmada mediante HMAC-SHA256. La vía adecuada para sus propios sistemas.
- REST hooks – destinos que una herramienta de automatización (Zapier, Make, n8n) registra y da de baja por sí misma mediante la API de servidor.
Eventos
- conversation.created – se ha creado una conversación. El canal está en
channel(web,whatsapp,email…). - message.created – se ha guardado un mensaje (del visitante, del asistente, de un agente o del sistema).
- handoff.requested – la conversación debe transferirse a una persona; con
reasonytarget. - csat.submitted – el visitante ha enviado una valoración;
score(1o-1) ycomment(con los datos personales ocultos). - fehlerfall.erstellt – se ha detectado un nuevo caso de error de JavaScript en su sitio web (véase Manual).
Las conversaciones de la búsqueda del sitio web (Smart Answer) solo generan eventos cuando se hacen visibles como chat.
Configurar el webhook firmado
En su cuenta, en Ajustes → Integraciones → Webhooks (permiso: gestionar integraciones):
- URL –
https://, hasta 1000 caracteres, accesible públicamente. Una dirección por espacio de trabajo. - Eventos – una selección; sin selección significa todos.
- Secreto de firma – se muestra en la página (64 caracteres hexadecimales). Regenerar invalida el anterior de inmediato; planifique el cambio para que su receptor acepte brevemente ambos.
Al guardar no se envía ninguna llamada de prueba. Tras activarlo, los eventos pueden tardar hasta unos 15 segundos en empezar a enviarse; los anteriores a la activación no se entregan retroactivamente.
La entrega
POST /rc-webhook HTTP/1.1
Host: hooks.your-company.com
Content-Type: application/json; charset=utf-8
User-Agent: reactive.chat-webhook/1.0
X-RC-Event: csat.submitted
X-RC-Delivery: 01JAB3C4D5E6F7G8H9JKMNPQRS
X-RC-Timestamp: 1792503600
X-RC-Signature: sha256=3f0a1c...
{"id":"01JAB3C4D5E6F7G8H9JKMNPQRS","event":"csat.submitted","ts":1792503600,"site_key":"ABCDEFGHJKLMNPQRSTUVWX12","conversation_id":"01JAB2...","session_id":"s_...","visitor_id":"v_...","data":{"score":5,"comment":"Very helpful"}}
Cabeceras:
- X-RC-Event – nombre del evento.
- X-RC-Delivery – ID de la entrega; se mantiene en los reintentos. Utilícelo para detectar duplicados.
- X-RC-Timestamp – segundos Unix.
- X-RC-Signature –
sha256=+hex(HMAC_SHA256(secret, X-RC-Timestamp + "." + raw_body)). La clave es la propia cadena hexadecimal mostrada.
Cuerpo:
- id (cadena) – igual que
X-RC-Delivery. - event (cadena).
- ts (entero) – segundos Unix.
- site_key, conversation_id, session_id, visitor_id (cadena o
null). - data (objeto) – datos específicos del evento. Para
fehlerfall.erstellt:fall_id,project_id,signatur,meldung,datei,zeile,spalte,seite,browser,os,stack,erste_ms,url(enlace al caso en su cuenta). Paramessage.created:messageconid,seq,kind,direction,author_type,author_name(solo para agente y asistente),via,body(con datos ocultos en el servidor),redacted,ts(milisegundos). Parahandoff.requested:reasonytarget. Paracsat.submitted:scoreycomment. Paraconversation.created:channel.
Verificar la firma
Verifique antes de cualquier procesamiento: marca de tiempo con una antigüedad máxima de 300 segundos, firma sobre el cuerpo sin procesar y comparación en tiempo constante.
PHP:
<?php
$geheim = getenv('RC_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_RC_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_RC_SIGNATURE'] ?? '';
if (!ctype_digit($ts) || abs(time() - (int) $ts) > 300) { http_response_code(401); exit; }
$soll = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $geheim);
if (!hash_equals($soll, $sig)) { http_response_code(401); exit; }
$ereignis = json_decode($body, true);
// detect duplicates: remember $ereignis['id']
http_response_code(204);
Node.js (Express):
const crypto = require('crypto');
app.post('/rc-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-RC-Timestamp') || '';
const sig = req.get('X-RC-Signature') || '';
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);
const soll = 'sha256=' + crypto.createHmac('sha256', process.env.RC_WEBHOOK_SECRET)
.update(ts + '.' + req.body.toString('utf8')).digest('hex');
if (sig.length !== soll.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(soll))) {
return res.sendStatus(401);
}
const ereignis = JSON.parse(req.body.toString('utf8'));
res.sendStatus(204);
verarbeiteSpaeter(ereignis); // respond quickly, work afterwards
});
Python (Flask):
import hmac, hashlib, os, time
from flask import request, abort
@app.post("/rc-webhook")
def rc_webhook():
ts = request.headers.get("X-RC-Timestamp", "")
sig = request.headers.get("X-RC-Signature", "")
body = request.get_data() # raw bytes
if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
abort(401)
soll = "sha256=" + hmac.new(os.environ["RC_WEBHOOK_SECRET"].encode(),
ts.encode() + b"." + body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(soll, sig):
abort(401)
return "", 204
Respuestas, reintentos y desactivación
Responda rápidamente con un 2xx y procese después. Tiempos de espera: 4 segundos para establecer la conexión y 10 segundos en total. No se siguen redirecciones.
- 2xx – entregado.
- 408, 429, 5xx, tiempo de espera agotado, error de red – reintento tras 1 s, 5 s, 25 s, 125 s y 625 s (hasta seis intentos en total); después se descarta.
- 410 Gone – la integración se desactiva. Utilice 410 solo si realmente no desea recibir más webhooks.
- otros 4xx – sin reintento.
Los eventos con más de una hora de antigüedad dejan de entregarse. Si se acumulan errores (5 en 60 segundos), la entrega a su destino se pausa durante 30 segundos; después se reintentan las tareas. El chat nunca espera a su webhook.
No se garantiza el orden de las entregas. Si es necesario, ordene por ts o por el seq de los mensajes de la API.
REST hooks (Zapier, Make, n8n)
Las herramientas de automatización registran sus destinos por sí mismas mediante la API de servidor. También puede hacerlo directamente:
curl -X POST https://reactive.chat/v1/hooks \
-H "Authorization: Bearer rc_live_..." \
-H "Content-Type: application/json" \
-d '{"event":"message.created","target_url":"https://hooks.your-company.com/rc","source":"n8n"}'
- POST /v1/hooks – un alcance con permiso de escritura.
event(uno de los eventos anteriores),target_url(https://, hasta 500 caracteres, pública),source(zapier,makeon8n, predeterminadozapier). Respuesta 201 conid. La misma combinación de evento y destino no se crea dos veces. Hasta 200 suscripciones por espacio de trabajo. - GET /v1/hooks – lista con
id,event,target_url,source,failures,created_at,last_ok_at. - DELETE /v1/hooks/{id} – cancelar la suscripción.
Entrega de un REST hook:
POST https://hooks.your-company.com/rc
Content-Type: application/json
User-Agent: reactive.chat-n8n/1.0
X-RC-Event: message.created
X-RC-Delivery: 01JAB3...
{"event":"message.created","delivery_id":"01JAB3...","data":{"name":"message.created","ts":1792503600123,"site_key":"...","conversation_id":"...","session_id":"...","visitor_id":"...","message":{"id":"...","seq":7,"kind":"text","direction":"in","author_type":"bot","author_name":"Assistant","via":null,"body":"…","redacted":false,"ts":1792503600120}}}
- Sin firma. Utilice una dirección de destino difícil de adivinar y compruebe
X-RC-Deliverypara detectar duplicados. Para sus propios sistemas, el webhook firmado es la mejor vía. dataes el evento completo: incluye ademásworkspace_id,delivery_idyattempt(número de intento). Aquí,data.tsestá en milisegundos.- El
datademessage.createdcontienemessageconid,seq,kind,direction,author_type,author_name(solo para agente y asistente),via,body(con datos ocultos en el servidor),redacted,ts.handoff.requestedincluyereasonytarget;csat.submitted,scoreycomment;conversation.created,channel. - Un 410 solo cancela esta suscripción concreta. Los hooks de una clave de API revocada dejan de atenderse.
- Actualmente, la entrega requiere que la integración correspondiente (Zapier, Make o n8n) esté activada en Integraciones del espacio de trabajo.
Qué no existe
- No hay un webhook entrante con el que su servidor introduzca mensajes de visitantes en una conversación. Envíe respuestas en nombre de su equipo mediante la API de servidor o MCP.
- No hay un periodo de transición al renovar el secreto de firma.
- No hay un evento cuando se elimina una conversación.
Siguiente: API de servidor
अंतिम अपडेट: