Webhooks
Owncast admite HTTP Webhooks para notificar a aplicaciones de terceros (como chatbots) sobre eventos en la transmisión. En otras palabras: los webhooks enviarán eventos a su código cuando ocurran cosas en su servidor Owncast.
A continuación se muestra una lista de eventos sobre los que puede recibir notificaciones.
| Tipo de evento | el webhook se activa cuando ... |
|---|---|
| CHAT | un usuario envía un mensaje de chat |
| NAME_CHANGE | un usuario cambia su nombre de usuario |
| USER_JOINED | un usuario se une al chat |
| USER_PARTED | la última conexión activa de un usuario se desconecta |
| STREAM_STARTED | se detecta una transmisión RTMP entrante |
| STREAM_STOPPED | una transmisión RTMP entrante se desconecta (por ejemplo, OBS se detiene) |
| STREAM_TITLE_UPDATED | el título de la transmisión se actualiza |
| VISIBILITY-UPDATE | un mensaje de chat enviado anteriormente se vuelve visible/invisible (configurado por un administrador/moderador) |
| FEDIVERSE_ENGAGEMENT_FOLLOW | un usuario de Fediverse sigue su servidor |
Cómo aceptar webhooks
- Visite
/admin/webhooksen su servidor owncast. - Haga clic en
Crear Webhook. - Introduzca la URL pública completa a un endpoint que pueda recibir este webhook.
- Keep or replace the pre-filled webhook secret. Owncast uses it to sign every delivery, and you'll use it to verify them. You can reveal or copy it later from the webhook list.
- Seleccione los eventos de los que desea recibir notificaciones.
- Guarde este nuevo webhook.
Su código
- En cualquier idioma, en cualquier tipo de servidor web, cree un endpoint que acepte una solicitud HTTP
POST. Aquí es donde Owncast enviará eventos. - Cada carga de evento tendrá una propiedad
typeque indica qué tipo de evento es y un objetoeventDataque incluye propiedades específicas de ese evento.
Verificando las solicitudes de webhook
Owncast 0.3.0 signs every webhook delivery. Earlier releases send unsigned requests with no signature header.
Every webhook has a secret, created with the webhook in the admin. Each delivery includes an owncast-signature header:
owncast-signature: t=1718400000.s=5f8a1c...
t is the Unix timestamp when the request was signed. s is a hex-encoded HMAC-SHA256 signature.
To verify a delivery:
- Parse
tandsfrom the header. - Reject the request if
tdiffers from the current time by more than 300 seconds. This blocks replayed deliveries. - Compute
HMAC-SHA256(secret, "<t>." + body), wherebodyis the exact raw request body. Don't re-serialize the JSON, since any formatting difference changes the signature. - Hex-encode the result and compare it to
susing a constant-time comparison.
A Node.js example:
const crypto = require("crypto");
function verifyWebhook(signatureHeader, rawBody, secret) {
const parts = {};
for (const part of signatureHeader.split(".")) {
const [key, value] = part.trim().split("=");
if (key === "t" || key === "s") parts[key] = value;
}
if (!parts.t || !parts.s) return false;
// Reject replays outside a 5 minute window.
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
if (parts.s.length !== expected.length) return false;
return crypto.timingSafeEqual(Buffer.from(parts.s), Buffer.from(expected));
}
Verification is optional. If you skip it, treat your endpoint as something anyone on the internet could call.
Webhooks de alto nivel
Los webhooks utilizan el método HTTP POST para enviar datos a un endpoint. El cuerpo de la solicitud del webhook es un JSON simple.
Por lo tanto, el encabezado ContentType de la solicitud es application/json. Cada cuerpo de webhook sigue una estructura JSON simple.
{
"type": "",
"eventData": {}
}
donde
- type da información sobre el tipo de evento que es (uno de los tipos de la tabla anterior).
- eventData proporciona más información sobre el evento. La estructura de
eventDataes diferente para cadatype.
Every eventData also includes a status object describing the current stream state and a serverURL string identifying the server that sent the event. The one exception is FEDIVERSE_ENGAGEMENT_FOLLOW, which carries serverURL but no status.
Ejemplos de lo que se puede esperar en eventData para cada tipo de evento se indican a continuación.
Ejemplos de Webhook
CHAT
{
"type": "CHAT",
"eventData": {
"status": {
"lastConnectTime": "2021-08-12T07:45:03.986220954Z",
"lastDisconnectTime": null,
"versionNumber": "0.2.5",
"streamTitle": "",
"viewerCount": 3,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 4,
"online": true
},
"serverURL": "https://stream.example.com",
"user": {
"id": "qSRQpeM7R",
"displayName": "lazyDaisy",
"displayColor": 182,
"createdAt": "2021-08-12T07:51:37.470812684Z",
"previousNames": ["lazyDaisy"],
"nameChangedAt": "2022-09-19T12:33:59.42313245+02:00",
"isBot": false,
"authenticated": false
},
"timestamp": "2021-08-12T07:53:12.061982913Z",
"body": "\u003cp\u003ehello world \u003cimg class=\"emoji\" alt=\":beerparrot:\" title=\":beerparrot:\" src=\"/img/emoji/beerparrot.gif\"\u003e\u003c/p\u003e",
"rawBody": "hello world :beerparrot:",
"id": "j-rXteG7R",
"clientId": 2,
"visible": true
}
}
bodyis the message rendered to sanitized HTML. Markdown is converted and emoji shortcodes are replaced with<img>tags.rawBodyis the original message text exactly as the user typed it.
Nota: el campo user en el chat fue introducido con v0.0.8. Antes de v0.0.8 se utilizaba un campo de cadena simple con el nombre author.
NAME_CHANGE
{
"type": "NAME_CHANGE",
"eventData": {
"status": {
"lastConnectTime": "2021-08-12T07:45:03.986220954Z",
"lastDisconnectTime": null,
"versionNumber": "0.2.5",
"streamTitle": "",
"viewerCount": 3,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 4,
"online": true
},
"serverURL": "https://stream.example.com",
"id": "GsxeK6MIg",
"timestamp": "2022-09-19T12:33:59.423278816+02:00",
"user": {
"id": "qSRQpeM7R",
"displayName": "NotSoLazyDaisy",
"displayColor": 182,
"createdAt": "2021-08-12T07:51:37.470812684Z",
"previousNames": ["lazyDaisy"],
"nameChangedAt": "2022-09-19T12:33:59.423278816+02:00",
"isBot": false,
"authenticated": false
},
"newName": "NotSoLazyDaisy"
}
}
USER_JOINED
{
"type": "USER_JOINED",
"eventData": {
"status": {
"lastConnectTime": "2021-08-12T07:45:03.986220954Z",
"lastDisconnectTime": null,
"versionNumber": "0.2.5",
"streamTitle": "",
"viewerCount": 3,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 4,
"online": true
},
"serverURL": "https://stream.example.com",
"id": "wAgcTeM7g",
"timestamp": "2021-08-12T08:19:28.921355401Z",
"user": {
"id": "yFgco6M7R",
"displayName": "laughing-cray",
"displayColor": 257,
"createdAt": "2021-08-12T08:19:28.759651178Z",
"previousNames": ["laughing-cray"],
"nameChangedAt": "0001-01-01T00:00:00Z",
"isBot": false,
"authenticated": false
}
}
}
USER_PARTED
USER_PARTED se envía 10 segundos después de que se desconecta la última conexión activa de un usuario al chat. Si el usuario se reconecta durante ese tiempo, el evento se cancela. Disabling visible join and part messages only hides the message in chat. The webhook is still sent.
{
"type": "USER_PARTED",
"eventData": {
"status": {
"lastConnectTime": "2021-08-12T07:45:03.986220954Z",
"lastDisconnectTime": null,
"versionNumber": "0.2.5",
"streamTitle": "",
"viewerCount": 3,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 4,
"online": true
},
"serverURL": "https://stream.example.com",
"id": "Ws4gTeM7R",
"timestamp": "2021-08-12T08:20:01.061982913Z",
"user": {
"id": "yFgco6M7R",
"displayName": "laughing-cray",
"displayColor": 257,
"createdAt": "2021-08-12T08:19:28.759651178Z",
"previousNames": ["laughing-cray"],
"nameChangedAt": "0001-01-01T00:00:00Z",
"isBot": false,
"authenticated": false
}
}
}
STREAM_STARTED
{
"type": "STREAM_STARTED",
"eventData": {
"id": "WtokptnVR",
"name": "Owncast",
"serverURL": "https://stream.example.com",
"status": {
"lastConnectTime": "2022-09-19T12:30:26.97907142+02:00",
"lastDisconnectTime": null,
"versionNumber": "0.2.5",
"streamTitle": "",
"viewerCount": 0,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 0,
"online": true
},
"streamTitle": "",
"summary": "Welcome to your new Owncast server! This description can be changed in the admin. Visit https://owncast.online/docs/configuration/ to learn more.",
"timestamp": "2022-09-19T12:30:26.97907142+02:00"
}
}
STREAM_STOPPED
{
"type": "STREAM_STOPPED",
"eventData": {
"id": "YP-aptn4g",
"name": "Owncast",
"serverURL": "https://stream.example.com",
"status": {
"lastConnectTime": "2022-09-19T12:30:26.97907142+02:00",
"lastDisconnectTime": "2022-09-19T12:40:21.205872269+02:00",
"versionNumber": "0.2.5",
"streamTitle": "",
"viewerCount": 0,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 2,
"online": false
},
"streamTitle": "",
"summary": "Welcome to your new Owncast server! This description can be changed in the admin. Visit https://owncast.online/docs/configuration/ to learn more.",
"timestamp": "2022-09-19T12:40:21.205872269+02:00"
}
}
STREAM_TITLE_UPDATED
{
"type": "STREAM_TITLE_UPDATED",
"eventData": {
"id": "DmeikEf4Rz",
"name": "New Owncast Server",
"serverURL": "https://stream.example.com",
"status": {
"lastConnectTime": null,
"lastDisconnectTime": "2024-10-24T22:35:05Z",
"versionNumber": "0.1.3",
"streamTitle": "Test stream title change",
"viewerCount": 0,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 2,
"online": false
},
"streamTitle": "Test stream title change",
"summary": "This is a new live video streaming server powered by Owncast.",
"timestamp": "2023-03-27T21:50:10.121391094-07:00"
}
}
VISIBILITY-UPDATE
{
"type": "VISIBILITY-UPDATE",
"eventData": {
"status": {
"lastConnectTime": "2022-09-19T12:30:26.97907142+02:00",
"lastDisconnectTime": null,
"versionNumber": "0.2.5",
"streamTitle": "",
"viewerCount": 3,
"overallMaxViewerCount": 7,
"sessionMaxViewerCount": 4,
"online": true
},
"serverURL": "https://stream.example.com",
"id": "zqGupt7VR",
"timestamp": "2022-09-19T12:44:28.225779601+02:00",
"user": null,
"visible": false,
"ids": ["-Zzltt74g", "rvd2ppn4g"]
}
}
idsis a list of IDs of messages that had their visibility changed.visibleis the new visibility of those messages.useris alwaysnullfor this event.
FEDIVERSE_ENGAGEMENT_FOLLOW
Owncast 0.3.0 adds the serverURL field to this event. Earlier releases send only id, timestamp, name, username, and image.
{
"type": "FEDIVERSE_ENGAGEMENT_FOLLOW",
"eventData": {
"id": "AqilY4hDR",
"timestamp": "2026-04-13T19:17:12.528099886Z",
"name": "Test Follower",
"username": "testfollower@fake-mastodon.example.com",
"image": "https://fake-mastodon.example.com/avatars/testfollower.png",
"serverURL": "https://stream.example.com"
}
}
eventData.ides un ID de evento de webhook generado por Owncast. No es el ID de actor de Fediverse ni el ID de solicitud de seguimiento.eventData.namees el nombre de visualización del seguidor.eventData.usernamees eluser@domaincompleto.eventData.imagees la URL del avatar del seguidor.- Unlike the other events,
eventDatadoes not include astatusobject.
clientId vs. user.id
Cuando un usuario está conectado desde múltiples dispositivos (o múltiples navegadores) al mismo tiempo con el mismo nombre de usuario, Owncast diferencia entre sus sesiones con un clientId. Los usuarios pueden tener múltiples clientIds: un solo clientId representa una única conexión a Owncast.
clientId es un número, mientras que user.id puede contener caracteres en mayúsculas, minúsculas y numéricos.
Pruebe webhooks en un entorno de desarrollo local
- Inicie Owncast localmente (por ejemplo, a través de docker).
- Visite
localhost:8080/admin, autentíquese con el nombre de usuario:adminy la clave de transmisión predeterminada:abc123. - Navegue al bloque de menú "Integración" en el lado izquierdo, haga clic en "Webhooks" y luego en "Crear Webhook".
- Establezca la dirección del Webhook para apuntar a su aplicación/integración (algo como:
http://localhost:8100/webhooks/incoming). - Seleccione los tipos de eventos que desea recibir.
- Presione "OK" para guardar el webhook.
- Inicie su integración/aplicación escuchando en la dirección configurada anteriormente.
- Opcionalmente, inicie un proxy de interceptación (por ejemplo, Burp) si desea inspeccionar los mensajes HTTP de antemano.
- Active eventos usted mismo (por ejemplo, escriba un mensaje en el chat, conecte/desconecte su software de transmisión a Owncast).
Pruebe webhooks antes de escribir cualquier código
Si desea probar cómo funcionan los webhooks antes de escribir código, cree un endpoint de prueba en RequestCatcher, y agregue la URL que le proporciona como un webhook en su administrador y vea cómo llegan las solicitudes.
Pruebe webhooks desde una instancia de producción de Owncast
Si ya tiene una instancia de Owncast ejecutándose en producción, escuchando en la web, es posible que desee usar ngrok para tunelar solicitudes HTTP a su entorno de desarrollo local.
Improve this page
See something missing or incorrect? Edit the English version of this page or help improve translations.


