Skip to content

Notifications

Nine delivery channels, one editor, one routing config. Subscribe each provider to whichever events you actually want, set per-provider quiet hours and a daily digest, customise templates per language. And a tenth destination that never leaves the farm: the notification centre behind the sidebar Bell, which fills for each person who logs in whether or not a single provider is configured.


Supported Providers

Provider Setup Features
Telegram Medium Via the BamDude bot, with actionable inline buttons (clear plate, mark maintenance done, pause/stop). Routes to every authorised chat that subscribed to the event.
Discord Easy Channel webhook URL, embed formatting, image attachments.
Email (SMTP) Medium STARTTLS / SSL / plain. Per-provider to_email so different users see different bodies.
Pushover Easy Priority levels, image attachment up to 2.5 MB.
ntfy Easy Topic-based, optional bearer token, image attachments.
Bark Easy iOS-only, no account. Interruption levels — Critical delivers through Silent mode and Focus. Public relay or your own bark-server.
CallMeBot Easy WhatsApp / Signal bridge — phone + API key, URL-encoded message.
Signal CLI API Medium Self-hosted signal-cli-rest-api — recipient numbers or one group, image attachments.
Home Assistant Easy persistent_notification.create or any notify.* service. Single global HA URL/token from Settings (or HA_URL / HA_TOKEN env).
Webhook Flexible Generic JSON or Slack-format POST, custom field names, base64 image, optional bearer token.

Adding a Provider

  1. Go to Settings > Notifications
  2. Click Add Provider
  3. Select provider type and enter configuration
  4. Click Send Test to verify
  5. Configure event triggers
  6. Click Add

Every provider subscribes to events on its own, and the form offers all of them, grouped exactly as the notification centre groups them — print, printers, filament, AMS, queue, inventory, sensors. What you see ticked when you add a provider is what it will be saved with.


Per-Provider Setup

ntfy

Topic-based, free, no account needed. The simplest channel to bring online.

Field Value
Server https://ntfy.sh (default) or your self-hosted instance URL
Topic A unique string — anyone who knows it can publish, so use something unguessable
Bearer token Optional; required for self-hosted ACL-protected topics

Subscribe on your phone with the ntfy Android or iOS app. ntfy supports 5 priority levels that BamDude maps per event:

Priority ntfy value Typical use
Min 1 Diagnostics-style pings — no sound, no badge
Low 2 Informational, non-urgent (e.g. "first layer complete")
Default 3 Standard notification
High 4 Audible / urgent (e.g. "filament low")
Urgent 5 Wakes the device, ignores Do-Not-Disturb (e.g. "print failed")

WhatsApp / Signal (CallMeBot)

Free WhatsApp / Signal bridge — no own bot infrastructure needed.

  1. Add CallMeBot to your contacts: +34 644 51 95 23
  2. Send I allow callmebot to send me messages via WhatsApp
  3. CallMeBot replies with your API key
Field Value
Phone number Your number in E.164 format (e.g. +1234567890)
API key The key CallMeBot returned

Discord

Channel webhook URL — easiest way to get rich embed messages with thumbnails into a Discord server.

  1. In Discord open the target channel's settings → Integrations → Webhooks
  2. Click New Webhook, customise name/avatar, Copy Webhook URL
  3. Paste the URL into BamDude's provider form

BamDude posts as embeds with the snapshot image inline when one is available.

Pushover

Per-user push service with native iOS/Android apps and on-device priority escalation.

  1. Create an account at pushover.net and install the app
  2. Create an Application in your dashboard
Field Value
User key From your Pushover account page
API token From the Application you just created

Pushover priority maps to numeric levels -2…+2 per event in BamDude — same idea as ntfy but with the Pushover scale.

Priority 2 (Emergency) needs two extra fields

Pushover mandates a retry interval and an expiry for Emergency alerts — they re-alert until you acknowledge them, so it refuses any priority-2 message that doesn't say how often and for how long. Set Priority to 2 and two fields appear:

Field Meaning Default
Emergency Retry (s) How often Pushover re-alerts 60 s (min 30 s)
Emergency Expire (s) When it gives up 3600 s (max 10800 s)

They're only sent at priority 2 — Pushover ignores them at every other level. Before 0.4.7b4 BamDude never sent them at all, so setting a provider to Emergency made every notification fail.

Bark (iOS)

