Push Notifications

OtaKit sends push notifications to your Capacitor app on iOS and Android. It uses the official @capacitor/push-notifications plugin on the device and talks to Apple (APNs) and Google (Firebase Cloud Messaging) directly with your own keys. There is no extra native SDK, and it works with or without OtaKit live updates.

Push is an add-on. Turn it on per workspace in the dashboard under Settings → Add-ons → Push notifications; a Push page then appears in the menu.

1. Upload your keys

Open Push → Setup and choose the app. Only workspace owners and admins can upload keys. Keys are encrypted at rest and never shown again after upload.

iOS: APNs key

  1. In the Apple Developer account, open Certificates, IDs & Profiles → Keys, create a key with Apple Push Notifications service (APNs) enabled, and download the .p8 file (Apple lets you download it once).
  2. Note the Key ID (10 characters) and your Team ID (top right of the developer account).
  3. Make sure your bundle ID is registered under Identifiers with the Push Notifications capability, in the same team as the key.
  4. Upload the .p8 file with the Key ID, Team ID and bundle ID, then click Test. One key works for development and production builds.

Android: Firebase service account

  1. In the Firebase console, add an Android app with your application ID and download google-services.json (you need it in step 2).
  2. Open Project settings → Service accounts and click Generate new private key.
  3. Upload the JSON file and click Test. A key created a minute ago can be rejected briefly while Google activates it.

2. Install

npm install @capacitor/push-notifications @otakit/push
npx cap sync

iOS project

In Xcode, add the Push Notifications capability to the app target, then add the two AppDelegate methods from the Capacitor push notifications guide:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
}

Android project

Copy google-services.json to android/app/. The Capacitor Android template applies the Google Services plugin when the file is present. Android 13 and newer show notifications only after the user grants the notification permission.

3. Register devices

import { PushNotifications } from '@capacitor/push-notifications';
import { OtaKitPush } from '@otakit/push';

OtaKitPush.init({ appId: 'YOUR_OTAKIT_APP_ID' });

PushNotifications.addListener('registration', ({ value }) => {
  OtaKitPush.syncToken(value, { userId: currentUser?.id ?? null, topics: ['news'] });
});

const permission = await PushNotifications.requestPermissions();
if (permission.receive === 'granted') {
  await PushNotifications.register();
}

// Open a screen when the user taps a notification.
PushNotifications.addListener('pushNotificationActionPerformed', ({ notification }) => {
  const url = notification.data?.url;
  if (url) router.push(url);
});

The app ID is the same one you use for live updates (plugins.OtaKit.appId). The device appears under Push → Devices within seconds.

OtaKitPush API

MethodWhat it does
init({ appId, serverUrl?, environment? })Configure once at startup. serverUrl is for self-hosted consoles.
syncToken(token, { userId?, topics? })Register or update the device. Sends only when something changed, or once a day.
setUser(userId | null)Attach your own user ID after sign-in, or clear it on sign-out.
subscribe(topic) / unsubscribe(topic)Manage topics: letters, digits, - and _, up to 20.
unregister()Remove this device from OtaKit.

Every method resolves with a status instead of throwing, so push setup never breaks your app. When OtaKit live updates are installed, the device's OTA channel and bundle version are attached automatically, so you can target by channel.

Development builds

Builds run from Xcode get sandbox tokens. On the first send, OtaKit notices when Apple rejects such a token on production, delivers it through the sandbox instead, and remembers that for the device. You can also pass environment: 'sandbox' to init in debug builds.

4. Send

Dashboard: Push → Send. Write the title and text, choose the audience, and check the device count before you confirm.

CLI (@otakit/cli 1.7.0 or newer):

otakit push send --title "Your order shipped" --body "Track it in the app" --url /orders --user user_42
otakit push campaigns

REST API with an organization key. Preview first to see the audience, then send with an idempotency key so a retry never sends twice:

curl -X POST https://console.otakit.app/api/v1/apps/$APP_ID/push/campaigns \
  -H "Authorization: Bearer $OTAKIT_TOKEN" \
  -H "Idempotency-Key: order-42-shipped" \
  -H "Content-Type: application/json" \
  -d '{
    "payload": { "title": "Your order shipped", "body": "Track it in the app", "url": "/orders" },
    "audience": { "userIds": ["user_42"] }
  }'

POST …/push/audience takes the same body and returns the device count and warnings without sending. GET …/push/campaigns/:id returns delivery counts.

Message

title (up to 100 characters) and body (up to 1,000) are required. Optional: url (a path like /inbox or an https link, delivered as data.url), data (up to 20 string values), sound, badge and ttlSeconds (default one day). The whole message must fit Apple's 4 KB limit.

Audience

Filter by platforms, topics, userIds, channels (OTA channel), runtimeVersions or deviceIds. Different filters must all match; values inside one filter match any. An empty audience sends to every device of the app.

Limits

PlanDevicesNotifications per month
Free10,000100,000
Starter50,0001,000,000
Pro250,0005,000,000
EnterpriseCustomCustom

Over the device limit, new devices are not stored and the app is told why; it keeps working. Invalid tokens reported by Apple or Google are removed automatically.

Troubleshooting

You seeFix
PUSH_DISABLEDTurn on Push notifications in Settings → Add-ons. Until then, devices are not registered.
InvalidProviderTokenApple rejected the key. Check the Key ID and Team ID, and that the key has APNs enabled.
TopicDisallowed / BadTopicThe key cannot send to this bundle ID. Register the bundle ID with Push Notifications in the same Apple team as the key.
BadDeviceToken / UnregisteredThe token is no longer valid (app deleted or reinstalled). OtaKit removes it; the app registers again on next launch.
PERMISSION_DENIEDGoogle rejected the service account. Wait a minute after creating a key, and check that the Firebase Cloud Messaging API is enabled.
SENDER_ID_MISMATCHThe device token belongs to a different Firebase project than the uploaded service account.
No devices match this audienceCheck the filters, and that devices appear under Push → Devices. iOS devices need an APNs key, Android devices a Firebase key.
accepted: false, device_limitThe workspace reached its device limit. Upgrade the plan or remove unused devices.