Plan web-app notifications from event to delivery
A notification plan should answer seven questions for each event: who needs to know, where, how soon, what can truthfully be said about delivery, what happens if the channel fails, when the notice stops being useful, and what the recipient can control. Copy the decision matrix and test it with the fictional training-cohort example below. The matrix separates a record saved in the app from an email handed to a provider or delivered to an address.
Download the editable notification matrix (CSV)Start with the person who must act
Imagine a fictional training app where a facilitator changes the time of tomorrow’s session. Learners who are enrolled need a timely, actionable notice; people browsing the catalogue do not. A facilitator also needs to know if the change failed to save, while the course owner may need an exception queue if messages cannot be sent. Those are three different audiences and three different jobs. Sending the same email to everyone hides those decisions.
Inventory consequential events in the app’s state model, then ask what the recipient can do with the information. Separate action required, awareness, security/account protection, and optional updates. For each event, name the authoritative trigger: a committed schedule change, an accepted invitation, a completed export, or a recovery action. A button click or queued job is not proof that the underlying event happened. The web-app state-model and audit-log guides cover committed transitions and attributable history; this guide decides what people are told about them.
Copy the notification decision matrix
Use one row per event and recipient group when the action, channel or preference differs. The CSV has a blank row followed by a clearly fictional worked set. Keep actual names, email addresses, message bodies and private record values out of a public worksheet. A row should be usable as a product decision and as an acceptance-test input.
- Event and committed trigger | Recipient and reason | Class and urgency | In-app surface and state | Email trigger and observable state | Content and safe link | Dedupe and expiry | Failure fallback and owner | Recipient control | Acceptance check.
- Recipient and reason: identify the person who can act, the person affected, and anyone who must investigate a failure. Do not infer recipients from a broad role alone; check access to the referenced record at send time and again when a link opens.
- Class and urgency: distinguish security or essential transaction notices from optional marketing. Choose a deadline or response window from the actual task, not a universal “urgent” label.
- Channel and state: decide whether the app needs a durable inbox entry, an immediate status message, email, or a combination. Specify exactly which evidence moves each channel to a new state.
- Dedupe and expiry: choose a stable event/recipient key, a quiet period or digest rule where appropriate, and the moment an old notice should close, disappear or point to current status.
- Failure and control: name an owner for delayed or failed delivery, a safe alternative route, and which channel or category the recipient may switch off. An essential security notice should not be buried in a marketing preference.
Choose a channel for the job, then write its limits
In-app notices work well for context, history and next actions while a person is signed in. A persistent inbox item can survive a page change, but it will not reach someone who never returns. A transient confirmation can say “Your request was saved” after durable acceptance; it should not say “Everyone was notified” merely because dispatch was scheduled. Put the action and current record behind an authorised link rather than copying sensitive details into a preview.
Email may help reach someone away from the app, particularly for a near-term change or an account-protection event. Verify the address and its ownership for the intended use, handle changes to it carefully, and plan for bounce or provider failure. OWASP’s email-verification guidance describes validation and verification as separate concerns; a syntactically plausible address is not proof that the intended person controls the mailbox. An email is also a poor sole record of a business decision because forwarding, filters and stale copies can separate it from the app’s current state.
For a recovery or authenticator change, consider an independently held, established notification address and a clear “report this wasn’t me” route. NIST SP 800-63B sets notification requirements for identity services in its scope, and OWASP recommends notice after successful recovery. Use these as security design references; their normative wording is not a universal legal rule for every small web app. Do not send a recovery secret, full private record or newly changed contact detail to an unverified destination just to make the notice look complete.
Report only the delivery state you can observe
Keep the business event and each notification attempt separate. For an in-app inbox, “created” means the notice was saved for that recipient. “Displayed” means the app rendered it in a particular session if that event is actually measured. “Read” should require an explicit user action or a defined acknowledgement; loading a list or briefly exposing a card does not prove comprehension. Avoid a read label altogether if the product does not need it.
For email, “queued” means the app has accepted a send job, and “provider accepted” means the provider accepted submission. Neither means the recipient got it. “Delayed” and “failed” require provider or transport evidence and may arrive after the first response. “Delivered” should be used only for a supported delivery event, defined by the provider or mail transport. RFC 3464 describes delivery-status notifications including delayed, failed and delivered; its delivered state does not mean the message was read. An absence of a failure event is not delivery proof. Do not claim inbox placement or human reading without separate, credible evidence.
Store a notification ID, business-event ID, recipient reference, channel, template version, attempt time and bounded state changes so support can explain what happened. Keep provider identifiers only as long as needed for reconciliation. Never place reset tokens, private message bodies or raw email addresses in product analytics. A failed email may leave a valid in-app notice; show those two facts separately to operators.
Fictional example: a training cohort changes its timetable
Imagine a small training organisation planning an app for one cohort. These people, rules and messages are invented for this worksheet; they are not a Kinetivy client, live system or measured outcome. A coordinator moves a session from Tuesday to Wednesday. The schedule change commits first, then the app creates one inbox notice per enrolled learner and queues an email to each verified address. The page immediately shows the coordinator that the schedule was saved and that notifications are in progress.
Suppose one provider response accepts the email and another returns a temporary delay. The operator view says “accepted by provider” for the first and “delayed; retry pending” for the second. The learner-facing schedule always shows Wednesday. If a retry eventually fails, the coordinator’s exception queue requests a phone or other agreed contact check without marking the email delivered. A later edit to Thursday supersedes the Wednesday notice and its action link opens the current schedule, with a visible change history if that history is part of the product.
- Session-time change; schedule update committed | Enrolled learners need to plan travel | Essential transaction; before the next session | Durable inbox item; created when saved, explicit acknowledgement if needed | Email queued only after commit; provider status tracked separately | “Session time changed”; authorised link to current schedule, no private attendance detail | One notice per schedule version and learner; supersede when changed again | Retry delayed email; coordinator exception queue before session | Learner may choose supplementary reminders, not suppress the essential change notice | Double submit produces one current notice; failed email never shows delivered.
- Coordinator save confirmation; schedule update committed | Coordinator needs confidence the edit saved | Immediate status | On-page status after durable save, no inbox item required | None | “Schedule saved; notifications in progress” | Replace with latest save result | If dispatch fails, show exception state in operator view | Not a marketing preference | Failed save never displays success.
- Optional weekly course news; approved weekly edition | Opted-in learners who want extra reading | Non-urgent optional update | Optional feed entry | Email only to current subscribers | Brief description and preference link | One edition per week; expire stale promotions | Failed send can wait for next edition; content owner reviews | Unsubscribe or switch off without disabling essential notices | Opt-out prevents future sends, while security and essential notices remain separately classified.
Control noise, privacy and stale actions
A useful notice can become harmful when it repeats on every retry. Key deduplication to the committed event and recipient, not to each worker attempt. Decide whether related low-urgency events should be bundled into a digest. Preserve separate records when different actions or deadlines matter. Expire a notice when its action is completed, access is revoked, the deadline passes or a newer version replaces it; retain any audit history under its own policy rather than keeping obsolete user-facing cards forever.
Preferences should describe consequences in plain language. Let people control optional marketing and, where practical, low-urgency reminders by channel. Keep consent and unsubscribe handling distinct from necessary account or service messages, and review applicable law and provider policy for the actual audience. RFC 8058 specifies one-click signalling for list email headers; it is relevant to list mail, not a blanket rule that every transactional notice requires the same header. Do not use the “transactional” label to smuggle promotions past an opt-out.
Minimise subject lines and lock-screen previews because those can be seen by someone other than the account holder. Avoid sensitive attachments and long-lived action tokens. Recheck authorisation when a link opens, and make links safe when the recipient is signed out or their access has since changed. Decide how addresses are verified, updated and retired; a notice sent to a stale address can be both a privacy incident and a missed action.
Make notices perceivable without interrupting every task
WCAG 2.2 success criterion 4.1.3 asks that status messages be programmatically determinable so assistive technology can present them without taking focus. Use a suitable status role for routine save or queue progress. The WAI-ARIA alert pattern is for brief, important and potentially time-sensitive information; it normally does not move keyboard focus, and repeated alerts can become disruptive. An alert dialog is a different pattern for an interruption that requires action. Do not make every new inbox item an assertive alert.
Give a persistent notice a visible heading, timestamp in a readable format, clear action text and an accessible way to dismiss or acknowledge it where appropriate. Do not rely on colour, a badge count or a toast that disappears before it can be read. Check keyboard order, screen-reader announcement, reduced-motion behaviour, mobile spacing and whether a refreshed list repeats old announcements. The acceptance-testing guide can turn each matrix row into checks for success, delay, failure, duplicate events, expired links and changed access.
Questions worth asking.
Does “email sent” mean a learner received it?
No. Say whether the app queued the job, the provider accepted it, or a supported transport event reported delivery. Delivery does not prove inbox placement or reading. Show delayed and failed states when the provider supplies them.
Should every event send both an in-app notice and email?
No. Choose by who needs to act, how soon and whether that person is likely to be in the app. Routine context may need only an in-app item; a time-sensitive or account-protection event may justify an independent channel.
Can users switch off security notices?
Treat account-protection and essential transaction notices separately from optional marketing and reminders. Define which notices are necessary for the service, offer control where appropriate, and make optional consent and unsubscribe effective without disabling essential information.
Is a notification marked read when it appears on screen?
Only call it displayed if rendering is actually observed. Reserve read or acknowledged for an explicit action with a documented meaning. A displayed card does not prove the person understood it.
Sources & further reading
- W3C: Understanding WCAG 2.2 status messages ↗︎
- W3C: WAI-ARIA Alert Pattern ↗︎
- W3C: WAI-ARIA 1.2 ↗︎
- NIST SP 800-63B: Authenticator Event Management ↗︎
- OWASP: Email Validation and Verification Cheat Sheet ↗︎
- OWASP: Forgot Password Cheat Sheet ↗︎
- RFC 3464: Delivery Status Notifications ↗︎
- RFC 8058: One-Click List-Unsubscribe ↗︎
Examples in this guide are illustrative. Read about our editorial approach.