Вебхуки
Owncast поддерживает HTTP-вебхуки для уведомления сторонних приложений (например, чат-ботов) о событиях в трансляции. Проще говоря: вебхуки будут отправлять события в ваш код, когда что-то происходит на вашем сервере Owncast.
Ниже приведён список событий, о которых вы можете получать уведомления.
| Тип события | вебхук срабатывает, когда ... |
|---|---|
| CHAT | пользователь отправляет сообщение в чат |
| NAME_CHANGE | пользователь изменяет своё имя пользователя |
| USER_JOINED | пользователь подключается к чату |
| USER_PARTED | последнее активное подключение пользователя к чату отключается |
| STREAM_STARTED | обнаружен входящий RTMP-поток |
| STREAM_STOPPED | входящий RTMP-поток отключается (например, OBS останавливается) |
| STREAM_TITLE_UPDATED | название трансляции обновлено |
| VISIBILITY-UPDATE | ранее отправленное сообщение в чате становится видимым/невидимым (установлено администратором/модератором) |
| FEDIVERSE_ENGAGEMENT_FOLLOW | пользователь Fediverse подписывается на ваш сервер |
Как принимать вебхуки
- Перейдите по пути
/admin/webhooksна вашем сервере Owncast. - Нажмите
Create Webhook. - Укажите полный публичный URL конечной точки, которая может принимать этот вебхук.
- 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.
- Выберите события, о которых вы хотите получать уведомления.
- Сохраните этот новый вебхук.
Ваш код
- На любом языке и на любом типе веб-сервера создайте конечную точку, которая принимает HTTP
POSTзапрос. Сюда Owncast будет отправлять события. - В теле каждого события будет свойство
type, указывающее тип события, и объектeventDataс конкретными свойствами этого события.
Проверка запросов вебхука
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.
Общие сведения о вебхуках
Вебхуки используют метод HTTP POST для отправки данных на конечную точку. Тело запроса вебхука — простой JSON.
Следовательно, заголовок ContentType для запроса — application/json. Тело каждого вебхука имеет простую JSON-структуру.
{
"type": "",
"eventData": {}
}
где
- type содержит информацию о том, какого рода событие это (один из типов из таблицы выше).
- eventData содержит дополнительную информацию о событии. Структура
eventDataразличается для каждогоtype.
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.
Ниже приведены примеры eventData, ожидаемые для каждого типа события.
Примеры вебхуков
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.
Примечание: поле user в чате было введено в версии v0.0.8. До v0.0.8 использовалось простое строковое поле с именем 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 отправляется через 10 секунд после отключения последнего активного подключения пользователя к чату. Если пользователь повторно подключится в это время, событие отменяется. 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.id— это идентификатор события вебхука, сгенерированный Owncast. Это не идентификатор участника Fediverse и не идентификатор запроса на подписку.eventData.name— отображаемое имя подписчика.eventData.username— полный идентификатор в форматеuser@domain.eventData.image— это URL аватара подписчика.- Unlike the other events,
eventDatadoes not include astatusobject.
clientId против user.id
Когда пользователь одновременно подключается с нескольких устройств (или из нескольких браузеров) под одним и тем же именем пользователя, Owncast различает их сессии с помощью clientId. У пользователя может быть несколько clientId — один clientId представляет одно подключение к Owncast.
clientId — число, тогда как user.id может содержать заглавные, строчные и цифровые символы.
Тестирование вебхуков в локальной среде разработки
- Запустите Owncast локально (например, через docker).
- Перейдите по адресу
localhost:8080/admin, аутентифицируйтесь с именем пользователя:adminи стандартным ключом потоковой передачи:abc123. - Откройте блок меню «Integration» слева, нажмите «Webhooks», затем «Create Webhook».
- Установите Webhook Address, указывающий на ваше приложение/интеграцию (например:
http://localhost:8100/webhooks/incoming). - Выберите типы событий, которые вы хотите получать.
- Нажмите «OK», чтобы сохранить вебхук.
- Запустите ваше приложение/интеграцию, чтобы оно прослушивало ранее настроенный адрес.
- При желании запустите прокси-перехватчик (например, Burp), если вы хотите предварительно просмотреть HTTP-сообщения.
- Вызовите события самостоятельно (например, отправьте сообщение в чат, подключите/отключите ваше стриминговое ПО от Owncast).
Тестируйте вебхуки перед написанием кода
Если вы хотите проверить, как работают вебхуки, прежде чем писать код, создайте тестовую конечную точку на RequestCatcher, добавьте предоставленный URL в качестве вебхука в админке и наблюдайте проходящие запросы.
Тестирование вебхуков на рабочем экземпляре Owncast
Если у вас уже есть экземпляр Owncast, работающий в продакшене и доступный в интернете, вы можете использовать ngrok для туннелирования HTTP-запросов в вашу локальную среду разработки.
Improve this page
See something missing or incorrect? Edit this page and improve the documentation for everyone.


