VOYAGE
Voyage / TECHNICAL / Notifications

Notifications

The notifications worker exposes internal send/outbox/trigger routes, admin notification/template/campaign routes, and customer preference routes (workers/notifications/src/index.ts:20-29).

Notifications

Current Shape

The notifications worker exposes internal send/outbox/trigger routes, admin notification/template/campaign routes, and customer preference routes (workers/notifications/src/index.ts:20-29).

The worker runs scheduled processing for due outbox rows and scheduled campaigns, and the Worker configuration schedules it once per minute (workers/notifications/src/index.ts:46-78, workers/notifications/wrangler.toml:5-6).

The Worker binds tenant ops D1 as TENANT_OPS_DB and documents provider secret names for SendGrid, Resend, Twilio, push, and webhook delivery (workers/notifications/wrangler.toml:8-18).

Outbox Pattern

Outbox processing uses a claim token prefix, a five-minute claim lease, three retry attempts, and a sixty-second base retry delay (workers/notifications/src/services/outbox.ts:130-141).

The system template registry includes booking, queue, calendar, follow-up, and event notification templates used when tenant-specific templates are not supplied (workers/notifications/src/services/outbox.ts:148-214).

Queueing a notification resolves the template, renders content, evaluates policy, records suppressed notifications immediately, and inserts deliverable notifications as queued outbox rows (workers/notifications/src/services/outbox.ts:754-888).

Due outbox selection filters queued rows by send_at, ignores rows with unexpired claim leases, sorts by schedule time, and applies a caller-provided processing limit (workers/notifications/src/services/outbox.ts:1021-1041).

Claiming an outbox row stores a provider reference claim token and increments attempts only when the row remains queued and claimable (workers/notifications/src/services/outbox.ts:1048-1077).

Processing re-evaluates policy immediately before delivery, dispatches through the provider layer, records attempts, marks success, schedules retry, or marks terminal failure (workers/notifications/src/services/outbox.ts:1079-1198).

Suppression and Preferences

Notification policy evaluation checks active suppression rows, marketing consent, and customer transactional email/SMS preferences before delivery (workers/notifications/src/services/outbox.ts:545-670).

Default customer preferences allow transactional email and SMS but disable marketing email and SMS unless preferences say otherwise (workers/notifications/src/services/outbox.ts:143-146).

Suppressed notifications are logged with status = 'suppressed' instead of being dropped silently (workers/notifications/src/services/outbox.ts:792-830).

Rendering

Current rendering is not LiquidJS; it is a small {{key}} replacement function that substitutes provided values and leaves missing variables as literal {{key}} text (workers/notifications/src/services/renderer.ts:1-7).

Templates should therefore be documented as simple variable interpolation templates, not Liquid templates, unless the renderer is replaced in future code (workers/notifications/src/services/renderer.ts:1-7).

Delivery

The dispatcher selects delivery implementation by channel and supports email, SMS, push, and webhook channels (workers/notifications/src/services/dispatcher.ts:20-39).

Email delivery supports sandbox mode when no provider key is configured, Resend delivery, SendGrid delivery, and retryable classification for rate-limit and 5xx responses (workers/notifications/src/services/dispatcher.ts:61-158).

SMS delivery supports Twilio or sandbox mode, strips HTML, truncates to 1600 characters, and treats rate-limit and 5xx responses as retryable (workers/notifications/src/services/dispatcher.ts:160-207).

Push delivery posts JSON payloads to configured endpoints, and webhook delivery posts JSON payloads to configured webhook URLs (workers/notifications/src/services/dispatcher.ts:209-273).

Corrections From Frozen HTML

The frozen page's outbox, suppression, and delivery-provider themes remain valid, but LiquidJS is not present in current code and missing variables are preserved rather than blanked (workers/notifications/src/services/renderer.ts:1-7).

The frozen page under-described scheduled processing; current code processes both due outbox entries and scheduled campaigns from the scheduled handler (workers/notifications/src/index.ts:46-78).


last verified: 2026-04-24, commit 14f3b190db8817399dcd30e1dc4e1ae7674bbf8a