Best Practices

AI Rules for Push Notifications

AI requests push permission on page load and sends the same notification to everyone. Rules for progressive permission, service worker registration, payload design, segmentation, and frequency capping.

7 min read·March 18, 2025

Permission prompt on first visit, same message to everyone, 5 notifications per hour — permanently blocked

Progressive permission, service worker push, rich payloads, segmentation, frequency capping

AI Spams Users Into Blocking Notifications

AI generates push notifications with: an immediate permission prompt on first visit (user has no relationship with the site yet — they block), the same message sent to all subscribers (a React tutorial notification sent to a Python developer), no frequency capping (5 notifications in one hour drives users to unsubscribe), plain text payloads only (no images, no action buttons, no deep links), and no opt-out mechanism besides browser settings (users cannot selectively disable categories). Users who block push notifications can never be prompted again — the permission is permanently denied.

Modern push notifications are: progressively requested (after value is demonstrated, not on first visit), segmented (target by interest, behavior, and preference), frequency-capped (maximum 2-3 per day, configurable per user), rich (title, body, icon, image, action buttons, deep link URL), and preference-managed (users choose notification categories from an in-app preference center). AI generates none of these.

These rules cover: progressive permission UX, service worker registration and push handling, rich notification payloads, audience segmentation, frequency capping, and opt-out preference management.

Rule 1: Progressive Permission Requests

The rule: 'Never request push permission on the first visit. Request after the user has demonstrated engagement: read 3 articles, completed a purchase, or used the app for a week. Before the browser prompt, show an in-app prompt explaining the value: "Get notified when articles in your favorite categories are published. You can customize which notifications you receive." The in-app prompt can be dismissed (user says no without triggering the permanent browser block). Only trigger the browser prompt after the user clicks "Enable notifications" on your in-app prompt.'

For the two-step pattern: '(1) In-app soft prompt (custom UI, dismissible, explains value, shows preview of a notification). (2) If the user clicks "Enable": trigger the browser permission prompt. (3) If the user dismisses: do not show again for 30 days (respect the no). (4) If the browser prompt is denied: never ask again (it is permanent). The two-step pattern: users who click dismiss on the soft prompt would have clicked block on the browser prompt. By intercepting with a soft prompt, the browser prompt is reserved for users who want notifications — resulting in 3x higher opt-in rates.'

AI generates: useEffect(() => { Notification.requestPermission(); }, []) — browser prompt on first mount. User has never used the site, does not know what notifications they would receive, clicks Block. Permission permanently denied. The site can never ask again. With the two-step pattern: only engaged users see the browser prompt, opt-in rates triple, and no permissions are permanently wasted.

  • Two-step: soft in-app prompt first, browser prompt only after user clicks Enable
  • Request after engagement: 3 articles read, purchase completed, or 1 week of usage
  • Soft prompt is dismissible: do not re-show for 30 days after dismissal
  • Browser deny is permanent: never waste it on unengaged visitors
  • 3x higher opt-in with two-step vs immediate browser prompt
💡 3x Higher Opt-In

Immediate browser prompt: user blocks, permission permanently denied. Two-step soft prompt: dismiss does not burn the browser permission. Only engaged users see the real prompt. Result: 3x higher opt-in rate, zero wasted permanent denials.

Rule 2: Service Worker Push Handling

The rule: 'Register a service worker that handles push events: self.addEventListener("push", (event) => { const data = event.data.json(); event.waitUntil(self.registration.showNotification(data.title, { body: data.body, icon: data.icon, badge: data.badge, image: data.image, actions: data.actions, data: { url: data.url } })); }). Handle notification clicks: self.addEventListener("notificationclick", (event) => { event.notification.close(); event.waitUntil(clients.openWindow(event.notification.data.url)); }). The service worker runs in the background — push notifications work even when the tab is closed.'

For subscription management: 'On permission grant: const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: VAPID_PUBLIC_KEY }). Send the subscription object to your server: POST /api/push/subscribe with { endpoint, keys: { p256dh, auth } }. Your server stores the subscription and uses web-push library to send: webpush.sendNotification(subscription, JSON.stringify(payload)). VAPID keys authenticate your server with the push service (FCM, Mozilla Push).'

AI generates: push subscription with no click handler. The notification appears, the user clicks it, nothing happens (no navigation). Or: the click opens the homepage instead of the relevant content. The notificationclick handler with event.notification.data.url: each notification deep-links to the relevant page. The user clicks a notification about a new article and lands on that article — not the homepage.

Rule 3: Rich Notification Payloads with Actions

The rule: 'Send rich notifications with: title (50 chars max — concise, actionable), body (100 chars — the key information), icon (your logo, 192x192, recognizable in the notification tray), badge (small monochrome icon for the notification bar on Android), image (hero image for visual notifications — articles, products), actions (up to 2 buttons: "Read Article" and "Save for Later"), and data.url (deep link for click navigation).'

