Notification — How it works
Overview
The notification engine manages reusable message templates and the events/deliveries that send them. An event is rendered from a template, dispatched through a pluggable provider, and every attempt is recorded append-only — so provider failures and retries never lose history. Recipient, body, and payload may be PHI and are never logged.
Data model & ownership
| Table | Purpose |
|---|---|
notification_template | Reusable template; relational channel (EMAIL/SMS/PUSH/IN_APP/WEBHOOK), code unique per tenant, {{variable}} subject/body + required_variables. |
notification_event | An enqueued message; relational status (PENDING/SENT/FAILED/CANCELLED), rendered subject/body, payload. |
notification_delivery | Append-only per-attempt outcome; relational status (SENT/FAILED), provider + non-sensitive response snapshot. |
Key rules & invariants
- Template validation rejects an
EMAILtemplate without a subject and any declared required variable not referenced in the subject/body; voided templates cannot be used for new deliveries. - Enqueue renders the subject/body from the template (
{{variable}}substitution) and rejects a missing required variable / invalid payload JSON. - Send dispatches via the
NotificationProviderSPI (aSimulatedNotificationProviderships by default) and records a newnotification_deliveryper attempt; a provider failure is captured as aFAILEDattempt (not thrown) and a retry appends another attempt — history is never lost. - No PHI/secrets are logged — only ids and statuses.
API
See the API Reference. Endpoint groups under /api/v1/notification: templates, and
events (enqueue/get/search) + /{id}/send, /{id}/deliveries.
Configuration & feature flags
Domain-module Feign URL notification.service.url (NOTIFICATION_SERVICE_URL). Real email/SMS/push
providers plug in behind the NotificationProvider SPI (follow-up).
Related features
- Billing (receipts), Concept/Demographic
(recipients). Domain plug-ins call via
exchange.client.notification.