Mobile Push Notifications: Send Pushes Users Want, Not Pushes That Get Disabled
Push Notification Strategy for Your New SaaS
Goal: Ship mobile push notifications that increase engagement without burning the channel — APNs (iOS) and FCM (Android) tokens collected gracefully, sent for high-signal events, batched intelligently, observed end-to-end, and respected when users opt out. Use OneSignal / Knock / Customer.io as the delivery layer; integrate with your existing in-app notifications catalog; track delivery and dismissal rates. Avoid the failure modes where founders ask for push permission on first launch (denied 70%+), send marketing pushes at 3am (uninstalls), or skip observability (silent delivery failures).
Process: Follow this chat pattern with your AI coding tool such as Claude or v0.app. Pay attention to the notes in [brackets] and replace the bracketed text with your own content.
Timeframe: Token registration + APNs/FCM integration in week 1. High-signal push triggers + tier mapping in week 2. Observability + dismissal tracking + quarterly review baked in.
Why Most Founder Push Strategies Are Broken
Three failure modes hit founders the same way:
- Ask permission on first launch. App opens for the first time; system prompt: "Allow [App] to send notifications?" User has no context; clicks "Don''t Allow"; you can never ask again on iOS without sending them to Settings. iOS push opt-in: 30-50% with bad timing; 70-80% with thoughtful timing.
- Send too aggressively. User installs app; receives 12 marketing pushes in week one; uninstalls. Or: user disables push entirely. Either way, you''ve burned the channel forever — push permission never returns easily.
- No observability. You send pushes; iOS/Android may silently drop them; you don''t know. User says "I never got the notification" and you can''t verify. Without delivery / dismissal tracking, every push is a guess.
The version that works is structured: ask permission at the right moment (after first value), send only high-signal events, batch / throttle aggressively, track delivery + dismissal rates, and respect quiet hours.
This guide assumes you have already done In-App Notifications (the broader notification system), have considered Notification Providers (Knock, Courier, OneSignal), have shipped Multi-Tenant Data Isolation (per-tenant push), and have a mobile app (web push is similar but covered by in-app-notifications).
1. Decide When to Ask for Permission
The single biggest factor in iOS push opt-in: when you ask. Get this right.
Help me decide when to ask for push permission.
The patterns:
**Pattern 1: First launch (worst)**
- App opens; immediate system prompt
- User has zero context; reflexively denies
- iOS opt-in: 25-40%
Don''t do this.
**Pattern 2: After first value**
- User completes a meaningful action (signed up, created first item, added a teammate)
- Soft prompt first ("Get notified when your team comments?")
- Then system prompt
- iOS opt-in: 60-75%
Default for most apps.
**Pattern 3: Just-in-time**
- User does an action that explicitly benefits from push ("Notify me when this finishes")
- Prompt at that exact moment
- Highest opt-in: 75-90%
Best for specific use cases.
**Pattern 4: Onboarding-tied**
- During onboarding flow, after value-prop is communicated
- Soft prompt: "Stay updated on [specific value]"
- Then system prompt
- iOS opt-in: 55-70%
Good middle ground.
**The provisional auth strategy (iOS)**:
- iOS 12+ supports "provisional" notifications (deliver quietly without asking)
- User sees them in Notification Center but they don''t alert
- After a few useful ones: ask for full permission
- Higher opt-in rates because user has seen value
**Critical implementation rules**:
1. **Soft prompt before system prompt.** A custom UI ("Get notified?") with Yes/No buttons. Yes → trigger system prompt. No → don''t prompt; ask later.
2. **Never call `requestAuthorization()` on first launch** without context.
3. **Track when you asked.** Don''t ask twice (iOS won''t show prompt again anyway).
4. **Provide a path to enable later** in your app settings.
For my product:
- What''s the moment when push value becomes obvious?
- Can I tie the ask to a meaningful action?
- Do I want provisional auth as a fallback?
Output:
1. The chosen strategy
2. The exact moment to soft-prompt
3. The opt-in target (60%+)
4. The "ask again later" mechanism
The biggest single push-effectiveness lever: timing the permission ask after first value. A 75% opt-in rate gives you 3× more reachable users than a 25% rate. Same downstream pushing strategy; vastly different addressable audience.
2. Register Tokens Properly
When user grants permission, you receive a token. Manage it carefully.
Design token registration.
The pattern:
**iOS (APNs)**:
```swift
// On app launch (after permission)
UIApplication.shared.registerForRemoteNotifications()
// Delegate method receives token
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
sendTokenToServer(token: token, platform: "ios")
}
Android (FCM):
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (!task.isSuccessful) return@addOnCompleteListener
val token = task.result
sendTokenToServer(token, platform = "android")
}
Server-side storage:
CREATE TABLE user_push_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
platform TEXT NOT NULL, -- 'ios' | 'android'
app_version TEXT,
os_version TEXT,
device_model TEXT,
device_label TEXT, -- "Alice''s iPhone"
permission_status TEXT NOT NULL DEFAULT 'authorized', -- 'authorized' | 'provisional' | 'denied'
invalidated_at TIMESTAMP,
last_used_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_push_tokens_user ON user_push_tokens(user_id) WHERE invalidated_at IS NULL;
Token rotation:
- iOS rotates tokens periodically (especially on app reinstall)
- Update on every app launch (POST current token; server reconciles)
- Mark old tokens invalidated when new ones arrive
Token cleanup:
- When APNs / FCM returns "invalid token" → mark invalidated
- When user logs out → optionally remove token (or keep for next login)
- When user uninstalls (no signal directly) → catch via send-failure response
Multi-device support:
- One user, multiple devices = multiple tokens
- Send to all active tokens
- Handle token expiry per device
Critical implementation rules:
- Never trust client-claimed user_id; bind token to authenticated session.
- Update on app launch, not just on registration.
- Process invalidation feedback from APNs/FCM (clean up dead tokens).
- Per-tenant scoping if multi-workspace (route based on context).
Don''t:
- Store raw tokens in logs (sensitive)
- Send to invalidated tokens (failures + reputation hit with providers)
- Forget to handle reinstalls (token changes)
Output:
- The token registration code
- The user_push_tokens schema
- The token-update flow on launch
- The cleanup / invalidation flow
The biggest invisible cost: **sending pushes to dead tokens.** Each dead-token send is a failed delivery; provider rate-limits accumulate; deliverability drops for legitimate sends. Process invalidation feedback aggressively.
---
## 3. Use a Push Provider (or Build Direct)
You can call APNs/FCM directly or use a service. Trade-offs.
Decide on provider.
The options:
Direct APNs / FCM (DIY):
- HTTP/2 protocol to APNs
- Firebase SDK for FCM
- You handle: certificates, token rotation, retries, delivery tracking
- Cost: free (apart from Firebase/Apple infra)
- Effort: weeks of integration
OneSignal:
- Hosted push provider; mature
- Free tier (unlimited subscribers, basic features)
- $9/mo Growth tier; scales by subscriber count
- Handles: APNs/FCM, segmentation, A/B test, in-app messaging
- 1M+ apps
Knock:
- Modern API-first notification platform
- Free tier (1K notifications/mo)
- $99/mo Starter
- Handles: multi-channel orchestration (push + email + Slack + SMS)
- Best when you''re already using Knock for in-app
Customer.io:
- Lifecycle marketing + push
- Mid-market pricing ($100/mo+)
- Behavior-triggered push as part of broader engagement
Courier:
- Multi-channel orchestration like Knock
- Free tier
- Workflow-builder approach
Firebase Cloud Messaging (FCM):
- Free; Google-hosted
- Cross-platform (iOS + Android)
- Less feature-rich than OneSignal / Knock for marketing-style sends
- Good for transactional pushes only
For most indie SaaS in 2026 with mobile app:
- OneSignal for pure push if you don''t need multi-channel
- Knock if you''re already using it for in-app + email
- FCM direct if you have engineering capacity and just need transactional
The migration question:
- Start with OneSignal / Knock (lower upfront effort)
- Migrate to direct APNs/FCM only if cost or feature limits force it
- Most indie SaaS never need to migrate
Don''t:
- Build direct APNs from scratch unless you have to
- Pay for enterprise push platforms at indie scale
- Use FCM if you also need rich engagement features (use a provider)
Output:
- The provider chosen
- The integration plan
- The cost estimate at current and projected scale
The biggest engineering-time saver: **using OneSignal or Knock instead of direct APNs/FCM**. The platforms handle the boring infrastructure (cert rotation, token cleanup, delivery tracking); you focus on the product.
---
## 4. Send Only High-Signal Events
Push is the most invasive channel. Use it carefully.
Define push-eligible events.
The pattern:
High-signal (always push):
- Direct messages / mentions: "Alice mentioned you in [Document]"
- Time-sensitive alerts: "Meeting in 5 minutes"
- Critical state changes: "Payment failed; please update card" (per dunning)
- User-triggered events: "Your export is ready"
- Real-time collaboration: "Alice is editing [Document] now" (only if user opted in)
These are events the user WANTS to know about immediately.
Medium-signal (push if user enabled):
- Comments on items the user owns
- Daily digest summary (early morning, opt-in)
- Workspace announcements (admin-driven)
Push only if user has explicitly opted in to this category.
Low-signal (don''t push):
- Marketing announcements
- Generic engagement nudges ("come back!")
- Updates on items the user doesn''t directly own
- Activity from people they don''t follow
Critical implementation rules:
- One notification per category preference (per in-app-notifications): user controls which categories push.
- Don''t push if in-app notification suffices. If user is actively in app, suppress push (avoid duplicate ping).
- Quiet hours. Don''t push between 10pm and 8am user-local; defer to morning.
- Per-user time-zone aware. Use the user''s time zone, not server''s.
- Frequency cap. Max N pushes per user per day (typically 5-10).
Anti-patterns:
- "Daily digest" push when no in-app feed
- Marketing pushes scheduled in the user''s evening
- Per-event push without user opt-in
- "We miss you" re-engagement pushes (almost always counter-productive)
Per-tier:
- Free tier: limit to 3 pushes/day max
- Paid tier: standard limits
- Enterprise: customer-configurable
For my product:
- The push-eligible events list
- The opt-in granularity
- The frequency cap
- The quiet-hours policy
Output:
- The event catalog with signal levels
- The frequency cap per tier
- The quiet-hours logic
- The user-preference UI
The single biggest reason users disable push: **pushes that don''t feel useful**. A user gets one "Alice mentioned you" push: useful. They get one "did you know we shipped a feature" push: less useful. Five of those in a week: disable push entirely. Ruthlessly curate which events qualify.
---
## 5. Track Delivery and Dismissal
Push has multiple failure modes. Observe.
Design push observability.
The metrics:
Delivery metrics:
push.sent: count of pushes sent to providerpush.delivered: provider confirmed delivery to OSpush.failed: delivery failure (invalid token, etc.)push.opened: user tapped the push (via deep-link tracking)push.dismissed: user swiped away (iOS only; harder to track)
Per-event metrics:
- Event type → push counts
- Event type → open rate
- Event type → conversion (did the user act after the push?)
Per-user metrics:
- Total pushes received this week
- Opens per push
- Last push received
Tools:
- Provider dashboards (OneSignal, Knock) show delivery/open
- Custom: track opens via deep-link parameters; pipe to analytics
Alerts:
- Push delivery rate drops <90%: investigate provider issue
- Open rate per event-type drops 50%: that event is over-pushing
- Specific user opted out of category: respect immediately
The iOS opt-in monitoring:
- Track per-user permission status over time
- Detect when users disable push (via OS notification)
- Don''t ask again; surface in-app: "Push is disabled. Re-enable in Settings?"
Delivery feedback:
- APNs returns "Unregistered" for invalidated tokens
- FCM returns similar
- Process feedback within 24 hours
Don''t:
- Skip the delivery dashboard
- Trust "sent" as success (delivery is different)
- Forget to track opens (the only conversion signal)
Output:
- The metric emission code
- The delivery feedback handler
- The dashboard structure
- The alert thresholds
The biggest invisible drift: **push delivery rate slowly declining**. A team that doesn''t track delivery sees engagement drop without knowing why. The provider may have flagged your sender; tokens may be stale; OS-level changes may have shifted behavior. Track to catch.
---
## 6. Respect User Preferences
Push permission is fragile. Lose it once; you don''t get it back.
Design preference management.
The pattern:
In-app preference UI:
- /settings/notifications page
- Per category: toggle for in-app, email, push
- Quiet-hours setting (start / end time, time zone)
- Vacation mode (pause all push for N days)
- Per-workspace overrides if multi-tenant
Granular preferences:
- Mention me: push (default ON)
- Comment on item I own: push (default ON)
- Activity in workspace: push (default OFF)
- Workspace announcements: push (default OFF)
- Marketing: push (default OFF)
The "all categories off" path:
- User can disable all push categories
- They''re still authorized at OS level
- You stop sending any pushes
- Don''t treat as "they hate us"; respect
The "OS-level disabled" path:
- User went to iOS Settings > [App] > Notifications and disabled
- You receive no signal directly
- Detect via APNs response on first failed delivery
- Mark token as denied
- Don''t resurrect without user explicit re-enable
Re-engagement after opt-out:
For users who disabled push:
- DON''T spam in-app to "re-enable push"
- Subtle settings: "Push is disabled — manage in Settings"
- Wait for natural moment (user does action where push would help): "Want a notification when this finishes? [Re-enable push]"
For users who never opted in:
- Don''t reprompt aggressively
- Soft re-prompts at value moments only
- Track: "asked at [time]; outcome [denied]; next ask after [N] days minimum"
Don''t:
- Send pushes ignoring user preferences
- Re-prompt for permission daily
- Hide the disable-all option
Output:
- The preferences UI
- The category structure
- The OS-level-disabled detection
- The re-engagement policy
The single biggest user-trust signal: **respecting preferences immediately.** A user who turns off marketing push and gets one anyway in the next week loses trust permanently. Process preference changes in real-time; never delay.
---
## 7. Optimize Push Content
The content of the push affects open rate. Craft carefully.
Optimize push content.
The pattern:
Title (40 chars max):
- Include sender name when relevant: "Alice"
- Or context: "[Workspace name]"
- Or category: "New mention"
Body (180 chars max for visible):
- The actual content of the notification
- Personalize: "[Document title] has 3 new comments"
- Include actionable info: "Meeting starts in 5 min"
Anti-patterns:
- "Hi [Name], we wanted to let you know..." (wastes characters)
- Marketing speak ("Don''t miss out!")
- ALL CAPS
- Generic ("You have new activity")
Rich content (iOS / Android):
- Image: small icon or attached photo
- Action buttons: "Reply" / "Mark as read" / "View"
- Categorization for grouping multiple
Deep linking:
- Push tapped → open specific page in app
- URL scheme:
myapp://document/123 - Track deep-link conversion
A/B test:
- Subject lines
- Send time
- Frequency
- Use provider features (OneSignal supports A/B test)
Localization:
- Send in user''s language
- Per-user locale stored
- Translate templates per locale
The "sound" decision:
- Default: silent push (users hate noisy)
- For urgent: critical sound (Apple has guidelines; misuse = rejection)
Don''t:
- Use marketing language in transactional pushes
- Skip the deep link
- Send without localization for international users
Output:
- The push template per event type
- The deep-link map
- The A/B test plan
- The localization plan
The biggest single open-rate optimization: **personalization in the body**. "Alice mentioned you in [Project]" beats "You have a new mention." Specific names + specific context drive opens; generic copy gets ignored.
---
## 8. Handle Edge Cases
Real push has weird cases. Plan for them.
The edge case checklist.
Edge case 1: User logs out / switches accounts
- Old tokens still tied to previous user
- Fix: clear/reassign on logout; bind to new user on login
Edge case 2: Multiple devices per user
- iPhone + iPad + multiple Macs (with iOS apps via Catalyst)
- Send to all active tokens
- Track per-device opens
Edge case 3: User in airplane mode
- Push queues at provider; delivers when online
- iOS APNs holds for ~14 days
- Don''t resend if delivery succeeds eventually
Edge case 4: Notification grouping
- 10 mentions arrive in 5 min — flood
- Use thread ID to group on iOS
- Or: batch into one "10 new mentions" push
Edge case 5: Critical alerts
- iOS has "Time Sensitive" + "Critical" categories
- Bypass quiet hours (sparingly!)
- Apple reviews critical-alert use; misuse = reject
Edge case 6: Push during onboarding
- User just signed up; first push within 5 minutes feels intrusive
- Defer first push by 24 hours unless user-triggered
Edge case 7: User in different time zone than server
- Quiet hours need user time zone
- Detect from device or ask during onboarding
Edge case 8: Account-deletion in flight
- User deletes account; queued pushes still in pipeline
- Cancel pending; process pending data per account-deletion
Edge case 9: Multi-tenant push routing
- User in workspace A wants pushes; in workspace B doesn''t
- Per-workspace preferences; route accordingly
Edge case 10: APNs / FCM outage
- Provider returns 5xx
- Retry with backoff
- Eventually fall back to email or in-app only
Output:
- Handling per edge case
- The grouping logic
- The retry / fallback policy
---
## 9. Quarterly Review
Push performance drifts. Quarterly review keeps it sharp.
Quarterly push review.
Health:
- Opt-in rate trend (per platform)
- Delivery rate
- Open rate per event type
- Per-user volume distribution
Engagement:
- Open rate trend
- Conversion (did opens lead to in-product action?)
- Top events by open rate
- Bottom events by open rate (kill candidates)
Cost:
- Provider fees
- Per-push cost
- ROI vs other channels
Compliance:
- Privacy policy current?
- User-preference flow respected?
- Localization current?
Output:
- Health snapshot
- 1-2 events to deprecate
- 1 event to add
- 1 process improvement
---
## What "Done" Looks Like
A working mobile push system in 2026 has:
- Permission asked at the right moment (60%+ opt-in target)
- Token registration with proper rotation
- A push provider (OneSignal, Knock, FCM direct)
- High-signal events only (no marketing-as-push)
- Per-category user preferences
- Quiet hours enforced per-user time zone
- Frequency caps to prevent flooding
- Delivery + open observability
- Deep-linking from push to specific pages
- Localization per user
- Edge-case handling (multi-device / outages / deletion)
- Quarterly review baked in
The hidden cost of poor push: **a user who disabled push due to abuse never re-enables.** You''ve lost the channel forever. The discipline of "push only what users want, when they want, in the way they want" sustains the channel for years; ignore it once and the damage is permanent. Push is the most invasive channel; treat it with the most respect.
---
## See Also
- [In-App Notifications](in-app-notifications-chat.md) — broader notification system
- [Email Deliverability](email-deliverability-chat.md) — companion channel
- [Onboarding Email Sequence](onboarding-email-sequence-chat.md) — email-first onboarding
- [Activation Funnel](activation-funnel-chat.md) — where push fits
- [Account Deletion & Data Export](account-deletion-data-export-chat.md) — push tokens cleaned up
- [Multi-Tenant Data Isolation](multi-tenancy-chat.md) — workspace push routing
- [Roles & Permissions](roles-permissions-chat.md) — who gets which pushes
- [Notification Providers](https://www.vibereference.com/backend-and-data/notification-providers) — Knock / Courier / OneSignal
- [Email Marketing Providers](https://www.vibereference.com/marketing-and-seo/email-marketing-providers) — Customer.io includes push
- [Customer Feedback Surveys](customer-feedback-surveys-chat.md) — alternative to push for re-engagement
[⬅️ Growth Overview](README.md)