Notifications

Notifications owns in-app notifications, event preferences, email templates, delivery queue, send logs, and Resend webhook updates.

On this page

Where to look

AreaMaintainer reference
Routes
Implementation referencesrc/app/(dashboard)/communications, /communications/outbound, /communications/outbound/settings, /communications/outbound/templates
Webhook
Implementation referencesrc/app/api/notifications/webhooks/resend
Event registry
Implementation referencesrc/features/notifications/shared/events/registry.ts
In-app
Implementation referencesrc/features/notifications/server/in-app, src/features/notifications/ui/bell.tsx, list.tsx
Delivery
Implementation referencesrc/features/notifications/server/delivery
Outbox
Implementation referencesrc/features/notifications/server/outbox, src/features/notifications/ui/outbox
Templates
Implementation referencesrc/features/notifications/server/templates, src/features/notifications/ui/templates
Settings
Implementation referencesrc/features/notifications/server/settings, src/features/notifications/ui/settings
Implementation reference4 areas

These Product code locations explain how the documented behavior is implemented. Expand them when you are ready to customize or maintain this area.

Canonical event registry

Notification keys are defined once so templates, preferences, delivery, and webhook flows use the same vocabulary.

  • src/features/notifications/shared/events/registry.ts

Queue and dedupe

Product code queues work into notification_outbox, canonicalizes event keys, and treats unique dedupe conflicts as already queued.

  • src/features/notifications/server/delivery/queue/notify-queue.ts

Worker and retry behavior

The worker atomically claims rows, dispatches through the delivery registry, marks success or failure, and moves exhausted rows to dead letter.

  • src/features/notifications/server/outbox/services/worker.ts
  • src/features/notifications/server/events/dispatch.ts

Templates and provider updates

Template runtime code renders event-specific messages, while Resend webhooks update delivery status after provider callbacks arrive.

  • src/features/notifications/server/templates/services/runtime.ts
  • src/features/notifications/server/templates/services/render.ts
  • src/app/api/notifications/webhooks/resend/route.ts

Reference paths are relative to the Shipflash-Product checkout.

Notification delivery flow

  1. 1

    Choose a canonical event key

    Callers use a canonical event key for preferences and delivery logs. Application-owned events may also have editable templates; team.invite remains Supabase Auth-owned.

    Relevant Product code
    • src/features/notifications/shared/events/registry.ts
  2. 2

    Queue the work

    The queue helper writes notification_outbox rows with recipient, payload, resource id, dedupe key, status, and next attempt time.

    Relevant Product code
    • src/features/notifications/server/delivery/queue/notify-queue.ts
  3. 3

    Claim atomically

    The worker uses claim_notification_batch to avoid two workers sending the same queued message.

    Relevant Product code
    • src/features/notifications/server/outbox/services/worker.ts
    • supabase/migrations/20260426000000_init.sql
  4. 4

    Dispatch, retry, or dead-letter

    Delivery runs through the registry, records provider status, retries transient failures with backoff, and dead-letters exhausted rows.

    Relevant Product code
    • src/features/notifications/server/outbox/services/worker.ts
    • src/features/notifications/server/events/dispatch.ts

Notification event registry

Use this example as a starting point, then adapt it to your product's rules and configuration.

This is the complete current registry. Add events here first, then explicitly decide whether delivery is Supabase Auth-owned or application-owned before adding an editable template.

ts
export const NOTIFICATION_EVENT_KEY_VALUES = [
  "lead.new",
  "lead.contact.message",
  "lead.contact.reply",
  "team.invite",
  "auth.signup.welcome",
  "system.alert",
  "operations.stripe.dispute.created",
  "billing.invoice.payment_succeeded",
  "billing.invoice.payment_failed",
  "billing.subscription.started",
  "billing.subscription.changed",
  "billing.subscription.canceled",
  "billing.subscription.expired",
  "billing.subscription.expiring_soon",
  "billing.refund.issued",
  "billing.order.completed",
  "billing.credit_pack.granted",
  "billing.credit_balance.low",
  "billing.usage.threshold_reached",
  "billing.usage.summary",
] as const;
Source reference
  • src/features/notifications/shared/events/registry.ts

Recipe: add an email event

Add a notification event that can be queued, templated, retried, and audited without sending from unrelated product code.

Implementation locations

Inspect

  • src/features/notifications/shared/events/registry.ts
  • src/features/notifications/server/events/dispatch.ts
  • src/features/notifications/server/templates/services/runtime.ts
  • src/features/notifications/server/outbox/services/worker.ts

Edit

  • src/features/notifications/shared/events/registry.ts
  • src/features/notifications/server/events/dispatch.ts
  • src/features/notifications/shared/templates/defaults.ts
  • src/features/notifications/shared/templates/constants.ts

Steps

  • Add the event key to the registry.
  • Add or update the default template and allowed variables.
  • Add delivery handling through the registry.
  • Queue the event from the product module using a dedupe key when duplicate source events are possible.

Verify

  • pnpm run test:features
  • pnpm run quality
  • Trigger the event in test mode and confirm notification_outbox status changes.

Common mistakes

  • Do not send provider email directly from billing, auth, team, or contact code.
  • Do not invent event keys outside the registry.
  • Do not hide retry errors from the outbox UI.

AI prompt: notification event

AI prompt
Add a notification event for <event>. Inspect the canonical event registry, delivery registry, template runtime, outbox worker, and the source module that queues the event. Keep provider sending inside the notification delivery boundary and preserve retry/dead-letter behavior.
Inspect first
  • src/features/notifications/shared/events/registry.ts
  • src/features/notifications/server/events/dispatch.ts
  • src/features/notifications/server/outbox/services/worker.ts

Verify

  • pnpm run test:features
  • pnpm run quality

Delivery rules

  • Queue provider delivery instead of sending from unrelated product code.
  • Keep retry state and failure reason visible in the outbox.
  • Use event keys from the registry so templates and preferences stay consistent.
  • Handle Resend webhook updates without trusting raw payloads as application state.