Última actualización
Webhooks
Una regla de alerta puede entregar a un endpoint HTTPS, además de dentro de la app, por correo y por Telegram. Cada entrega es un POST JSON firmado con el secreto del canal, que se muestra una sola vez al crear el canal.
Configurar un endpoint
- En el panel, abre Alertas → Reglas → Nueva regla, elige los tipos de evento y agrega un canal Webhook con tu URL HTTPS. Con una clave de API, envía la misma regla a
POST /v1/alerts/rules(alcancewebhook:manage). - Copia el secreto de firma (
whsec_…). Se devuelve una sola vez, enwebhookSecrets[].signingSecret, uno por cada canal de webhook. Guárdalo junto con tu endpoint. - Responde con un
2xxen menos de 10 segundos y procesa el trabajo de forma asíncrona. No se siguen redirecciones, y se rechazan las direcciones privadas, de loopback o link-local. POST /v1/alerts/rules/{id}/testenvía una entrega de ejemplo de tipoenvelope(sourceEventIdempieza portest_) para que puedas probar tu handler.
Eventos
Una regla indica en eventTypes los tipos de evento a los que reacciona; una lista vacía coincide con todos los tipos. Estos son los tipos que vale la pena alertar:
| Tipo | Se activa cuando |
|---|---|
envelope | Se observó un cofre del tesoro en una sala monitoreada. Se aplica el valor mínimo en diamantes de la regla. |
goodyBag | Se observó una goody bag en una sala monitoreada. Se aplica el valor mínimo en diamantes de la regla. |
gift | Se envió un regalo en una sala monitoreada. Se activa con cada regalo: el valor mínimo en diamantes no se aplica. |
minDiamondCount vale 49 de forma predeterminada y, junto con un mínimo de 1.51 diamantes por espectador, filtra solo los cofres del tesoro y las goody bags. Los eventos de inicio y fin de LIVE todavía no se entregan.
Encabezados
| Encabezado | Valor |
|---|---|
content-type | application/json |
x-roomscope-alert-id | <ruleId>:<eventId>, el mismo valor que alertId en el cuerpo. Usa este valor para eliminar duplicados. |
x-roomscope-timestamp | Hora Unix en segundos en que se firmó este intento. |
x-roomscope-schema-version | Versión del esquema del payload, actualmente 2026-11-03 (agrega `links`). |
x-roomscope-signature | sha256=<HMAC-SHA256 en hex de "<timestamp>.<raw body>">. |
Payload
Un evento por solicitud, en JSON compacto. diamondCount, peopleCount y unpackAt (segundos Unix) se completan para envelope y goodyBag, y son null para los demás tipos.
{ "schemaVersion": "2026-11-03", "alertId": "8f0c…:evt_…", "rule": { "id": "8f0c…", "name": "Big treasure boxes" }, "event": { "type": "envelope", "sourceEventId": "evt_…", "roomId": "7432…", "creatorUniqueId": "room.one", "region": "VN", "diamondCount": 5000, "peopleCount": 120, "score": 5000, "unpackAt": 1793520000, "observedAt": "2026-11-01T10:02:31.123Z" }, "links": { "creator": "https://app.toktokdata.live/creators/room.one", "session": "https://app.toktokdata.live/live?creator=room.one" }, "occurredAt": "2026-11-01T10:02:31.123Z", "deliveredAt": "2026-11-01T10:02:33.456Z"}Verificar firmas
La firma es un HMAC-SHA256, con el secreto whsec_… completo como clave, calculado sobre el encabezado de timestamp, un punto y el cuerpo sin procesar: <timestamp>.<body>. El digest en hexadecimal se envía como sha256=<hex> en x-roomscope-signature. Compara en tiempo constante y rechaza los timestamps fuera de una ventana que tú elijas (cinco minutos en el ejemplo de abajo) para evitar ataques de repetición.
import { createHmac, timingSafeEqual } from "node:crypto"; // rawBody: the exact bytes received, e.g. express.raw({ type: "application/json" }).// secret: the full whsec_… value shown when the webhook channel was created.export function verify(rawBody, headers, secret, toleranceSec = 300) { const signature = headers["x-roomscope-signature"]; const timestamp = headers["x-roomscope-timestamp"]; if (!signature || !/^\d+$/.test(timestamp ?? "")) return false; if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSec) return false; const expected = "sha256=" + createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex"); const a = Buffer.from(signature, "utf8"); const b = Buffer.from(expected, "utf8"); return a.length === b.length && timingSafeEqual(a, b);}Reintentos y fallas
- Un estado menor que 300 es un éxito. Un
5xx,408,429, un error de red o un tiempo de espera agotado se reintentan; cualquier otro4xxo un3xxes una falla permanente. - Una entrega tiene como máximo 4 intentos, enviados uno tras otro sin espera. Cada intento se vuelve a firmar, así que
deliveredAt, el timestamp y la firma cambian. Elimina duplicados segúnalertId, no según el cuerpo. - Después de una entrega exitosa, el mismo
alertIdno se vuelve a entregar durante 7 días. - Cada webhook entregado cuesta 1 crédito. Un espacio de trabajo puede recibir hasta 100,000 webhooks por mes calendario (UTC); las entregas que superen esa cifra se descartan.
- Las entregas fallidas todavía no aparecen en el panel. Registra las solicitudes que recibes si necesitas un registro de auditoría.
Telegram y correo
La misma regla también puede entregar a un chat de Telegram conectado o a una dirección de correo; esos canales no requieren manejar firmas. Conecta Telegram desde Alertas → Canales.