Files
cacti/docs/notification-api.md
Thomas Vincent 66a2808cc9 Let one notification address several recipients (#7782)
* feat(notify): let one notification address several recipients

Symfony calls a channel once per recipient, so a caller that needs a single
message sent to several people could not express it. The email 'to' option
folds the extra addresses into the same mailer() call and drops any that
match the recipient.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* fix(notify): compare the local part of an address exactly

RFC 5321 leaves local part case to the receiving host, so folding the whole
address deduplicated OPS@example.com and ops@example.com into one mailbox.
Only the domain folds now.

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
2026-09-02 21:01:03 -07:00

111 lines
3.2 KiB
Markdown

# Notification API
Cacti provides a plugin-extensible notification API backed by Symfony
Notifier. The API uses Cacti's existing mail settings for the built-in email
channel, including native mail, sendmail, SMTP, and OAuth2 delivery.
This is an internal PHP API for Cacti core and plugins. It is not an HTTP
endpoint.
## Send a notification
Load the API and pass a subject, content, and one or more recipients:
```php
require_once(CACTI_PATH_LIBRARY . '/api_notification.php');
$sent = api_notification_send(
'Device unavailable',
'The core router is not responding.',
'Operations <ops@example.com>'
);
```
The recipient argument accepts the same email forms as Cacti's mailer, a
Symfony `RecipientInterface`, or a list of Symfony recipients. The function
returns `true` when every selected channel accepts every recipient. Delivery
errors are logged to the `NOTIFIER` facility and return `false`.
Email-specific options preserve the capabilities of Cacti's mailer:
```php
use Symfony\Component\Notifier\Notification\Notification;
api_notification_send(
'Nightly report',
'<h1>Report complete</h1>',
'reports@example.com',
options: [
'importance' => Notification::IMPORTANCE_LOW,
'email' => [
'html' => true,
'text' => 'Report complete',
'cc' => 'audit@example.com',
'reply_to' => 'noreply@example.com',
'attachments' => $attachments,
'headers' => ['X-Cacti-Source' => 'reports'],
],
]
);
```
Supported email options are `from`, `to`, `cc`, `bcc`, `reply_to`, `html`,
`text`, `attachments`, `headers`, and `expand_ids`.
Symfony calls a channel once per recipient, so passing several recipients
sends several messages. When one message has to reach several people, pass one
recipient and put the rest in the `to` option:
```php
api_notification_send(
'Devices rebooted',
$body,
'first@example.com',
options: ['email' => ['to' => 'second@example.com,third@example.com']]
);
```
Addresses matching the recipient are dropped, so the recipient is never listed
twice. Matching compares the domain without regard to case and the local part
exactly, because RFC 5321 leaves local part case to the receiving host.
Cores released before this option ignore it and deliver to the recipient only.
Probe for it before joining addresses:
```php
if (function_exists('api_notification_email_supports_multiple_to')
&& api_notification_email_supports_multiple_to()) {
// safe to pass the 'to' option
}
```
## Add a plugin channel
Plugins can register the `notification_channels` hook. The hook receives and
must return an array keyed by channel name. Each value must implement Symfony's
`ChannelInterface`:
```php
function plugin_name_notification_channels(array $channels) : array {
$channels['chat'] = new PluginChatChannel();
return $channels;
}
```
Call the API with the registered channel name and an appropriate Symfony
recipient:
```php
api_notification_send(
'Device unavailable',
'The core router is not responding.',
$recipient,
['email', 'chat']
);
```
Channel implementations control their own credentials and configuration.
Secrets must remain in Cacti settings or another protected runtime store, not
in notification content or source files.