@voyant-travel/notifications, with a provider abstraction, first-party providers for local development and Voyant (email and SMS), database-backed templates and delivery logs, reminder rules and runs, and Hono routes.
Key concepts
The architecture rests on a few clear ideas, kept consistent with the glossary verbs Deliver (push an issued artifact over a channel) and Issue (produce the artifact itself).- Templated notifications. Database-backed templates render with provider-agnostic data into transport fields (subject, text, html). Feature code chooses a template and a channel, not a vendor.
- Multi-channel delivery. A notification names a channel (
email,sms, and future channels such aspush). Provider resolution happens in the infrastructure layer, by channel, not in feature code. - Providers. A provider is a transport implementation. It formats the vendor request, sends it, and returns a result in the shared shape. It does not own product business rules.
- Channels. The delivery surface (email, SMS) that selects which registered provider sends a message.
- Delivery. What was sent, through which channel, by which provider, with what result. Notifications guarantees only delivery intent and the transport result it actually knows, never a stronger promise than the provider can offer.
Notification delivery is transport, not CRM history. This module owns templates, delivery attempts, provider message ids, and reminder-oriented sends. Customer-relationship timelines belong to a higher-level product surface, not the transport layer.
What it owns
Providers, channels, and delivery
Providers implement theNotificationProvider contract. Which one sends is decided by the deployment’s email and sms provider roles, and the bring-your-own path is first class: implement NotificationProvider against Resend, Twilio, SES, or anything else and select it as the role’s provider. When you register a set of providers, later providers override earlier ones on a channel conflict, and sendWith(name, payload) dispatches to a named provider directly.
Templates and delivery logs
Templates and delivery logs are database-backed (exposed through the./schema and ./validation subpaths and managed through routes). Delivery logs record the transport result, channel, provider, and provider message id, which is the honest record of what the runtime actually did.
Reminders
Reminder rules drive scheduled operational sends and can currently target abooking_payment_schedule or an invoice. A scheduled sweep is run with sendDueNotificationReminders(...). Stage cadence and maxSendsInStage are evaluated against reminder run attempts: a queued, sent, skipped, or failed run consumes the stage slot, and a failed run is terminal until an operator or recovery flow requeues it.
Finance-aware and document sends
Specialized routes compose the core service for collection and document flows: sending a payment session or an invoice, listing a booking’s document bundle, and sending booking documents. These resolve recipients from the payment session, invoice, and linked booking travelers, then render the selected template with finance context such as payment links, invoice balances, and booking references. Booking document sends bundle the latest customer-facing contract attachment and the ready invoice or proforma rendition for a booking. Sensitive attachments resolve access at send time from durable storage metadata rather than relying on stale persisted signed URLs. Public, editorial assets may use stable public URLs, but private documents use signed or authenticated access. Override resolution withdocumentAttachmentResolver (or resolveDocumentAttachmentResolver) when mounting the routes so attachment URLs reflect the current runtime and storage context.
Working with it
Create the service with a provider set and send a notification through the shared surface:@voyant-travel/notifications is part of the standard product graph and contributes its runtime ports through the runtime entry in its voyant.package.v1 manifest.
Which transport actually sends is a provider selection, made in the deployment profile rather than in code:
voyant.package.v1 manifest and the graph activates it exactly once:
Workflows, routes, and subscribers are all fine trigger points, but delivery should always converge on the shared notification service. Do not open-code provider-specific delivery in a workflow or feature module.
Links to other modules
- Finance. Payment-session and invoice sends, collection reminders, and the fully-paid document bundle all read finance context (payment links, invoice balances) and compose finance generators. See Finance.
- Legal. Booking document sends bundle the latest customer-facing contract attachment; the confirmation policy generates a contract through
ensureLegalDocuments. See Legal. - Bookings. Recipients resolve from linked booking travelers, and the document bundle lifecycle subscribes to booking confirmation and fully-paid signals.
- Inventory. Product brochures stay an extension point via
resolveBrochureDocuments, so apps that install@voyant-travel/inventorycan add brochure artifacts without making notifications depend on products at runtime.
React package
@voyant-travel/notifications-react provides hooks, a client, query keys, reusable UI, and admin surfaces for template management, delivery listing, and reminder management.
./hooks, ./client, ./query-keys, ./ui, ./admin, and ./components/*).
Next steps
Finance
The payment sessions, invoices, and schedules behind finance-aware sends.
Legal
The contracts bundled into booking document sends.
Jobs
Run reminders and delivery through package-owned background work.
Glossary
The Issue and Deliver verbs and the channel vocabulary.