An internal email notification service for your Jellyfin server. It replaces ad-hoc messaging with:
- a new-media newsletter on a schedule you choose (weekly on a given day, monthly, every N days, or daily), with cover art and links back to each title;
- a personalised weekly recap for each user: what they watched this week, up to three titles to pick up where they left off, and their running total for the year;
- new-episode notifications for the series each user follows, with a bell on the series page;
- announcements written from the dashboard, sent now or at a date you pick, as plain text, full HTML, or laid out with the Easy Notif template;
- incident alerts for the admin: a banner on the config page and, if you set an address, one digest email a day when something went wrong.
Each user picks which categories they want, which libraries feed their newsletter, and sets their own contact address. Every email respects what each user is allowed to see in Jellyfin. The plugin reads the library and playback data only. It never changes anything.
Part of the JellyUX plugin family.

The Settings tab, grouped in sections. The Status, Campaigns, Templates, Manual email, User
preferences and Logs tabs sit alongside it.

The Templates tab: clone a base template, edit the HTML, preview it with sample data.
- Jellyfin 12.0 or later 12.x (tested on 12.4). Servers on Jellyfin 10.11 stay on Easy Notif 1.3.0, the last release for 10.11: Jellyfin keeps offering it to them and hides 2.x.
- The File Transformation plugin, used to inject the per-user settings panel, the series bell and the invitation banner into the web client. Without it they do not appear; the rest of the plugin still works. Install it from its catalogue: Jellyfin picks the build that matches your server. Reinstall or update it after every Jellyfin upgrade.
- A Resend account (free tier is enough) and a domain you can add DNS records to. You will publish SPF, DKIM and DMARC records for a dedicated sending sub-domain, and create an API key with Sending access scoped to that sub-domain.
- Jellyfin dashboard: Plugins > Repositories > Add, paste one of:
or, for the whole JellyUX suite:
https://raw.githubusercontent.com/JellyUX/Easy_Notif/main/manifest.jsonhttps://raw.githubusercontent.com/JellyUX/.github/main/manifest.json - Plugins > Catalog, install Easy Notif, restart Jellyfin.
- Open Dashboard > Plugins > Easy Notif.
Easy Notif 1.3.0 runs on 10.11 and 12; 2.0.0 and later need 12. Follow these steps in order:
- Update Easy Notif to 1.3.0 while the server still runs 10.11.
- Pause the newsletter. On the Campaigns tab, newsletter card, tick Send nothing on the next scheduled run and save; the Status tab then shows "nothing will be sent". The next scheduled run goes by without sending anything (what was added before it is never announced), then the box unticks itself. If Jellyfin is still scanning the library when that run comes, tick it again. Run now ignores it: do not use it during the upgrade.
- Back up Easy Notif by hand. Jellyfin's own backup covers neither of these, copy both:
- the data directory
<jellyfin-data>/Jellyfin.Plugin.EasyNotif/(/config/data/...in the official Docker image): preferences, followed series, history, secrets; - the settings file
<jellyfin-config>/plugins/configurations/Jellyfin.Plugin.EasyNotif.xml(/config/plugins/configurations/...in Docker): sender, time zone, quotas.
- the data directory
- Remove the plugins, as the Jellyfin 12.0 release notes ask. Uninstalling does not delete the data directory or the settings file: keep them.
- Mount your media at the same paths before the first start in 12. On that start, Jellyfin deletes the items whose file it cannot find when the library folder is reachable.
- Start Jellyfin 12, then install Easy Notif and File Transformation again from the catalogue and restart. The catalogue now offers Easy Notif 2.x. Both must show Active under Plugins; without File Transformation the settings panel and the bell do not appear, and a warning says so at the top of the Easy Notif config page.
- Check the Status tab. Series that Jellyfin no longer finds are counted as orphaned subscriptions.
Keep the time between steps 4 and 6 short: while Easy Notif is not installed, the unsubscribe links of the emails already sent answer 404.
Add a dedicated sub-domain in Resend (for example send.example.com, not the root domain, so
your existing mail is never touched). Resend gives you an MX record, an SPF TXT and a DKIM TXT;
add them at your DNS host as plain records, not proxied, then add a DMARC TXT on
_dmarc.send.example.com (v=DMARC1; p=none; rua=mailto:you@example.com). Click Verify until
all records are green.
- API Keys > Create API Key: permission Sending access, scoped to the sub-domain. Copy the
re_...value once. - Webhooks > Add Endpoint: URL
https://<your-jellyfin>/EasyNotif/webhooks/resend, eventsemail.sent,email.delivered,email.delivery_delayed,email.bounced,email.complained,email.suppressed. Copy thewhsec_...Signing Secret.
The webhook is what makes bounces and complaints count: an address that bounces permanently (or that Resend suppresses) is no longer mailed until its owner saves it again, and a spam complaint turns off every category for that user.
Fill in the Settings tab of the plugin config page. The fields are grouped in sections:
| Section | Field | Value |
|---|---|---|
| Sending | Sender address | any address on the verified sub-domain (it does not need a real mailbox) |
| Sender name | what recipients see as the sender | |
| Reply-To address | optional; a real address if you want replies to reach you | |
| Resend API key | the re_... key (stored in the plugin's data directory, masked on read, never logged) |
|
| Webhook signing secret | the whsec_... secret |
|
| Server | Public server URL | your Jellyfin's public URL, used for poster images, deep links and unsubscribe links. Leave blank to attach the posters to the email instead (emails then carry no unsubscribe link) |
| Schedule time zone | the time zone of every schedule (Europe/Paris by default; UTC if the zone is unknown) | |
| Newsletter | Movies listed / Series listed | 1 to 200 each (30 by default); beyond that, a section ends with "and N more" |
| Sending limits | Per day / Over 30 days | your Resend plan's quotas (100 and 3000 on the free plan; 0 per day if your plan has no daily limit) |
| Incident alerts | Address and language | where the daily incident digest goes; leave empty to rely on the banner of the config page |
| Web interface | Three switches | add the settings panel to Jellyfin Web, show the follow bell on series pages, invite users without a contact email (each takes effect after a Jellyfin restart) |
The Status tab shows the Resend transport, the sends of today and of the last 30 days, the last webhook received, each campaign's next run and last error, the background writers, the incidents of the last 7 days and the latest sends (with masked addresses).
- Campaigns tab: the newsletter and the weekly recap ship disabled. Set a schedule, pick a language and (optionally) a custom template, enable it. Send a preview to me mails the current content to your own contact address; for the recap, it is filled with your own viewing.
- Templates tab: the built-in templates are read-only. Clone the newsletter or the recap, edit the HTML, preview it with sample data, and point a campaign at your copy. The new-episode and announcement templates can be previewed but not cloned. Placeholder reference and the template mini-language: docs/CUSTOM_TEMPLATES.md.
- Manual email tab: an announcement, plain text or HTML, with attachments up to 10 MB. Tick Lay out with the Easy Notif template to wrap it in the house style (French or English). All users reaches everyone with a contact address except the users who turned off announcements; Selected users reaches the users you tick, whatever their preferences, and carries no unsubscribe link. Send it now, or schedule it for a later date (without attachments); upcoming announcements are listed and can be cancelled.
- User preferences tab: every user's contact address and categories (new media, weekly recap, announcements, new episodes) with their number of followed series. You can switch new media and the weekly recap for a user; the rest is set by each user.
- Logs tab: the plugin's own log file, filterable by level.
- Settings panel (in each user's Jellyfin settings, under Email notifications): contact address, the four categories, the libraries included in the newsletter, the followed series (with a Remove button), a test-send button.
- Invitation banner: a user with no contact address sees a banner on the home page offering to set one up, until they close it or follow it.
- Unsubscribe: every campaign email carries a one-click
List-Unsubscribeheader and a footer link to a small confirmation page, as long as the public server URL is set. Without it, emails carry neither, and the Settings tab says so. With anhttp://URL, the header and the link stay but the header is not one-click (RFC 8058 requires https). Changes take effect immediately.
The recap lists the week's movies and episodes, up to three titles in progress (started, not finished, watched in the last 30 days) and the total for the year. On a server where Easy Notif arrives mid-year, the yearly total starts from what Jellyfin already marks as watched this year: each such title counts once, and the email says it is an estimate.
- Following a series: open a series page in Jellyfin Web and click the bell in the action bar. Click it again to stop. The bell only appears on series the user can see. The followed series are listed in the user's settings panel, where each one can be removed.
- What is sent: one email per user, grouping every new episode of every followed series, with the series poster and a link to its page. A series with more than 5 new episodes is summed up in one line; specials are labelled as such. An episode already notified, or a file that only replaces one already in the library (an upgrade or a move), is not announced again.
- Delay: the email leaves once no new episode has arrived for 30 minutes, so episodes added a few minutes apart end up in the same email. With the 15-minute check, expect it 30 to 45 minutes after the last episode (at most 6 hours after the first one when episodes keep arriving).
- Limits: at most 3 new-episode emails per user per day (UTC). They never use the share of the daily Resend quota held back for a campaign due the same day; what is held back goes out later.
- Stopping: each series in the email has its own "Stop following this series" link, and the
footer has a link that stops every new-episode email (also sent as the
List-Unsubscribeheader). Users can also untick New episodes in their settings panel. A user with no contact address gets nothing; the bell's tooltip says so. - Turning it off: on the Settings tab, untick Show the follow bell on series pages (takes effect after a Jellyfin restart). Existing subscriptions keep working.
- A send that fails for a temporary reason (network, Resend overloaded or rate limited) is retried 15 minutes, 1 hour, 4 hours and 12 hours later, then given up and reported as an incident. Each email carries an idempotency key, so a retry never mails the same person twice.
- A large manual send runs in the background: you can close the page, follow its progress later and retry only the failures.
- When Resend refuses a send for quota, every send pauses until the next midnight UTC.
- A newsletter does not announce a title again within six months, even when a file is replaced or a movie is added a second time.
Every piece of state the plugin keeps lives in one directory,
<jellyfin-data>/Jellyfin.Plugin.EasyNotif/ (preferences, followed series, campaigns and scheduled
announcements, playback history, custom templates, send log and retry queue, quota, incidents,
secrets, logs). The only other files are the resized posters, which Jellyfin's own image processor
keeps in Jellyfin's image cache like any other cached image. Contact addresses go to Resend
(United States) to send the mail; nothing is sent to any other third party. Addresses and user ids
are masked in the logs.
What is stored about users, and for how long:
| Data | File | Kept |
|---|---|---|
| Contact address, categories, newsletter libraries | preferences.json |
until the user clears it or the administrator deletes the file |
| Followed series | series-notify.json |
until the user unfollows or the administrator deletes the file |
| Viewing history (titles watched, every user, for the weekly recap) | playback-history.json |
90 days |
| Send log (masked address, user id, subject, status) | send-log.jsonl |
last 500 entries |
| Incidents | incidents.json |
7 days |
| Activity logs (addresses and ids masked) | logs/ |
the Jellyfin activity log retention, 30 days by default |
| Unsubscribe links in sent emails | (signed, not stored) | valid 180 days |
Only the server administrators can read these files (file system, admin page). Each user sees and edits their own address and choices in their Jellyfin settings.
Uninstalling the plugin does not delete this directory: the data stays on the server until the administrator deletes it. To remove everything: uninstall the plugin and delete that directory. The server is then exactly as it was before.
Once the plugin is uninstalled, the unsubscribe links of the emails already sent (valid 180 days) answer 404. Before a permanent uninstall, disable every campaign so that no new email carries such a link.
docker/scripts/smoke-test.ps1 builds the plugin and checks that it loads in a real, disposable
Jellyfin container. It needs Docker and Windows PowerShell; run it from the repository root:
powershell -File docker/scripts/smoke-test.ps1 -JellyfinVersion 12.4The script starts jellyfin/jellyfin:<version> (pulled if missing) on port 8100 with no host
mount, completes the startup wizard through the API, installs File Transformation from its
catalogue (Jellyfin picks the build that matches its version), copies the local Release build in
and restarts. It then checks that Easy Notif and File Transformation are Active, that the
anonymous /EasyNotif/* assets answer 200, that the served index.html loads enotif-user.js,
and that the Jellyfin log since the restart has no [ERR] line naming EasyNotif.
It prints a PASS/FAIL table and the run time, and exits with code 1 if any check fails. The
container is removed at the end; -Keep leaves it running and prints the admin credentials,
-Port picks another port. No email can be sent: the container has no Resend key.
GPL-3.0. See LICENSE.md.