Webhooks
Receptores de webhooks
Construye un endpoint que reciba los webhooks de Rhombus de forma segura: verifica las firmas sobre el cuerpo sin procesar, responde rápido y encola el trabajo, llega a sistemas de una red privada sin abrirla a internet y prueba en local.
En esta página
Esta página se tradujo automáticamente. Si encuentras errores o tienes sugerencias, escríbenos.
Un receptor de webhooks es el endpoint HTTPS al que Rhombus envía los eventos. Esta guía construye uno que verifica cada solicitud, responde dentro de los límites de tiempo de Rhombus y entrega el trabajo al resto de tus sistemas, incluidos los de una red privada que no puede aceptar conexiones desde internet.
Antes de empezar, lee Webhooks para conocer los dos tipos de webhook (la acción de webhook de una regla y los webhooks de Actividad y Diagnóstico de la organización), sus cargas útiles y sus reglas de entrega.
Qué debe hacer el receptor
- Aceptar HTTPS en una dirección pública con un certificado de una autoridad de certificación pública.
- Verificar la firma sobre el cuerpo sin procesar antes de confiar en su contenido.
- Responder
200en pocos segundos. Rhombus da 2 segundos para conectar en los webhooks de reglas y hace un solo intento; el trabajo lento va después de la respuesta. - Encolar el evento para tus trabajadores, y dejar que eliminen duplicados por los IDs de la carga útil.
Un receptor en Node.js y Python
Ambos ejemplos verifican x-rhombus-signature-sha256, el HMAC-SHA256 en hexadecimal en minúsculas del cuerpo sin procesar, con el secreto del webhook como clave. Las acciones de webhook de reglas y los webhooks de la organización lo envían, así que el mismo receptor sirve para ambos tipos; los webhooks de la organización también envían x-rhombus-signature-sha1, que no necesitas. La firma cubre exactamente los bytes que envió Rhombus, así que lee el cuerpo sin procesar: un analizador JSON que se ejecute antes (como express.json()) lo modifica, y la verificación falla.
import crypto from "node:crypto";
import express from "express";
const SECRET = process.env.RHOMBUS_WEBHOOK_SECRET; // the webhook's secret
const app = express();
// express.raw keeps the body as bytes so the signature can be checked.
app.post("/rhombus/webhook", express.raw({ type: "*/*", limit: "1mb" }), (req, res) => {
const sent = Buffer.from(req.get("x-rhombus-signature-sha256") ?? "");
const expected = Buffer.from(crypto.createHmac("sha256", SECRET).update(req.body).digest("hex"));
if (sent.length !== expected.length || !crypto.timingSafeEqual(sent, expected)) { // constant time
return res.sendStatus(401);
}
res.sendStatus(200); // answer first
queue.push(JSON.parse(req.body.toString("utf8"))); // then hand off: SQS, Pub/Sub, Kafka, a table
});
app.listen(8080);import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
SECRET = os.environ["RHOMBUS_WEBHOOK_SECRET"].encode() # the webhook's secret
app = Flask(__name__)
@app.post("/rhombus/webhook")
def rhombus_webhook():
body = request.get_data() # the raw bytes the signature covers
sent = request.headers.get("x-rhombus-signature-sha256", "")
expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sent, expected): # constant time
abort(401)
queue.put(json.loads(body)) # SQS, Pub/Sub, Kafka, a table
return "", 200El cuerpo de un webhook de regla describe la regla que se activó y los eventos que coincidieron:
const { ruleUuid, triggeredTimestampMs, deviceEvents = [] } = event;
for (const deviceEvent of deviceEvents) {
console.log(ruleUuid, deviceEvent.deviceUuid, deviceEvent.activities, deviceEvent.timestampMs);
}Webhooks muestra ambas cargas útiles completas.
Dónde ejecutarlo
| Situación | Ejecuta el receptor | Notas |
|---|---|---|
| Tus sistemas están en la nube | Un contenedor o una función detrás de tu balanceador de carga o tu API gateway | La opción más simple |
| Tus sistemas están en una red privada | Un pequeño relé público (una función serverless y una cola) que verifica y encola; los trabajadores privados toman los eventos de la cola | Ninguna conexión entrante a la red privada |
| Estás desarrollando en local | Tu equipo, detrás de un túnel temporal de un servicio de túneles | Solo para pruebas; cierra el túnel al terminar |
Redes privadas: relé a través de una cola
Los sistemas que no aceptan conexiones desde internet sí pueden salir. Pon solo el receptor en internet y deja que tus sistemas privados recojan los eventos:
- El receptor solo tiene el secreto del webhook, nunca una clave de API.
- Los trabajadores necesitan acceso saliente a la cola y nada entrante.
- Si la cola o los trabajadores no están disponibles, los eventos esperan en la cola; si el receptor no está disponible, Rhombus no reenvía, así que concilia con la API.
Evita reenviar webhooks a la red privada mediante un túnel SSH o una redirección de puertos: expone una vía de entrada a un host interno, y el túnel se convierte en una credencial y un punto único de fallo que mantener.
Pruebas locales
Para probar un receptor en tu equipo, expón su puerto a través de un servicio de túneles, agrega la URL HTTPS del túnel como URL del webhook y activa la regla. Elimina el webhook de Rhombus y cierra el túnel al terminar.
Configura Rhombus
- Agrega el webhook
Para la acción de webhook de una regla, abre la regla en la consola, agrega una acción de webhook con la URL HTTPS de tu receptor y guarda. Para un webhook de organización, ve a Configuración → Integraciones y recursos para desarrolladores → Webhooks, agrega la URL y elige Actividad o Diagnóstico.
- Guarda el secreto
Copia el secreto del webhook en la configuración de tu receptor, desde un gestor de secretos y no desde el código fuente. Encuentra tu secreto muestra dónde lo guarda cada tipo.
- Activa un evento de prueba
Provoca un evento que cubra la regla o el webhook y confirma que el receptor registra una solicitud verificada y devuelve
200.
Ponlo en producción
- Monitorea el receptor: alerta sobre respuestas que no sean
2xx, fallos de firma y respuestas lentas. Una entrega fallida de un webhook de regla también aparece en Rhombus como un evento de diagnósticoEXTERNAL_WEBHOOK_FAILURE. - Rechaza las solicitudes sin firma. Una solicitud sin una firma válida no vino de Rhombus.
- Mantén las cargas útiles fuera de los registros compartidos. Nombran personas, lugares y dispositivos; registra solo los IDs que necesites.
- Reemplaza un secreto filtrado: elimina el webhook y créalo de nuevo, lo que le da un secreto nuevo, y después actualiza el receptor.