For action buttons: 'Notification actions provide quick responses without opening the app: actions: [{ action: "read", title: "Read Now", icon: "/icons/read.png" }, { action: "later", title: "Save for Later", icon: "/icons/bookmark.png" }]. Handle in the service worker: if (event.action === "read") openWindow(url); else if (event.action === "later") saveToReadingList(url). Actions reduce friction: the user responds to the notification without navigating through the app.'

AI generates: showNotification('New article', { body: 'A new article was published' }) — no icon (browser default), no image, no actions, no deep link, and a generic body that says nothing about what the article is. Rich notification: title "AI Rules for Maps", body "Lazy loading, clustering, and cost management", your logo icon, a hero image, and "Read Now" / "Save" action buttons. The notification is a mini-preview of the content, not a vague alert.

  • Title: 50 chars, concise and actionable — 'New: AI Rules for Maps Integration'
  • Body: 100 chars, the key value proposition — what the user gets by clicking
  • Icon: 192x192 app logo — recognizable in the notification tray
  • Image: hero image for visual content — articles, products, events
  • Actions: up to 2 buttons with direct responses — 'Read Now', 'Save for Later'

Rule 4: Audience Segmentation and Targeting

The rule: 'Segment subscribers by: interests (categories they read or follow), behavior (active daily, weekly, dormant), subscription recency (new subscribers get onboarding sequence, long-term get re-engagement), and explicit preferences (categories selected in the preference center). Send notifications to relevant segments only: a React article notification goes to subscribers who follow React, not to Python developers. Irrelevant notifications are the primary driver of unsubscribes.'

For the subscription data model: 'Table push_subscriptions: { id, userId, endpoint, keys, createdAt, lastActiveAt }. Table push_preferences: { userId, category, enabled }. When sending: SELECT s.* FROM push_subscriptions s JOIN push_preferences p ON s.userId = p.userId WHERE p.category = 'react' AND p.enabled = true AND s.lastActiveAt > now() - interval '30 days'. This targets: users who follow React, have notifications enabled for that category, and were active in the last 30 days. Not everyone. Not dormant users. Not uninterested users.'

AI generates: webpush.sendNotification for every subscription in the database. 10,000 subscribers, 10% interested in React: 9,000 irrelevant notifications. 9,000 users annoyed, some unsubscribe, notification channel credibility eroded. Segmented send: 1,000 relevant notifications, high click-through rate, zero annoyance. Send less, target better, retain more subscribers.

⚠️ 9,000 Irrelevant Notifications

10,000 subscribers, 10% interested in React: blast everyone = 9,000 irrelevant notifications, annoyed users, unsubscribes. Segmented send: 1,000 relevant notifications, high click-through, zero annoyance. Send less, target better, retain more.

Rule 5: Frequency Capping and Opt-Out Management

The rule: 'Cap notification frequency per user: maximum 2-3 notifications per day, maximum 1 per hour (except breaking/critical alerts). Track sends per user: before sending, check if the user has received their daily maximum. If exceeded: queue the notification for tomorrow or skip if time-sensitive. Users who receive too many notifications disable them entirely — frequency capping preserves the channel long-term.'

For the preference center: 'Provide an in-app notification preference page: list of categories with toggles (Articles: ON, Product Updates: ON, Marketing: OFF). A "Pause all" option (mute for 1 day, 1 week, or 1 month — not permanent unsubscribe). A frequency control: "Notify me: immediately / daily digest / weekly digest." An unsubscribe link in every notification payload (not just browser settings). Make opting out easy — the alternative is the user blocking notifications permanently in browser settings, which you cannot undo.'

AI generates: no frequency limits, no preferences, no unsubscribe mechanism. 5 notifications in one hour: user opens browser settings, blocks notifications from your site. Permission permanently revoked. You cannot ask again. With frequency capping + preference center: the user adjusts their preferences (fewer notifications, only certain categories). They stay subscribed. The channel remains available for future use.

ℹ️ Block Is Permanent

5 notifications in one hour: user opens browser settings, blocks your site. Permission permanently revoked. You cannot ask again, ever. Frequency capping (max 2-3/day) + preference center preserves the channel. The user adjusts instead of blocking.

Complete Push Notification Rules Template

Consolidated rules for push notifications.

  • Two-step permission: soft in-app prompt first, browser prompt only after Enable click
  • Request after engagement: not first visit — 3 articles, 1 purchase, or 1 week of usage
  • Service worker: push event handler + notificationclick with deep link navigation
  • Rich payloads: title, body, icon, image, action buttons, deep link URL
  • Audience segmentation: interests, behavior, recency — irrelevant = unsubscribes
  • Frequency cap: max 2-3/day, max 1/hour — preserve the channel long-term
  • Preference center: category toggles, pause option, frequency control, easy unsubscribe
  • Browser deny is permanent: never waste the prompt on unengaged visitors