Notifications & Email
In one line: Royal Glow reaches people through email (the durable
record — Resend for transactional, Brevo for marketing) and Web Push (the
instant nudge). All three are guarded extension points: with a key absent the
send is a no-op that logs and returns cleanly.
Email and push are guarded extension points. Each helper reads its keys
directly behind a truthy guard — Resend behind
RESEND_API_KEY, Brevo behind
BREVO_API_KEY, and Web Push behind the VAPID keys. With a key absent, the
send is a no-op that logs and returns cleanly, so the app builds and runs with
no email or push configured.Why two email services
Because authentication is Google OAuth only, the app sends zero auth emails — no password resets, no magic links. Every email is a business event. Those split naturally into two very different infrastructure problems, so they use two services.Email providers
Resend handles transactional mail; Brevo handles marketing. The split is strict — invoices and confirmations never go through the marketing pipeline, and promotional blasts never go through the transactional one.- Tab Title
- Tab Title
Resend is built for instant, reliable 1-to-1 delivery, has first-class support
for PDF attachments (invoices), and integrates natively with React Email. Its
free tier — 3,000 emails/month, 100/day — covers the entire launch phase.
All transactional mail is sent
from hello@theroyalglow.in.Marketing email requires explicit consent. A customer is only added to Brevo
lists when they tick the marketing consent box at onboarding
(
marketing_consent = true). Transactional email — invoices and booking
confirmations — is always sent and cannot be fully disabled.React Email templates
Transactional templates are built with React Email as typed.tsx
components in packages/emails/, styled in Royal Glow’s brand. They receive
strongly typed props (money as integer paise, dates as DD/MM/YYYY) and
render to HTML at send time.
A typical send generates the invoice PDF, then hands the React component and the
attachment to Resend:
Brevo marketing emails do not use React Email. They are designed in
Brevo’s drag-and-drop editor and managed entirely in the Brevo dashboard, so
marketing content can change without a deploy.
Web Push notifications
Web Push delivers a short, tappable alert to a customer’s device — booking confirmed, reminder due, gems earned — even when the tab is closed. It uses the browser’s native Push API and the VAPID protocol; no third-party push vendor is involved.Subscription flow
1
Permission
The browser’s native prompt is shown after the customer’s first successful booking, not on first load.2
Subscribe
Once granted, the service worker callspushManager.subscribe({ userVisibleOnly: true, applicationServerKey: VAPID_PUBLIC_KEY }),
returning a PushSubscription.3
Store
The client posts it toPOST /api/push/subscribe, which upserts a row in the
push_subscription table keyed by endpoint and bound to the signed-in user.4
Send
When an event fires, the server fetches the user’s subscriptions and theweb-push library signs and delivers the payload; the service worker shows the
notification.Storage
POST /api/push/subscribe persists the subscription serialised by the browser’s
PushManager:
DELETE /api/push/subscribe soft-unsubscribes by deactivating the row for a
given endpoint. Both routes are session-scoped, so one user can never touch
another’s subscriptions. See the
Notifications & Realtime API for the full
request and response contracts.
Web Push is gated on the VAPID keys (
VAPID_PUBLIC_KEY /
VAPID_PRIVATE_KEY). When they are absent, subscription and delivery no-op and
log — the booking still succeeds, it simply sends no push. Reminder jobs always
check the customer’s preferences before sending, and skip a booking that is no
longer confirmed.