Prompt
I'm building a SaaS app and want transactional push notifications from backend events
Latest observation
Oct 14, 2026
OpenAI APIWeb search: off
A good way to think about this is: backend event happens → notification service decides if it should notify → push provider sends to device tokens.
For a SaaS app, a solid transactional push setup usually looks like this:
Recommended architecture
-
Your backend emits an event
- Examples:
invoice.paid,task.assigned,comment.created,build.failed - Do this via an internal event bus, message queue, or job system so notification delivery is asynchronous.
- Examples:
-
Notification service consumes the event
- A dedicated notifications worker/service:
- checks user preferences
- applies templates
- localizes content
- resolves target devices
- sends pushes
- A dedicated notifications worker/service:
-
Store device tokens per user
- Each device registers a push token.
- Keep metadata:
- user_id
- platform (
ios,android,web) - push token
- last_seen
- app version
- locale/timezone
- Remove invalid tokens when providers reject them.
-
Send via a push provider
- Common options:
- FCM for Android + web, and often as part of a broader setup
- APNs for iOS
- Or a wrapper service like OneSignal, Firebase, AWS SNS, Expo (if relevant)
- For transactional notifications, direct integration with FCM/APNs gives more control.
- Common options:
-
Track delivery and failures
- Log:
- notification_id
- event_type
- user_id
- device token
- provider response
- status (
queued,sent,failed,invalidated)
- Add retries with backoff for transient failures.
- Log:
Important product rules
-
Make notifications idempotent
- Same backend event should not create duplicate pushes if retried.
- Use an idempotency key like
event_id + notification_type + user_id.
-
Respect user preferences
- Let users choose:
- all transactional push on/off
- per category settings
- quiet hours, if appropriate
- Let users choose:
-
Keep payloads small
- Push payloads should be minimal and include only what the app needs.
- Put full details behind an in-app fetch if needed.
-
Use deep links
- The notification should open the exact screen in your app.
-
Don’t rely on push as the source of truth
- Treat push as a delivery mechanism, not the actual data store.
- The app should fetch current state from your backend after opening.
A simple event flow example
Example: invoice.paid
- Billing service marks invoice as paid.
- Backend publishes
invoice.paidevent. - Notifications worker receives the event.
- Worker finds the right recipients.
- Worker checks whether they have push enabled.
- Worker creates a notification record.
- Worker sends push via FCM/APNs.
- App opens to the invoice details page.
Suggested data model
device_tokens
iduser_idplatformtokenapp_idlocaletimezonelast_seen_atcreated_atupdated_atrevoked_at
notifications
idevent_iduser_idtypetitlebodydata_jsonstatusproviderprovider_message_idsent_atfailed_at
If you want a fast implementation
If you’re trying to ship quickly, the shortest path is usually:
- backend events in a queue
- notifications worker
- OneSignal or Firebase
- device token registration endpoint
- notification preferences
- deep links
- logging + retries
Common pitfalls
- Sending push directly from request/response cycle
- No deduplication
- No invalid token cleanup
- No user preferences
- Too much data in payload
- No retry strategy
- No audit trail for what was sent
If you want, I can help you design:
- a reference architecture
- a database schema
- an API design for device registration
- or a sample implementation in Node.js, Python, or Go