Backend (Convex)
Notifications
The enqueue pipeline, policy engine, and push delivery.
The pipeline overview and diagrams live in Notification Pipeline; this page is the backend-oriented reference.
Key functions
| Function | File | Notes |
|---|---|---|
enqueueNotification (internal) / enqueueNotificationHelper | convex/notification/mutations.ts | The only entry point for producers |
evaluatePolicy | convex/notification/policies.ts | Pure; runs inside the enqueueing transaction |
sendPushNotification (internal action) | convex/notification/actions.ts | Expo send per active token, 3 retries, idempotent on terminal status |
processPendingToWorkpool | convex/notification/mutations.ts | 5-min cron; re-enqueues unclaimed rows past scheduledAt |
markAsRead / markAllAsRead / deleteNotification | convex/notification/mutations.ts | Unread counter decrements under countsTowardUnread |
Producer obligations
- Render the final
title/bodyyourself — templates exist but are not auto-rendered by the pipeline. - Pass a
producerModule(webhook,gamificationLoyalty,dashboard, …) — it lands on the row for auditing. - Bulk sends cap at 100 users per mutation; larger audiences go through
sendBroadcastNotification.
Data model
notifications (with policyResult, producerModule, traceId, channelStatus, enqueuedAt), notificationPreferences (channel toggles, disabled types, quiet hours, timezone), pushTokens (mirrored into the Expo push component), notificationTemplates. Aggregate: unread:<userId> sharded counter.
Mobile exposure
toMobileFormat() in convex/notification/queries.ts is the field firewall — mobile sees only what it explicitly maps. New backend fields are invisible to the app until added there.