Webhooks – Real-Time Events
BigBlueButton Webhooks let your application receive real-time HTTP POST notifications whenever meeting events occur. Instead of repeatedly polling getMeetings or getMeetingInfo, you register a callback URL and BigBlueButton pushes events to you automatically. This chapter covers the three webhook API endpoints, the callback format, event types, retry behaviour, and server configuration.
Webhooks require the bbb-webhooks package to be installed on the server. Several details described in this chapter are not covered by the official BigBlueButton documentation and have been determined through source-code analysis. undocumented
Prerequisites
The bbb-webhooks package must be installed on the BigBlueButton server before webhook endpoints become available:
sudo apt-get install bbb-webhooks If the webhook application is not running when a meeting is created, no internal meeting mapping is established. As a result, no callbacks will be triggered for that meeting.
hooks/create — Register a Webhook
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/create?<parameters>&checksum=replace-with-checksum The official documentation does not specify an HTTP method. The Express router registers GET routes only — POST requests are not accepted. undocumented
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
callbackURL | String | Yes | — | The URL that will receive POST callbacks. Duplicate detection is based solely on this URL — the same URL cannot be registered twice, even with a different meetingID or eventID. |
meetingID | String | No | — | Restricts the hook to a specific meeting (external meeting ID). If omitted, the hook becomes a global hook that fires for all meetings. |
eventID | String | No | — | Comma-separated list of event types to listen for (e.g. user-joined,meeting-ended). If omitted, all events are delivered. The filter is case-insensitive. Available since BBB 2.5. |
getRaw | Boolean | No | false | When set to true, callbacks contain the raw Redis message instead of the processed event data. |
Successful Response
<response>
<returncode>SUCCESS</returncode>
<hookID>replace-with-hook-id</hookID>
<permanentHook>false</permanentHook>
<rawData>false</rawData>
</response> Duplicate Registration
If the same callbackURL is registered again, the response still returns SUCCESS but includes a duplicateWarning. The existing hook is kept unchanged — meetingID, eventID, and getRaw are not updated.
<response>
<returncode>SUCCESS</returncode>
<hookID>replace-with-hook-id</hookID>
<messageKey>duplicateWarning</messageKey>
<message>There is already a hook for this callback URL.</message>
</response> Error Responses
messageKey | Description |
|---|---|
checksumError | The checksum is invalid or missing. |
missingParamCallbackURL | The callbackURL parameter was not provided. undocumented |
createHookError | An internal error occurred (e.g. Redis unreachable). Check the server logs. |
hooks/list — List Registered Webhooks
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/list?<parameters>&checksum=replace-with-checksum | Parameter | Type | Required | Description |
|---|---|---|---|
meetingID | String | No | Filters by meeting ID. The result includes both hooks for the specified meeting and all global hooks. If omitted, all registered hooks are returned. |
Successful Response
<response>
<returncode>SUCCESS</returncode>
<hooks>
<hook>
<hookID>replace-with-hook-id</hookID>
<callbackURL><![CDATA[https://api-guide.bbbserver.com/callbacks/webhook]]></callbackURL>
<meetingID><![CDATA[replace-with-meeting-id]]></meetingID>
<eventID>user-joined,meeting-ended</eventID>
<permanentHook>false</permanentHook>
<rawData>false</rawData>
</hook>
</hooks>
</response> The <meetingID> element only appears for meeting-specific hooks. Global hooks have no <meetingID> element. Likewise, <eventID> only appears when an event filter was set during creation.
Error Responses
messageKey | Description |
|---|---|
checksumError | The checksum is invalid or missing. |
listHookError | An internal error occurred while listing hooks. Check the server logs. undocumented |
hooks/destroy — Remove a Webhook
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/destroy?<parameters>&checksum=replace-with-checksum | Parameter | Type | Required | Description |
|---|---|---|---|
hookID | String | Yes | The ID of the hook to remove (obtained from hooks/create or hooks/list). |
Successful Response
<response>
<returncode>SUCCESS</returncode>
<removed>true</removed>
</response> Permanent hooks (configured server-side via permanentURLs) cannot be deleted through the API. Attempting to do so returns a destroyMissingHook error as if the hook does not exist.
Error Responses
messageKey | Description |
|---|---|
checksumError | The checksum is invalid or missing. |
missingParamHookID | The hookID parameter was not provided. |
destroyMissingHook | The hook ID does not exist or belongs to a permanent hook. |
destroyHookError | An internal error occurred while removing the hook. Check the server logs. |
hooks/ping — Health Check
GET https://api-guide.bbbserver.com/bigbluebutton/api/hooks/ping This diagnostic endpoint requires no checksum. If the webhook application is reachable it returns the plain-text string bbb-webhooks API up! (not XML, content type text/plain). undocumented
Callback Format
Webhook callbacks are sent as HTTP POST requests with content type application/x-www-form-urlencoded. The request body contains three fields:
| Field | Description |
|---|---|
domain | The server domain (from the webhook configuration). undocumented |
event | A JSON array containing the event data. Always an array, even for a single event. |
timestamp | A Unix timestamp in milliseconds at the time of dispatch. |
Processed Callback Body (getRaw=false)
domain=myserver.com&event=[{"data":{"type":"event","id":"user-joined","attributes":{...},"event":{"ts":1532718316938}}}]×tamp=1532718316953 Raw Callback Body (getRaw=true)
domain=myserver.com&event=[{"envelope":{"name":"UserJoinedMeetingEvtMsg","routing":{"sender":"bbb-apps-akka"}},"core":{"header":{...},"body":{...}}}]×tamp=1532718316953 When the server is configured with multiEvent greater than 1, multiple events can be bundled in the event array within a single callback.
Callback Checksum Validation
In the default mode, each callback URL receives a checksum query parameter that your application should verify:
https://api-guide.bbbserver.com/callbacks/webhook?checksum=replace-with-checksum The validation formula is:
sha<algo>(<callback-URL-without-checksum> + <URL-encoded-body> + <shared-secret>) Take the registered callback URL without the checksum parameter.
Append the full URL-encoded request body (domain=...&event=[...]×tamp=...).
Append the BBB shared secret.
Hash the resulting string with the configured algorithm (default: sha1, configurable via hookChecksumAlgorithm).
Auth 2.0 / Bearer Mode
Alternatively, the server can be configured with auth2_0: true. In this mode, no checksum parameter is appended to the callback URL. Instead, the shared secret is sent as a Bearer token in the Authorization header: undocumented
Authorization: Bearer <shared-secret> Callback Behaviour
- Accepted HTTP status codes: HTTP
2xxand HTTP401are treated as successful delivery. All other status codes trigger a retry. undocumented - Redirects: HTTP
3xxresponses are followed (up to 10 hops). Only if no2xx/401is reached after 10 redirects does the callback count as failed. - Timeout: Each callback request has a timeout of 5 seconds (configurable via
requestTimeout). - Retry intervals: On failure, retries use exponential back-off:
100ms, 500ms, 1s, 2s, 4s, 8s, 10s, 30s, 60s, 60s, 60s, 60s(12 attempts over approximately 5 minutes, configurable viaretryIntervals). - After all retries are exhausted: Non-permanent hooks are automatically removed. Permanent hooks continue retrying indefinitely at 60-second intervals (configurable via
permanentIntervalReset).
Event Types
Meeting Events
| Event ID | Description |
|---|---|
meeting-created | A meeting was created. |
meeting-ended | A meeting has ended. This also triggers synthetic user-left events for all connected users. |
meeting-recording-started | Recording has started. |
meeting-recording-stopped | Recording has stopped. |
meeting-recording-unhandled | Unhandled recording event (fallback when recording status is neither true nor false). |
meeting-screenshare-started | Screen sharing started (includes presenter user data). |
meeting-screenshare-stopped | Screen sharing stopped (includes owner user data). |
meeting-presentation-changed | The active presentation changed (includes presentation-id). |
User Events
| Event ID | Description |
|---|---|
user-joined | A user joined the meeting (includes guest flag, user data, IP address, User-Agent). |
user-left | A user left the meeting (includes guest flag). Also generated synthetically on meeting-ended. |
user-audio-voice-enabled | Audio enabled (includes listening-only, sharing-mic, muted flags). |
user-audio-voice-disabled | Audio disabled. |
user-audio-muted | User was muted. |
user-audio-unmuted | User was unmuted. |
user-audio-unhandled | Unhandled audio event (fallback). |
user-cam-broadcast-start | Webcam started (includes stream-id). |
user-cam-broadcast-end | Webcam stopped. |
user-presenter-assigned | User was assigned the presenter role. |
user-presenter-unassigned | User was removed from the presenter role. |
user-emoji-changed | User emoji or reaction changed (includes emoji value). |
user-raise-hand-changed | User raised or lowered their hand (includes raise-hand boolean). On BBB versions prior to 2.7, this is generated synthetically from user-emoji-changed. |
Chat and Notes Events
| Event ID | Description |
|---|---|
chat-group-message-sent | A message was sent in the public chat. Private chat messages do not generate webhook events. |
transcript-updated | Transcription updated (includes transcript, locale, final flag). |
pad-content | Shared notes content changed (includes pad-id, rev, text). |
Poll Events
| Event ID | Description |
|---|---|
poll-started | A poll was started (includes question and answer options). |
poll-responded | A participant responded to a poll (includes answer IDs). |
Record and Playback (RAP) Events
| Event ID | Description |
|---|---|
rap-archive-started | Archiving started. |
rap-archive-ended | Archiving completed (includes recorded flag and duration). |
rap-sanity-started | Integrity check started. |
rap-sanity-ended | Integrity check completed. |
rap-post-archive-started | Post-archiving started. |
rap-post-archive-ended | Post-archiving completed. |
rap-process-started | Processing started. |
rap-process-ended | Processing completed. |
rap-post-process-started | Post-processing started. |
rap-post-process-ended | Post-processing completed. |
rap-publish-started | Publishing started. |
rap-publish-ended | Publishing completed (includes full recording object with metadata, playback, and download information). |
rap-post-publish-started | Post-publishing started. |
rap-post-publish-ended | Post-publishing completed. |
rap-published | Recording published. |
rap-unpublished | Recording unpublished. |
rap-deleted | Recording deleted. |
Most RAP events include record-id, success (boolean), and step-time. Events with a workflow field additionally contain the processing workflow name.
Server Configuration
The following options can be configured in /etc/bigbluebutton/bbb-webhooks.yml or config/default.yml. Most of these options are not covered in the official documentation. undocumented
| Option | Default | Description |
|---|---|---|
hookChecksumAlgorithm | sha1 | Hash algorithm for callback checksums (sha1, sha256, sha384, sha512). |
api.supportedChecksumAlgorithms | [sha1, sha256, sha384, sha512] | Accepted algorithms for incoming API checksums. |
api.port | 3005 | Port of the webhook API server. |
api.bind | 127.0.0.1 | Bind address of the API server. |
includeEvents | [] | Server-wide event filter: only these events are delivered to all hooks. Empty means all events. |
excludeEvents | [] | Server-wide event filter: these events are suppressed for all hooks. Empty means no exclusions. |
permanentURLs | [] | List of permanent hook URLs that are registered on startup and cannot be removed via the API. |
retryIntervals | [100,500,1000,...,60000] | Retry intervals in milliseconds for failed callbacks. |
permanentIntervalReset | 60000 | Retry interval in milliseconds for permanent hooks after all regular retries are exhausted. |
requestTimeout | 5000 | HTTP timeout in milliseconds for callback requests. |
multiEvent | 1 | When set to a value greater than 1, multiple events are bundled in the event array per callback. |
queueSize | 10000 | Maximum size of the internal event queue. |
bbb.auth2_0 | false | Enables Bearer token authentication instead of URL checksum for callbacks. |
includeEvents and excludeEvents are server-wide filters that apply in addition to the per-hook eventID filter. An event must pass both filters to be delivered.
Operational Notes
- Persistence: Hooks are stored in Redis and survive server restarts (re-synced on startup).
- Ordering: Callbacks are sent sequentially per hook, one at a time, preserving event order.
- URL uniqueness: Duplicate detection is based solely on the callback URL. The same URL cannot be registered with different
meetingIDoreventIDfilters — the first registration wins. - Data cleanup: Internal meeting mappings are removed after one week of inactivity (configurable via
mappings.timeout, default: 604800000 ms). - Synthetic events: When a meeting ends,
user-leftevents are automatically generated for all still-connected users, because BigBlueButton itself does not emitUserLeftMeetingEvtMsgat meeting end. - Private chats: Only public chat messages generate
chat-group-message-sentevents. Private messages are silently ignored.
meetingID is required for every hook — global hooks without a meetingID are not supported; (2) personal data in events is automatically obfuscated for privacy (e.g. participant IP addresses are not visible).Frequently Asked Questions
bbb-webhooks package must be installed via apt-get install bbb-webhooks. Without it, the hooks/create, hooks/list, and hooks/destroy endpoints are not available.duplicateWarning and the existing hook remains unchanged. To monitor specific meetings separately, use distinct callback URLs.MAIN-PUBLIC-GROUP-CHAT) trigger a chat-group-message-sent event. Private messages between participants do not generate any webhook events.getRaw=false (the default), you receive processed event data with a standardised structure containing type, id, and attributes fields. With getRaw=true, you receive the raw Redis message as published by BigBlueButton internally, which includes envelope and core fields. The raw format is useful for debugging or when you need data not included in the processed format.permanentURLs setting and cannot be removed through the API. Calling hooks/destroy on a permanent hook returns a destroyMissingHook error as if the hook does not exist.2xx and HTTP 401 are both treated as successful delivery. All other status codes, including 3xx (after following up to 10 redirects), 4xx (except 401), and 5xx, trigger a retry. This behaviour is not documented in the official BigBlueButton documentation.