Notifications¶
The notification feature sends transactional email through Brevo (formerly Sendinblue). It supports two delivery models on top of the same Brevo client:
List-based notifications – visitors opt in (double opt-in) to a Brevo contact list attached to a content object. Sending a notification mails everyone on that list.
Direct notifications – an editor picks specific users and groups from the site and mails exactly those people, resolving their email addresses from their Plone user properties. No opt-in list is involved.
Both models share the same registry configuration, the same email template, and the same after-commit delivery mechanism.
Configuration¶
Notification delivery is configured through four registry records. All default to empty and must be filled in before notifications can be sent.
Registry record |
Purpose |
|---|---|
|
Brevo API key used for all API calls. |
|
Brevo folder id under which new contact lists are created. |
|
Brevo template id for the double opt-in confirmation email. |
|
Brevo template id for the notification email itself. |
The notification template receives three parameters that you can reference in
the Brevo template: title, text, and url.
Enabling Notifications on Content¶
Notifications are enabled per content object through one of two behaviors:
Brevo Push notifications (
wcs.backend.notification.push.brevo) – the list-based model. It stores anenabledflag and the Brevolist_idfor that object. When enabled, visitors can sign up and the object owns its own Brevo contact list (created lazily on first use).Brevo Direct Push notifications (
wcs.backend.notification.direct.push.brevo) – the direct model. It stores only anenabledflag (enabled by default).
A content object must provide one of these behaviors for the notification endpoints to be available on it.
Recipient Resolution¶
How recipients are resolved depends on the delivery model.
List-based recipients¶
For list-based notifications, recipients are the contacts in the object’s Brevo
list. They are fetched from Brevo in pages of 500 and reduced to an
{email, name} pair each, where the name is built from the contact’s
FIRSTNAME and LASTNAME attributes (falling back to the email if no name is
stored).
Visitors are added to the list through the sign-up endpoint, which by default triggers Brevo’s double opt-in flow: Brevo sends the confirmation email (using the double opt-in template) and only adds confirmed contacts to the list.
Direct recipients¶
For direct notifications, the editor selects users and groups. Group selections
are expanded to their members (the AuthenticatedUsers group expands to all
site users). Each resulting user id is resolved to an {email, name} pair from
the Plone user’s email and fullname properties.
Users without a matching account or without an email property are dropped and logged. The send routine logs how many recipients were resolved versus how many user ids were selected, and which addresses were ultimately targeted, so dropped recipients can be diagnosed from the logs.
Delivery (After-Commit Hook)¶
Sending a notification does not dispatch email immediately. Recipients are resolved synchronously (so they reflect the state at request time), but the actual Brevo API calls are deferred to a transaction after-commit hook. Email is only sent if the transaction commits successfully – if the request fails and the transaction is aborted, no email goes out.
During delivery, recipients are sent as BCC entries in batches (up to 50 per
batch) against Brevo’s transactional email endpoint, with a short pause between
batches to stay within rate limits. The current user’s email address (when
available) is attached as the replyTo of the message, so recipients can reply
directly to the person who triggered the notification. Batch failures are logged
and do not abort the remaining batches.
REST API¶
Both endpoints require the content object to provide one of the notification behaviors.
POST @sign-up¶
Signs a visitor up for the object’s notification list. Requires firstname,
lastname, and email; any missing field returns 400 Bad Request. By default
this starts Brevo’s double opt-in flow, so the visitor receives a confirmation
email before being added to the list.
POST /Plone/news/@sign-up HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json
{
"firstname": "Jane",
"lastname": "Doe",
"email": "jane@example.com"
}
Response:
{
"status": "success",
"message": "User signed up for notifications."
}
POST @send-notification¶
Sends a notification for the current context. Optional title (defaults to the
object’s title) and message may be supplied in the body. The object’s URL is
used as the url template parameter.
POST /Plone/news/@send-notification HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json
{
"title": "New article published",
"message": "<p>Read our latest update.</p>"
}
Response:
{
"status": "success",
"message": "Notification sent to users."
}
The success response confirms the notification was queued for delivery via the after-commit hook; the actual emails are sent once the request transaction commits.
Consuming the endpoints¶
JavaScript – sign up a visitor:
async function signUp(objectUrl, firstname, lastname, email) {
const response = await fetch(`${objectUrl}/@sign-up`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({ firstname, lastname, email })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || `Sign-up failed: ${response.status}`);
}
return response.json();
}
// Usage
await signUp(
'http://localhost:8080/Plone/news',
'Jane', 'Doe', 'jane@example.com'
);
JavaScript – send a notification:
async function sendNotification(objectUrl, title, message) {
const response = await fetch(`${objectUrl}/@send-notification`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({ title, message })
});
return response.json();
}
// Usage
await sendNotification(
'http://localhost:8080/Plone/news',
'New article published',
'<p>Read our latest update.</p>'
);
Python – send a notification:
import requests
response = requests.post(
'http://localhost:8080/Plone/news/@send-notification',
auth=('editor', 'password'),
headers={
'Accept': 'application/json',
'Content-Type': 'application/json',
},
json={
'title': 'New article published',
'message': '<p>Read our latest update.</p>',
},
)
response.raise_for_status()
print(response.json()['status'])