Bark is a free iOS push app with no account at all — install it, copy the device key it shows you, paste it here. Nothing else is required.

Field Value
Device Key The key the Bark app shows on its first screen — required
Server https://api.day.app (default) or your own bark-server
Group Optional — puts BamDude's notifications in their own group in the app
Sound Optional — one of the sound names the Bark app lists
Interruption Level Default / Active / Time Sensitive / Critical

Critical is the reason to have Bark next to ntfy and Pushover

Level What iOS does with it
Critical Delivered through Silent mode and through Focus — the print that stopped at 03:00 gets through
Time Sensitive Breaks through scheduled summaries, without going that far
Active A normal notification
Passive Arrives with no sound — recorded, not announced

Wire print failed and filament runout to a Critical Bark provider and leave everything else on a Default one, rather than making every event wake you.

A self-hosted bark-server is subject to the same address rules as a self-hosted ntfy: on your own network is fine, anything that is not a real HTTP service is refused. If it sits behind a Cloudflare challenge, BamDude says so instead of dumping the challenge page at you.

An 'OK' that isn't one

bark-server reports some failures inside an HTTP 200 body. BamDude reads the body rather than trusting the status, so a wrong device key is reported as a failure in Send Test instead of showing as sent and never arriving.

SMTP / Gmail

Plain SMTP — works with any provider that exposes username + password auth.

Field Example
SMTP host smtp.gmail.com
Port 587 (STARTTLS) or 465 (SSL)
Security STARTTLS / SSL / plain
Username Your full email address
Password App password (not the account password — Gmail rejects the latter)
From address The sender address recipients see
To address Per-provider; lets different team members get different bodies

For Gmail: enable 2FA, then generate an App Password and use that here.

Home Assistant

Zero-config when HA is already wired up under Settings → Network → Home Assistant (or the HA_URL / HA_TOKEN env vars). Left empty, events become persistent_notification.create calls in your HA dashboard.

Field Value
Service Optional — any HA service, e.g. notify.mobile_app_myphone. Accepted as notify.x, notify/x or api/services/notify/x. Empty = persistent_notification.create
Data (JSON) Optional — a JSON object forwarded as HA's nested data, the same place an automation puts it

The Data field is what makes an Android push behave. HA's mobile-app integration takes its options there, not in the title and message:

{ "priority": "high", "ttl": 0, "channel": "BamDude" }

priority: high + ttl: 0 are what make a notification arrive immediately rather than whenever the phone next wakes; channel gives printer alerts their own sound instead of burying them among everything else HA sends.

Why JSON and not key=value lines

So ttl: 0 stays the number HA expects, and so nested options are possible at all. It is validated when you save it and again when it is sent — a malformed field is refused in front of you rather than becoming a notification that quietly never arrives.

Leave it empty and nothing changes: it is only sent when filled in, because persistent_notification.create rejects extra keys.

Forward HA notifications to other channels

Use HA automations to mirror these persistent notifications to the HA Companion app, Telegram, ntfy, etc. — gives you a single audit log in HA plus your usual mobile push.

Signal CLI API

