> ## Documentation Index
> Fetch the complete documentation index at: https://docs.a0.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Push Notifications

> Send push notifications to iOS and Android devices from your Convex backend

## Push Notifications

a0 provides built-in push notification delivery through FCM (Android) and APNs (iOS). Your Convex backend sends notifications via a0's API — no third-party push services needed.

<Warning>
  Push notifications do NOT work in Expo Go or the a0 preview app.
  Test with a custom APK (Android) or TestFlight build (iOS).
</Warning>

## How it works

1. Your app collects device push tokens using `expo-notifications`
2. Your Convex backend calls `internal.notifications.sendNotification`
3. a0 detects the platform from the token and delivers via FCM or APNs

The `convex/notifications.ts` file is automatically included in your project. It's an **internal action** — only your server-side Convex functions can call it, not the client.

## Getting a device push token

Use `getDevicePushTokenAsync()` from `expo-notifications` — **not** `getExpoPushTokenAsync()`:

```ts theme={null}
import * as Notifications from "expo-notifications";

async function registerForPushNotifications() {
  const { status } = await Notifications.requestPermissionsAsync();
  if (status !== "granted") return null;

  const { data: token } = await Notifications.getDevicePushTokenAsync();
  return token; // Save this to your Convex database
}
```

## Sending notifications

Call `sendNotification` from your Convex backend using `internal` (not `api`):

```ts theme={null}
import { internal } from "./_generated/api";

// From a mutation — schedule it since mutations can't call actions directly
await ctx.scheduler.runAfter(0, internal.notifications.sendNotification, {
  to: devicePushToken,
  title: "New Message",
  body: "You have a new message",
  data: { screen: "chat", chatId: "abc123" },
});

// From an action — call directly
await ctx.runAction(internal.notifications.sendNotification, {
  to: [token1, token2, token3],
  title: "Announcement",
  body: "Check out our new feature!",
  badge: 1,
});
```

| Argument | Type                                                  | Required | Description                |
| -------- | ----------------------------------------------------- | -------- | -------------------------- |
| `to`     | `string \| string[]`                                  | Yes      | Device push token(s)       |
| `title`  | `string`                                              | Yes      | Notification title         |
| `body`   | `string`                                              | Yes      | Notification body          |
| `data`   | `Record<string, string \| number \| boolean \| null>` | No       | Custom data payload        |
| `badge`  | `number`                                              | No       | App icon badge count (iOS) |

## Android Setup

Follow the [Android FCM Setup guide](/development/android-fcm-setup) to configure Firebase Cloud Messaging credentials:

1. Create a [Firebase project](https://console.firebase.google.com/) and add your Android app
2. [Download `google-services.json`](/development/android-fcm-setup#step-2-download-google-servicesjson)
3. [Create a Firebase service account key](/development/android-fcm-setup#step-3-create-a-firebase-service-account-key) (for FCM v1 delivery)
4. [Upload both](/development/android-fcm-setup#step-4-upload-to-a0) in **Project Settings → Build Settings → Android Credentials**
5. Create a new APK build

## iOS Setup

iOS push notifications are configured per Apple Developer team:

1. Connect your Apple Developer account via the **Publish → App Store** flow
2. Set up APNs credentials (automatic or manual) from the team management dialog
3. Create a new App Store / TestFlight build

The `aps-environment` entitlement is included automatically in all builds.

## Handling notifications in your app

```ts theme={null}
import * as Notifications from "expo-notifications";

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldPlaySound: true,
    shouldSetBadge: true,
    shouldShowBanner: true,
    shouldShowList: true,
  }),
});

// When user taps a notification
Notifications.addNotificationResponseReceivedListener((response) => {
  const data = response.notification.request.content.data;
  // Navigate based on data
});
```

## Troubleshooting

| Issue                                        | Fix                                                                |
| -------------------------------------------- | ------------------------------------------------------------------ |
| "no valid aps-environment entitlement"       | Create a new build — older builds may not include this entitlement |
| "BadDeviceToken"                             | Device token expired or from wrong environment — get a fresh token |
| "Firebase service account is not configured" | Upload credentials in Build Settings → Android Credentials         |
| "APNs credentials are not configured"        | Set up APNs key in Publish → App Store → team management           |
| Notifications not received                   | Must test on TestFlight (iOS) or custom APK (Android), not Expo Go |
