From 5fd0cfd6c28c951cd903041e8ee043f01985e913 Mon Sep 17 00:00:00 2001 From: Martin DONADIEU Date: Thu, 28 May 2026 14:00:19 +0200 Subject: [PATCH] docs: add native notifications guide --- apps/docs/src/config/llmsCustomSets.ts | 1 + apps/docs/src/config/sidebar.mjs | 1 + .../docs/plugins/notifications/debugging.mdx | 301 ++++++++++++++++++ .../plugins/notifications/getting-started.mdx | 279 ++++++++++++++++ .../docs/docs/plugins/notifications/index.mdx | 107 +++++++ apps/web/src/config/plugins.ts | 2 + .../en/capacitor-notifications.md | 108 +++++++ apps/web/src/data/github-stars.json | 7 +- apps/web/src/data/github-stats.json | 125 ++++---- apps/web/src/data/npm-downloads.json | 73 ++--- 10 files changed, 907 insertions(+), 97 deletions(-) create mode 100644 apps/docs/src/content/docs/docs/plugins/notifications/debugging.mdx create mode 100644 apps/docs/src/content/docs/docs/plugins/notifications/getting-started.mdx create mode 100644 apps/docs/src/content/docs/docs/plugins/notifications/index.mdx create mode 100644 apps/web/src/content/plugins-tutorials/en/capacitor-notifications.md diff --git a/apps/docs/src/config/llmsCustomSets.ts b/apps/docs/src/config/llmsCustomSets.ts index 89b33811d..78caddb71 100644 --- a/apps/docs/src/config/llmsCustomSets.ts +++ b/apps/docs/src/config/llmsCustomSets.ts @@ -74,6 +74,7 @@ Plugin Native Biometric|biometric authentication plugin for fingerprint and face Plugin Native Geocoder|native geocoding plugin for address lookup|docs/plugins/nativegeocoder/** Plugin Native Market|app store deep linking plugin|docs/plugins/native-market/** Plugin Native Navigation|native navbar, tabbar, and transition shell plugin for Capacitor WebView apps|docs/plugins/native-navigation/** +Plugin Notifications|native push notifications, badges, campaign stats, device lookup, and silent live-update checks|docs/plugins/notifications/** Plugin Native Purchases|in-app purchases, subscriptions, paywalls, revenue playbook, and monetization plugin|docs/plugins/native-purchases/** Plugin Navigation Bar|Android navigation bar customization plugin|docs/plugins/navigation-bar/** Plugin NFC|NFC reading and writing plugin|docs/plugins/nfc/** diff --git a/apps/docs/src/config/sidebar.mjs b/apps/docs/src/config/sidebar.mjs index 236582d85..688af800f 100644 --- a/apps/docs/src/config/sidebar.mjs +++ b/apps/docs/src/config/sidebar.mjs @@ -136,6 +136,7 @@ const pluginEntries = [ ['Native Geocoder', 'nativegeocoder'], ['Native Market', 'native-market'], ['Native Navigation', 'native-navigation'], + ['Notifications', 'notifications', [linkItem('Debugging', '/docs/plugins/notifications/debugging')]], section( 'Native Purchases', [ diff --git a/apps/docs/src/content/docs/docs/plugins/notifications/debugging.mdx b/apps/docs/src/content/docs/docs/plugins/notifications/debugging.mdx new file mode 100644 index 000000000..deb7e7947 --- /dev/null +++ b/apps/docs/src/content/docs/docs/plugins/notifications/debugging.mdx @@ -0,0 +1,301 @@ +--- +title: Debugging +description: "Troubleshoot Capgo native notification setup, device registration, delivery, foreground events, background notifications, badges, and silent update checks." +sidebar: + order: 3 +--- + +Use this checklist when a notification does not register, does not arrive, does not show, or does not update Capgo stats. + +## Start With The Device Record + +Before debugging native code, confirm that Capgo can see the device. + +1. Open the app and sign in as the user you want to test. +2. Call `CapgoNotifications.register(...)` after sign-in. +3. In Capgo, open **Notifications > Recipient lookup**. +4. Search by the same external customer ID. + +You should see at least one active device with: + +- `recipientKey` +- `deviceKey` +- platform `android` or `ios` +- permission state +- app version +- plugin version +- tags and attributes + +If lookup returns no device, the send path cannot target that user. + +## Add Temporary Debug Listeners + +Add temporary listeners while testing. Remove noisy logs before shipping. + +```typescript +await CapgoNotifications.addListener('registrationChanged', (token) => { + console.log('[CapgoNotifications] registrationChanged', token.value.slice(0, 12)) +}) + +await CapgoNotifications.addListener('notificationReceived', (notification) => { + console.log('[CapgoNotifications] notificationReceived', notification.id, notification.data) +}) + +await CapgoNotifications.addListener('notificationOpened', (event) => { + console.log('[CapgoNotifications] notificationOpened', event.notification.id, event.actionId) +}) + +await CapgoNotifications.addListener('backgroundNotification', async (event) => { + console.log('[CapgoNotifications] backgroundNotification', event.notification.id, event.notification.data) + await event.finish() +}) +``` + +## Collect This Information + +When debugging with your team or Capgo support, collect: + +- Capgo app ID. +- App package ID or iOS bundle ID. +- Device platform and OS version. +- App version and build number. +- Plugin version. +- External customer ID. +- `recipientKey` and `deviceKey` from registration or recipient lookup. +- Campaign ID or notification ID. +- Whether the app was foreground, background, force-closed, or freshly installed. +- Device logs from the run that reproduced the issue. + +## Use Device Logs + +Keep one real device connected while you send a test notification. + +On Android: + +- Open Android Studio Logcat. +- Filter by the app package ID. +- Watch for the notification permission request, native token refresh, message receive, and JavaScript listener logs. +- If a visible notification does not show, inspect the notification channel importance and Android 13+ permission state first. + +On iOS: + +- Run the app from Xcode on a physical device. +- Open the Xcode console or **Devices and Simulators** logs. +- Filter by the bundle ID and `CapgoNotifications`. +- Confirm `AppDelegate.swift` forwards remote notifications and that the background mode capability is enabled. + +Send one foreground test first, then one background test, then one silent update-check test. This order separates JavaScript listener issues from OS background delivery limits. + +## Registration Problems + +### CLI Setup Did Not Finish + +Run the setup command from the folder that contains `capacitor.config.*`: + +```bash +npx @capgo/cli@latest notifications setup com.example.app +``` + +If the command cannot infer your app ID, pass it explicitly as shown above. If package installation fails, confirm Capgo has enabled private-preview package access for your npm account, then rerun the command. + +### Device Does Not Appear In Recipient Lookup + +Check: + +- `register` is called after your app has an authenticated user. +- `externalId` matches the user ID you search in the dashboard. +- `identityProof` was minted by your backend for the same `appId` and `externalId`. +- `appId` in `configure` matches the Capgo app. +- `consent` is not set to `false` unless the user opted out. +- The device has network access to `https://api.capgo.app`. +- The native push token was created. Use `registrationChanged` to confirm token refresh. + +### Invalid Identity Proof + +The proof is bound to the Capgo app ID and external ID. If either value changes, mint a new proof. + +Do not cache one proof forever or reuse a proof across apps. Mint it from your backend after login, return it to the app, and call `register`. + +### Device Registered But Has Permission Denied + +The plugin can register device state even when the user denied permission. You can still see the device, but visible notifications will not show. + +Use a permission primer screen before the OS prompt. Explain what the user gets, then ask for permission only when the action makes sense. + +## Delivery Problems + +### Queued But Not Sent + +Check: + +- Platform credential status is `configured` in Capgo. +- The worker environment contains the exact secret reference shown by the dashboard. +- The package ID or bundle ID in the app matches the platform push setup. +- The target audience resolves to at least one active device. +- The campaign is not limited to a tag or segment the device does not have. + +### Sent But Not Received + +Check: + +- The device is online. +- The app was not force-stopped by the user. +- The OS notification permission is granted. +- Android battery restrictions are not blocking the app during testing. +- iOS Low Power Mode and background refresh restrictions are not affecting background delivery. +- The notification was not replaced by another notification with the same collapse ID. + +Native push platforms can accept a notification and still delay, throttle, coalesce, or drop delivery later. Treat provider accepted stats as "accepted for delivery", not proof that the device displayed it. + +### Received But Not Displayed + +Check: + +- The app was not foregrounded. Foreground notifications are usually delivered to JavaScript so your app can decide what UI to show. +- Android notification channel importance is high enough to display an alert. +- Android 13+ notification permission is granted. +- iOS Focus, notification summary, or per-app notification settings are not hiding the notification. +- Badge clearing or app-open logic is not removing delivered notifications during testing. + +## Background Notification Problems + +### Background Callback Does Not Run + +Background notifications are best-effort. The OS can skip them. + +Check: + +- iOS has **Background Modes > Remote notifications** enabled. +- iOS `AppDelegate.swift` forwards remote notifications to `CapgoNotificationsRemoteNotification`. +- You test iOS background behavior on a physical device. +- The app was not force-quit by the user. +- The background handler calls `finish()`. +- Work inside the callback is short, network-safe, and idempotent. + +On iOS, background pushes may be throttled if you send too many, use too much time, or the user rarely opens the app. This is expected platform behavior. + +### Background Started But Not Finished + +If stats show `background_started` without `background_finished`, the JavaScript handler likely threw, timed out, or did not call `finish()`. + +Wrap the handler in `try/finally`: + +```typescript +await CapgoNotifications.addListener('backgroundNotification', async (event) => { + try { + await doShortBackgroundWork(event.notification.data) + } finally { + await event.finish() + } +}) +``` + +## Silent Update Check Problems + +### Update Check Notification Arrives But No Update Installs + +Check: + +- `@capgo/capacitor-updater` is installed and configured. +- `autoUpdater` is `true` or `enableUpdaterIntegration` was called. +- The app's Notifications settings allow push update checks. +- The target device belongs to the channel you expect. +- The app has a newer bundle available in Capgo. +- Your update install mode is correct: `next` queues for the next restart or background cycle, `set` installs as soon as the updater can safely do it. + +Run a manual check while the app is open: + +```typescript +const result = await CapgoNotifications.runUpdateCheck({ + enabled: true, + installMode: 'next', +}) + +console.log(result) +``` + +If the manual check returns `unavailable`, inspect the updater plugin setup first. + +## Badge Problems + +Check: + +- The target resolves to the right device in recipient lookup. +- The platform supports app badges for the launcher or home screen being tested. +- The user has not disabled badges in OS notification settings. +- The app does not clear badges immediately on startup. +- You are not racing local `setBadge` calls against backend badge sends. + +## Stats Problems + +### Stats Look Duplicated + +Notification sending is at-least-once. Queue retry and platform retry can duplicate a send. Use notification IDs and collapse IDs when your app action must be idempotent. + +### Stats Are Missing For Old Devices + +The Analytics Engine registry is for active devices, not a forever database. The plugin should refresh registration on app start, token refresh, external ID change, and periodically before the active-device retention window. + +### Open Events Are Missing + +Check: + +- The notification includes a stable `id`. +- `notificationOpened` listener is registered during app startup. +- The app is not replacing the native open flow with custom code before the plugin sees it. +- The user actually tapped the notification rather than opening the app manually. + +## API Debug Commands + +Lookup a recipient: + +```bash +curl -X POST 'https://api.capgo.app/notifications/recipients/lookup' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: CAPGO_API_KEY' \ + -d '{ + "appId": "com.example.app", + "externalId": "customer-user-123" + }' +``` + +Read stats: + +```bash +curl 'https://api.capgo.app/notifications/stats?app_id=com.example.app&days=7' \ + -H 'x-api-key: CAPGO_API_KEY' +``` + +Send a foreground test: + +```bash +curl -X POST 'https://api.capgo.app/notifications/send' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: CAPGO_API_KEY' \ + -d '{ + "appId": "com.example.app", + "target": { "externalId": "customer-user-123" }, + "payload": { + "title": "Capgo test", + "body": "Open this notification to test events.", + "data": { "debug": "true" } + } + }' +``` + +## Common Root Causes + +| Symptom | Likely cause | +| --- | --- | +| Device missing from lookup | `register` not called, proof mismatch, consent false, app ID mismatch. | +| Permission denied | OS prompt denied or not requested yet. | +| Queued but no sent stats | Platform credentials are missing or disabled. | +| Sent but no received stats | Device offline, OS throttling, app force-stopped, or token invalid. | +| Foreground notification logs but no banner | App is foregrounded and must show its own in-app UI. | +| Background never runs on iOS | Missing capabilities, missing AppDelegate forwarding, force-quit app, or OS throttling. | +| Update check does nothing | Updater integration disabled, no newer bundle, wrong channel, or install mode misunderstood. | +| Badge resets | App startup code clears badges or local and backend badge writes race. | + +## Keep going from Debugging + +After the device registers and a test notification works, use [Getting Started](/docs/plugins/notifications/getting-started/) to wire badges, campaign targeting, and silent update checks into your production app. diff --git a/apps/docs/src/content/docs/docs/plugins/notifications/getting-started.mdx b/apps/docs/src/content/docs/docs/plugins/notifications/getting-started.mdx new file mode 100644 index 000000000..b0ceacc28 --- /dev/null +++ b/apps/docs/src/content/docs/docs/plugins/notifications/getting-started.mdx @@ -0,0 +1,279 @@ +--- +title: Getting Started +description: "Install @capgo/capacitor-notifications, configure platform credentials, register devices, send test notifications, and enable silent update checks." +sidebar: + order: 2 +--- + +`@capgo/capacitor-notifications` is the first-party Capgo plugin for native iOS and Android push notifications. It is built for Capgo's dashboard, public API, Analytics Engine device registry, campaign stats, badge updates, and silent live-update checks. + +The package is currently in private preview. Capgo must enable package access for your npm account before the install command works. + +## Requirements + +- A Capacitor app already added to Capgo. +- Access to the Capgo app's Notifications tab. +- A Capgo API key with write access for backend proof minting and API sends. +- iOS and/or Android platform push authority for the app. +- `@capgo/capacitor-updater` if you want silent push update checks. + +## 1. Configure Capgo Platform Credentials + +Open the app in Capgo, then go to **Notifications**. + +Add one platform credential entry for each platform you want to support: + +- **Android** - app package ID and Android push project metadata. +- **iOS** - bundle ID, team ID, key ID, and the matching iOS push key metadata. + +Capgo shows the exact environment secret name that must exist in the API worker before the platform is marked configured. The dashboard stores metadata and the expected secret reference. The private credential itself stays in the worker environment. + +## 2. Install + +For the fastest setup, run the Capgo CLI from your app project: + +```bash +npx @capgo/cli@latest notifications setup com.example.app +``` + +The command installs the notification package, saves the Capacitor plugin config, creates a small helper file, and runs Capacitor sync. Use this path for new apps unless you need to wire every file manually. + +Manual install: + +```bash +npm install @capgo/capacitor-notifications @capgo/capacitor-updater +npx cap sync +``` + +If you are not using silent Capgo update checks, you can omit `@capgo/capacitor-updater`. + +## 3. Configure The Plugin + +Configure the plugin once when your app starts. + +```typescript +import { CapgoNotifications } from '@capgo/capacitor-notifications' + +await CapgoNotifications.configure({ + appId: 'com.example.app', + autoUpdater: true, + updateInstallMode: 'next', +}) +``` + +Use `updateInstallMode: 'next'` to download an update and install it on the next restart or background cycle. Use `updateInstallMode: 'set'` only when you want Capgo to install the update as soon as the updater can safely do it. + +## 4. Mint An Identity Proof + +Do not put your Capgo API key in the mobile app. Your backend should ask Capgo for an `identityProof` after your own user authentication succeeds. + +```bash +curl -X POST 'https://api.capgo.app/notifications/recipients/proof' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: CAPGO_API_KEY' \ + -d '{ + "appId": "com.example.app", + "externalId": "customer-user-123" + }' +``` + +Return the `identityProof` to the app with your own session response. + +## 5. Register The Device + +Register only after you know which customer user is signed in. + +```typescript +const registration = await CapgoNotifications.register({ + externalId: 'customer-user-123', + identityProof, + tags: ['paid', 'beta'], + attributes: { + plan: 'team', + locale: 'en-US', + }, + consent: true, +}) + +console.log(registration.recipientKey, registration.deviceKey) +``` + +Call `register` again when: + +- The app starts. +- The native push token changes. +- The signed-in user changes. +- Tags, attributes, or consent change. +- The app has not refreshed registration for a long time. + +## 6. Add Event Listeners + +Register listeners during app startup so foreground, opened, and background events are visible to JavaScript. + +```typescript +await CapgoNotifications.addListener('registrationChanged', () => { + void CapgoNotifications.register({ + externalId: currentUser.id, + identityProof: currentUser.capgoNotificationProof, + tags: currentUser.notificationTags, + consent: currentUser.pushConsent, + }) +}) + +await CapgoNotifications.addListener('notificationReceived', (notification) => { + console.log('Notification received', notification) +}) + +await CapgoNotifications.addListener('notificationOpened', (event) => { + console.log('Notification opened', event.notification.id) +}) + +await CapgoNotifications.addListener('backgroundNotification', async (event) => { + try { + console.log('Background notification', event.notification.data) + } finally { + await event.finish() + } +}) +``` + +Always call `finish()` for background notifications after your work is done. Keep the work short and idempotent. + +## 7. iOS Setup + +In Xcode, open the app target and enable: + +- **Push Notifications** +- **Background Modes > Remote notifications** + +Forward remote notifications from `ios/App/App/AppDelegate.swift`: + +```swift +func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + NotificationCenter.default.post(name: Notification.Name("CapgoNotificationsRemoteNotification"), object: userInfo) + completionHandler(.newData) +} +``` + +Then run: + +```bash +npx cap sync ios +``` + +Use a physical iOS device when testing background notifications. Simulators are useful for UI work but do not represent production background push behavior. + +## 8. Android Setup + +Run: + +```bash +npx cap sync android +``` + +Then verify: + +- Your Android platform credential is configured in Capgo. +- The app package ID matches the package ID used for platform push setup. +- Android 13+ notification permission is requested before expecting visible notifications. +- The app has a notification icon and channel strategy that matches your brand. +- You test on a physical device or emulator with Google Play services. + +Create a default Android channel when your app starts: + +```typescript +await CapgoNotifications.configure({ appId: 'com.example.app' }) + +await CapgoNotifications.register({ + externalId: currentUser.id, + identityProof: currentUser.capgoNotificationProof, + consent: true, +}) +``` + +The plugin declares the Android push messaging service. Keep app backup, data extraction, network security, and notification icon policy in the host app. + +## 9. Send A Test Notification + +Use **Notifications > Test send** in Capgo, or call the public API from your backend: + +```bash +curl -X POST 'https://api.capgo.app/notifications/send' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: CAPGO_API_KEY' \ + -d '{ + "appId": "com.example.app", + "target": { "externalId": "customer-user-123" }, + "payload": { + "title": "Hello from Capgo", + "body": "This is a test notification.", + "data": { "screen": "inbox" } + } + }' +``` + +For a campaign, create it in the dashboard or call `/notifications/campaigns`, then send to an external ID, tag, segment, or broadcast audience. + +## 10. Set Badges + +```typescript +await CapgoNotifications.setBadge(4) +await CapgoNotifications.incrementBadge() +await CapgoNotifications.clearBadge() +``` + +From your backend: + +```bash +curl -X POST 'https://api.capgo.app/notifications/badge' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: CAPGO_API_KEY' \ + -d '{ + "appId": "com.example.app", + "target": { "externalId": "customer-user-123" }, + "badge": 4 + }' +``` + +## 11. Enable Silent Update Checks + +Silent update checks connect this plugin with `@capgo/capacitor-updater`. + +In the app: + +```typescript +await CapgoNotifications.enableUpdaterIntegration({ + enabled: true, + installMode: 'next', +}) +``` + +In Capgo, enable **Push update to users** in the app's Notifications settings. Then send an update check from the dashboard or API: + +```bash +curl -X POST 'https://api.capgo.app/notifications/update-check' \ + -H 'Content-Type: application/json' \ + -H 'x-api-key: CAPGO_API_KEY' \ + -d '{ + "appId": "com.example.app", + "target": { "externalId": "customer-user-123" }, + "installMode": "next" + }' +``` + +The notification is silent and uses a collapse ID so repeated update checks replace each other when the platform supports collapse behavior. + +## Validation Checklist + +- The app appears in Capgo recipient lookup for the expected `externalId`. +- Permission is `granted` or the user has accepted notification permission. +- The registered platform is `android` or `ios`. +- `registrationChanged` fires after a token refresh. +- A foreground test logs `notificationReceived`. +- Opening a notification logs `notificationOpened`. +- Dashboard stats show queued and sent events, then received/opened when the device reports them. +- Silent update checks log a result from `runUpdateCheck` or the updater integration. + +## Keep going from Getting Started + +If setup does not work, use [Debugging](/docs/plugins/notifications/debugging/) before changing app code. Most failures are caused by identity proof mismatch, platform credential setup, OS permission state, background throttling, or app/package ID mismatch. diff --git a/apps/docs/src/content/docs/docs/plugins/notifications/index.mdx b/apps/docs/src/content/docs/docs/plugins/notifications/index.mdx new file mode 100644 index 000000000..0af6c980f --- /dev/null +++ b/apps/docs/src/content/docs/docs/plugins/notifications/index.mdx @@ -0,0 +1,107 @@ +--- +title: "@capgo/capacitor-notifications" +description: "Send native iOS and Android push notifications from Capgo without storing a per-device notification table in Capgo Postgres." +tableOfContents: false +next: false +prev: false +sidebar: + order: 1 + label: "Introduction" +hero: + tagline: "Capgo-managed native notifications for campaigns, badges, user lookup, delivery stats, and silent live-update checks." + actions: + - text: Get started + link: /docs/plugins/notifications/getting-started/ + icon: right-arrow + variant: primary + - text: Debug setup + link: /docs/plugins/notifications/debugging/ + icon: external + variant: minimal +--- + +## Overview + +`@capgo/capacitor-notifications` connects a Capacitor app to Capgo's native notification pipeline. Your app registers a signed customer user ID with Capgo, Capgo keeps the latest active device state in Cloudflare Analytics Engine, and you can send notifications, set badges, inspect delivery stats, and trigger silent Capgo live-update checks from the Capgo dashboard or API. + +The feature is designed to avoid a high-cardinality notification device table in Capgo Postgres. Postgres stores only low-cardinality control plane data such as app settings, platform credential metadata, campaign drafts, schedules, and aggregate campaign records. Active device state and notification events are append-only Analytics Engine rows. + +## What Capgo Stores + +| Data | Where it lives | Why | +| --- | --- | --- | +| Platform credential metadata | Capgo Postgres | Needed to manage app setup and permissions. | +| Private platform secrets | Worker environment | Avoids storing private credentials in customer-facing tables. | +| Campaign drafts and settings | Capgo Postgres | Low-cardinality control plane data. | +| Active device registration state | Cloudflare Analytics Engine | Cheap append-only active-device registry. | +| Notification events | Cloudflare Analytics Engine | Cheap stats for queued, sent, received, opened, failed, and background events. | + +## Identity Model + +Capgo does not ask you to upload a list of users or maintain a device table. + +- Your backend knows the real customer user ID. +- Your backend asks Capgo for a short-lived `identityProof`. +- The app registers with `externalId` and that proof after your own user authentication succeeds. +- Capgo derives deterministic internal keys for the recipient and the native install. +- Sending to the same `externalId` finds the user's active devices again. + +This lets you look up a user's devices, set badges, and target notification campaigns without storing per-device notification rows in Capgo Postgres. + +## Core Capabilities + +- Register devices with an authenticated external user ID. +- Refresh tags, attributes, consent, and token state on app start or token changes. +- Receive foreground notifications in JavaScript. +- Track notification received and opened events. +- Handle background data notifications. +- Set, clear, and increment app badges. +- Create Android notification channels. +- Send one-off notifications, campaign notifications, badge updates, and silent update checks. +- Trigger `@capgo/capacitor-updater` from a silent notification. + +## Stats + +Capgo writes notification events to Cloudflare Analytics Engine so dashboard stats stay cheap: + +- `queued` +- `sent` +- `provider_accepted` +- `received` +- `opened` +- `failed` +- `permission_changed` +- `background_started` +- `background_finished` + +Stats are operational signals, not a permanent per-recipient ledger. Analytics Engine retention means active devices must refresh registration periodically. + +## Public API + +| Method | Description | +| --- | --- | +| `configure` | Sets the Capgo app ID, API host, and updater integration behavior. | +| `register` | Registers the current install for an external customer user ID. | +| `setExternalId` | Changes the external customer user ID after login or account switch. | +| `setTags` | Replaces the tags used for audience targeting. | +| `setBadge` | Sets the native app badge. | +| `clearBadge` | Clears the native app badge. | +| `incrementBadge` | Increments the native app badge. | +| `enableUpdaterIntegration` | Enables silent update-check handling through `@capgo/capacitor-updater`. | +| `runUpdateCheck` | Runs the updater integration manually for testing. | +| `trackReceived` | Manually writes a received event. | +| `trackOpened` | Manually writes an opened event. | +| `addListener('notificationReceived')` | Fires when a notification is received while JavaScript is active. | +| `addListener('notificationOpened')` | Fires when a user opens a notification. | +| `addListener('backgroundNotification')` | Fires for background data notifications and exposes `finish()`. | +| `addListener('registrationChanged')` | Fires when the native push token changes. | + +## Delivery Guarantees + +Native push is at-least-once. A campaign can be retried, and a device can receive a duplicate if the platform accepts a repeated message. Use collapse IDs for update checks and make background work idempotent. + +iOS background notifications are best-effort. The OS can throttle or skip background execution based on battery, user behavior, force-quit state, and system policy. Do not rely on silent notifications for hard deadlines. + +## Keep going from @capgo/capacitor-notifications + +If you are using **@capgo/capacitor-notifications** to plan push messaging, connect it with [Getting Started](/docs/plugins/notifications/getting-started/) for setup, [Debugging](/docs/plugins/notifications/debugging/) for troubleshooting, [@capgo/capacitor-updater](/docs/plugins/updater/) for silent update checks, and [Capgo Plugin Directory](/plugins/) for other native plugins. diff --git a/apps/web/src/config/plugins.ts b/apps/web/src/config/plugins.ts index e0b233e1b..3d779dd6a 100644 --- a/apps/web/src/config/plugins.ts +++ b/apps/web/src/config/plugins.ts @@ -30,6 +30,7 @@ const actionDefinitionRows = @capgo/capacitor-calendar|github.com/Cap-go|Manage native calendar events on iOS and Android, with iOS Reminders support|https://github.com/Cap-go/capacitor-calendar/|Calendar @capgo/capacitor-date-picker|github.com/Cap-go|Native date, time, date-time, year-month, and range picker for iOS, Android, and Web|https://github.com/Cap-go/capacitor-date-picker/|Date Picker @capgo/capacitor-updater|github.com/Cap-go|Deploy live updates instantly to your users without app store review delays|https://github.com/Cap-go/capacitor-updater/|Updater +@capgo/capacitor-notifications|github.com/Cap-go|Send native iOS and Android push notifications from Capgo with user lookup, badges, stats, and silent update checks|https://github.com/Cap-go/capgo/tree/main/packages/capacitor-notifications/|Notifications @capgo/electron-updater|github.com/Cap-go|OTA live updates for Electron apps with the same API surface as capacitor-updater|https://github.com/Cap-go/electron-updater/|Electron Updater @capgo/capacitor-uploader|github.com/Cap-go|Upload large files reliably in background with progress tracking and retry support|https://github.com/Cap-go/capacitor-uploader/|Uploader @revenuecat/purchases-capacitor|github.com/Cap-go|Implement in-app subscriptions and purchases with RevenueCat SDK for cross-platform monetization|https://github.com/RevenueCat/purchases-capacitor/|Purchases @@ -171,6 +172,7 @@ const pluginIconsByName: Record = { '@capgo/capacitor-calendar': 'CalendarDays', '@capgo/capacitor-date-picker': 'CalendarDays', '@capgo/capacitor-updater': 'ArrowPath', + '@capgo/capacitor-notifications': 'Bell', '@capgo/electron-updater': 'ArrowPath', '@capgo/capacitor-uploader': 'ArrowUpOnSquare', '@revenuecat/purchases-capacitor': 'CurrencyDollar', diff --git a/apps/web/src/content/plugins-tutorials/en/capacitor-notifications.md b/apps/web/src/content/plugins-tutorials/en/capacitor-notifications.md new file mode 100644 index 000000000..17aad37f6 --- /dev/null +++ b/apps/web/src/content/plugins-tutorials/en/capacitor-notifications.md @@ -0,0 +1,108 @@ +--- +locale: en +--- +# Using @capgo/capacitor-notifications + +Send native iOS and Android push notifications from Capgo with user lookup, badges, foreground/open tracking, background callbacks, and silent Capgo live-update checks. + +The plugin is built for the Capgo notification pipeline. Capgo stores platform credential metadata and campaign control data in Postgres, while active device state and delivery events are written to Cloudflare Analytics Engine. + +## Install + +This package is currently in private preview. Capgo must enable package access for your npm account before installation works. + +Use the Capgo CLI for a guided setup: + +```bash +npx @capgo/cli@latest notifications setup com.example.app +``` + +Manual install: + +```bash +npm install @capgo/capacitor-notifications @capgo/capacitor-updater +npx cap sync +``` + +## Configure + +```typescript +import { CapgoNotifications } from '@capgo/capacitor-notifications' + +await CapgoNotifications.configure({ + appId: 'com.example.app', + autoUpdater: true, + updateInstallMode: 'next', +}) +``` + +## Register A Signed User + +Mint `identityProof` from your backend with `POST /notifications/recipients/proof`, then register the device after your own login succeeds. + +```typescript +await CapgoNotifications.register({ + externalId: 'customer-user-123', + identityProof, + tags: ['paid'], + attributes: { plan: 'team' }, + consent: true, +}) +``` + +## Listen For Events + +```typescript +await CapgoNotifications.addListener('notificationReceived', (notification) => { + console.log('Received', notification) +}) + +await CapgoNotifications.addListener('notificationOpened', (event) => { + console.log('Opened', event.notification.id) +}) + +await CapgoNotifications.addListener('backgroundNotification', async (event) => { + try { + console.log('Background payload', event.notification.data) + } finally { + await event.finish() + } +}) +``` + +## iOS Setup + +Enable **Push Notifications** and **Background Modes > Remote notifications** in Xcode. + +Forward remote notifications from `ios/App/App/AppDelegate.swift`: + +```swift +func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + NotificationCenter.default.post(name: Notification.Name("CapgoNotificationsRemoteNotification"), object: userInfo) + completionHandler(.newData) +} +``` + +## Android Setup + +Run `npx cap sync android`, configure Android platform credentials in Capgo, request notification permission on Android 13+, and test on a device or emulator with Google Play services. + +## Debugging Checklist + +- Run `npx @capgo/cli@latest notifications setup com.example.app` from the folder that contains `capacitor.config.*`. +- Search the recipient in Capgo by external customer ID. +- Confirm the device has platform `android` or `ios` and permission is granted. +- Add temporary listeners for `registrationChanged`, `notificationReceived`, `notificationOpened`, and `backgroundNotification`. +- Send a test notification from the dashboard. +- Check stats for queued, sent, received, and opened events. +- For silent update checks, verify `@capgo/capacitor-updater` is installed and `autoUpdater` is enabled. + +## Full Reference + +- Docs: /docs/plugins/notifications/ +- Debugging: /docs/plugins/notifications/debugging/ +- Source: https://github.com/Cap-go/capgo/tree/main/packages/capacitor-notifications/ + +## Keep going from Using @capgo/capacitor-notifications + +If you are using **Using @capgo/capacitor-notifications** to plan native push messaging, connect it with [@capgo/capacitor-notifications](/docs/plugins/notifications/) for the implementation detail, [Debugging](/docs/plugins/notifications/debugging/) for troubleshooting, [@capgo/capacitor-updater](/docs/plugins/updater/) for silent update checks, and [Capgo Plugin Directory](/plugins/) for other native plugins. diff --git a/apps/web/src/data/github-stars.json b/apps/web/src/data/github-stars.json index 833862226..f6c7b9ef4 100644 --- a/apps/web/src/data/github-stars.json +++ b/apps/web/src/data/github-stars.json @@ -5,10 +5,11 @@ "https://github.com/Cap-go/capacitor-transitions/": 11, "https://github.com/Cap-go/capacitor-sheets/": 2, "https://github.com/Cap-go/capacitor-native-biometric/": 74, - "https://github.com/Cap-go/capacitor-camera-preview/": 43, - "https://github.com/Cap-go/capacitor-calendar/": 0, + "https://github.com/Cap-go/capacitor-camera-preview/": 44, + "https://github.com/Cap-go/capacitor-calendar/": 1, "https://github.com/Cap-go/capacitor-date-picker/": 0, "https://github.com/Cap-go/capacitor-updater/": 768, + "https://github.com/Cap-go/capgo/tree/main/packages/capacitor-notifications/": 195, "https://github.com/Cap-go/electron-updater/": 3, "https://github.com/Cap-go/capacitor-uploader/": 24, "https://github.com/RevenueCat/purchases-capacitor/": 230, @@ -72,7 +73,7 @@ "https://github.com/Cap-go/capacitor-sim/": 9, "https://github.com/Cap-go/capacitor-speech-recognition/": 14, "https://github.com/Cap-go/capacitor-textinteraction/": 7, - "https://github.com/Cap-go/capacitor-twilio-video/": 0, + "https://github.com/Cap-go/capacitor-twilio-video/": 1, "https://github.com/Cap-go/capacitor-twilio-voice/": 8, "https://github.com/Cap-go/capacitor-video-player/": 11, "https://github.com/Cap-go/capacitor-volume-buttons/": 6, diff --git a/apps/web/src/data/github-stats.json b/apps/web/src/data/github-stats.json index 9fca6b7f0..d38fd8089 100644 --- a/apps/web/src/data/github-stats.json +++ b/apps/web/src/data/github-stats.json @@ -1,21 +1,21 @@ { - "repositoryCount": 123, - "contributorCount": 540, - "versionCount": 7673, - "releaseCount": 3365, - "closedIssueCount": 996, - "mergedPullRequestCount": 3257, - "resolvedItemCount": 4253, - "totalStars": 2341, + "repositoryCount": 124, + "contributorCount": 559, + "versionCount": 12532, + "releaseCount": 4140, + "closedIssueCount": 1403, + "mergedPullRequestCount": 4568, + "resolvedItemCount": 5971, + "totalStars": 2539, "repositories": { "Cap-go/capacitor-native-market": { "url": "https://github.com/Cap-go/capacitor-native-market", "stars": 14, "contributors": 7, - "versionTags": 132, - "releases": 57, + "versionTags": 133, + "releases": 58, "closedIssues": 9, - "mergedPullRequests": 28 + "mergedPullRequests": 29 }, "Cap-go/capacitor-native-navigation": { "url": "https://github.com/Cap-go/capacitor-native-navigation", @@ -64,7 +64,7 @@ }, "Cap-go/capacitor-camera-preview": { "url": "https://github.com/Cap-go/capacitor-camera-preview", - "stars": 43, + "stars": 44, "contributors": 50, "versionTags": 378, "releases": 85, @@ -73,10 +73,10 @@ }, "Cap-go/capacitor-calendar": { "url": "https://github.com/Cap-go/capacitor-calendar", - "stars": 0, + "stars": 1, "contributors": 1, - "versionTags": 0, - "releases": 0, + "versionTags": 1, + "releases": 1, "closedIssues": 0, "mergedPullRequests": 0 }, @@ -93,10 +93,19 @@ "url": "https://github.com/Cap-go/capacitor-updater", "stars": 768, "contributors": 45, - "versionTags": 718, - "releases": 163, + "versionTags": 719, + "releases": 164, "closedIssues": 242, - "mergedPullRequests": 392 + "mergedPullRequests": 393 + }, + "Cap-go/capgo": { + "url": "https://github.com/Cap-go/capgo", + "stars": 195, + "contributors": 27, + "versionTags": 4842, + "releases": 758, + "closedIssues": 402, + "mergedPullRequests": 1292 }, "Cap-go/electron-updater": { "url": "https://github.com/Cap-go/electron-updater", @@ -183,19 +192,19 @@ "url": "https://github.com/Cap-go/capacitor-nativegeocoder", "stars": 40, "contributors": 5, - "versionTags": 249, - "releases": 52, + "versionTags": 250, + "releases": 53, "closedIssues": 12, - "mergedPullRequests": 126 + "mergedPullRequests": 127 }, "Cap-go/capacitor-inappbrowser": { "url": "https://github.com/Cap-go/capacitor-inappbrowser", "stars": 125, "contributors": 38, - "versionTags": 452, - "releases": 102, - "closedIssues": 157, - "mergedPullRequests": 295 + "versionTags": 454, + "releases": 104, + "closedIssues": 158, + "mergedPullRequests": 299 }, "Cap-go/capacitor-mqtt": { "url": "https://github.com/Cap-go/capacitor-mqtt", @@ -219,10 +228,10 @@ "url": "https://github.com/Cap-go/capacitor-native-audio", "stars": 72, "contributors": 23, - "versionTags": 296, - "releases": 92, + "versionTags": 297, + "releases": 93, "closedIssues": 50, - "mergedPullRequests": 171 + "mergedPullRequests": 172 }, "Cap-go/capacitor-shake": { "url": "https://github.com/Cap-go/capacitor-shake", @@ -336,10 +345,10 @@ "url": "https://github.com/Cap-go/capacitor-ricoh360-camera-plugin", "stars": 6, "contributors": 2, - "versionTags": 39, - "releases": 26, + "versionTags": 40, + "releases": 27, "closedIssues": 0, - "mergedPullRequests": 3 + "mergedPullRequests": 4 }, "Cap-go/capacitor-admob": { "url": "https://github.com/Cap-go/capacitor-admob", @@ -353,11 +362,11 @@ "Cap-go/capacitor-alarm": { "url": "https://github.com/Cap-go/capacitor-alarm", "stars": 10, - "contributors": 1, - "versionTags": 56, - "releases": 45, + "contributors": 2, + "versionTags": 57, + "releases": 46, "closedIssues": 4, - "mergedPullRequests": 2 + "mergedPullRequests": 3 }, "Cap-go/capacitor-android-inline-install": { "url": "https://github.com/Cap-go/capacitor-android-inline-install", @@ -426,10 +435,10 @@ "url": "https://github.com/Cap-go/capacitor-background-geolocation", "stars": 17, "contributors": 7, - "versionTags": 67, - "releases": 49, - "closedIssues": 4, - "mergedPullRequests": 23 + "versionTags": 69, + "releases": 51, + "closedIssues": 5, + "mergedPullRequests": 25 }, "Cap-go/capacitor-background-task": { "url": "https://github.com/Cap-go/capacitor-background-task", @@ -498,10 +507,10 @@ "url": "https://github.com/Cap-go/capacitor-health", "stars": 19, "contributors": 3, - "versionTags": 70, - "releases": 69, + "versionTags": 71, + "releases": 70, "closedIssues": 16, - "mergedPullRequests": 26 + "mergedPullRequests": 28 }, "Cap-go/capacitor-is-root": { "url": "https://github.com/Cap-go/capacitor-is-root", @@ -525,15 +534,15 @@ "url": "https://github.com/Cap-go/capacitor-launch-navigator", "stars": 8, "contributors": 1, - "versionTags": 45, - "releases": 36, - "closedIssues": 3, - "mergedPullRequests": 12 + "versionTags": 46, + "releases": 37, + "closedIssues": 4, + "mergedPullRequests": 13 }, "Cap-go/capacitor-live-activities": { "url": "https://github.com/Cap-go/capacitor-live-activities", "stars": 1, - "contributors": 0, + "contributors": 1, "versionTags": 0, "releases": 0, "closedIssues": 0, @@ -658,8 +667,8 @@ }, "Cap-go/capacitor-twilio-video": { "url": "https://github.com/Cap-go/capacitor-twilio-video", - "stars": 0, - "contributors": 0, + "stars": 1, + "contributors": 2, "versionTags": 0, "releases": 0, "closedIssues": 0, @@ -758,11 +767,11 @@ "Cap-go/capacitor-pretty-toast": { "url": "https://github.com/Cap-go/capacitor-pretty-toast", "stars": 1, - "contributors": 1, - "versionTags": 3, - "releases": 3, - "closedIssues": 1, - "mergedPullRequests": 4 + "contributors": 2, + "versionTags": 6, + "releases": 6, + "closedIssues": 2, + "mergedPullRequests": 7 }, "Cap-go/capacitor-patch": { "url": "https://github.com/Cap-go/capacitor-patch", @@ -1029,10 +1038,10 @@ "url": "https://github.com/Cap-go/capacitor-bluetooth-low-energy", "stars": 6, "contributors": 2, - "versionTags": 20, - "releases": 16, - "closedIssues": 2, - "mergedPullRequests": 4 + "versionTags": 21, + "releases": 17, + "closedIssues": 3, + "mergedPullRequests": 5 }, "Cap-go/capacitor-keep-awake": { "url": "https://github.com/Cap-go/capacitor-keep-awake", diff --git a/apps/web/src/data/npm-downloads.json b/apps/web/src/data/npm-downloads.json index 8a4c0076b..6f99f0bda 100644 --- a/apps/web/src/data/npm-downloads.json +++ b/apps/web/src/data/npm-downloads.json @@ -1,43 +1,44 @@ { - "@capgo/native-market": 30616, - "@capgo/capacitor-native-navigation": 6460, - "@capgo/capacitor-auto": 72, - "@capgo/capacitor-transitions": 6484, - "@capgo/capacitor-sheets": 5342, - "@capgo/capacitor-native-biometric": 657220, - "@capgo/camera-preview": 52746, - "@capgo/capacitor-calendar": 0, - "@capgo/capacitor-date-picker": 0, - "@capgo/capacitor-updater": 1120281, - "@capgo/electron-updater": 1240, - "@capgo/capacitor-uploader": 26129, - "@revenuecat/purchases-capacitor": 1162626, - "@capgo/capacitor-flash": 53971, - "@capgo/capacitor-screen-recorder": 35170, - "@capgo/capacitor-crisp": 32854, - "@capgo/capacitor-intercom": 6638, - "@capgo/capacitor-appsflyer": 7479, + "@capgo/native-market": 31046, + "@capgo/capacitor-native-navigation": 6852, + "@capgo/capacitor-auto": 74, + "@capgo/capacitor-transitions": 6881, + "@capgo/capacitor-sheets": 5726, + "@capgo/capacitor-native-biometric": 668943, + "@capgo/camera-preview": 54077, + "@capgo/capacitor-calendar": 48, + "@capgo/capacitor-date-picker": 243, + "@capgo/capacitor-updater": 1143191, + "@capgo/capacitor-notifications": 0, + "@capgo/electron-updater": 1242, + "@capgo/capacitor-uploader": 27012, + "@revenuecat/purchases-capacitor": 1178872, + "@capgo/capacitor-flash": 54432, + "@capgo/capacitor-screen-recorder": 35597, + "@capgo/capacitor-crisp": 33393, + "@capgo/capacitor-intercom": 7069, + "@capgo/capacitor-appsflyer": 7926, "@capgo/capacitor-contentsquare": 346, - "@capgo/capacitor-facebook-analytics": 336, - "@capgo/capacitor-nativegeocoder": 72, - "@capgo/inappbrowser": 373281, - "@capgo/capacitor-mqtt": 5213, - "@capgo/capacitor-mute": 47683, - "@capgo/native-audio": 73111, - "@capgo/capacitor-shake": 39242, - "@capgo/capacitor-navigation-bar": 160060, - "@capgo/ivs-player": 1203, - "@capgo/home-indicator": 18575, - "@capgo/native-purchases": 161958, - "@capgo/capacitor-data-storage-sqlite": 13262, - "@capgo/capacitor-android-usagestatsmanager": 11517, + "@capgo/capacitor-facebook-analytics": 382, + "@capgo/capacitor-nativegeocoder": 74, + "@capgo/inappbrowser": 379438, + "@capgo/capacitor-mqtt": 5593, + "@capgo/capacitor-mute": 48428, + "@capgo/native-audio": 73907, + "@capgo/capacitor-shake": 40072, + "@capgo/capacitor-navigation-bar": 162357, + "@capgo/ivs-player": 1204, + "@capgo/home-indicator": 18776, + "@capgo/native-purchases": 164181, + "@capgo/capacitor-data-storage-sqlite": 13742, + "@capgo/capacitor-android-usagestatsmanager": 11925, "@capgo/capacitor-streamcall": 0, - "@capgo/capacitor-autofill-save-password": 40276, - "@capgo/capacitor-social-login": 808065, - "@capgo/capacitor-passkey": 6308, - "@capgo/capacitor-jw-player": 5836, + "@capgo/capacitor-autofill-save-password": 41492, + "@capgo/capacitor-social-login": 823537, + "@capgo/capacitor-passkey": 6632, + "@capgo/capacitor-jw-player": 6215, "@capgo/capacitor-ricoh360-camera-plugin": 0, - "@capgo/capacitor-admob": 7904, + "@capgo/capacitor-admob": 8224, "@capgo/capacitor-alarm": 1235, "@capgo/capacitor-android-inline-install": 10242, "@capgo/capacitor-android-kiosk": 3098,