Notifications
Dawn notifications are code-defined transactional workflows. They are separate from conversational messages and run as listeners for domain events.
The first implementation borrows the durable workflow concepts used by Knock without adding a workflow editor or notification UI.
Concepts
| Concept | Dawn representation |
|---|---|
| Workflow | A typed entry in notifications/workflows.ts with a stable key |
| Trigger | A key, idempotency key, recipients, tenant, actor, and typed data |
| Recipient run | One workflow execution result per recipient |
| Channel step | A condition, template, and provider-neutral channel name |
| Preference | A resolver that can allow or skip each recipient and channel |
| Message | Rendered content and a stable ID for one recipient and channel step |
| Receipt | The sent or skipped result, including a provider message ID |
Every current workflow is transactional. Workflow keys and trigger data are
linked by NotificationWorkflowDataMap, so a trigger with missing or incorrect
data fails typechecking.
Current workflows
| Workflow key | Domain event | Recipient | Channel |
|---|---|---|---|
patient-welcome | patient.created | Patient | console |
appointment-confirmation | appointment.created | Patient | console |
The event envelope contains identifiers only. A retryable notification step loads the patient or appointment from the domain stores before triggering its notification workflow.
domain mutation
-> domain event
-> Vercel event workflow
-> retryable notification step
-> transactional notification workflow
-> channelThe event ID is the notification idempotency key. Run and message IDs are derived from the workflow key, event ID, recipient ID, and step reference so retries address the same logical delivery.
Console channel
The console channel is the only provider today. It writes one structured
transactional.notification record with:
- message, workflow, step, recipient, and tenant IDs
- rendered subject and body
It does not write the recipient email address or phone number. Rendered content can still contain personal information and must be treated as application data.
Adding a workflow
- Add its key and data shape to
NotificationWorkflowDataMap. - Define its channel steps in
notificationWorkflows. - Add a typed trigger method to the transactional notification service.
- Call that trigger from a retryable event-listener step.
Keep domain event payloads small. Load mutable or sensitive context inside the notification step.
Adding a channel
Implement NotificationChannel behind a provider-neutral adapter, add its name
to NotificationChannelName, and provide it when creating the engine. Templates
and event listeners must not depend on a vendor SDK.
The engine already provides conditions, preference checks, channel capability checks, recipient fan-out, and delivery receipts. Delays, batching, cancellation, persisted message history, and real email or SMS providers are not implemented yet.