diff --git a/docs/apis/plugintypes/index.md b/docs/apis/plugintypes/index.md
index 1c285c010e..9ef70c6114 100644
--- a/docs/apis/plugintypes/index.md
+++ b/docs/apis/plugintypes/index.md
@@ -55,6 +55,7 @@ The underscore character is not supported in activity modules for legacy reasons
| [Assignment submission plugins](./assign/submission.md) | assignsubmission | /mod/assign/submission | Different forms of assignment submissions | 2.3+ |
| [Assignment feedback plugins](./assign/feedback.md) | assignfeedback | /mod/assign/feedback | Different forms of assignment feedbacks | 2.3+ |
| [Book tools](./mod_book/index.md) | booktool | /mod/book/tool | Small information-displays or tools that can be moved around pages | 2.1+ |
+| [BigBlueButton activity extensions](./mod_bigbluebuttonbn/index.md) | bbbext | /mod/bigbluebuttonbn/extension | Extend or override the behaviour of the BigBlueButton activity — settings form fields, completion rules, meeting URL parameters, meeting events, settings navigation, and the activity view page — without modifying core | 4.3+ |
| [Custom fields](./customfield/index.md) | customfield | /customfield/field | Custom field types, used in Custom course fields | 3.7+ |
| [Database fields](./mod_data/fields.md) | datafield | /mod/data/field | Different types of data that may be added to the Database activity module | 1.6+ |
| [Database presets](./mod_data/presets.md) | datapreset | /mod/data/preset | Pre-defined templates for the Database activity module | 1.6+ |
diff --git a/docs/apis/plugintypes/mod_bigbluebuttonbn/index.md b/docs/apis/plugintypes/mod_bigbluebuttonbn/index.md
new file mode 100644
index 0000000000..a0a37d0467
--- /dev/null
+++ b/docs/apis/plugintypes/mod_bigbluebuttonbn/index.md
@@ -0,0 +1,371 @@
+---
+title: BigBlueButton activity extensions
+tags:
+ - mod_bigbluebuttonbn
+ - bbbext
+ - plugintype
+ - subplugin
+---
+
+
+
+The [BigBlueButton activity](https://docs.moodle.org/en/BigBlueButton) supports a subplugin type, `bbbext`, which lets third-party code extend or override the behaviour of the activity without patching `mod_bigbluebuttonbn` itself. This is the supported way to add features to BigBlueButton activities, and it keeps your customisations upgrade-safe.
+
+A `bbbext` subplugin can, for example:
+
+- Add extra fields to the activity settings form, persist them in its own tables, and validate them.
+- Contribute custom activity completion rules.
+- Add parameters to the URLs used to create meetings and join sessions.
+- React to meeting lifecycle events reported back by BigBlueButton.
+- Add entries to, or completely replace, the activity's settings navigation.
+- Override what is rendered on the activity view page.
+
+A minimal, public reference implementation is the [`bbbext_simple`](https://github.com/call-learning/moodle-bbbext_simple) test subplugin. See [Reference implementations](#reference-implementations) at the end of this page for more examples.
+
+## How extensions are discovered {/* #discovery */}
+
+Each hook point is backed by a base class or interface in the `mod_bigbluebuttonbn\local\extension` namespace. To participate in a hook, a subplugin provides a class that extends the relevant base class (or implements the relevant interface) and lives at a fixed, predictable location:
+
+```
+\bbbext_\bigbluebuttonbn\
+```
+
+where `` is the subplugin name and `` is the short class name of the base class (for example `mod_form_addons` or `navigation_append_addon`). `mod_bigbluebuttonbn` scans all installed, enabled `bbbext` subplugins, and for each hook it instantiates every class it finds at that path that is a subclass of the corresponding base class. There is no registration file: matching the name and location _is_ the contract.
+
+A subplugin only needs to provide classes for the hooks it actually uses; every hook is optional.
+
+The order in which extensions are invoked follows the sort order configured on the **Manage BigBlueButton extensions** admin page. This matters for hooks that _append_ (every matching extension is called in order) and for hooks that _override_ (only the first matching extension is used). See each hook's reference below.
+
+## File structure {/* #file-structure */}
+
+import { ComponentFileSummary } from '../../../_utils';
+
+`bbbext` subplugins are located in the `mod/bigbluebuttonbn/extension` directory. Each subplugin is in its own subdirectory, whose name is the subplugin's ``, giving a component name of `bbbext_`.
+
+A subplugin consists of a small number of _mandatory files_, one class per hook it implements, and any other files it needs.
+
+
+ View an example directory layout for a minimal `bbbext_example` subplugin.
+
+```console
+mod/bigbluebuttonbn/extension/example
+├── classes
+│ ├── bigbluebuttonbn
+│ │ ├── action_url_addons.php
+│ │ ├── custom_completion_addons.php
+│ │ ├── mod_form_addons.php
+│ │ ├── mod_instance_helper.php
+│ │ ├── navigation_append_addon.php
+│ │ ├── navigation_override_addon.php
+│ │ └── view_page_addons.php
+│ └── privacy
+│ └── provider.php
+├── db
+│ └── install.xml
+├── lang
+│ └── en
+│ └── bbbext_example.php
+├── settings.php
+└── version.php
+```
+
+
+
+All the hook classes live under `classes/bigbluebuttonbn/`, so that they autoload as `\bbbext_\bigbluebuttonbn\`. See the [common plugin files](../../commonfiles/index.mdx) documentation for details of other files (such as `db/access.php`, `db/events.php`, backup classes, or a privacy provider) that may be useful in your plugin.
+
+### version.php {/* #versionphp */}
+
+
+
+As with any Moodle plugin, `version.php` declares the component and its version metadata. The component name must be `bbbext_`:
+
+```php title="version.php"
+component = 'bbbext_example';
+$plugin->version = 2025010100;
+$plugin->requires = 2024100700; // Must be a Moodle version that ships every bbbext hook your subplugin uses.
+$plugin->maturity = MATURITY_STABLE;
+$plugin->release = '1.0';
+```
+
+:::important
+
+Set `$plugin->requires` to a Moodle version that provides **all** of the hooks your subplugin actually implements. The hooks were not all introduced at once: in particular, the [settings navigation](#navigation-append-addon) and [activity view page](#view-page-addons) hooks are only available from Moodle 5.3. The example directory layout above lists every hook for completeness, but you only implement — and only need to require the Moodle version for — the ones you use. A subplugin that requires an older version but implements a newer hook will fail when its classes are discovered.
+
+:::
+
+### Language file {/* #language-file */}
+
+
+
+At minimum the language file must define `pluginname`, which is used by the admin **Manage BigBlueButton extensions** page and elsewhere:
+
+```php title="lang/en/bbbext_example.php"
+
+
+If present, `settings.php` is loaded into the admin tree by the `bbbext` plugininfo class under the BigBlueButton extensions category. Use the standard `$settings` object provided to the file, exactly as for any other plugin type.
+
+## Hook reference {/* #hooks */}
+
+Each of the following hooks is optional. To use one, add a class at the path shown that extends the given base class (or implements the given interface).
+
+### Activity settings form {/* #mod-form-addons */}
+
+Extend `\mod_bigbluebuttonbn\local\extension\mod_form_addons` in `classes/bigbluebuttonbn/mod_form_addons.php` to add fields to the activity settings form, validate them, and post-process the submitted data. The parent form calls every matching extension when the form is built.
+
+The base class receives the `MoodleQuickForm`, the current instance data (if any), and the form field suffix through its constructor, exposed as `$this->mform`, `$this->bigbluebuttonbndata`, and `$this->suffix`. You must implement `add_fields()`, `validation()`, `data_postprocessing()`, `data_preprocessing()`, and `add_completion_rules()`; `completion_rule_enabled()` and `definition_after_data()` may be overridden as needed.
+
+`data_preprocessing(?array &$defaultvalues)` is called on every extension when an existing activity is opened for editing. Implement it to load the values your extension stored through the [instance lifecycle hook](#mod-instance-helper) back into the form.
+
+:::caution[data_preprocessing is currently required]
+
+Although the base class does not declare `data_preprocessing()`, the activity form calls it unconditionally on every extension. You must therefore implement it in your subplugin even if its body is empty — otherwise editing an existing activity raises a fatal *"call to undefined method"* error. This inconsistency is tracked in [MDL-89849](https://moodle.atlassian.net/browse/MDL-89849) and is expected to be addressed in a future version by adding a default (no-op) implementation to the base class, after which the method will become optional.
+
+:::
+
+```php title="classes/bigbluebuttonbn/mod_form_addons.php"
+mform->addElement('advcheckbox', 'example_enable', get_string('example_enable', 'bbbext_example'));
+ $this->mform->setType('example_enable', PARAM_BOOL);
+ }
+
+ public function validation(array $data, array $files): array {
+ return [];
+ }
+
+ public function data_postprocessing(stdClass &$data): void {
+ // Normalise or derive values on $data before it is persisted.
+ }
+
+ public function data_preprocessing(?array &$defaultvalues): void {
+ // Load your extension's stored values into $defaultvalues when editing.
+ }
+
+ public function add_completion_rules(): array {
+ return [];
+ }
+}
+```
+
+Any fields you add here should be persisted through the [instance lifecycle hook](#mod-instance-helper).
+
+### Instance lifecycle and additional tables {/* #mod-instance-helper */}
+
+Extend `\mod_bigbluebuttonbn\local\extension\mod_instance_helper` in `classes/bigbluebuttonbn/mod_instance_helper.php` to persist extension data when a BigBlueButton activity is created, updated, or deleted, and to declare any additional database tables your subplugin joins to an instance.
+
+Every table returned by `get_join_tables()` must contain a `bigbluebuttonbnid` column that references the BigBlueButton instance; tables without it are ignored (with a debugging message). These tables are used by core to build the full instance record.
+
+```php title="classes/bigbluebuttonbn/mod_instance_helper.php"
+insert_record('bbbext_example', (object) [
+ 'bigbluebuttonbnid' => $bigbluebuttonbn->id,
+ 'enable' => $bigbluebuttonbn->example_enable ?? 0,
+ ]);
+ }
+
+ public function update_instance(stdClass $bigbluebuttonbn): void {
+ // Update your extension's records for $bigbluebuttonbn->id.
+ }
+
+ public function delete_instance(int $cmid): void {
+ // Clean up your extension's records.
+ }
+
+ public function get_join_tables(): array {
+ return ['bbbext_example'];
+ }
+}
+```
+
+### Custom completion rules {/* #custom-completion-addons */}
+
+
+
+Extend `\mod_bigbluebuttonbn\local\extension\custom_completion_addons` in `classes/bigbluebuttonbn/custom_completion_addons.php` to contribute activity completion rules. This works together with the completion rules you register on the settings form via `mod_form_addons::add_completion_rules()`.
+
+You must implement `get_state()`, the static `get_defined_custom_rules()`, `get_custom_rule_descriptions()`, and `get_sort_order()`. The base class constructor provides `$this->cm`, `$this->userid`, and `$this->completionstate`.
+
+```php title="classes/bigbluebuttonbn/custom_completion_addons.php"
+ get_string('completionexample', 'bbbext_example')];
+ }
+
+ public function get_sort_order(): array {
+ return ['completionexample'];
+ }
+}
+```
+
+### Meeting action URLs {/* #action-url-addons */}
+
+Extend `\mod_bigbluebuttonbn\local\extension\action_url_addons` in `classes/bigbluebuttonbn/action_url_addons.php` to add parameters to the URLs that `mod_bigbluebuttonbn` sends to BigBlueButton (for example when creating a meeting or building a join URL).
+
+Every matching extension's `execute()` is called and the results are merged, so by contract an extension must return **only** the parameters it wants to add, under the `data` and `metadata` keys — never the input it received. This keeps extensions independent and prevents one extension from clobbering another's contribution.
+
+```php title="classes/bigbluebuttonbn/action_url_addons.php"
+ ['meta_example' => 'value'],
+ 'metadata' => [],
+ ];
+ }
+}
+```
+
+### Meeting events {/* #broker-meeting-events-addons */}
+
+Extend `\mod_bigbluebuttonbn\local\extension\broker_meeting_events_addons` in `classes/bigbluebuttonbn/broker_meeting_events_addons.php` to react to meeting lifecycle events that BigBlueButton reports back to Moodle. Every matching extension is instantiated with the `instance` and the raw event payload and has its `process_action()` method called.
+
+```php title="classes/bigbluebuttonbn/broker_meeting_events_addons.php"
+instance and $this->data are available here.
+ }
+}
+```
+
+### Settings navigation (append) {/* #navigation-append-addon */}
+
+
+
+Implement `\mod_bigbluebuttonbn\local\extension\navigation_append_addon` in `classes/bigbluebuttonbn/navigation_append_addon.php` to add nodes to the activity's settings navigation. All extensions implementing this interface are called, in admin-configured order, _after_ the core navigation has been built — unless an [override](#navigation-override-addon) is present, in which case appenders are not called.
+
+```php title="classes/bigbluebuttonbn/navigation_append_addon.php"
+add(
+ get_string('example_nav', 'bbbext_example'),
+ new \moodle_url('/mod/bigbluebuttonbn/extension/example/index.php'),
+ \navigation_node::TYPE_SETTING
+ );
+ }
+}
+```
+
+### Settings navigation (override) {/* #navigation-override-addon */}
+
+
+
+Implement `\mod_bigbluebuttonbn\local\extension\navigation_override_addon` in `classes/bigbluebuttonbn/navigation_override_addon.php` to replace the settings navigation entirely. Only the **first** matching extension (by admin-configured order) is used; when an override is present, the core navigation logic and all appenders are skipped.
+
+```php title="classes/bigbluebuttonbn/navigation_override_addon.php"
+
+
+Extend `\mod_bigbluebuttonbn\local\extension\view_page_addons` in `classes/bigbluebuttonbn/view_page_addons.php` to override what is rendered on the activity's view page. The base class extends the core `mod_bigbluebuttonbn\output\view_page` renderable, so your class is a drop-in replacement rendered in its place.
+
+Only the **first** matching extension is used. When present, it fully replaces the default view page rendering; if no extension provides this class, the default view page is used.
+
+```php title="classes/bigbluebuttonbn/view_page_addons.php"
+instance = $instance;
+ }
+
+ public function export_for_template(renderer_base $output): stdClass {
+ // Return the template context for your overridden view.
+ return (object) [];
+ }
+}
+```
+
+## Language strings {/* #language-strings */}
+
+The only string a subplugin is strictly required to define is `pluginname`, in `lang/en/bbbext_.php`. Beyond that, define strings for anything your subplugin surfaces to users, following the usual conventions:
+
+- form field labels and their `_help` strings for fields added in `mod_form_addons`;
+- completion rule labels used by `custom_completion_addons::get_custom_rule_descriptions()`;
+- navigation node labels;
+- setting labels and descriptions for `settings.php`;
+- capability descriptions (`:`) if you declare capabilities in `db/access.php`.
+
+The `subplugintype_bbbext` and `subplugintype_bbbext_plural` strings that name the subplugin type as a whole are owned by `mod_bigbluebuttonbn`.
+
+## Reference implementations {/* #reference-implementations */}
+
+A public, minimal reference implementation is the [`bbbext_simple`](https://github.com/call-learning/moodle-bbbext_simple) test subplugin, developed alongside the subplugin type. It demonstrates the settings form, instance persistence with additional tables, custom completion rules, and action URL parameters in isolation, which makes it a good starting point for a new subplugin.
+
+For a fuller, real-world implementation that additionally overrides the settings navigation and the activity view page, see `bbbext_bnx`, maintained by [Blindside Networks](https://blindsidenetworks.com/). It is distributed as a separate subplugin rather than as part of the `mod_bigbluebuttonbn` module.
diff --git a/project-words.txt b/project-words.txt
index 5976a148ef..0600e095b7 100644
--- a/project-words.txt
+++ b/project-words.txt
@@ -99,6 +99,7 @@ allcountrycodes
allowcaching
assignfeedback
assignsubmission
+bbbext
behat
behats
behatsnapshots