Enable Notifications#
Notifications keep you informed about activity in your courses, alerting you via one or both of in-platform notifications and email notifications. They span activities such as Discussions updates, Course updates, Grading updates, and ORA review reminders.
The notification tray allows learners to access platform notifications from the top-right corner of the Open edX interface. Notification emails keep them updated when they are away from the platform. We do not have support for push notifications for Open edX mobile app at the moment.
The recommended way to enable notifications is by using the official Tutor plugin: tutor-contrib-platform-notifications.
Install and enable the plugin#
Install the plugin:
pip install tutor-contrib-platform-notifications
Enable the plugin in your Tutor environment:
tutor plugins enable notifications
When enabled, the plugin requires two additional steps to fully set up notifications:
Initialize notifications plugin
Run the following command:
tutor [local/dev/k8s] do init --limit=notifications
This configures the required waffle flags and environment variables:
Waffle flags: -
notifications.enable_notifications-notifications.enable_email_notificationsEnvironment variables: -
SHOW_EMAIL_CHANNEL(defaults to TRUE) -SHOW_PUSH_CHANNEL(defaults to FALSE)
Rebuild the MFE image
After initialization, rebuild the MFE image so the notification tray appears:
tutor images build mfe
Configure the notifications plugin#
The notifications plugin exposes several configuration options that can be customized through the Tutor configuration. These options are as follows:
NOTIFICATIONS_DEFAULT_FROM_EMAIL#
Specifies the From email address used when sending notification emails.
If not explicitly set, this value defaults to the platform contact email.
NOTIFICATIONS_ENABLE_SHOW_EMAIL_CHANNEL#
Controls whether the Email notifications toggle is visible on the learner’s account settings page.
Type: Boolean
Default:
True
When enabled, learners get toggle to opt in or out of receiving email notifications.
Important
The Open edX platform does not currently support push notifications for mobile apps. This toggle is added as part of ground work for when that support will be built.
NOTIFICATIONS_ENABLE_SHOW_PUSH_CHANNEL#
Controls whether the Push notifications toggle is visible on the learner’s account settings page.
Type: Boolean
Default:
False
When enabled, learners get toggle to opt in or out of push notifications.
Notification Digest Email#
Overview#
This change replaces the cron-job-driven digest email system with a self-scheduling Celery task approach. Instead of a periodic job that sweeps all users at a fixed interval, digest emails are now scheduled automatically — per user, at the exact configured delivery time — the moment a qualifying notification is created.
How it works#
send_notifications(user_ids, ...) ← called when a notification is created
│
├── [immediate cadence users] ─────► send_immediate_cadence_email(...)
│
└── [daily/weekly cadence users]
│
▼
schedule_bulk_digest_emails({user_id: cadence_type, ...})
│
├── get_next_digest_delivery_time(cadence_type) ← compute ETA
├── SELECT existing DigestSchedule records ← 1 query, skip already-scheduled
├── bulk_create new DigestSchedule records ← 1 query
├── Notification.objects.update(email_scheduled=True) ← 1 query
│
└── on transaction.commit():
send_user_digest_email_task.apply_async(
kwargs={user_id, cadence_type},
eta=delivery_time,
task_id=<dedupe_key>, ← Celery-level dedup
)
────────────── At configured delivery time ──────────────
send_user_digest_email_task(user_id, cadence_type)
│
├── _claim_digest_schedule(...) ← atomic DB delete (prevents double-send)
│ └── returns False if row already gone → skip
│
├── check: was digest already sent in this window? ← cron co-existence guard
│
├── send_digest_email_to_user(user, start_date, end_date, ...)
│ └── Notification.filter(email_sent_on__isnull=True) ← skip already-sent rows
│
└── Notification.update(email_scheduled=False) ← clean up flags
Key characteristics#
Aspect |
Detail |
|---|---|
Trigger |
Automatically when |
Entry point |
|
Task |
|
Delivery time control |
Settings: |
Deduplication |
Three layers: |
Failure handling |
Auto-retry up to 3×, exponential backoff (5 min → 10 min → 20 min) |
Scalability |
~3 DB queries per cadence group regardless of user count; tasks run in parallel |
Timezone handling |
|
New Settings#
As of Verawood, new settings have been added in openedx/envs/common.py that govern how digests are delivered.
NOTIFICATION_DAILY_DIGEST_DELIVERY_HOUR = 17 # 5 PM UTC
NOTIFICATION_DAILY_DIGEST_DELIVERY_MINUTE = 0
NOTIFICATION_WEEKLY_DIGEST_DELIVERY_DAY = 0 # Monday (0=Mon … 6=Sun)
NOTIFICATION_WEEKLY_DIGEST_DELIVERY_HOUR = 17 # 5 PM UTC
NOTIFICATION_WEEKLY_DIGEST_DELIVERY_MINUTE = 0
Override these in your deployment settings to change when digests are delivered.
Warning
send_email_digest management command, introduced in Ulmo, is DEPRECATED. You should remove any cron jobs that call it.
The management command still exists but is now a no-op with a deprecation warning:
WARNING: This command is deprecated. Digest emails are now scheduled automatically. Please remove cron jobs using this command.
If you are on an Ulmo system, please view this doc in the ulmo version for instructions on cron job configuration.
Creating a new notification#
This documentation provides instructions to developers on how to add new notifications to the existing notification system.
Overview of terms#
- Type
every notification a user sees has a defined type. This type describes the notification’s behaviour and template text.
- App
each notification type is associated with a notification app. A notification app is simply a way to group notifications, and to provide a mechanism for shared behaviour.
- Core notification
a notification type can be labelled as a core notification. In this case, the behaviour is managed at the app level, not at the level of the individual notification type.
Defining a notification#
The configuration consists of notification types and notification apps. Follow the steps below to define a new notification type.
Define the Notification App#
The first step to defining a new notification is deciding on an app to associate it with. Either choose an existing one or create a new one in COURSE_NOTIFICATION_APPS. For example, here is an app named “discussion”:
COURSE_NOTIFICATION_APPS = {
'discussion': {
'enabled': True,
'core_info': '',
'core_web': True,
'core_email': True,
'core_push': True,
'non_editable': [],
'core_email_cadence': 'weekly'
}
}
The app name (the key) can be any name you wish to add but ideally it should represent existing Django apps in the project. For an explanation of the available fields, see NotificationApp.
Define the Notification Type#
Now you can define the notification type itself. To do this, add a new entry to COURSE_NOTIFICATION_TYPES. For example, here is a notification defined for a new response to a discussion forum post, associated with the “discussion” app example from the previous step:
COURSE_NOTIFICATION_TYPES = {
'new_response': {
'notification_app': 'discussion',
'name': 'new_response',
'is_core': False,
'web': True,
'email': True,
'push': True,
'info': 'Response on post',
'non_editable': [],
'content_template': _('<{p}><{strong}>{replier_name}</{strong}> responded to your post <{strong}>{post_title}</{strong}></{p}>'),
'content_context': {
'post_title': 'Post title',
'replier_name': 'replier name',
},
'email_template': '',
},
}
For an explanation of the available fields, see NotificationType.
Sending a notification#
To send a notification, you need to send the USER_NOTIFICATION_REQUESTED signal with an instance of UserNotificationData containing information about the notification to send.
Below is an example function to build and send the new_response notification type from earlier.
from openedx_events.learning.signals import USER_NOTIFICATION_REQUESTED
from openedx_events.learning.data import UserNotificationData
def send_new_response_notification(user_ids, course, thread, replier_user):
notification_data = UserNotificationData(
user_ids=user_ids,
notification_type="new_response",
content_url=f"/{course.id}/posts/{thread.id}",
app_name='discussion',
course_key=course.id,
context={
'post_title': thread.title,
'replier_name': replier_user.username,
},
)
USER_NOTIFICATION_REQUESTED.send_event(notification_data=notification_data)
Explanation of the parameters for UserNotificationData:
Name |
Type |
Description |
|---|---|---|
user_ids |
list[int] |
List of user IDs to send the notification to. |
notification_type |
str |
The type of notification to send. This must be a key of COURSE_NOTIFICATION_TYPES. |
content_url |
str |
Url the user will navigate to if they click on the notification. |
app_name |
str |
The app this notification is associated with. This must be a key of COURSE_NOTIFICATION_APPS. |
course_key |
CourseKey |
The course that this notification will be associated with. |
context |
dict[str, str] |
Context variables and values to pass to the notification content template. Keys are the variable names defined in the notification type. |
That’s it! You have implemented the code to send a new user notification using the USER_NOTIFICATION_REQUESTED signal.
Grouping notifications#
For some notification types, the volume for a learner can be huge and can cause annoyance. For example, if a learner creates a post, and other learners and staff members start adding responses to his post, if for each comment, we add a response, it could result in dozens of notifications. To avoid these scenarios, we have implemented a feature that allows grouping more than one similar notifications into a single notification.
Steps to group a notification:
Enable grouping waffle flag
notifications.enable_notification_grouping.Add
group_by_idin context before sending theUSER_NOTIFICATION_REQUESTEDevent (see discussions_notifications.py for an example).Implement a grouper class to modify content_context.
Legal#
When adding a new notification type, you will need a Privacy threshold assessment done by legal.
Troubleshooting#
If you have followed the above steps and notifications are still not working, check if the notifications.enable_notifications waffle flag is enabled.
How to override default notification preferences#
This document explains how to override notification preferences defaults for new users who sign up on the platform. Learn more about notification, preferences and defaults here.
Note
These overrides will only apply to new users who sign up after the override has been configured.
Definitions#
- Notification
An alert that goes out to the user as a result of some activity e.g. “Mark responded to your post” etc.
- Notification Preference
A per-user setting for each notification type (web, email etc.). This is explained in the table below.
- Notification apps
For organization purposes, notification preferences are grouped by apps/platform workflows that they are related to. As of now, we have 3 apps: discussions, grading, and updates. Note that there is no app-level on/off switch for notifications types in it.
- App-level defaults / Bundled preferences
Each app discussed above has a set of app-level defaults (bundled preferences). A notification type marked use_app_defaults: True inherits its preferences from the app’s app-level defaults instead of using its own default preferences. This enables grouping several notification types under one row of preferences visible to the user. For example, in case of discussions, 7 notification types are controlled by the app-level defaults of the discussions app, which appears on the Account Settings page as a single row labeled “Activity Notifications”. This reduces clutter on the settings page.
What you can override#
The table below lists all the preferences that you can override for each notification along with possible values. You can override defaults for both using the following dictionaries in your lms.yml, cms.yml, or settings.py:
Use
NOTIFICATION_TYPES_OVERRIDEto override defaults for individual notification types.Use
NOTIFICATION_APPS_OVERRIDEto override app-level defaults for notification types marked asuse_app_defaults: True. Use the internal keys listed below (web,email,push,email_cadence,non_editable).
Key |
Type |
Possible values |
What it does |
|---|---|---|---|
web |
bool |
True OR False |
Determines if the user gets notifications in the tray. |
bool |
True OR False |
Determines if the user gets notification in email. |
|
push |
bool |
True OR False |
Determines if user gets push notification in mobile apps (not implemented) |
email_cadence |
string |
‘Immediately’ OR ‘Daily’ OR ‘Weekly’ OR ‘Never’ |
Determines when a user receives email notification. |
non_editable |
list of strings |
Any subset of [‘web’,’email’,’push’] |
Determines toggles of which of the 3 channels will not be editable by the user. |
Notification keys are listed in the table below. More notifications may be added in the future. You can find notification keys in this code
# |
Notification app |
Notification key |
uses app defaults |
Appears on Account Settings page as |
|---|---|---|---|---|
1 |
discussion |
new_response |
True |
Activity Notifications |
2 |
discussion |
new_comment |
True |
Activity Notifications |
3 |
discussion |
new_comment_on_response |
True |
Activity Notifications |
4 |
discussion |
response_on_followed_post |
True |
Activity Notifications |
5 |
discussion |
comment_on_followed_post |
True |
Activity Notifications |
6 |
discussion |
response_endorsed_on_thread |
True |
Activity Notifications |
7 |
discussion |
response_endorsed |
True |
Activity Notifications |
8 |
discussion |
content_reported |
False |
Reported content |
9 |
discussion |
new_question_post |
False |
New question posts |
10 |
discussion |
new_discussion_post |
False |
New discussion posts |
11 |
discussion |
new_instructor_all_learners_post |
False |
New posts from instructors |
12 |
updates |
course_updates |
False |
Course updates |
13 |
grading |
ora_staff_notifications |
False |
New ORA submission for staff |
14 |
grading |
ora_grade_assigned |
False |
ORA grade received |
Example configurations#
Example configuration for overriding notification preferences
NOTIFICATION_TYPES_OVERRIDE = {
# Turn off tray and turn on email notifications for new discussion posts and set daily cadence.
'new_discussion_post': {
'email': True,
'web': False,
'email_cadence': 'Daily',
},
# Turn off email notifications for "Course Updates" and prevent users from changing it.
'course_updates': {
'email': False,
'non_editable': ['email'],
}
}
Example configuration for overriding app-level defaults for notifications marked as use_app_defaults: True
NOTIFICATION_APPS_OVERRIDE = {
# For the 'discussion' app, set tray on and email off for all app-default notifications.
'discussion': {
'email': False,
'web': True,
'email_cadence': 'Immediately',
}
}
Why isn’t my override working?#
See if you are using the exact key name (e.g.
new_discussion_postand notNew_discussion_post).If a notification is marked as
use_app_defaults: Truein the code, it will ignore overrides inNOTIFICATION_TYPES_OVERRIDE. You must override usingNOTIFICATION_APPS_OVERRIDEinstead.
See also
ORA Reminder Notifications — configure periodic nudges for learners who have not completed peer or self review steps in ORA assignments.
Maintenance chart
Review Date |
Reviewer |
Release |
Test situation |
2026-05-05 |
Ahtisham Shahid |
Verawood |
Pass |
2026-04-30 |
Sara Burns |
Verawood |
Pass |
2025-12-18 |
BTR WG |
Ulmo |
Pass |