VibeWeek
Home/Grow/Mobile Push Notifications: Send Pushes Users Want, Not Pushes That Get Disabled

Mobile Push Notifications: Send Pushes Users Want, Not Pushes That Get Disabled

⬅️ Growth Overview

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:

  1. Never trust client-claimed user_id; bind token to authenticated session.
  2. Update on app launch, not just on registration.
  3. Process invalidation feedback from APNs/FCM (clean up dead tokens).
  4. 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:

  1. The token registration code
  2. The user_push_tokens schema
  3. The token-update flow on launch
  4. 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:

  1. The provider chosen
  2. The integration plan
  3. 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:

  1. One notification per category preference (per in-app-notifications): user controls which categories push.
  2. Don''t push if in-app notification suffices. If user is actively in app, suppress push (avoid duplicate ping).
  3. Quiet hours. Don''t push between 10pm and 8am user-local; defer to morning.
  4. Per-user time-zone aware. Use the user''s time zone, not server''s.
  5. 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:

  1. The event catalog with signal levels
  2. The frequency cap per tier
  3. The quiet-hours logic
  4. 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 provider
  • push.delivered: provider confirmed delivery to OS
  • push.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:

  1. The metric emission code
  2. The delivery feedback handler
  3. The dashboard structure
  4. 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:

  1. The preferences UI
  2. The category structure
  3. The OS-level-disabled detection
  4. 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:

  1. The push template per event type
  2. The deep-link map
  3. The A/B test plan
  4. 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:

  1. Handling per edge case
  2. The grouping logic
  3. 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)