Notifications Domain
Technical architecture of Slack and Web Push integrations
Overview
The notifications domain manages multi-channel alerts and team communication. It leverages @repo/slack for operational summaries and @repo/notifications for browser-level Web Push.
Slack Integration (@repo/slack & apps/slack-bot)
Slack notifications use a consolidated microservice architecture:
- Thin Client (
@repo/slack): Resolves channel routing configuration fromslack_configsbut does not perform any Block Kit rendering or direct Slack Web API calls. - Slack Bot (
apps/slack-bot): A stateless render-and-dispatch service that validates incoming payloads, renders Block Kit templates, and dispatches messages to Slack viachat.postMessageusing the managed workspace bot token and@slack/web-api. Installation-token decryption is historical and is not the current credential authority.
Low-Latency Dispatch Path
For critical alerts (such as incidents or announcements), notifications are written to the notification_queue table and immediately drained using Next.js after() via the triggerNotificationQueueDrain() helper, bypassing cron latency to deliver alerts in under 2 seconds.
Dynamic Routing
Routing config is resolved locally by notification-router.ts in @repo/slack based on:
teamId(Required)formType(e.g.,shift_report,incident,preshift)locationId(Optional location-specific override)
Once resolved, the client sends a POST request to ${SLACK_BOT_URL}/api/notifications/dispatch containing the target channelId and the raw domain payload.
Template System
All Block Kit templates live in apps/slack-bot/lib/templates/ and include:
- Shift reports — summary with location, ratings, and hero mention
- Manager reports — daily operational summary
- Incidents — severity-tagged alerts with status and escalation tracking
- Pre-shift briefings — staff goals with roles and POS targets
- Labor variance alerts — multi-location threshold breaches
- Schedule summaries — weekly roster overview
- Anonymous feedback — category-tagged feedback with truncation
- Coupon/Survey alerts — privacy-safe lifecycle and sentiment notifications; contactable recovery remains gated
All templates enforce Slack's 2900-character section limit and 50-block message limit via truncate() helpers.
Reliability Patterns
- Retry Logic: Failed dispatch requests are retried using exponential backoff.
- Queue Fallback: If
apps/slack-botis unavailable orafter()fails, a background cron job at/api/notificationsprocesses pending items in thenotification_queue. - Response Receipts: Bot messages include a
Danvascontext footer with timestamp for traceability.
Coupon and survey delivery
Guest events are accepted transactionally with the reservation, feedback, or survey mutation, using stable event identities. After commit, the existing notification queue and Slack delivery ledger own retries. A provider failure never reverses a redeemed reservation or an applied survey response.
The canonical worker reloads the durable event and authoritative records before deciding whether delivery is eligible, a lifecycle no-op, or a recovery disposition. Legacy rating hints cannot authorize an alert without its durable feedback/response identity. Routing uses the actual restaurant rather than the campaign's first assignment. Event metadata excludes raw names, email, phone, comments, public/private tokens, cookies, and staff proof.
The approved NPS mode is anonymous-only: sentiment events do not create Contact records or contactable Operations Inbox recovery. Coupon feedback remains a separate 1–5 metric. See Guest NPS and the guest feature contract.
Web Push Notifications (@repo/notifications)
Browser-native notifications used for high-urgency alerts.
- Subscriptions: Stored in the
push_subscriptionstable. - VAPID Keys: Used for secure delivery to Google/Apple/Mozilla push services.
- Service Worker: Handled by the Next.js PWA configuration (
serwist).
Email Notifications (@repo/email)
Transactional emails delivered via Resend.
- Templates: Built with React Email for consistent styling and type safety.
- Provider:
@repo/emailwraps the Resend SDK with standardized logging and error handling.
PII Safety in Observability
All observability surfaces that could carry customer contact details (emails, phone numbers) are wired through a single redaction primitive at @repo/security/pii:
- Sentry —
scrubSentryEventruns inbeforeSendfor the client, server, and edge runtimes. It redactsmessage, exception values, breadcrumbs, andextrabefore the event leaves the process. - PostHog —
captureSecureEventin@repo/telemetry/serverscrubs event properties throughscrubPIIbefore the server-side PostHog SDK transmits them. Prefer this helper over callinganalytics.capturedirectly so call sites can't accidentally leak PII. - AI chat — The
/api/assistant/chatroute callsredactPIIon user-supplied text before it is forwarded to the model or persisted.
If you need a new pattern matched (e.g. an employee ID format), add it to packages/security/pii.ts — do not re-implement redaction locally.