Welcome to the BigBlueButton API PHP
This is the official PHP client library for the BigBlueButton API. It enables PHP applications to create meetings, manage users and recordings, and use all other integration endpoints of a BigBlueButton server.
In this documentation, we explain the installation and usage, checking out samples, setting up different configurations, and advanced settings of the library.
Library Objectives
- Full API coverage: support all endpoints of the official BigBlueButton integration API across supported server versions (BBB 2.x, 3.x and 4.x), including new endpoints and parameters shortly after they appear in the server.
- Zero runtime dependencies: besides PHP extensions (curl, json, mbstring, simplexml), the library ships without dependencies. It works out of the box with curl and can optionally use any injected PSR-18 http client (the PSR interfaces are a suggested installation).
- Typed and self-documenting: every endpoint has a dedicated parameters and response class with typed getters, backed by enums for closed value sets (layouts, roles, policies, features).
- Tested against real servers: unit tests run against captured fixtures, integration tests run the complete suite against live BigBlueButton servers — with both transports (curl and an injected PSR-18 client).
- Backwards compatible: the library supports old server versions and avoids breaking consumer code; former members are kept as deprecated stubs where formats changed.
- Quality gates: PHPStan (level 8), php-cs-fixer and PHPUnit must pass cleanly; the pre-commit hooks enforce them on every commit.
Getting Started
Requirements
- PHP 8.2 or above.
- curl extension.
- mbstring extension.
- SimpleXML extension (ext-simplexml).
- JSON extension (ext-json).
The library itself has no runtime package dependencies. It sends requests with curl by default; alternatively you can inject any PSR-18 http client.
Installation
bigbluebutton-api-php can be installed via Composer CLI
composer require bigbluebutton/bigbluebutton-api-php
or by editing composer.json
{
"require": {
"bigbluebutton/bigbluebutton-api-php": "^3.0"
}
}
Configuration
The library reads the connection settings from two environment variables:
BBB_SERVER_BASE_URL=https://your-bbb-server.example.com/bigbluebutton/
BBB_SECRET=your-secret
You get both from your BigBlueButton server with bbb-conf --secret (see Server Configuration). In Laravel, add them to your .env; in other frameworks use the mechanism your application provides.
Alternatively, pass both values explicitly to the constructor:
use BigBlueButton\BigBlueButton;
$bbb = new BigBlueButton('https://your-bbb-server.example.com/bigbluebutton/', 'your-secret');
First call
A simple usage example that creates a meeting:
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\CreateMeetingParameters;
$bbb = new BigBlueButton();
$createMeetingParams = new CreateMeetingParameters('bbb-meeting-uid-65', 'BigBlueButton API Meeting');
$response = $bbb->createMeeting($createMeetingParams);
echo 'Created Meeting with ID: ' . $response->getMeetingId();
From here, continue with the Meetings chapter and the Full Usage Sample.
HTTP Client
By default, this library sends all requests with PHP’s curl extension — no HTTP client package is required:
use BigBlueButton\BigBlueButton;
$bbb = new BigBlueButton('https://your-server.example.com/bigbluebutton/', 'your-secret');
Injecting a PSR-18 http client
Alternatively, you can inject any PSR-18 http client together with the PSR-17 request and stream factories. This makes the library independent of curl and lets you reuse the client, its configuration and its logging/middleware stack from your application:
use BigBlueButton\BigBlueButton;
$bbb = BigBlueButton::createWithHttpClient(
$httpClient, // Psr\Http\Client\ClientInterface
$requestFactory, // Psr\Http\Message\RequestFactoryInterface
$streamFactory, // Psr\Http\Message\StreamFactoryInterface
'https://your-server.example.com/bigbluebutton/',
'your-secret',
);
The library itself has no package requirements — the PSR interfaces (psr/http-client, psr/http-factory) are a suggested installation and ship with every common PSR-18 implementation anyway. Bring your own client.
Example: Guzzle
use BigBlueButton\BigBlueButton;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
$client = new Client(['timeout' => 10]);
$factory = new HttpFactory(); // implements all PSR-17 interfaces
$bbb = BigBlueButton::createWithHttpClient(
$client,
$factory,
$factory,
'https://your-server.example.com/bigbluebutton/',
'your-secret',
);
Example: Symfony HttpClient
use BigBlueButton\BigBlueButton;
use Nyholm\Psr7\Factory\Psr17Factory;
use Symfony\Component\HttpClient\HttplugClient;
$client = new HttplugClient(); // PSR-18 compatible
$factory = new Psr17Factory();
$bbb = BigBlueButton::createWithHttpClient(
$client,
$factory,
$factory,
'https://your-server.example.com/bigbluebutton/',
'your-secret',
);
Example: lightweight php-http/curl-client
use BigBlueButton\BigBlueButton;
use Http\Client\Curl\Client;
use Nyholm\Psr7\Factory\Psr17Factory;
$factory = new Psr17Factory();
$client = new Client($factory, $factory, [
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 20,
]);
$bbb = BigBlueButton::createWithHttpClient(
$client,
$factory,
$factory,
'https://your-server.example.com/bigbluebutton/',
'your-secret',
);
Behavior with an injected client
- Timeouts and transport options are the responsibility of your client.
setTimeOut()andsetCurlOpts()have no effect on an instance created withcreateWithHttpClient(). Configure timeouts, SSL verification and proxies on the http client you pass in. - Redirects: the built-in curl transport follows redirects. If your client does not follow redirects by default, enable it if you rely on redirecting calls (e.g.
joinwithredirect=true). - Multipart uploads (e.g. uploading a caption track via
putRecordingTextTrack) are fully supported with an injected client — the library builds themultipart/form-datarequest itself. - Error handling stays the same: non-2xx responses throw a
BadResponseExceptionregardless of the transport used.
Cookies and the JSESSIONID
The BigBlueButton server sets a JSESSIONID cookie on some API interactions. This library does not maintain a cookie session — it captures exactly that one value and exposes it to the application.
What the library does
- No cookie jar is kept between requests. Each API call is stateless; the library never sends cookies back to the server.
- On every response, the
JSESSIONIDsent by the BBB-Server is read and validated (format checks; values containing path traversal, markup or script-like content are rejected). - A successfully captured session id is available to your application:
$bbb->joinMeeting($joinMeetingParams);
$sessionId = $bbb->getJSessionId();
- You can also set it manually via
setJSessionId()(e.g. to propagate an id captured elsewhere).
Transport specifics
Default curl transport
The curl transport uses a temporary cookie file per request to collect the cookies of the response. After the request, the file is read, the JSESSIONID is extracted and validated, and the temporary file is discarded. Nothing is persisted to disk beyond the lifetime of the request.
Injected PSR-18 http client
With createWithHttpClient(), the Set-Cookie headers of each response are inspected with the same validation as in the curl transport. The client itself may of course maintain its own cookie handling — that is outside the library’s scope.
What the library deliberately does not do
- It does not store cookies across processes or requests.
- It does not send the
JSESSIONID(or any other cookie) back to the server. - It does not handle authentication cookies of your application — inject your own http client if you need full cookie middleware.
Meetings
In the BigBlueButton-world a video-conference is called a meeting. Once a meeting is created, it is a “ready-to-use” video-conference sitting on the BBB-Server and is waiting for people to join. A BBB-meeting is not something that would be created in advance (e.g. one week prior) in order to distribute a meeting-link inside an invitation to the participants.
Administration
Creating
One of the first steps is the creation of a meeting. A successfully created meeting is the prerequisite to enable participants (moderators and viewers) to join that meeting in a second step.
Default meeting
In order to create a new meeting, you only need to initiate a new object of the CreateMeetingParameters-class and pass an identifier ($meetingId) and a name ($meetingName) to the constructor. This parameter object ($createMeetingParameters) must now be passed to the createMeeting-function to launch the request to the BBB-Server. This function returns the BBB-server’s response ($createMeetingResponse).
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\CreateMeetingParameters;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// you can choose your own meeting number and title
$meetingId = 123456;
$meetingName = "My first BBB-meeting";
// define the required parameters for the meeting
$createMeetingParameters = new CreateMeetingParameters($meetingId, $meetingName);
// launch the request to the BBB-Server and receive its response
$createMeetingResponse = $bbb->createMeeting($createMeetingParameters);
if (!$createMeetingResponse->success()) {
throw new \Exception($createMeetingResponse->getMessage());
}
// steps once meeting has been created
Customized meeting
To adapt the predefined parameters of a meeting, the parameters for the creation of a meeting must be adapted before sending the creation-request to the BBB-Server. Please check the official API-Reference for all the possible settings.
// ...
$createMeetingParameters
->setWelcomeMessage('Dear Student, welcome to our lesson today!')
->setWebcamsOnlyForModerator(true)
;
// ...
Plugin metadata
BBB 3.0+ plugins can receive per-meeting configuration values through plugin_* create parameters. When the BBB-Server loads a plugin manifest (see pluginManifests / pluginManifestsFetchUrl), it replaces placeholders in the manifest with the matching values.
A manifest may for example contain:
{
"name": "my-plugin",
"settings": {
"api-base-url": "${plugin_api-base-url:https://fallback.example.com}"
}
}
When the meeting is created, ${plugin_api-base-url:...} is replaced by the value of the plugin_api-base-url parameter (or by the default after the : if the parameter is missing).
// ...
$createMeetingParameters
->addPluginMeta('api-base-url', 'https://my-server.example.com') // sent as plugin_api-base-url
->addPluginMeta('vendor-name', 'Riadvice')
;
// several values at once
$createMeetingParameters->setPluginMeta([
'api-base-url' => 'https://my-server.example.com',
'vendor-name' => 'Riadvice',
]);
// ...
The key is provided without the plugin_-prefix (a provided prefix is stripped). Note that the BBB-Server lowercases the parameter name, so lowercase keys should be preferred. Placeholders in the manifest must reference the lowercased name.
Shared notes (BBB 3.0)
The editor of the shared-notes area can be selected and pre-filled with initial content.
// ...
$createMeetingParameters
->setSharedNotesEditor('blockNote') // 'etherpad' (default) or 'blockNote'
->setSharedNotesInitialContentJsonUrl('https://cdn.example.com/notes.json') // initial content fetched by the client
->setSharedNotesInitialContentJson('{"type":"doc","content":[]}') // ...or sent inline as POST module
;
// ...
The initial content can also be provided as raw Markdown (BBB 3.0.33+). The BlockNote JSON takes precedence over the Markdown; within the Markdown variants the URL is resolved first, then the inline parameter, then the POST module.
// ...
$createMeetingParameters
->setSharedNotesInitialContentMarkdownUrl('https://cdn.example.com/notes.md') // fetched by the BBB-Server (HTTPS only)
->setSharedNotesInitialContentMarkdown('# Short notes') // ...or inline as create parameter
->setSharedNotesInitialContentMarkdownModule('# Long notes...') // ...or as POST module for large content
;
// ...
BBB 4.0 additions
// ...
$createMeetingParameters
->setLockSettingsPresenterPolicy(PresenterPolicy::FREE_FOR_ALL) // 'Request to Present' policy: moderatorOnly | requireApproval (default) | freeForAll
->setNotifyRecordingAppend('This session is recorded for training.') // appended to the recording notification (requires notifyRecordingIsOn)
->setRequireUserConsentBeforeUnmuting(true) // consent dialog before moderators may unmute a user
;
// ...
Note that BBB 4.0 removed some parameters that are still supported by this library for older server versions: copyright and webVoice (create) and webVoiceConf (join) are obsolete, lockSettingsDisableNote (singular) is replaced by lockSettingsDisableNotes, and meetingLayout only accepts UNIFIED_LAYOUT (new default), CAMERAS_ONLY, PARTICIPANTS_AND_CHAT_ONLY, PRESENTATION_ONLY and MEDIA_ONLY anymore.
Client Settings Override
The BigBlueButton PHP API supports overriding HTML5 client settings from the settings.yml file. This feature allows you to customize the client behavior for specific meetings without modifying the server configuration.
Important
For security reasons, the client settings override feature is disabled by default. You must explicitly enable it by setting
allowOverrideClientSettingsOnCreateCall=true.
Basic Usage
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\CreateMeetingParameters;
use BigBlueButton\Core\ClientSettingsOverride;
// create an instance of the BBB-Client
$bbb = new BigBlueButton();
// define the required parameters for the meeting
$createMeetingParameters = new CreateMeetingParameters($meetingId, $meetingName);
// enable client settings override
$createMeetingParameters->setAllowOverrideClientSettingsOnCreateCall(true);
// create client settings override
$clientSettings = new ClientSettingsOverride([
'public' => [
'kurento' => [
'wsUrl' => 'wss://test.bigbluebutton.org/bbb-webrtc-sfu'
],
'media' => [
'sipjsHackViaWs' => false
],
'app' => [
'appName' => 'Test Meeting',
'helpLink' => 'https://www.bigbluebutton.org',
'autoJoin' => false,
'askForConfirmationOnLeave' => false,
'userSettingsStorage' => 'localStorage',
'defaultSettings' => [
'application' => [
'overrideLocale' => 'en'
]
]
]
]
]);
// set the client settings override
$createMeetingParameters->setClientSettingsOverride($clientSettings);
// launch the request to the BBB-Server
$createMeetingResponse = $bbb->createMeeting($createMeetingParameters);
Advanced Usage with Individual Settings
You can also set individual settings using dot notation:
$clientSettings = new ClientSettingsOverride();
// set individual settings
$clientSettings->setSetting('public.app.appName', 'Custom Meeting Name');
$clientSettings->setSetting('public.kurento.wsUrl', 'wss://custom.example.com/sfu');
$clientSettings->setSetting('public.media.sipjsHackViaWs', false);
// get individual settings
$appName = $clientSettings->getSetting('public.app.appName');
$wsUrl = $clientSettings->getSetting('public.kurento.wsUrl', 'wss://default.example.com');
// remove settings
$clientSettings->removeSetting('public.media.sipjsHackViaWs');
// set the client settings override
$createMeetingParameters->setClientSettingsOverride($clientSettings);
Creating from JSON
You can create a ClientSettingsOverride object from a JSON string:
$jsonSettings = '{
"public": {
"app": {
"appName": "JSON Meeting",
"helpLink": "https://help.example.com"
}
}
}';
$clientSettings = ClientSettingsOverride::fromJson($jsonSettings);
$createMeetingParameters->setClientSettingsOverride($clientSettings);
Common Override Settings
Here are some commonly overridden settings:
Application Settings:
public.app.appName- Custom application namepublic.app.helpLink- Custom help linkpublic.app.autoJoin- Auto-join meeting (true/false)public.app.askForConfirmationOnLeave- Ask for confirmation when leavingpublic.app.userSettingsStorage- Storage type for user settings
Kurento/WebRTC Settings:
public.kurento.wsUrl- Custom WebRTC SFU URLpublic.kurento.turnUrl- Custom TURN server URL
Media Settings:
public.media.sipjsHackViaWs- SIP.js WebSocket hackpublic.media.audio.codec- Preferred audio codecpublic.media.video.codec- Preferred video codec
Theme Settings:
public.theme.branding.target- Custom branding targetpublic.theme.custom_css_url- Custom CSS URL
Note
The client settings override takes precedence over server configuration files. Use this feature carefully to avoid unexpected behavior.
Insert Document
Documents can be added either during the creation of a meeting (see $createMeetingParameters) or can be added once needed. This section is about adding documents into a running meeting.
old way (presentations)
Note
addPresentation()is deprecated — use the document-based API below instead.
use BigBlueButton\BigBlueButton;
use BigBlueButton\Enum\DocumentOption;
use BigBlueButton\Parameters\Config\DocumentOptions;
use BigBlueButton\Parameters\InsertDocumentParameters;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// define your variables
$meetingId = 123456;
$url = 'https://your.file.url/example.pdf';
$file = __DIR__ . '/foldername/example.png';
// define the document options
$documentOptions = new DocumentOptions();
$documentOptions->addOption(DocumentOption::CURRENT, true);
$documentOptions->addOption(DocumentOption::REMOVABLE, false);
$documentOptions->addOption(DocumentOption::DOWNLOADABLE, true);
// announce 3 documents that shall to be added into the meeting
$insertDocumentParameters = new InsertDocumentParameters($meetingId);
$insertDocumentParameters
->addPresentation($url) // by a URL (with default document options)
->addPresentation($url, null, null, $documentOptions) // by a URL and defining the document options
->addPresentation($url, null, 'new_name.pdf') // by a URL and rename the file
->addPresentation('filename.pdf', file_get_contents($file)); // by injecting a data stream and define the filename used on BBB-server
// launch the request to the BBB-Server and receive its response
$insertDocumentResponse = $bbb->insertDocument($insertDocumentParameters);
if (!$insertDocumentResponse->success()) {
throw new \Exception($insertDocumentResponse->getMessage());
}
// steps once document has been added
new way (documents)
The document-based API replaces the deprecated addPresentation() calls. A document is either a DocumentUrl (referenced by URL) or a DocumentFile (read from the local filesystem); options like current, removable and downloadable are set directly on the document:
use BigBlueButton\Core\DocumentFile;
use BigBlueButton\Core\DocumentUrl;
use BigBlueButton\Parameters\InsertDocumentParameters;
// ...
$insertDocumentParameters = new InsertDocumentParameters($meetingId);
// by URL, marked as the current presentation
$insertDocumentParameters->addDocument(
(new DocumentUrl('https://files.example.com/slides.pdf', 'slides.pdf'))->setCurrent(true)
);
// from the local filesystem, downloadable for the participants
$insertDocumentParameters->addDocument(
(new DocumentFile('/path/to/handout.pdf', 'handout.pdf'))->setDownloadable(true)
);
$insertDocumentResponse = $bbb->insertDocument($insertDocumentParameters);
The same document objects are used when pre-uploading presentations into a meeting on create — see the DocumentableTrait methods on CreateMeetingParameters.
Joining
Once a meeting is created successfully, it is ready to let the participants into the meeting. This will be done with the join command. It needs to define into which meeting ($meetingId) and by what name ($name) the participant shall join the meeting. Additionally the role of the participant needs to be declared: either as moderator (Role::MODERATOR) or as a regular viewer (Role::VIEWER).
Important
The standard way to join a meeting is to redirect the user’s browser to a join URL, so that the BBB-Server can set the session cookie and forward the user to the html5 client:
$joinUrl = $bbb->getJoinMeetingURL($joinMeetingParameters); header('Location: ' . $joinUrl);Calling
joinMeeting()server-side skips that cookie and typically requiresallowRequestsWithoutSession=trueon the meeting, which weakens the meeting’s security. Use it only for special cases where you explicitly need the join response (e.g. session tokens for API-driven clients).
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\JoinMeetingParameters;
use BigBlueButton\Enum\Role;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// define your variables
$meetingID = 123456;
$name = "Peter Parker";
$role1 = Role::MODERATOR; // choose MODERATOR for a coordinating person
$role2 = Role::VIEWER; // choose VIEWER for normal participants
// define the required parameters for the user to join the meeting
$joinMeetingParameters = new JoinMeetingParameters($meetingID, $name, $role1);
$joinMeetingParameters->setRedirect(true); // will ensure that the user is redirected to the BBB-Server
// launch the request to the BBB-Server
$joinMeetingResponse = $bbb->joinMeeting($joinMeetingParameters);
if (!$joinMeetingResponse->success()) {
throw new \Exception($joinMeetingResponse->getMessage());
}
$url = $joinMeetingResponse->getUrl();
// ...
In the example above, the user is redirected directly (setRedirect(true)) to the meeting on the BBB-Server. In case the user shall not be redirected (setRedirect(false)), the request will provide a URL in its response. This URL can be used to redirect the user later (e.g. by button or link).
Re-joining an existing user
BBB 3.0+ allows to create an additional session for an already joined user by providing the internal user id (existingUserID). All sessions of the user then appear as the same user in the user list. Optionally the original session can be invalidated (replaceSessionToken) and the new session can be named (sessionName) for easier identification. These are the same parameters used by the URLs returned from Get Join URL.
// ...
$joinMeetingParameters
->setExistingUserId('w_abc123def') // internal user id of the joined user
->setSessionName('Mobile Device Transfer') // optional: name the new session
->setReplaceSessionToken('st-orig-token') // optional: invalidate the original session
;
// ...
Ending
A meeting can be ended (destroyed) by calling the endMeeting-command.
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\EndMeetingParameters;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// define your variables
$meetingID = 123456;
// define the required parameters to end a meeting
$endMeetingParameters = new EndMeetingParameters($meetingID);
// launch the request to the BBB-Server
$endMeetingResponse = $bbb->endMeeting($endMeetingParameters);
if (!$endMeetingResponse->success()) {
throw new \Exception($endMeetingResponse->getMessage());
}
// ...
Monitoring
Is Meeting Running
This command will check if a meeting is currently running.
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\IsMeetingRunningParameters;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// define your variables
$meetingID = 123456;
// define the required parameters for the user to join the meeting
$isMeetingRunningParameters = new IsMeetingRunningParameters($meetingID);
// launch the request to the BBB-Server
$isMeetingRunningResponse = $bbb->isMeetingRunning($isMeetingRunningParameters);
if (!$isMeetingRunningResponse->success()) {
throw new \Exception($isMeetingRunningResponse->getMessage());
}
if (!$isMeetingRunningResponse->isRunning()) {
// meeting is not running
} else {
// meeting is running
}
Warning
The BBB-server is understanding as a “running” meeting, where at least one participant has joint. This function deliver
falseif the meeting has been created only and no one has joint yet.
Is Meeting Existing
This command will check if a meeting is existing and just check if a meeting is available (successfully created) on the BBB-Server. In contrast with isRunning this command will not check if participants have been joined.
use BigBlueButton\BigBlueButton;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// define your variables
$meetingID = 123456;
// launch the request to the BBB-Server
$isMeetingExisting = $bbb->isMeetingExisting($meetingID);
if (!$isMeetingExisting) {
// meeting is not existing
} else {
// meeting is existing
}
Note
This function is a shortcut and runs
getMeetingInfo-command under the hood. This is why its usage is a bit different compared to other interactions with the BBB-Server (e.g. no Parameter-Object needs to be used)
Get Meeting Info
This command will provide a lot of details of a meeting.
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\GetMeetingInfoParameters;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// define your variables
$meetingID = 123456;
// define the required parameters
$getMeetingInfoParameters = new GetMeetingInfoParameters($meetingID);
// launch the request to the BBB-Server
$getMeetingInfoResponse = $bbb->getMeetingInfo($getMeetingInfoParameters);
if (!$getMeetingInfoResponse->success()) {
throw new \Exception($getMeetingInfoResponse->getMessage());
}
// get the meeting object
$meeting = $getMeetingInfoResponse->getMeeting();
// example of provided information
$meetingName = $meeting->getMeetingName();
Get Meetings
This command will provide a list of the existing meetings in the BBB-Server.
use BigBlueButton\BigBlueButton;
// create an instance of the BBB-Client (see details in the setup description)
$bbb = new BigBlueButton();
// launch the request to the BBB-Server
$getMeetingsResponse = $bbb->getMeetings();
if (!$getMeetingsResponse->success()) {
throw new \Exception($getMeetingsResponse->getMessage());
}
// loop over all meetings
foreach ($getMeetingsResponse->getMeetings() as $meeting) {
// treat meeting
}
Get Join URL
The getJoinUrl endpoint generates a new /join URL that can be used to create a new session for an existing user. By associating the new session token with the same user ID, all sessions will appear as the same user in the user list, ensuring accurate user counts.
Important
The
sessionTokenmust belong to a connected HTML5 client session. A session token obtained from an API join (redirect=false) without a running client is rejected (e.g."Meeting not found"on BBB 3.x).
This feature is particularly useful for:
- Hybrid environments where multiple screens in the same room each require a distinct session with different layouts
- Session transfers enabling seamless user session transfers to another device (e.g., mobile device scanning a QR code displayed on a computer)
- Multi-device scenarios where a user wants to join the same meeting from multiple devices simultaneously
API Endpoint
GET http://yourserver.com/bigbluebutton/api/getJoinUrl?[parameters]
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sessionToken | String | Yes | Session token to identify the user who is requesting a new join URL |
replaceSession | Boolean | No | When set to true, using the newly generated join URL will immediately invalidate the original session. Default: false |
sessionName | String | No | Assign a descriptive name to the newly created session. Allows quick understanding of the session’s origin or purpose when reviewing user’s session history |
enforceLayout | String | No | Specify a layout enforcement setting for the new session. Overrides the enforceLayout parameter inherited from the original user’s session. If not specified, the new session inherits the layout behavior of the original session |
userdata-* | String | No | Include additional user data parameters prefixed with userdata-. These parameters merge with the original user’s existing userdata settings. New session parameters take precedence over duplicates |
Usage Examples
Basic Usage
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\GetJoinUrlParameters;
$bbb = new BigBlueButton();
// Get a new join URL for an existing session
$getJoinUrlParams = new GetJoinUrlParameters('existing-session-token-123');
$response = $bbb->getJoinUrl($getJoinUrlParams);
if ($response->success()) {
$newJoinUrl = $response->getUrl();
echo "New join URL: " . $newJoinUrl;
} else {
echo "Error: " . $response->getMessage();
}
Advanced Usage with Session Replacement
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\GetJoinUrlParameters;
use BigBlueButton\Enum\MeetingLayout;
$bbb = new BigBlueButton();
// Create parameters with session replacement
$getJoinUrlParams = new GetJoinUrlParameters('mobile-session-token-456');
// Replace the original session when the new one is used
$getJoinUrlParams->setReplaceSession(true);
// Set a descriptive session name
$getJoinUrlParams->setSessionName('Mobile Device Transfer');
// Enforce a specific layout for the new session
$getJoinUrlParams->setEnforceLayout(MeetingLayout::VIDEO_FOCUS);
// Add custom userdata parameters
$getJoinUrlParams->addUserData('device-type', 'mobile');
$getJoinUrlParams->addUserData('transfer-source', 'desktop');
$getJoinUrlParams->addUserData('screen-size', 'small');
$response = $bbb->getJoinUrl($getJoinUrlParams);
if ($response->success()) {
echo "New join URL: " . $response->getUrl();
echo "Session Token: " . $response->getSessionToken();
echo "Session Name: " . $response->getSessionName();
echo "Replace Session: " . ($response->isReplaceSession() ? 'Yes' : 'No');
} else {
echo "Error: " . $response->getMessage();
}
QR Code Generation for Session Transfer
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\GetJoinUrlParameters;
$bbb = new BigBlueButton();
// Generate a join URL for mobile device transfer
$getJoinUrlParams = new GetJoinUrlParameters('desktop-session-token-789');
$getJoinUrlParams->setSessionName('Mobile Transfer from Desktop');
$getJoinUrlParams->addUserData('transfer-initiated', date('Y-m-d H:i:s'));
$getJoinUrlParams->addUserData('device-platform', 'mobile');
$response = $bbb->getJoinUrl($getJoinUrlParams);
if ($response->success()) {
$joinUrl = $response->getUrl();
// Generate QR code (you'll need a QR code library)
// $qrCode = generateQRCode($joinUrl);
echo "Scan this QR code to transfer your session to mobile device:";
echo "Join URL: " . $joinUrl;
} else {
echo "Failed to generate transfer URL: " . $response->getMessage();
}
Multi-Screen Setup
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\GetJoinUrlParameters;
use BigBlueButton\Enum\MeetingLayout;
$bbb = new BigBlueButton();
// Original session token
$originalToken = 'main-screen-session-001';
// Create second screen with presentation focus
$secondScreenParams = new GetJoinUrlParameters($originalToken);
$secondScreenParams->setSessionName('Second Screen - Presentation View');
$secondScreenParams->setEnforceLayout(MeetingLayout::PRESENTATION_FOCUS);
$secondScreenParams->addUserData('screen-role', 'presentation');
$secondScreenResponse = $bbb->getJoinUrl($secondScreenParams);
// Create third screen with participant focus
$thirdScreenParams = new GetJoinUrlParameters($originalToken);
$thirdScreenParams->setSessionName('Third Screen - Participants View');
$thirdScreenParams->setEnforceLayout(MeetingLayout::PARTICIPANTS_CHAT_ONLY);
$thirdScreenParams->addUserData('screen-role', 'participants');
$thirdScreenResponse = $bbb->getJoinUrl($thirdScreenParams);
if ($secondScreenResponse->success() && $thirdScreenResponse->success()) {
echo "Second Screen URL: " . $secondScreenResponse->getUrl();
echo "Third Screen URL: " . $thirdScreenResponse->getUrl();
}
Response Fields
The response is JSON and provides the following fields:
| Field | Type | Description |
|---|---|---|
url | String | The generated join URL (including its checksum). Successful responses only |
sessionToken | String | The session token that was rejected. Failed responses only |
Example of a successful response:
{
"response": {
"returncode": "SUCCESS",
"message": "Join URL provided successfully.",
"url": "https://yourserver.com/bigbluebutton/api/join?&redirect=true&existingUserID=w_t18rn7uc1wjm&role=MODERATOR&checksum=..."
}
}
Response Handling
$response = $bbb->getJoinUrl($getJoinUrlParams);
if ($response->success()) {
echo "Join URL: " . $response->getUrl();
} else {
echo "Error: " . $response->getMessage();
echo "Rejected session token: " . $response->getSessionToken();
}
Layout Options
The enforceLayout parameter accepts the same values as the meeting creation:
use BigBlueButton\Enum\MeetingLayout;
// Available layout options
MeetingLayout::UNIFIED_LAYOUT // BBB 3.0+ (default in 4.0)
MeetingLayout::CAMERAS_ONLY
MeetingLayout::PARTICIPANTS_AND_CHAT_ONLY // BBB 3.0+ (replaces PARTICIPANTS_CHAT_ONLY)
MeetingLayout::PRESENTATION_ONLY
MeetingLayout::PLUGINS_ONLY // BBB 3.0+
MeetingLayout::MEDIA_ONLY
Warning
BBB 4.0 no longer accepts
CUSTOM_LAYOUT,SMART_LAYOUT,PRESENTATION_FOCUSandVIDEO_FOCUS. The cases remain available in this library for BBB 2.x/3.x servers, but using them against a 4.0 server has no effect.
Userdata Parameters
Userdata parameters allow you to pass additional information about the session:
Common Userdata Parameters
| Parameter | Example Value | Description |
|---|---|---|
userdata-device-type | mobile, desktop, tablet | Type of device |
userdata-screen-role | main, presentation, participants | Screen purpose in multi-screen setup |
userdata-transfer-source | desktop, mobile, web | Source device for session transfer |
userdata-platform | iOS, Android, Windows, macOS | Operating system |
userdata-app-version | 2.1.0 | Application version |
Adding Userdata Parameters
// Single parameter
$getJoinUrlParams->addUserData('device-type', 'mobile');
// Multiple parameters
$getJoinUrlParams->addUserData('device-type', 'mobile');
$getJoinUrlParams->addUserData('platform', 'iOS');
$getJoinUrlParams->addUserData('app-version', '2.1.0');
// Complex data (JSON encoded)
$deviceInfo = [
'type' => 'mobile',
'os' => 'iOS',
'version' => '15.0',
'screen' => [
'width' => 375,
'height' => 667
]
];
$getJoinUrlParams->addUserData('device-info', json_encode($deviceInfo));
Security Considerations
Session Token Security
- Session tokens are sensitive and should be handled securely
- Only share session tokens with authorized users
- Implement proper validation before generating new join URLs
Userdata Validation
- Validate userdata parameters on both client and server side
- Sanitize user input to prevent injection attacks
- Consider implementing a blocklist for sensitive userdata parameters
Session Replacement
- Use
replaceSession=truecarefully as it immediately invalidates the original session - Inform users when their original session will be replaced
- Implement proper error handling for session replacement scenarios
Error Handling
Common error scenarios and their handling:
$response = $bbb->getJoinUrl($getJoinUrlParams);
if (!$response->success()) {
$message = $response->getMessage();
$statusCode = $response->getStatusCode();
switch ($statusCode) {
case '404':
// Session token not found
echo "Invalid or expired session token";
break;
case '403':
// Access denied
echo "Permission denied for session transfer";
break;
case '400':
// Bad request
echo "Invalid parameters provided";
break;
default:
echo "Unknown error: " . $message;
}
}
Best Practices
- Session Naming: Use descriptive session names to help users identify different sessions
- Layout Selection: Choose appropriate layouts for different device types and use cases
- Userdata Organization: Use consistent naming conventions for userdata parameters
- Error Handling: Implement comprehensive error handling for all scenarios
- Security: Validate and sanitize all input parameters
- User Experience: Provide clear feedback about session transfers and multi-screen setups
Use Case Examples
Education Scenario
A professor wants to display the main presentation on a projector while managing participants on a tablet:
// Main screen (projector) - presentation focus
$projectorParams = new GetJoinUrlParameters($professorSessionToken);
$projectorParams->setSessionName('Projector - Presentation View');
$projectorParams->setEnforceLayout(MeetingLayout::PRESENTATION_FOCUS);
// Tablet screen - participants management
$tabletParams = new GetJoinUrlParameters($professorSessionToken);
$tabletParams->setSessionName('Tablet - Participants Management');
$tabletParams->setEnforceLayout(MeetingLayout::PARTICIPANTS_CHAT_ONLY);
Corporate Scenario
An executive wants to transfer a meeting from desktop to mobile for commuting:
$transferParams = new GetJoinUrlParameters($desktopSessionToken);
$transferParams->setReplaceSession(true);
$transferParams->setSessionName('Mobile Transfer - ' . date('H:i'));
$transferParams->addUserData('transfer-reason', 'commute');
$transferParams->addUserData('connection-type', 'mobile');
Support Scenario
A support agent needs to join a customer meeting with elevated permissions:
$supportParams = new GetJoinUrlParameters($customerSessionToken);
$supportParams->setSessionName('Support Agent Session');
$supportParams->addUserData('role', 'support');
$supportParams->addUserData('support-id', $supportAgentId);
$supportParams->addUserData('elevated-permissions', 'true');
This API provides powerful flexibility for managing user sessions across different devices and scenarios while maintaining user identity and meeting continuity.
Learning Dashboard
The learningDashboard endpoint returns the Learning Analytics Dashboard data for the session’s meeting.
Access is restricted:
- the session token must belong to a user with the
MODERATORrole - the meeting must be running
- the
learningDashboardfeature must not be listed in the meeting’sdisabledFeatures
The data field of the response contains the dashboard’s JSON document serialized as a string.
Note
This endpoint is part of the client-facing API. A session token obtained through an API join (
redirect=false) without a connected HTML5 client will be rejected withInvalid session token.
API Endpoint
GET http://yourserver.com/bigbluebutton/api/learningDashboard?[parameters]
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sessionToken | String | Yes | Session token identifying the user requesting the dashboard data. Issued by /join and only known to the joined client |
Usage Example
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\LearningDashboardParameters;
$bbb = new BigBlueButton();
$learningDashboardParams = new LearningDashboardParameters('xyn1fbqlrhug1j6z');
$response = $bbb->learningDashboard($learningDashboardParams);
if ($response->success()) {
// the dashboard data is a JSON-encoded string
$dashboardData = json_decode($response->getData(), true);
foreach ($dashboardData['users'] as $userId => $user) {
echo sprintf('%s (%s)', $user['name'], $user['role']) . PHP_EOL;
}
} else {
echo 'Error: ' . $response->getMessage();
}
Response Fields
| Field | Type | Description |
|---|---|---|
data | String | The learning dashboard data as a JSON-encoded string (successful responses only) |
sessionToken | String | The session token that was used for the request |
Get Sessions
The getSessions endpoint returns all user sessions that currently exist on the BBB-Server. A session is created each time a user joins a meeting — even if the same user joins multiple times. Therefore the same meeting can appear multiple times in the result, once per session, distinguished by userName.
This is different from getMeetings, which returns one entry per meeting.
Typical use cases:
- Monitoring active sessions: see which users currently hold a session token on the server
- Session auditing: track multi-device or repeated joins of the same user
- Debugging join flows: verify that generated join URLs actually created sessions
API Endpoint
GET http://yourserver.com/bigbluebutton/api/getSessions?checksum=<checksum>
The endpoint takes no parameters besides the checksum.
Usage Example
use BigBlueButton\BigBlueButton;
$bbb = new BigBlueButton();
$response = $bbb->getSessions();
if ($response->success()) {
foreach ($response->getSessions() as $session) {
echo sprintf(
'Meeting: %s (%s) - User: %s',
$session->getMeetingName(),
$session->getMeetingId(),
$session->getUserName()
) . PHP_EOL;
}
} else {
echo 'Error: ' . $response->getMessage();
}
Response Fields
Each session provides the following fields:
| Field | Type | Description |
|---|---|---|
meetingId | String | The internal meeting id of the meeting the session belongs to |
meetingName | String | The name of the meeting |
userName | String | The full name of the user that holds the session |
If no sessions exist, the response contains the message key noSessions.
Remarks
- Sessions created through an API join without a connected HTML5 client are removed by the server after a short period (about 2 minutes).
- The
meetingIDin the response is the internal meeting id (as returned byCreateMeetingResponse::getInternalMeetingId()), not the external id used when creating the meeting. - Available on BBB 2.x, 3.x and 4.x servers.
Client Settings Override
The Client Settings Override feature allows you to customize the HTML5 client behavior for specific meetings by overriding settings from the server’s settings.yml file. This feature is available in BigBlueButton 3.0.0 and later.
Overview
Client settings override provides a way to:
- Customize the application name and appearance
- Configure WebRTC and media settings
- Modify user interface behavior
- Set custom themes and branding
- Override locale and language settings
Important
For security reasons, this feature is disabled by default. You must explicitly enable it by setting
allowOverrideClientSettingsOnCreateCall=true.
Variant via URL (BBB 3.0.25+)
Instead of passing the settings inline, you can reference a hosted JSON file. The URL variant takes precedence over the inline clientSettingsOverride payload and does not require allowOverrideClientSettingsOnCreateCall:
$createMeetingParameters->setClientSettingsOverrideJsonUrl('https://your-server.example.com/settings-override.json');
The BBB-Server fetches the JSON file when the meeting is created.
Core Classes
ClientSettingsOverride
The main class for handling client settings override.
use BigBlueButton\Core\ClientSettingsOverride;
Constructor
public function __construct(array $settings = [])
Creates a new ClientSettingsOverride instance with optional initial settings.
Parameters:
$settings(array) - Initial settings array
Methods
setSettings()
public function setSettings(array $settings): self
Sets all settings at once.
Parameters:
$settings(array) - The settings array
getSettings()
public function getSettings(): array
Returns all settings as an array.
setSetting()
public function setSetting(string $key, mixed $value): self
Sets a specific setting using dot notation.
Parameters:
$key(string) - The setting key (e.g., ‘public.app.appName’)$value(mixed) - The setting value
getSetting()
public function getSetting(string $key, mixed $default = null): mixed
Gets a specific setting using dot notation.
Parameters:
$key(string) - The setting key$default(mixed) - Default value if key doesn’t exist
removeSetting()
public function removeSetting(string $key): self
Removes a specific setting using dot notation.
Parameters:
$key(string) - The setting key to remove
toXML()
public function toXML(): string
Converts the settings to XML format for the BigBlueButton API request.
fromJson()
public static function fromJson(string $jsonString): self
Creates a ClientSettingsOverride instance from a JSON string.
Parameters:
$jsonString(string) - Valid JSON string containing settings
Throws:
\InvalidArgumentExceptionif JSON is invalid
Integration with CreateMeetingParameters
The ClientSettingsOverride is used with the CreateMeetingParameters class:
setAllowOverrideClientSettingsOnCreateCall()
public function setAllowOverrideClientSettingsOnCreateCall(bool $allow): self
Enables or disables the client settings override feature.
setClientSettingsOverride()
public function setClientSettingsOverride(?ClientSettingsOverride $override): self
Sets the client settings override for the meeting.
getClientSettingsOverride()
public function getClientSettingsOverride(): ?ClientSettingsOverride
Returns the current client settings override.
Usage Examples
Basic Example
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\CreateMeetingParameters;
use BigBlueButton\Core\ClientSettingsOverride;
$bbb = new BigBlueButton();
$createParams = new CreateMeetingParameters('meeting123', 'Test Meeting');
// Enable client settings override
$createParams->setAllowOverrideClientSettingsOnCreateCall(true);
// Create settings override
$settings = new ClientSettingsOverride([
'public' => [
'app' => [
'appName' => 'Custom Meeting Name',
'helpLink' => 'https://help.example.com'
]
]
]);
// Apply settings
$createParams->setClientSettingsOverride($settings);
// Create meeting
$response = $bbb->createMeeting($createParams);
Advanced Configuration
$settings = new ClientSettingsOverride();
// Application settings
$settings->setSetting('public.app.appName', 'Enterprise Meeting');
$settings->setSetting('public.app.helpLink', 'https://support.company.com');
$settings->setSetting('public.app.autoJoin', false);
$settings->setSetting('public.app.askForConfirmationOnLeave', true);
$settings->setSetting('public.app.userSettingsStorage', 'localStorage');
// WebRTC settings
$settings->setSetting('public.kurento.wsUrl', 'wss://webrtc.company.com/sfu');
$settings->setSetting('public.kurento.turnUrl', 'turn:turn.company.com:443');
// Media settings
$settings->setSetting('public.media.sipjsHackViaWs', false);
$settings->setSetting('public.media.audio.codec', 'opus');
$settings->setSetting('public.media.video.codec', 'vp8');
// Theme settings
$settings->setSetting('public.theme.branding.target', '.branding-element');
$settings->setSetting('public.theme.custom_css_url', 'https://assets.company.com/theme.css');
// Locale settings
$settings->setSetting('public.defaultSettings.application.overrideLocale', 'fr');
$createParams->setClientSettingsOverride($settings);
JSON Configuration
$jsonConfig = '{
"public": {
"app": {
"appName": "JSON Configured Meeting",
"helpLink": "https://docs.example.com",
"autoJoin": true
},
"kurento": {
"wsUrl": "wss://webrtc.example.com/sfu"
},
"theme": {
"branding": {
"target": ".custom-branding"
}
}
}
}';
$settings = ClientSettingsOverride::fromJson($jsonConfig);
$createParams->setClientSettingsOverride($settings);
Available Settings
Application Settings (public.app)
| Setting | Type | Description |
|---|---|---|
appName | string | Custom application name |
helpLink | string | Custom help documentation URL |
autoJoin | boolean | Auto-join meeting when page loads |
askForConfirmationOnLeave | boolean | Show confirmation dialog when leaving |
userSettingsStorage | string | Storage type: ‘localStorage’, ‘sessionStorage’, ‘cookie’ |
displayBrandingArea | boolean | Show/hide branding area |
WebRTC Settings (public.kurento)
| Setting | Type | Description |
|---|---|---|
wsUrl | string | Custom WebRTC SFU WebSocket URL |
turnUrl | string | Custom TURN server URL |
turnUsername | string | TURN server username |
turnCredential | string | TURN server credential |
Media Settings (public.media)
| Setting | Type | Description |
|---|---|---|
sipjsHackViaWs | boolean | Enable SIP.js WebSocket hack |
audio.codec | string | Preferred audio codec: ‘opus’, ‘pcmu’, ‘pcma’ |
video.codec | string | Preferred video codec: ‘vp8’, ‘vp9’, ‘h264’ |
video.resolution | string | Preferred video resolution |
Theme Settings (public.theme)
| Setting | Type | Description |
|---|---|---|
branding.target | string | CSS selector for branding elements |
custom_css_url | string | URL to custom CSS file |
logo.url | string | URL to custom logo image |
favicon.url | string | URL to custom favicon |
Default Settings (public.defaultSettings)
| Setting | Type | Description |
|---|---|---|
application.overrideLocale | string | Override locale (e.g., ‘en’, ‘fr’, ‘es’) |
application.chat.enabled | boolean | Enable/disable chat |
application.poll.enabled | boolean | Enable/disable polls |
Security Considerations
-
Enable Only When Needed: Only enable
allowOverrideClientSettingsOnCreateCallwhen you actually need to override settings. -
Validate Input: Always validate user-provided settings before applying them.
-
Sensitive Data: Avoid exposing sensitive configuration through client settings override.
-
Network Security: Ensure custom WebSocket URLs are from trusted sources.
Error Handling
try {
$settings = ClientSettingsOverride::fromJson($invalidJson);
} catch (\InvalidArgumentException $e) {
// Handle invalid JSON
error_log('Invalid JSON: ' . $e->getMessage());
$settings = new ClientSettingsOverride(); // Fallback to empty settings
}
Best Practices
-
Use Specific Settings: Only override the settings you actually need to change.
-
Document Changes: Keep track of which settings you’re overriding for debugging purposes.
-
Test Thoroughly: Test client settings override in a development environment before production use.
-
Fallback Values: Provide sensible defaults when getting settings that might not exist.
-
Performance: Avoid overly complex nested structures that might impact client performance.
Troubleshooting
Settings Not Applied
- Verify
allowOverrideClientSettingsOnCreateCallis set totrue - Check that the settings structure matches the expected format
- Ensure the BigBlueButton server version supports client settings override (3.0.0+)
Invalid JSON
- Use
try-catchblocks when callingfromJson() - Validate JSON structure before processing
WebSocket Connection Issues
- Verify custom WebSocket URLs are accessible
- Check firewall and network configuration
- Ensure TURN server credentials are correct
Migration Notes
If you’re upgrading from an earlier version of BigBlueButton:
- Ensure your server is running BigBlueButton 3.0.0 or later
- Update your PHP API library to the latest version
- Review existing meeting creation code to add client settings override if needed
- Test thoroughly in a development environment
For more information about the available settings, refer to the official BigBlueButton documentation and your server’s settings.yml file.
Feedback
Warning
Experimental. The
/api/feedbackendpoint is reserved in the BBB server source but is not routed yet on current BBB releases — it returns HTTP 404 on BBB 3.x and 4.0-beta servers. This client implementation is provided in anticipation of the endpoint shipping in a future BBB release. Do not rely on it in production, and expectBadResponseExceptionuntil your server actually provides the endpoint.
The feedback endpoint allows users to submit feedback about their meeting experience. This endpoint replaces the old /html5client/feedback endpoint with /api/feedback, providing a more standardized API approach for collecting user feedback.
This feature is particularly useful for:
- User Experience Improvement - Collect structured feedback about meeting quality
- Quality Assurance - Monitor user satisfaction and identify issues
- Analytics - Gather data for improving the BigBlueButton platform
- Support - Allow users to report problems or suggestions
API Endpoint
POST http://yourserver.com/bigbluebutton/api/feedback?[parameters]
Response Format: JSON (application/json)
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sessionToken | String | Yes | Session token to identify the user who is submitting feedback |
rating | Integer | No | Numeric rating for the meeting experience (typically 1-5) |
comment | String | No | Textual feedback or comments about the meeting experience |
meetingID | String | No | Meeting ID for which the feedback is being submitted |
userID | String | No | User ID of the person submitting the feedback |
Usage Examples
Basic Feedback Submission
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\FeedbackParameters;
$bbb = new BigBlueButton();
// Submit basic feedback with just a session token
$feedbackParams = new FeedbackParameters('user-session-token-123');
$response = $bbb->feedback($feedbackParams);
if ($response->success()) {
echo "Feedback submitted successfully!";
echo "Feedback ID: " . $response->getFeedbackID();
echo "Status: " . $response->getStatus();
} else {
echo "Error: " . $response->getMessage();
}
Complete Feedback Submission
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\FeedbackParameters;
$bbb = new BigBlueButton();
// Create comprehensive feedback
$feedbackParams = new FeedbackParameters('session-token-456');
$feedbackParams->setRating(4);
$feedbackParams->setComment('Great meeting overall! Audio quality was excellent, but video could be smoother.');
$feedbackParams->setMeetingID('weekly-team-meeting-001');
$feedbackParams->setUserID('participant-john-doe');
$response = $bbb->feedback($feedbackParams);
if ($response->success()) {
echo "Feedback submitted successfully!";
echo "Feedback ID: " . $response->getFeedbackID();
echo "Submitted at: " . $response->getSubmittedAt();
echo "Processed: " . ($response->getProcessed() ? 'Yes' : 'No');
} else {
echo "Error: " . $response->getMessage();
echo "Status Code: " . $response->getStatusCode();
}
Mobile App Feedback
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\FeedbackParameters;
$bbb = new BigBlueButton();
// Mobile app feedback with additional context
$feedbackParams = new FeedbackParameters('mobile-session-token-789');
$feedbackParams->setRating(5);
$feedbackParams->setComment('Excellent mobile experience! The app worked perfectly on my tablet.');
$feedbackParams->setMeetingID('mobile-presentation-123');
$feedbackParams->setUserID('mobile-user-456');
$response = $bbb->feedback($feedbackParams);
if ($response->success()) {
// Store feedback ID for follow-up
$feedbackId = $response->getFeedbackID();
// Could also store additional metadata locally
$feedbackData = [
'feedback_id' => $feedbackId,
'session_token' => $response->getSessionToken(),
'rating' => $response->getRating(),
'comment' => $response->getComment(),
'submitted_at' => $response->getSubmittedAt()
];
echo "Mobile feedback submitted with ID: " . $feedbackId;
}
Post-Meeting Feedback Collection
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\FeedbackParameters;
function collectMeetingFeedback($sessionToken, $meetingId, $userId) {
$bbb = new BigBlueButton();
// Create feedback parameters
$feedbackParams = new FeedbackParameters($sessionToken);
$feedbackParams->setMeetingID($meetingId);
$feedbackParams->setUserID($userId);
// In a real application, you would collect rating and comment from user input
$rating = $_POST['rating'] ?? null; // 1-5 star rating
$comment = $_POST['comment'] ?? ''; // User comments
if ($rating !== null) {
$feedbackParams->setRating((int)$rating);
}
if (!empty($comment)) {
$feedbackParams->setComment($comment);
}
// Submit feedback
$response = $bbb->feedback($feedbackParams);
return $response;
}
// Usage after meeting ends
$sessionToken = 'user-session-from-meeting';
$meetingId = 'meeting-that-just-ended';
$userId = 'user-who-attended';
$feedbackResponse = collectMeetingFeedback($sessionToken, $meetingId, $userId);
if ($feedbackResponse->success()) {
echo "Thank you for your feedback!";
} else {
echo "Unable to submit feedback: " . $feedbackResponse->getMessage();
}
Batch Feedback Collection
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\FeedbackParameters;
$bbb = new BigBlueButton();
// Collect feedback from multiple users
$sessionTokens = [
'user1-session-token',
'user2-session-token',
'user3-session-token'
];
$feedbackResults = [];
foreach ($sessionTokens as $sessionToken) {
$feedbackParams = new FeedbackParameters($sessionToken);
$feedbackParams->setRating(rand(3, 5)); // Simulate different ratings
$feedbackParams->setComment('Automated feedback collection');
$feedbackParams->setMeetingID('batch-feedback-meeting');
$response = $bbb->feedback($feedbackParams);
$feedbackResults[] = [
'session_token' => $sessionToken,
'success' => $response->success(),
'feedback_id' => $response->success() ? $response->getFeedbackID() : null,
'message' => $response->getMessage()
];
}
// Process results
$successful = array_filter($feedbackResults, fn($r) => $r['success']);
echo "Successfully submitted " . count($successful) . " out of " . count($feedbackResults) . " feedback entries.";
Response Format
The feedback API returns responses in JSON format with application/json content type.
Successful Response Example
{
"status": "ok",
"feedback_id": "feedback-123456",
"session_token": "session-token-789",
"meeting_id": "meeting456",
"user_id": "user123",
"rating": 4,
"comment": "Great meeting experience!",
"submitted_at": "2023-01-15T14:30:00Z",
"processed": true,
"feedback_type": "meeting_feedback",
"additional_data": {
"device-type": "mobile",
"platform": "iOS"
}
}
Minimal Response Example
{
"status": "ok",
"feedback_id": "feedback-minimal-789",
"session_token": "minimal-session-token"
}
Error Response Example
{
"status": "error",
"message": "Invalid session token",
"statuscode": "404"
}
Response Fields
The FeedbackResponse provides the following fields:
Required Fields
| Field | Type | Description |
|---|---|---|
feedback_id | String | Unique identifier for the feedback submission |
session_token | String | The session token used for the feedback submission |
Optional Fields
| Field | Type | Description |
|---|---|---|
meeting_id | String | Meeting ID if provided in the request |
user_id | String | User ID if provided in the request |
rating | Integer | Rating value if provided in the request |
comment | String | Comment text if provided in the request |
submitted_at | String | Timestamp when the feedback was submitted |
processed | Boolean | Whether the feedback has been processed |
feedback_type | String | Type/category of feedback |
additional_data | Array | Additional metadata about the feedback |
Response Handling
$response = $bbb->feedback($feedbackParams);
if ($response->success()) {
// Basic information
echo "Feedback ID: " . $response->getFeedbackID();
echo "Session Token: " . $response->getSessionToken();
// Optional information
if ($response->getMeetingID()) {
echo "Meeting ID: " . $response->getMeetingID();
}
if ($response->getUserID()) {
echo "User ID: " . $response->getUserID();
}
if ($response->getRating()) {
echo "Rating: " . $response->getRating();
}
if ($response->getComment()) {
echo "Comment: " . $response->getComment();
}
if ($response->getSubmittedAt()) {
echo "Submitted At: " . $response->getSubmittedAt();
}
echo "Processed: " . ($response->getProcessed() ? 'Yes' : 'No');
// Additional data handling
$additionalData = $response->getAdditionalData();
foreach ($additionalData as $key => $value) {
echo "Additional Data - {$key}: {$value}";
}
} else {
echo "Error: " . $response->getMessage();
echo "Status Code: " . $response->getStatusCode();
}
Rating Guidelines
When implementing rating collection, consider these standard practices:
5-Star Rating Scale
function getRatingLabel($rating) {
$labels = [
1 => 'Very Poor',
2 => 'Poor',
3 => 'Average',
4 => 'Good',
5 => 'Excellent'
];
return $labels[$rating] ?? 'Not Rated';
}
// Usage
$rating = 4;
echo "User rated the meeting: " . getRatingLabel($rating);
Rating Validation
function validateRating($rating) {
// Rating should be between 1 and 5
if ($rating !== null && ($rating < 1 || $rating > 5)) {
throw new InvalidArgumentException('Rating must be between 1 and 5');
}
return $rating;
}
// Usage
try {
$rating = validateRating($_POST['rating']);
$feedbackParams->setRating($rating);
} catch (InvalidArgumentException $e) {
echo "Invalid rating: " . $e->getMessage();
}
Comment Guidelines
Comment Length Validation
function validateComment($comment) {
$maxLength = 1000; // Maximum comment length
if (strlen($comment) > $maxLength) {
throw new InvalidArgumentException("Comment must be less than {$maxLength} characters");
}
// Remove excessive whitespace
$comment = preg_replace('/\s+/', ' ', trim($comment));
return $comment;
}
// Usage
try {
$comment = validateComment($_POST['comment'] ?? '');
if (!empty($comment)) {
$feedbackParams->setComment($comment);
}
} catch (InvalidArgumentException $e) {
echo "Invalid comment: " . $e->getMessage();
}
Comment Sanitization
function sanitizeComment($comment) {
// Basic sanitization - remove HTML tags and special characters
$comment = strip_tags($comment);
$comment = htmlspecialchars($comment, ENT_QUOTES, 'UTF-8');
return $comment;
}
// Usage
$rawComment = $_POST['comment'] ?? '';
$cleanComment = sanitizeComment($rawComment);
$feedbackParams->setComment($cleanComment);
Error Handling
Common error scenarios and their handling:
$response = $bbb->feedback($feedbackParams);
if (!$response->success()) {
$message = $response->getMessage();
$statusCode = $response->getStatusCode();
switch ($statusCode) {
case '400':
// Bad request - invalid parameters
echo "Invalid feedback parameters provided";
break;
case '401':
// Unauthorized - invalid checksum or secret
echo "Authentication failed";
break;
case '404':
// Not found - invalid session token
echo "Invalid or expired session token";
break;
case '429':
// Too many requests - rate limiting
echo "Too many feedback submissions. Please try again later.";
break;
case '500':
// Internal server error
echo "Server error occurred. Please try again later.";
break;
default:
echo "Unknown error: " . $message;
}
}
Best Practices
1. Timing
- Collect feedback immediately after the meeting ends
- Consider follow-up emails for longer feedback collection periods
2. User Experience
- Keep the feedback form simple and quick to complete
- Use clear rating scales and descriptive labels
- Provide optional comment fields for detailed feedback
3. Data Privacy
- Inform users about how their feedback will be used
- Consider anonymizing feedback data for analysis
- Follow data protection regulations (GDPR, CCPA, etc.)
4. Rate Limiting
- Implement client-side rate limiting to prevent spam
- Consider server-side validation for multiple submissions
5. Error Handling
- Provide clear error messages to users
- Implement retry logic for temporary failures
- Log feedback submission failures for debugging
Integration Examples
Web Application Integration
// In your meeting end controller
public function submitFeedback(Request $request) {
try {
$sessionToken = $request->input('session_token');
$rating = $request->input('rating');
$comment = $request->input('comment');
$feedbackParams = new FeedbackParameters($sessionToken);
if ($rating) {
$feedbackParams->setRating((int)$rating);
}
if ($comment) {
$feedbackParams->setComment($comment);
}
$bbb = new BigBlueButton();
$response = $bbb->feedback($feedbackParams);
if ($response->success()) {
return response()->json([
'success' => true,
'feedback_id' => $response->getFeedbackID(),
'message' => 'Thank you for your feedback!'
]);
} else {
return response()->json([
'success' => false,
'message' => $response->getMessage()
], 400);
}
} catch (Exception $e) {
return response()->json([
'success' => false,
'message' => 'An error occurred while submitting feedback'
], 500);
}
}
JavaScript/AJAX Integration
// Frontend feedback submission
function submitFeedback(sessionToken, rating, comment) {
const data = {
sessionToken: sessionToken,
rating: rating,
comment: comment
};
fetch('/api/feedback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Thank you for your feedback!');
console.log('Feedback ID:', data.feedback_id);
} else {
alert('Error: ' + data.message);
}
})
.catch(error => {
console.error('Error submitting feedback:', error);
alert('An error occurred while submitting feedback');
});
}
// Usage
submitFeedback('user-session-token', 4, 'Great meeting experience!');
Analytics and Reporting
Feedback Summary
function generateFeedbackSummary($feedbackResponses) {
$totalFeedback = count($feedbackResponses);
$averageRating = 0;
$ratingDistribution = [1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0];
foreach ($feedbackResponses as $response) {
if ($response->getRating()) {
$rating = $response->getRating();
$averageRating += $rating;
$ratingDistribution[$rating]++;
}
}
if ($totalFeedback > 0) {
$averageRating = $averageRating / $totalFeedback;
}
return [
'total_feedback' => $totalFeedback,
'average_rating' => round($averageRating, 2),
'rating_distribution' => $ratingDistribution
];
}
This feedback API provides a standardized way to collect user feedback, enabling continuous improvement of the BigBlueButton platform through structured user input.
Recordings
Manage Recordings
getRecordings
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\GetRecordingsParameters;
$bbb = new BigBlueButton();
$recordingParams = new GetRecordingsParameters();
// optionally filter by meeting ids, recording ids, state or metadata
$recordingParams->setMeetingId('my-meeting-id');
$recordingParams->setState('published');
$response = $bbb->getRecordings($recordingParams);
if ($response->success()) {
foreach ($response->getRecords() as $recording) {
// each recording exposes id, meetingId, name, state, playback formats, ...
echo $recording->getRecordId() . ': ' . $recording->getName() . PHP_EOL;
}
}
Note that BigBlueButton needs several minutes to process a recording until it becomes available. You can follow the processing with bbb-record --watch on the server.
publishRecordings
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\PublishRecordingsParameters;
$bbb = new BigBlueButton();
$publishParams = new PublishRecordingsParameters($recordingId, true); // true = publish, false = unpublish
$response = $bbb->publishRecordings($publishParams);
updateRecordings
Updates the metadata of a recording:
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\UpdateRecordingsParameters;
$bbb = new BigBlueButton();
$updateParams = new UpdateRecordingsParameters($recordingId);
$updateParams->addMeta('presenter', 'John Doe');
$response = $bbb->updateRecordings($updateParams);
deleteRecordings
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\DeleteRecordingsParameters;
$bbb = new BigBlueButton();
$deleteRecordingsParams = new DeleteRecordingsParameters($recordingId); // get from "getRecordings"
$response = $bbb->deleteRecordings($deleteRecordingsParams);
if ($response->success()) {
// recording deleted
}
Manage Tracks
Caption/subtitle tracks can be attached to recordings as WebVTT files.
getRecordingTextTracks
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\GetRecordingTextTracksParameters;
$bbb = new BigBlueButton();
$response = $bbb->getRecordingTextTracks(new GetRecordingTextTracksParameters($recordingId));
if ($response->success()) {
foreach ($response->getTracks() as $track) {
echo $track->getHref() . ' (' . $track->getLang() . ')' . PHP_EOL;
}
}
putRecordingTextTrack
Uploads a caption track. The file is sent as multipart form-data; kind is either subtitles or captions:
use BigBlueButton\BigBlueButton;
use BigBlueButton\Parameters\PutRecordingTextTrackParameters;
$bbb = new BigBlueButton();
$trackParams = new PutRecordingTextTrackParameters($recordingId, 'subtitles', 'en', 'English');
$trackParams->setTrackFile('/path/to/captions.en.vtt');
$response = $bbb->putRecordingTextTrack($trackParams);
if ($response->isUploadTrackSuccess()) {
// track uploaded, it will appear in getRecordingTextTracks
}
The upload works with both transports — the built-in curl transport and any injected PSR-18 http client.
Hooks
Web hooks let your application receive HTTP POST callbacks whenever an event happens on the BigBlueButton server — a meeting is created or ends, a user joins or leaves, a recording is published, etc. The library manages the hook lifecycle through the hooksCreate, hooksList and hooksDestroy API calls.
Creating a hook
use BigBlueButton\BigBlueButton;
use BigBlueButton\Enum\WebHookEvent;
use BigBlueButton\Parameters\HooksCreateParameters;
$bbb = new BigBlueButton();
$hooksCreateParams = new HooksCreateParameters('https://app.example.com/hooks/callback');
// optionally: restrict the hook to one meeting
$hooksCreateParams->setMeetingId('my-meeting-id');
// optionally: only receive specific events (BBB 2.5+)
$hooksCreateParams->setEventId([WebHookEvent::USER_JOINED, WebHookEvent::USER_LEFT]);
// optionally: receive the raw event payloads instead of the processed ones
$hooksCreateParams->setGetRaw(true);
$response = $bbb->hooksCreate($hooksCreateParams);
if ($response->success()) {
$hookId = $response->getHookId();
}
The callback URL receives a POST request with the event data for every matching event. A hook registered without a meeting id is global and receives events of all meetings on the server.
Listing hooks
$response = $bbb->hooksList();
foreach ($response->getHooks() as $hook) {
echo $hook->getHookId() . ' -> ' . $hook->getCallbackUrl() . PHP_EOL;
}
Destroying a hook
use BigBlueButton\Parameters\HooksDestroyParameters;
$response = $bbb->hooksDestroy(new HooksDestroyParameters($hookId));
Events
The WebHookEvent enum lists all events the server can deliver, among them:
- Meeting lifecycle:
meeting-created,meeting-ended,meeting-recording-started/-stopped - Users:
user-joined,user-left,user-emoji-changed,user-raise-hand-changed, presenter assignments - Media: audio/camera/screenshare state changes,
chat-group-message-sent - Recordings processing:
rap-*events (archive, process, publish steps) andrap-published/rap-deleted - Polls and pads:
poll-started,poll-responded,pad-content
Hashing on older servers
Important
BBB servers below 3.0 accept only SHA-1 checksums for the webhooks endpoints. Since the library cannot detect the server version, hook URLs are always built with SHA-1 (safe on every server version); set the
HASH_ALGO_FOR_HOOKSenvironment variable to use a different algorithm for hooks, e.g.sha256on servers 3.0 and above.
Server Configuration
The library connects to the BigBlueButton integration API, which is protected by a shared secret. Two pieces of information from your server are needed:
BBB_SERVER_BASE_URL— the base URL of your server’s API, always ending with/bigbluebutton/(for examplehttps://bbb.example.com/bigbluebutton/).BBB_SECRET— the shared secret of the server.
Retrieving URL and secret
Run the following command on your BigBlueButton server:
bbb-conf --secret
It prints both values, for example:
URL: https://bbb.example.com/bigbluebutton/
Secret: 8cd8ef52e8e101574e400365b55e11a6
Providing them to the library
The recommended way are the environment variables BBB_SERVER_BASE_URL and BBB_SECRET (see Getting Started). How to set environment variables depends on your hosting: SetEnv for Apache2 (e.g. in /etc/apache2/envvars), fastcgi_param for nginx, .env for Laravel, etc. Keep the secret out of your source code repository.
Alternatively pass them programmatically:
use BigBlueButton\BigBlueButton;
$bbb = new BigBlueButton('https://bbb.example.com/bigbluebutton/', 'your-secret');
Checksums and hashing algorithms
Every API call is signed with a checksum of methodName + queryString + secret. The library uses SHA-256 by default, which every BigBlueButton 2.3+ server accepts. Older servers accepted SHA-1 only; if you operate one, the algorithm can be configured via the UrlBuilder.
Note
For BBB servers below 3.0 the webhooks endpoints only accept SHA-1 checksums. The library therefore always signs hook calls with SHA-1; see Hooks for the
HASH_ALGO_FOR_HOOKSoverride.
How to Contribute
Contributions are welcome — bug fixes, new API support, documentation and tests. This page explains the workflow; the library objectives describe the direction of the project.
Getting started
git clone https://github.com/bigbluebutton/bigbluebutton-api-php.git
cd bigbluebutton-api-php
composer install
vendor/bin/captainhook install # installs the git hooks, if not done automatically
To run the tests against your own BigBlueButton server, copy .env to .env.local and set BBB_SERVER_BASE_URL and BBB_SECRET (see Testing). .env.local is git-ignored — never commit server credentials.
Workflow
- Fork the repository and create a feature branch from
develop. - Make your changes in small, self-contained commits (one topic per commit).
- Ensure all quality gates pass (see below).
- Open a pull request against
developand describe what changed and why. For new API parameters or endpoints, reference the BigBlueButton API documentation or the server source that specifies them.
Quality gates
The following must pass before every commit (the pre-commit hooks run them for you):
composer code-fix # php-cs-fixer (style)
composer code-check # PHPStan (level 8)
composer code-test # PHPUnit (incl. live tests against the configured server)
Do not skip hooks with --no-verify. If a gate fails, fix the cause rather than bypassing it.
Commit message convention
We follow Chris Beams’ commit message style: a short imperative subject line (max 50 characters, no trailing period) and an optional body wrapped at 72 characters. Reference the GitHub issue number in the subject or body when applicable (e.g. (#223)).
What to contribute
- New endpoints / parameters: mirror the official BigBlueButton API, add parameter + response classes, fixtures captured from a real server, unit tests, integration tests and a documentation page.
- Bug fixes: include a failing test that proves the bug, then the fix.
- Documentation: the mdBook sources live in
docs/src/; keep examples runnable and verify them against a real server where possible.
For details see the Style Guide, Testing and Documentation pages.
Style Guide
PHP code style
The code style is enforced by PHP-CS-Fixer with the configuration in .php-cs-fixer.php (including a custom StrlenFixer rule in dev/Util/). Before every commit, run:
composer code-fix
The pre-commit hooks run the same tool in dry-run mode and reject commits with unformatted code.
Static analysis
PHPStan analyses src/ and tests/ at level 8:
composer code-check
All findings must be resolved; the pre-commit hook enforces it. When a genuine false positive cannot be avoided, use a narrow @phpstan-ignore-line or @phpstan-ignore-next-line with a short justification — never disable the rule globally.
Conventions worth knowing
- Typed everything: properties, parameters and return types are declared throughout;
nullis explicit (?string), never implicit. - API parameter mapping: getter methods carry the
#[ApiParameterMapper(attributeName: '...')]attribute that binds them to the query parameter name used by the BBB server. - Value sets as enums: closed parameter sets (layouts, roles, guest policies, presenter policies, disabled features, webhook events) are backed by string enums in
BigBlueButton\Enum. - Backwards compatibility: former members are kept as deprecated stubs rather than removed (see the library objectives).
- Comments state constraints the code cannot express — not what the next line does.
Commit messages
We follow Chris Beams’ commit message style: an imperative subject line of at most 50 characters without a trailing period, optionally a body wrapped at 72 characters. Reference the GitHub issue where applicable, e.g. (#223).
Testing
Test landscape
The suite (composer code-test) combines three kinds of tests:
- Offline unit tests — parameters (HTTP query generation), responses (fixtures captured from real servers), core value objects and the API methods tested against a stub PSR-18 client (
StubHttpClient). They need no BigBlueButton server. - Live integration tests —
BigBlueButtonTestandFixturesTestrun against a real BigBlueButton server configured via.env.local. - Dual-transport tests —
BigBlueButtonHttpClientTestandFixturesHttpClientTestrepeat the live suites with an injected PSR-18 http client instead of curl, proving both transports behave identically.
Connecting a test server
Copy .env to .env.local and configure:
BBB_SERVER_BASE_URL=https://your-bbb-server.example.com/bigbluebutton/
BBB_SECRET=your-secret
.env.local is git-ignored — never commit server credentials. The live tests create, join and end meetings and create/destroy hooks on that server; use a dedicated test server.
Running
composer code-test # full suite (offline + live)
./vendor/bin/phpunit --filter testApiVersion # a single test
./vendor/bin/phpunit tests/Parameters/ # a single directory
Coverage
Regular runs do not collect coverage. To generate an HTML report (requires a coverage driver such as Xdebug or PCOV):
composer code-coverage
The report lands in ./var/coverage/. The project maintains 100 percent class, method and line coverage; practically unreachable defensive guards are marked with @codeCoverageIgnore.
Fixtures
Server responses are captured as fixtures in tests/fixtures/responses/. FixturesTest validates for every XML fixture that the live server still answers with the same structure, so format changes on the server surface early. Fixtures that cannot be validated against a live server (e.g. they require a processed recording) are listed with reasons in FixturesTest::$xmlFilesThatAreNotTestable.
Documentation
This documentation is built with mdBook. The sources live in docs/src/; the build output goes to docs/book/ (git-ignored).
-
Install rust
-
Install mdbook
cargo install mdbook
- Install this to ensure external links will be opened in a new tab https://crates.io/crates/mdbook-external-links
cargo install mdbook-external-links
- Install extention for alerts https://crates.io/crates/mdbook-alerts
cargo install mdbook-alerts
Caution
??? How to prevent the markdown below of being parsed?
> [!NOTE]
> Highlights information that users should take into account, even when skimming.
> [!TIP]
> Optional information to help a user be more successful.
> [!IMPORTANT]
> Crucial information necessary for users to succeed.
> [!WARNING]
> Critical content demanding immediate user attention due to potential risks.
> [!CAUTION]
> Negative potential consequences of an action.
- build the book locally
mdbook serve --open
Full Usage Example
Introduction
You have been using BigBlueButton for years or you are still discovering it, and you have PHP within your solutions sphere and considering managing your BigBlueButton meetings with PHP, we are writing this tutorial right for you and your team.
BigBlueButton officially offers a PHP library to use for its API. In this tutorial you will learn how to use this PHP library to create a meeting then join it.
Pre-requisites
Before we can show you how to use the library, it is important to have the following point done:
- BigBlueButton server installed. Easy enough, it takes 15 minutes or less. Just follow this link https://bigbluebutton.org/2018/03/28/install-bigbluebutton-in-15-minutes/ if not already done.
- PHP 8.2 or higher.
- curl, mbstring, SimpleXML and JSON PHP extensions. They are active by default in most PHP distributions.
- A running HTTP server, Apache2 or nginx.
- Composer PHP dependency manager pre-installed.
Installation and configuration
First, we need to create our composer project.
composer init –name 'bigbluebutton-join-form'
Then we need to add the library available on packagist.
composer require bigbluebutton/bigbluebutton-api-php
Once we have defined the required dependency, we need to install it using the command below.
composer install -o --no-dev
Adding --no-dev options, means that we omit development packages that are mainly used to unit test the library.
The library package has now been downloaded to vendor directory in your project root. A configuration final step
is required for the library.
As you know, the call BigBlueButton API you need the server URL and the shred secret. You can get them from you BigBlueButton server with ‘bbb-conf’ http://docs.bigbluebutton.org/install/bbb-conf.html#–secret
Once you have them, create two environment variables. For Apache2 you can use the SetEnv directive or the
fastcgi_param for nginx. For Apache2, we advise putting the variables in the /etc/apache2/envvars to keep themaway from your source code repository.
BBB_SECRET='8cd8ef52e8e101574e400365b55e11a6'
BBB_SERVER_BASE_URL='http://test-install.blindsidenetworks.com/bigbluebutton/'
Set up a basic join meeting form
Let’s go ahead and create our HTML form to join BigBlueButton meeting. The contact form will contain the following fields: username, a combo-box for the meeting name, a second combo-box for the user role and a checkbox for the client type.
Your form should look like the image below, and the source code is just below the image.
<!DOCTYPE html>
<html lang="en">
<style>
body {
padding : 20px 40px;
font-size : 14px;
font-family : Verdana, Tahoma, sans-serif;
}
h2 {
color : #273E83;
}
input:not([type=checkbox]), select {
border-radius : 3px;
padding : 10px;
border : 1px solid #E2E2E2;
width : 200px;
color : #666666;
box-shadow : rgba(0, 0, 0, 0.1) 0 0 4px;
}
input:hover, select:hover,
input:focus, select:focus {
border : 1px solid #273E83;
box-shadow : rgba(0, 0, 0, 0.2) 0 0 6px;
}
.form label {
margin-left : 12px;
color : #BBBBBB;
}
.submit input {
background-color : #273E83;
color : #FFFFFF;
border-radius : 3px;
}
</style>
<head>
<meta charset="UTF-8">
<title>BigBlueButton Join Meeting - PHP Form</title>
</head>
<body>
<h2>BigBlueButton Join Meeting - PHP Form</h2>
<form class="form" action="join_bbb.php" method="post">
<p class="username">
<input type="text" name="username" id="username" placeholder="University Teacher"/>
<label for="username">Username</label>
</p>
<p class="role">
<select name="role" id="role">
<option value="moderator">Moderator</option>
<option value="attendee">Attendee</option>
</select>
<label for="role">Role</label>
</p>
<p class="meeting">
<select name="meeting" id="meeting">
<option value="mc">Molecular Chemistry</option>
<option value="it">Information Theory</option>
<option value="pm">Project Management</option>
</select>
<label for="meeting">Course</label>
</p>
<p class="submit">
<input type="submit" value="Join"/>
</p>
</form>
</body>
</html>
The HTML form is now ready. Let’s see how to handle posted data step by step. All data will be sent then
processed by join-bbb.php file.
Preparing PHP form processor
Do you remember we used composer to install the library? Composer creates a file named autoload.php inside
vendor directory. Just import it to load the necessary classes.
<?php
require_once './vendor/autoload.php';
After asking PHP to use the file ./vendor/autoload.php to handle the necessary classes loading. We define our
meeting names in the $meetings, then we define the $passwords, both in associative arrays.
// Define meetings names
$meetings = array('mc' => 'Molecular Chemistry',
'it' => 'Information Theory',
'pm' => 'Project Management');
$passwords = array('moderator' => 'mPass',
'attendee' => 'aPass');
The PHP API offers an easy way to handle calls. Creating an instance of BigBlueButton class without giving any
parameter. After it we are storing the meeting id we got from our form inside the $meetingId variable.
// Init BigBlueButton API
$bbb = new BigBlueButton();
$meetingId = $_POST['meeting'];
Creating the meeting
To create a meeting, only two parameters are mandatory: the meeting ID and the meeting name. For security reasons we
discourage leaving the moderator password and the attendee password empty. For that reason, we are filling them in
the CreateMeetingParameters instance.
// Create the meeting
$createParams = new CreateMeetingParameters($meetingId, $meetings[$meetingId]);
$createParams = $createParams->setModeratorPassword($passwords['moderator'])
->setAttendeePassword($passwords['attendee']);
$bbb->createMeeting($createParams);
Joining the meeting
A meeting can be joined by two different ways. The first way is to let the BigBlueButton server do the redirection for
you. The second way is to ask for an XML response then construct the URL using getSessionToken. Both of the methods
are detailed below.
Joining the meeting is done in two steps. In the first step we create an instance of CreateMeetingParameters and fill
the previously saved $meetingId, then username and role values from POST values. The third required parameter is
password, the role of the user is determined by the system depending on the provided password. For that reason we will
read it from $passwords using the $_POST['role'] key.
// Send a join meeting request
$joinParams = new JoinMeetingParameters($meetingId, $_POST['username'], $passwords[$_POST['role']]);
Following the server redirection
We set redirect to true if we want an immediate redirection to the meeting.
// Ask for immediate redirection
$joinParams->setRedirect(true)
Lastly, we ask PHP to follow the join meeting redirection using the header function. The redirection will be done by
BigBlueButton by calling $bbb->getJoinMeetingURL($joinParams)). You should now see the meeting page.
// Join the meeting by redirecting the user to the generated URL
header('Status: 301 Moved Permanently', false, 301);
header('Location:' . $bbb->getJoinMeetingURL($joinParams));
Storing join response to join manually
There is also a different way to join a meeting. To achieve it, we set redirect to false.
// Let the prorgrammer do the redirection later
$joinParams->setRedirect(false)
In this particular case the server will return an XML response. To handle it you need to call the joinMeeting method.
$joinResponse = $bbb->joinMeeting($joinParams);
Then we prepare the server URL for joining the meeting.
// Prepare the server URL
$bbbServerUrl = "https://my-bbb-server.com";
The user is redirected to the HTML5 client using the session token of the join response.
// Join the HTML5 client
header('Status: 301 Moved Permanently', false, 301);
header('Location:' . $bbbServerUrl . "/html5client/join?sessionToken=" . $joinResponse->getSessionToken());
Conclusion
You have discovered how to setup a BigBlueButton meeting then join it using the PHP API client library. Go ahead and explore the library features to implement your own meeting management system for BigBlueButton.
FAQ
When I join a meeting, my users see an error / the client misbehaves. What is wrong?
The most common trap: joinMeeting() executes the join server-side, so the user’s browser never receives the session cookie the BBB server sets on join. The standard flow is to redirect the user’s browser to a generated join URL:
$joinUrl = $bbb->getJoinMeetingURL($joinMeetingParameters);
header('Location: ' . $joinUrl);
Use joinMeeting() (with setRedirect(false)) only when you explicitly need the join response — e.g. session tokens for API-driven clients. See also the Joining chapter.
Do I need allowRequestsWithoutSession=true?
Only if you join meetings server-side (see above) or drive clients that cannot hold a session. It weakens the meeting’s security — prefer the redirect flow.
Which BBB server versions are supported?
The library tracks the current BigBlueButton API across BBB 2.x, 3.x and 4.x: parameters removed on newer servers remain available (marked deprecated) so integrations against older servers keep working, and new server parameters are added as soon as they are documented.
The feedback endpoint returns 404
The /api/feedback endpoint is reserved in the BBB server source but not routed on current releases. The library ships the implementation marked experimental; it will start working once a BBB release actually provides the endpoint.
How do I get the URL and secret of my server?
bbb-conf --secret on the server — see Server Configuration.
Can I use my own HTTP client (Guzzle, Symfony)?
Yes — any PSR-18 client with PSR-17 factories can be injected, see HTTP Client. curl stays the dependency-free default.
What does the library do with cookies?
It captures only the JSESSIONID the server sets on some calls, validates it, and exposes it via getJSessionId(). No cookies are persisted or sent back — details in Cookies and the JSESSIONID.
Where do I report bugs or request features?
On GitHub — with a reproducing code sample against the current release if possible.