Skip to content

Expo push notifications work in development but not in the APK

The common reasons Expo push notifications stop in production builds: FCM v1 credentials, google-services.json, Expo Go limits, receipts and Android channels.

PushUpdated 19 October 20266 min read

The push works in development and stops in the APK, or works on iOS and not Android. Almost every case is one of five things. Check them in this order.

1. No FCM v1 key in EAS

On Android, Expo sends through your Firebase project. Since Google retired the FCM legacy API in 2024, Expo needs an FCM v1 service account key for your project. Without it, Android pushes fail while iOS keeps working.

Upload it once:

eas credentials
# Android > production > Google Service Account
# > Manage your Google Service Account Key for Push Notifications (FCM V1)

You can also do it on expo.dev under your project’s Credentials, Android, then your application id, then FCM V1 service account key.

2. google-services.json missing from the build

The build must include the google-services.json from the same Firebase project the key came from. Point to it in app.json:

{
  "expo": {
    "android": {
      "package": "in.kiranaexpress.app",
      "googleServicesFile": "./google-services.json"
    }
  }
}

A file from another project gives you tokens that your key cannot send to.

3. Testing in Expo Go

Recent Expo SDKs removed remote push from Expo Go on Android, and Expo Go never used your own credentials anyway. Make a development build with eas build --profile development and test there.

4. Reading the ticket instead of the receipt

The push API answers with a ticket. "status": "ok" means Expo queued it. Delivery problems appear later in the receipt:

// after sending, keep the ticket ids, then a few minutes later:
const res = await fetch("https://exp.host/--/api/v2/push/getReceipts", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ ids: ticketIds }),
});
const { data } = await res.json();
// data[id].status === "error" with details.error such as
// "DeviceNotRegistered" (delete the token) or "InvalidCredentials" (fix step 1)

5. Android’s own reasons

Once Expo hands the message to FCM, the usual Android causes apply: the notification permission on Android 13 and later, a missing channel, and battery savers on Xiaomi, Oppo and Vivo. Create a channel at start with Notifications.setNotificationChannelAsync, ask for permission with requestPermissionsAsync, and read why pushes go missing on Xiaomi, Oppo and Vivo.

Sending from your own server instead

Expo’s push service is free and fine for Expo apps. If you also want topics, scheduling, a composer for your marketing team, push approval for logins or email codes on the same user id, SignOps Push takes your FCM and APNs credentials directly and works the same for Expo, bare React Native, Flutter and native apps.

Sources: Expo: FCM v1 migration, Expo: support for FCM HTTP v1, Expo forums: push not working after EAS build.

Frequently asked questions

More questions? Email us and the answer goes on this page.

hello@signops.dev
Why do Expo push notifications work in Expo Go but not in my APK?

A standalone Android build sends through your own Firebase project, so it needs your google-services.json in the build and an FCM v1 service account key uploaded to EAS. Expo Go uses Expo's own credentials, which hides the problem until you build.

Do Expo push notifications work in Expo Go on Android?

Recent Expo SDKs removed remote push notifications from Expo Go on Android. Test pushes in a development build made with EAS instead.

The Expo push API says ok but nothing arrives. Why?

The ticket only means Expo accepted the message. Fetch the push receipt a few minutes later; errors such as DeviceNotRegistered or InvalidCredentials appear there, not in the ticket.

Send your first push today.

Free for 1,000 push devices, 100 approvals a day, 10,000 passkey sign-ins a month and 500 email codes to try. No card until you pass a free amount.