Alert types are Stream's notifiers. Each one decides what happens when a record matches an alert's triggers (email, Slack, highlight, β¦). This guide is enough to scaffold a working type without reading every builtin file.
Related: ARCHITECTURE.md (lifecycle) Β· adding-a-connector.md (how records get into Stream)
| Kind | Location |
|---|---|
| Abstract contract | classes/class-alert-type.php (WP_Stream\Alert_Type) |
| Registry | classes/class-alerts.php (WP_Stream\Alerts::load_alert_types()) |
| Builtin types | alerts/class-alert-type-{slug}.php |
Builtins are listed in the $alert_types array inside Alerts::load_alert_types(). On init priority 9, Stream includes each alerts/class-alert-type-{slug}.php, instantiates the class, then applies the wp_stream_alert_types filter so other plugins can add more.
Stream does not autoload files under docs/ or alerts/ except the slugs in that array. A third-party type lives in your plugin.
alerts/class-alert-type-die.php and alerts/class-alert-type-menu-alert.php are on disk but not default-loaded. They only appear if something adds them through wp_stream_alert_types. Do not copy them as a template.
Extend WP_Stream\Alert_Type and set:
$nameβ translated label in the Stream β Alerts dropdown. Example:Error Log (example).$slugβ unique key stored on the alert post (lowercase, hyphens ok). Example:error-log.alert( $record_id, $recordarr, $alert )β dispatch. Stream passes theAlertobject as the third argument (not a plain options array).display_fields( $alert )β configuration UI for that type. Optional, but this is how users set destinations.
Optional:
$creatable(defaulttrue) β returnfalseto hide the type from the new-alert UI. Existing alerts still fire and remain editable (IFTTT uses this).is_dependency_satisfied()β returnfalseto drop the type from the registry when a required plugin is missing.
Alert_Type::save_fields() exists on the abstract class and is never called. Triggers persist through Alert_Trigger::save_fields() (a different class); alert types persist through the wp_stream_alerts_save_meta filter, applied from Alerts_Admin_UI::save_new_alert() and Alerts_List.
Hook that filter with a static callable ([ Alert_Type_Example::class, 'add_alert_meta' ]) or a named function. Do not use a closure or [$this, 'method']. Template for fields: alerts/class-alert-type-email.php.
If the type stores credentials, add those meta keys to wp_stream_secret_alert_meta_keys so Abilities/MCP output redacts them. The error-log sample does not store secrets.
Do not pass class-name strings to the registry. wp_stream_alert_types receives instances keyed by $slug.
DB insert
β wp_stream_record_inserted
β Alerts_Trigger_Engine::check_records()
β WP_Query enabled wp_stream_alerts posts
β Alert::check_record()
β wp_stream_alert_trigger_check (AND of author / context / action)
β Alert::send_alert()
β Alert_Type::alert()
Matching is synchronous on each record insert (one WP_Query per insert). Empty trigger meta means βanyβ. All configured triggers must pass.
The Alert model is a CPT (wp_stream_alerts) with statuses wp_stream_enabled / wp_stream_disabled. Disabled alerts are not queried.
Call add_filter( 'wp_stream_alert_types', β¦ ) from the plugin file. WordPress includes plugin files before plugins_loaded, and Alerts are constructed on init priority 9, so a top-level add_filter is early enough. Registering the filter on init priority 10 is too late.
The constructor needs a Plugin instance (unlike connectors). Read it from wp_stream_get_instance() when the filter runs β that helper is defined in Streamβs plugin file, so it is present whenever this filter fires. Guard on class_exists( \WP_Stream\Alert_Type::class ) so a direct call to the register function does not fatal if Stream is inactive.
add_filter( 'wp_stream_alert_types', 'my_plugin_register_stream_alert_type' );
function my_plugin_register_stream_alert_type( $types ) {
if ( ! class_exists( \WP_Stream\Alert_Type::class ) ) {
return $types;
}
$plugin = wp_stream_get_instance();
if ( ! $plugin instanceof \WP_Stream\Plugin ) {
return $types;
}
require_once __DIR__ . '/class-alert-type-example.php';
$type = new \WP_Stream\Alert_Type_Example( $plugin );
$types[ $type->slug ] = $type;
return $types;
}Use this path only when the type belongs in Stream itself.
- Add the slug to the
$alert_typesarray inAlerts::load_alert_types()inclasses/class-alerts.php. Hyphens in the slug become underscores in the class name (error-logβAlert_Type_Error_Log). - Create
alerts/class-alert-type-{slug}.phpwithclass Alert_Type_{Slug} extends Alert_Typein theWP_Streamnamespace. - Set
$name/$slug, implementalert(), and adddisplay_fields()plus awp_stream_alerts_save_metacallback if the type has settings. Template:alerts/class-alert-type-email.php. - Add PHPUnit coverage under
tests/phpunit/.
Do not copy the error-log sample into alerts/ or into load_alert_types(). That sample is a third-party plugin, not a Stream builtin. Alert_Type_None (none) is the silent builtin; it is not this example.
A complete sample plugin lives at docs/examples/stream-error-log-alert/. Stream does not load it. WordPress will not auto-activate it from inside this directory (plugins are discovered one level under wp-content/plugins/).
-
Copy the folder into
wp-content/plugins/(WordPress only discovers plugins one level under that directory, not from inside Stream):cp -R docs/examples/stream-error-log-alert wp-content/plugins/
On the wp-env stack from contributing.md:
npm run cli -- cp -R /var/www/html/wp-content/plugins/stream/docs/examples/stream-error-log-alert /var/www/html/wp-content/plugins/ npm run cli -- wp plugin activate stream-error-log-alert --network
-
Otherwise activate it from Plugins in wp-admin.
-
In wp-admin open Stream β Alerts β Add New.
-
Choose Error Log (example) as the alert type. Leave the author / connector / action triggers empty (match any record). Enable the alert and save.
-
Cause a Stream record: update a post, or (if you also installed the noop connector sample) click Log a noop event.
-
Check the PHP error log. On wp-env:
npm run logs. You should see a line likeStream alert: record 123 β¦.
stream-error-log-alert.phpregistersstream_error_log_alert_register()onwp_stream_alert_typesfrom the plugin file, and skips registration when Stream is inactive.class-alert-type-error-log.phpis the type:$slug = 'error-log',$name = 'Error Log (example)', optional prefix field,alert()callserror_log().
Copy those two files into your own plugin, rename the slug / text domain, and replace error_log() with the real notifier (HTTP, email, β¦).