Webhooks

Owncast supports HTTP Webhooks to notify third-party applications (such as chatbots) about events on the stream. In other words: Webhooks will send events to your code when things happen on your Owncast server.

The following is a list of events you can get notified about.

Event Typewebhook triggers when …
CHATuser sends a chat message
NAME_CHANGEDuser changes their username
USER_JOINEDuser joins the chat
STREAM_STARTEDan incoming RTMP stream is detected
STREAM_STOPPEDan incoming RTMP stream disconnects (e.g. OBS stops)
STREAM_TITLE_UPDATEDthe title of the stream is updated
VISIBILITY-UPDATEa previously sent chat message becomes visible/invisible (set by an Administrator/Moderator)

How to accept webhooks

  1. Visit /admin/webhooks on your owncast server.
  2. Click Create Webhook.
  3. Put in the full public URL to an endpoint that can receive this webhook.
  4. Select the events you want to be notified of.
  5. Save this new webhook.

Your code

  1. In any language, on any kind of web server, create an endpoint that accepts a HTTP POST request. This is where Owncast will be sending events.
  2. Each event payload will have a type property that states which event type it is, and an eventData object that includes specific properties of that event.
  3. If you need a starting point see our example projects.

High level webhooks

Webhooks utilize the HTTP POST method to push data to an endpoint. The request body of the webhook is plain JSON. Thus the ContentType header for the request is application/json. Each webhook body follows a simple JSON structure.

{
    "type": "",
    "eventData": {}
}

where

  • type gives information about what kind of event it is (one of the types from the table above).
  • eventData gives more information on the event. The structure of eventData is different for each type.

Examples of what eventData to expect for each event type are below.

Webhook Examples

CHAT

{
    "type": "CHAT",
    "eventData": {
        "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
        },
        "clientId": 2,
        "body": "hello world \u003cimg class=\"emoji\" alt=\":beerparrot:\" title=\":beerparrot:\" src=\"/img/emoji/beerparrot.gif\"\u003e",
        "rawBody": "hello world \u003cimg class=\"emoji\" alt=\":beerparrot:\" title=\":beerparrot:\" src=\"/img/emoji/beerparrot.gif\"\u003e",
        "id": "j-rXteG7R",
        "visible": true,
        "timestamp": "2021-08-12T07:53:12.061982913Z"
    }
}

Note: the field user in the chat was introduced with v0.0.8. Before v0.0.8 a simple string field with the name author was used.

NAME_CHANGED

{
    "type": "NAME_CHANGE",
    "eventData": {
        "type": "NAME_CHANGE",
        "id": "",
        "timestamp": "0001-01-01T00:00:00Z",
        "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
        },
        "clientId": 2,
        "newName": "NotSoLazyDaisy"
    }
}

USER_JOINED

{
    "type": "USER_JOINED",
    "eventData": {
        "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
        },
        "clientId": 2
    }
}

STREAM_STARTED

{
    "type": "STREAM_STARTED",
    "eventData": {
        "id": "WtokptnVR",
        "name": "Owncast",
        "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",
        "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",
    "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": {
        "id": "zqGupt7VR",
        "MessageIDs": [
            "-Zzltt74g",
            "rvd2ppn4g"
        ],
        "timestamp": "2022-09-19T12:44:28.225779601+02:00",
        "Visible": false
    }
}
  • MessageIDs is a list of IDs of messages that had their visibility changed.

clientId vs. user.id

When a user is connected from multiple devices (or multiple browsers) at the same time with the same username, Owncast differentiates between their sessions with a clientId. Users can have multiple clientIds - a single clientId represents a single connection to Owncast.

clientId is a number, whereas user.id can container uppercase, lowercase and numeric characters.

Test webhooks on a local development environment

  1. Start Owncast locally (e.g. via docker).
  2. Visit localhost:8080/admin, authenticate with Username: admin and the default streaming key: abc123.
  3. Navigate to the “Integration” menu-block on the left side, click “Webhooks”, then “Create Webhook”.
  4. Set the Webhook Address to point to your application/integration (something like: http://localhost:8100/webhooks/incoming).
  5. Select the types of events that you want to receive.
  6. Press “OK” to save the webhook.
  7. Start your integration/application listening on the previously configured address.
    1. Optionally, start an interception proxy (e.g. Burp) if you want to inspect the HTTP messages beforehand.
  8. Trigger events yourself (e.g. write a message to the chat, connect/disconnect your streaming software to Owncast).

Test webhooks before writing any code

If you want to test how webhooks work before you write any code, create a test endpoint at RequestCatcher, and add the URL it gives you as a webhook in your admin and see the requests come through.

Test webhooks from a production instance of Owncast

If you already have an Owncast instance running in production, listening to the world wide web, you might want to make use ngrok to tunnel HTTP requests to your local development environment.