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.
Push notifications do NOT work in Expo Go or the a0 preview app.
Test with a custom APK (Android) or TestFlight build (iOS).
How it works
- Your app collects device push tokens using
expo-notifications
- Your Convex backend calls
internal.notifications.sendNotification
- 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():
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):
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 to configure Firebase Cloud Messaging credentials:
- Create a Firebase project and add your Android app
- Download
google-services.json
- Create a Firebase service account key (for FCM v1 delivery)
- Upload both in Project Settings → Build Settings → Android Credentials
- Create a new APK build
iOS Setup
iOS push notifications are configured per Apple Developer team:
- Connect your Apple Developer account via the Publish → App Store flow
- Set up APNs credentials (automatic or manual) from the team management dialog
- Create a new App Store / TestFlight build
The aps-environment entitlement is included automatically in all builds.
Handling notifications in your app
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 |