Signal messages through a signal-cli-rest-api instance you host yourself. The provider is named for what it talks to: there is no official Signal API, and BamDude does not pretend to be one. Register a number with signal-cli first (that project's README walks through it); BamDude then sends from that number.

Field Value
Signal API URL The base URL of your signal-cli-rest-api, e.g. http://192.168.1.50:8080. Pasting the full /v2/send endpoint from its docs is tolerated — BamDude strips it back to the base.
Sender Number The number registered with signal-cli, in E.164 form (+15551234567)
Recipient Type Phone Numbers — one or more recipients, added row by row — or Group ID — a single Signal group. signal-cli-rest-api cannot mix the two in one request, so a provider is one or the other; add a second provider if you want both.
Group ID The group's id as signal-cli lists it; a bare id gets the group. prefix for you
Authorization Optional — the Authorization header value when the API sits behind an authenticating reverse proxy (Bearer …, or just the token)

Print-finish photos attach to the message. The URL is subject to the same address rules as a self-hosted ntfy or bark-server: your own network is fine, anything that is not a real HTTP service is refused.

Running BamDude in Docker with no signal-cli-rest-api yet? The shipped docker-compose.signal.yml runs one next to BamDude and docker-install.sh offers it — see Docker → Signal notifications sidecar.


Generic Webhook

For everything else — n8n, Node-RED, custom HTTP endpoints, Slack-format integrations.

Field Value
URL Your endpoint (HTTPS recommended)
Headers Optional — use for Authorization: Bearer … and similar
Format generic (structured BamDude JSON) or slack ({"text": "..."} only)

See Webhook Payload Schema below for the structured-JSON shape.


Webhook Payload Schema

Generic-format webhooks send a standardised JSON envelope: title, message, timestamp, source, event (the event-type string), plus all event-specific fields hoisted to top-level keys so automation tools can branch on event without parsing the message text.

print_complete:

{
  "title": "Print Complete",
  "message": "Workshop X1C: benchy.3mf completed in 2h 15m",
  "timestamp": "2026-04-02T14:30:00.123456",
  "source": "BamDude",
  "event": "print_complete",
  "printer": "Workshop X1C",
  "filename": "benchy.3mf",
  "duration": "2h 15m",
  "filament_grams": "15.2",
  "filament_details": "AMS-A T1 PLA: 15.2g"
}

print_failed (and print_stopped) carry extra progress + reason fields:

{
  "title": "Print Failed",
  "message": "Workshop X1C: benchy.3mf failed at 50%",
  "timestamp": "2026-04-02T15:15:00.123456",
  "source": "BamDude",
  "event": "print_failed",
  "printer": "Workshop X1C",
  "filename": "benchy.3mf",
  "duration": "0h 45m",
  "filament_grams": "7.6",
  "filament_details": "PLA: 7.6g",
  "progress": "50",
  "reason": "Filament runout"
}

printer_offline — minimal payload, only what's relevant:

{
  "title": "Printer Offline",
  "message": "Workshop X1C is offline",
  "timestamp": "2026-04-02T14:30:00.123456",
  "source": "BamDude",
  "event": "printer_offline",
  "printer": "Workshop X1C"
}

first_layer_complete — includes a base64-encoded JPEG snapshot in the image field:

{
  "title": "First Layer Complete",
  "message": "Workshop X1C: benchy.3mf — Layer 1/200 done",
  "timestamp": "2026-04-02T14:30:00.123456",
  "source": "BamDude",
  "event": "first_layer_complete",
  "printer": "Workshop X1C",
  "filename": "benchy.3mf",
  "total_layers": "200",
  "image": "/9j/4AAQSkZJRg..."
}

Decoding the image

The image field is a standard base64-encoded JPEG. Home Assistant: pass it to notify.mobile_app_* as image data via a template. Node-RED: Buffer.from(msg.payload.image, 'base64'). The field is only present when a snapshot was actually captured — not all events include it.

Slack / Mattermost format compatibility

With format = slack, only {"text": "..."} is sent — structured event fields are dropped. Use the generic format for any automation that needs to read structured data; use slack only for human-readable channel posts.


Event Triggers

Each provider subscribes independently. Toggling an event off on one provider doesn't stop it on others.

Print:

Event Fires when
print_start Print starts on a printer
first_layer_complete Layer 1 finishes (catch first-layer fails fast)
print_progress At 25% / 50% / 75% progress. A minimum duration floor can mute these for prints estimated shorter than N minutes — and the value belongs to each recipient: every Telegram chat carries its own (beside the Progress Milestones checkbox in the chat's settings) and every other provider carries its own (on its card, beside the same toggle). Empty or 0 = always send; there is no shared global value. The duration is estimated from the printer's own remaining-time report at each milestone, and an unknown estimate sends rather than guesses away.
print_paused Printer transitioned RUNNING→PAUSE — body carries a normalised {reason} (door open / filament runout / presence-check / file-pause-command / AI defect / plate-objects / paused by user / HMS-other) plus the underlying {hms_code} for forensics. Default ON for new providers + included in the default Telegram-chat event set.
print_resumed Printer transitioned PAUSE→RUNNING — body carries {paused_for} (mm:ss) computed from the matching pause edge. Default ON for new providers; opt-in for Telegram chats.

The pause-state is also visualised on the Printers page in real time — a paused card surfaces a small <PauseChip> next to the status badge with the classified reason and a live mm:ss counter, plus a yellow warning pip in the corner. The chip disappears the moment the printer resumes; the resume event keeps the same {paused_for} value the notification body carries.

| print_complete | Print finishes successfully | | print_failed | HMS error / hardware fault stopped the print | | print_stopped | User-initiated stop | | bed_cooled | Bed cooled to threshold (post-print cleanup signal) |

AMS / filament:

Event Fires when
print_missing_spool_assignment Print started without complete spool→AMS mapping
filament_low Spool remaining below low_stock_threshold (or the spool's own override). A spool bound to your inventory is checked when a print's consumption lands, once per run-down; a Spoolman-bound slot is checked on every AMS change, once per spool in a slot. A slot with no inventory binding is never judged by the printer's own counter — a spool without an RFID tag reports 0 %, which is no answer — so an unbound slot stays silent.
ams_humidity_high / ams_temperature_high AMS exceeds its threshold
sensor_above_max / sensor_below_min A sensor reading left the limits set for it
sensor_back_in_range …and came back
sensor_silent / sensor_speaking_again A sensor stopped reporting, and started again

Printer:

Event Fires when
printer_offline MQTT disconnect
printer_error HMS error code triggered (BamDude includes the human-readable translation)
ai_failure_detection AI failure detection (Obico) flags a likely print failure — opt-in, off by default. Split out of printer_error so you can be paged on AI alerts without every HMS hardware code. It has its own template, its own per-provider toggle, and its own per-chat Telegram item; the body carries the printer, job name, confidence score, and the action BamDude took (notify / pause / pause + power off).
plate_not_empty Bed-occupancy gate caught the next-print start (auto-pause)
maintenance_due Scheduled maintenance interval reached

Queue:

Event Fires when
queue_job_added / queue_job_started / queue_job_waiting / queue_job_skipped / queue_job_failed Self-explanatory queue lifecycle events. Only the events you opt into.
queue_completed The entire install's queues drain — fires only once every printer is idle with nothing pending.
printer_queue_completed An individual printer's own queue drains — fires the moment that printer finishes its last pending job, regardless of what other printers are doing.

Global vs. per-printer queue completion

On a single-printer setup the two are equivalent. On a multi-printer farm they differ: queue_completed waits for every printer to go idle, so a long job on one printer suppresses it for all the others. printer_queue_completed fires per printer the moment that printer's own queue empties — pick this one if you want a "this machine is free, load the next plate" ping per printer. printer_queue_completed is ON by default for new providers; queue_completed is off by default.

User / system:

Event Fires when
user_created, password_reset Account-management emails (HTML + plain).
user_print_start / user_print_complete / user_print_failed / user_print_stopped Per-user email notifications when the user owns the print.
test Validation send from the provider editor.

Actionable Telegram Notifications

When using Telegram as a notification provider, BamDude sends actionable notifications with inline buttons:

Event Actions
Print Complete Clear plate button
Maintenance Due Mark done button
Print Progress Pause / Stop buttons

See Telegram Bot Setup for full configuration.

Per-chat event routing

Telegram notifications are not routed to a single hard-coded chat -- they are fanned out to every authorized chat whose telegram_chats.notification_events setting includes the firing event. So one chat can subscribe to "Print Complete" + "HMS Error" only, while another chat takes everything. Configure each chat's subscriptions under Settings > Notifications > Telegram Chats.

Localized templates per user

Notification bodies are rendered from notification_templates_{en,uk}.json. The template language is picked per-recipient -- Telegram uses the chat's owning user's settings.language, email uses the recipient user's language, etc. Adding a new template key means updating both en and uk JSON files (BamDude ships en + uk only).


Per-event priority (ntfy & Pushover)

Both ntfy and Pushover support priority levels — default / high / urgent for ntfy, -2…+2 for Pushover. BamDude lets you pick the priority per event type on each provider, so a finished print doesn't push to the lock-screen but a print failure does:

Event type Suggested ntfy priority Why
print_complete, bed_cooled default Informational — read when convenient.
print_failed, printer_error, plate_not_empty high or urgent Action-required.
filament_low, maintenance_due default Plan-ahead, not interrupt-now.
ams_humidity_high high Affects filament you're about to use.

Configure under each provider's edit form: there's a per-event priority dropdown next to the event-subscribe toggle. Defaults map every event to default priority — opt-in to escalation only where it matters. Pushover's same control accepts the numeric levels.

This is independent of the daily digest / quiet hours pipeline below — a quiet-hour-suppressed event isn't sent at any priority; an active event still respects the per-event priority you picked.


Quiet hours & daily digest

Configuration shape varies by provider type — the Telegram bot is special.

Non-telegram providers (email / ntfy / pushover / discord / webhook / homeassistant / callmebot / signal) carry both settings on the provider row itself:

Setting Where Effect
quiet_hours_enabled + quiet_hours_start / quiet_hours_end Provider config Events that fire inside the window are dropped (not queued — quiet hours is "shut up", not "delay").
daily_digest_enabled + daily_digest_time Provider config Events that fire any time in the day are queued in notification_digest_queue; the next time the wall clock crosses daily_digest_time BamDude flushes the queue as a single digest message.

Telegram (m045) is structured differently: the bot/provider row keeps only the schedule (daily_digest_enabled + daily_digest_time), while the per-event opt-in, quiet hours, and digest opt-in all live on each TelegramChat row. So one chat can be in quiet hours while another stays loud, both fed by the same bot. See Telegram Bot Setup for the per-chat fields.


Template editor

Every event has a default template in data/notification_templates_{en,uk}.json. The Templates tab under Settings → Notifications lets you override any of them — title + body — with a MarkdownV2 toolbar and live preview.

The Templates tab groups the default templates by purpose so a glance tells you which dispatch path each one feeds:

Group Count What it's for
Print events 9 print_start/complete/failed/stopped/progress, plate_not_empty, bed_cooled, first_layer_complete, print_missing_spool_assignment
Printer status 4 printer_offline, printer_error, filament_low, maintenance_due
AMS environmental 2 ams_humidity_high, ams_temperature_high (also reused at runtime for the AMS-HT events)
Sensor alerts 5 sensor_above_max, sensor_below_min, sensor_back_in_range, sensor_silent, sensor_speaking_again — see Environment Sensors
Print queue 7 queue_job_added/started/waiting/skipped/failed, queue_completed, printer_queue_completed
Job owner emails 4 user_print_start/complete/failed/stopped — SMTP-only, sent to the print job owner
System emails 2 user_created (welcome), password_reset
Test 1 test — used by the "Send test" buttons

Each card carries a small UPPERCASE channel badge:

  • Green ALL — fan-out to every provider type that wants the event (TG / email / ntfy / pushover / discord / webhook / homeassistant / callmebot / signal). The entries in the first 4 groups.
  • Blue EMAIL — SMTP-only flow. The 4 user_print_* job-owner emails plus user_created / password_reset.
  • Amber TEST — internal test-button helper.

The mapping is metadata about which dispatch path consumes each template; it's not stored on the row, just rendered from a static lookup in the frontend.

Variable substitution uses simple curly-brace placeholders ({printer_name}, {filament_grams}, {eta}, etc.); the schema is locked per-event so the editor warns when a placeholder doesn't resolve.

Templates are picked per recipient language: a Telegram chat owned by an operator with settings.language=uk gets the Ukrainian body; an email to a different user with settings.language=en gets the English one. Add new keys to both JSON files — BamDude ships en + uk only.


Daily Digest example

When a provider has daily_digest_enabled + daily_digest_time set, every event that fires during the day is queued and bundled into one summary message at the digest time:

Daily Print Summary (Apr 14)

3 prints completed
1 print failed
Total time: 8h 45m
Filament used: 245g

Details:
- Benchy (2h 15m) - completed
- Phone Stand (45m) - completed
- Cable Clip (15m) - completed
- Prototype v3 (3h 30m) - failed

The digest message respects the same template language pick as immediate notifications — Telegram chats owned by uk-language operators get the Ukrainian summary, an English-language email recipient gets the English one.


Message Template variables

Templates substitute {variable} placeholders. The schema is locked per event, so the template editor warns when an unknown placeholder is used. Variables are grouped by event category:

Print events (print_start, print_complete, print_failed, print_stopped, print_progress):

Variable Meaning
{printer_name} (alias {printer}) Printer display name
{print_name} (alias {filename}) The file currently printing
{progress} Completion percentage (failed/stopped only)
{eta_minutes} / {eta} Wall-clock completion time
{estimated_time} Estimated print duration (e.g. 1h 23m)
{duration} Actual elapsed print time
{filament_used_g} (alias {filament_grams}) Total grams (scaled by progress for failures)
{filament_details} Per-spool breakdown (e.g. AMS-A T1 PLA: 15.2g)
{material} Aggregate material name
{reason} Failure reason (failed/stopped only)
{finish_photo_url} Camera snapshot URL (see below)

Printer events (printer_offline, printer_error):

Variable Meaning
{printer_name} Printer display name
{error_code} (alias {error_type}) HMS error code
{error_message} (alias {error_detail}) Human-readable description (BamDude translates the 853-code catalogue)

AMS events (ams_humidity_high, ams_temperature_high, filament_low, print_missing_spool_assignment):

Variable Meaning
{ams_id} The AMS unit (AMS-A, AMS-B, …)
{slot} Tray index (T1–T4)
{material} Material assigned to the slot
{remaining_percent} Filament left (filament_low)
{humidity} Humidity percentage (humidity events)
{missing_slots} Comma-separated slot labels (A1, A3) for print_missing_spool_assignment
{missing_slot_details} Per-slot breakdown with expected profile (- A1: PLA Basic)

Common to every event: {timestamp}, {app_name} (always "BamDude").

Click Reset to default in the editor to restore the original template from notification_templates_{en,uk}.json.

Finish Photo URL

The {finish_photo_url} placeholder puts a camera snapshot of the finished plate into completion / failure notifications. It needs a reachable external URL to work:

  1. Settings → System → External URL — set it to the address recipients can reach (e.g. https://bamdude.example.com or http://192.168.1.100:8000)
  2. The setting auto-detects from your browser the first time you open System settings
  3. Edit your template and add {finish_photo_url} wherever you want the photo

Email inlines the photo, not just a link

For email providers, when the rendered body contains the substituted {finish_photo_url} and a finish photo was actually captured, BamDude sends the message as a multipart/related email with the JPEG embedded inline (via a Content-ID referenced from the HTML part) — the photo shows in the mail body itself, not as a bare link. The plain-text alternative still carries the clickable URL for text-only clients. When the template doesn't reference {finish_photo_url} (or no photo exists), the original single-part text email is used — no surprise attachment. Non-email channels (WhatsApp / webhook / …) still receive the link, which is why the External URL below has to be reachable.

External URL prerequisite

Without a configured External URL the placeholder renders empty. Camera snapshots also gate on the stream-token camera flow — the URL embeds a short-lived token so recipients can fetch the JPEG without an Authorization header.


Quick Disable

A global mute toggle lives in the sidebar — click the bell icon to drop every outgoing notification across every provider until you click again. Useful during maintenance windows, demo runs, or noisy migrations where you don't want to flood the team chat.

The toggle does not delete digests-in-progress — events that fired into the digest queue before mute still flush at the next daily_digest_time. To hold a digest, disable the daily-digest toggle on the provider instead.


Per-Printer Filtering

Each non-telegram provider has a printer scope — All (default), one, or any subset, ticked on the provider form. Events from printers outside the picked set never reach that provider, regardless of event toggles. For Telegram the scope lives on each chat instead (see Telegram Bot → Printer scope), where it also scopes the bot itself — lists, cameras, queue and controls. Useful patterns:

  • One Discord webhook per workshop — each scoped to that workshop's printers
  • A "VIP printer" Telegram chat scoped to your one revenue-generating production unit
  • A maintenance-only ntfy provider scoped to printers due for filter changes / belt swaps

The notification centre

Every notification used to leave the farm — Telegram, e-mail, ntfy, Discord, whatever you had configured. An install with no provider at all had nowhere to put an alarm about its own hardware: the event fired, matched nobody, and was gone. The Bell in the sidebar now opens a per-user notification centre with three tabs, and the inbox fills whether or not a single provider is set up. That is the point of it — providers are how a notification reaches your phone, the inbox is how it reaches whoever logs in.

Read and unread are per person. One operator opening an alarm does not clear it for anybody else. That is the difference between an inbox and a shared log: everyone subscribed to an event has to deal with it themselves.

Inbox

The events you are subscribed to, newest first. Unread rows are marked; clicking a row marks it read and expands its full text. Every row carries:

Shown on the row What it tells you
Severity info, warning or error
Printer which printer the event came from
Age how long ago it happened

Four filters narrow the list — severity, printer, unread only, and the period: last 24 hours, last 7 days, last 30 days, or all time. A long history loads a page at a time behind a Show more button instead of all at once.

Mark all read and Clear act on the filter, not on the whole inbox

Both buttons apply to exactly what the filters currently select. With error + one printer + last 7 days picked, Clear deletes those rows and leaves everything else alone — and with no filters set at all, it deletes everything. Clear deletes; it asks for confirmation first. Single rows can also be deleted one at a time from the row itself.

Subscriptions

A checkbox per event, grouped by area — print jobs, printers, filament, AMS, queue, inventory, sensors — each shown with the severity it carries, so you can see what a subscription will cost you before you tick it.

By default a person receives warnings and errors only. The inbox stays quiet unless something actually wants attention, and Reset to defaults puts it back to exactly that after any amount of experimenting. Every change saves immediately — there is no Save button to forget.

Subscriptions belong to the person, not to the farm: two operators on the same install can watch completely different things, and neither one's choices touch the other's inbox.

Email

The four per-user e-mail switches that used to be this whole page, unchanged in behaviour — see Per-User Email Notifications below for what they send and when.

The tab appears only when Advanced Authentication is on, the User Notifications master switch is on, and you hold the notifications:user_email permission — the same three conditions as before.

The unread badge

The sidebar Bell carries a live unread count. It updates over the WebSocket connection the rest of the interface already uses, so a new event lands on the bell without a page refresh and without polling for it. On narrow screens the compact header carries the same bell and the same badge.

Permission

The notification centre requires the notifications:inbox permission.

Upgrade note — a custom group does not get it on its own

Administrators, Operators and Viewers all receive notifications:inbox on upgrade. A group you built yourself does not — an upgrade will not add permissions to a group somebody hand-made. Until an administrator grants it, that group's members lose the page entirely, including the per-user e-mail switches they had before. Walk your custom groups right after upgrading.

Retention

Settings → Data Management carries Inbox notifications (days).

Setting Default Range
Inbox notifications 30 days 1–365 days

The daily cleanup removes anything older than the window, whether or not it was read — an inbox nobody opens does not grow forever. Deleting a user removes that user's inbox along with them.


Per-User Email Notifications

Separate from the provider system above, BamDude can email the owner of a print directly when it completes / fails / stops — useful in shared / multi-tenant deployments where each user wants their own prints' mail in their personal inbox.

Requirements

  • Authentication enabled (it always is on 0.4.0+)
  • SMTP configured under Settings → System → Email
  • Settings → Notifications → User Notifications toggled on
  • The user has an email address on their account
  • The user holds the notifications:user_email permission (granted to Administrators + Operators by default — see Authentication)
  • The user holds the notifications:inbox permission — the switches are a tab of the notification centre, and without it the page cannot be opened at all

Supported Events

Event Fires on
user_print_start The user's print begins
user_print_complete Their print finishes successfully
user_print_failed Their print errored
user_print_stopped They cancelled their own print

Each user opts in and out of the four events individually on the Email tab of their own notification centre — the Bell in the sidebar, which used to open this list of switches and nothing else. Operators / admins control the global User Notifications master switch under Settings → Notifications.


Getting an action-required event through the night

Some events stall the farm until somebody acts on them:

  • plate_not_empty — a non-empty plate was caught before a queue job started, and dispatch is now paused. Sleeping through this means the queue stalls until you wake.
  • bed_cooled — the bed dropped below your configured threshold (default 35 °C) after a print, so the part is safe to remove.
  • The sensor threshold alarms — a room too cold or too damp for the filament that is printing in it.

Quiet hours are per provider, not per event

There is no per-event bypass. A provider inside its quiet window drops everything, including the three above — so a provider that is quiet from 22:00 will not tell you the queue stalled at 01:00.

Route it instead: give the action-required events their own provider with quiet hours off, and leave the chatty ones (progress, print started) on the provider that does sleep. On iOS a Bark provider at Critical is the strongest version of this — it comes through Silent mode and Focus as well.

The daily digest is a separate opt-in channel and never delays anything: every notification is sent when it happens, and the digest is an extra summary on top.


Testing

Every provider has a Send Test button next to the save action. Clicking it fires a synthetic event through the full pipeline (template render, quiet-hour gate, priority mapping, transport-specific wrap) so the resulting message is a faithful preview of what real events will look like — not a stripped-down "hello world".

Re-test after editing templates, switching priorities, or changing transport-level fields like SMTP credentials. The test bypasses the digest queue (always sent immediately) so you don't have to wait until your digest time to see the result.


Tips

Start with ntfy

ntfy is the easiest provider to set up -- no account needed, just pick a topic name and subscribe on your phone.

Multiple Providers

You can configure multiple providers to receive notifications through different channels simultaneously.

Originally based on Bambuddy documentation.