Notifications & Email
How Royal Glow reaches customers and staff — transactional email via Resend, marketing email via Brevo, React Email templates, and browser Web Push notifications.
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.
Royal Glow keeps people informed through two channels: email (the durable record — confirmations, invoices, reminders, offers) and Web Push (the instant nudge that lands on a phone or laptop even when the site is closed). This page covers how each is wired up and which service handles what.
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.
| Concern | Transactional | Marketing |
|---|---|---|
| Volume | Low, 1-to-1 | High, 1-to-many |
| Speed | Instant delivery critical | Scheduled / batched |
| Attachments | Yes (PDF invoices) | No |
| Unsubscribe management | Not required | Legally required |
| Deliverability focus | Individual inbox | Bulk sender reputation |
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.
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 [email protected].
| Triggered by | |
|---|---|
| Welcome / onboarding | Onboarding completed |
| Booking confirmation (pending) | Booking created |
| Booking approved / rejected | Receptionist confirms or rejects |
| Appointment reminder (24h / 1h) | Scheduled reminder job |
| Invoice — service | Booking moves to completed (+ PDF) |
| Invoice — membership purchase / session | Membership created / session recorded (+ PDF) |
| Membership expiry reminder (30d / 7d / 1d) | Daily expiry job |
| Membership expired | Auto-expire job |
Brevo is built for bulk sending to subscriber lists and, critically, manages unsubscribe automatically — a legal requirement for marketing email under CAN-SPAM, GDPR, and India's DPDP Act. Its free tier is 300 emails/day with unlimited contacts.
| Triggered by | |
|---|---|
| Post-service follow-up | 2 days after a completed booking |
| Re-engagement (inactive 60+ days) | Brevo automation |
| Seasonal / festival offer | Manual campaign |
| New offer announcement | Owner publishes an offer |
| Membership renewal nudge | 7 days after a membership expired |
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:
await resend.emails.send({
from: 'Royal Glow <[email protected]>',
to: customer.email,
subject: `Hey ${customer.firstName}, your Royal Glow receipt is ready.`,
react: InvoiceServiceEmail(props),
attachments: [{
filename: `RoyalGlow-Invoice-${invoice.invoiceNumber}.pdf`,
content: pdfBuffer,
}],
})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
Permission
The browser's native prompt is shown after the customer's first successful booking, not on first load.
Subscribe
Once granted, the service worker calls
pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: VAPID_PUBLIC_KEY }),
returning a PushSubscription.
Store
The client posts it to POST /api/push/subscribe, which upserts a row in the
push_subscription table keyed by endpoint and bound to the signed-in user.
Send
When an event fires, the server fetches the user's subscriptions and the
web-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:
Prop
Type
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.
Related links
Notifications & Realtime API
POST /api/push/subscribe and the in-app notification feed.
Realtime (Ably)
Live in-page updates — the instant counterpart to push.
Background Jobs
The scheduled jobs that trigger reminders and marketing email.
Jobs API
The job endpoints that send reminders and follow-ups.
Environment Variables
Resend, Brevo, and VAPID keys.
Was this page helpful?