If you are wiring up Android push notifications, you will hit a step that says “upload your Firebase service account JSON” and it is not obvious what that means, where it lives, or why it is different from the other Firebase JSON file you already added to your app.
This covers what FCM actually is, why the credential exists, and how to get it. It takes about three minutes once you know where to click.
What FCM Is
Firebase Cloud Messaging is the only way to deliver a push notification to an Android device. It is not a preference or a vendor choice. Android’s push transport runs through Google’s servers, and FCM is the interface to it.
The flow has three parties:
- Your app registers with FCM on the device and receives a device token, a long string that uniquely identifies that installation.
- Your server stores that token and, when something happens worth notifying about, calls FCM saying “deliver this payload to this token.”
- FCM does the actual delivery to the device, handling the OS-level connection, retries, and the case where the phone is offline.
You cannot skip step 2 by having the app notify itself. A push notification exists precisely so that something can reach the user when your app is not running.
Why a Service Account JSON
Step 2 needs authentication. FCM will not accept “send a notification to every user of app X” from anyone who asks.
Historically that was a server key: a long static string you copied out of the Firebase console. If you find a tutorial telling you to do that, it is out of date. The legacy server key and the legacy send endpoint were deprecated and then switched off, and newer Firebase projects never had one at all.
The current mechanism is the HTTP v1 API, which authenticates with a service account: a non-human identity belonging to your Firebase project. The JSON file you download is that identity’s private key. Your server uses it to mint short-lived access tokens, and those tokens authorise the send.
Practically, this is better. The credential is scoped, revocable, and rotatable, and a leaked file can be killed without regenerating anything user-facing.
The File Everyone Confuses It With
This trips up nearly everyone, so it is worth being explicit.
google-services.json | Service account JSON | |
|---|---|---|
| What it is | Client configuration | A private credential |
| Where it goes | Inside your Android app (app/) | On your server, or your push provider |
| Contains | Project id, API key, app id | A private key |
| Safe in the app bundle? | Yes, it is designed for that | Absolutely not |
| Safe in git? | Generally yes | No |
| Downloaded from | Project Settings → General → Your apps | Project Settings → Service accounts |
Both are JSON, both come from the same console, and they are not interchangeable. If push is failing with an authentication error, the most common cause is that one has been used in place of the other.
The rule: google-services.json identifies your app. The service account key proves your server is allowed to send.
Getting the Service Account JSON
1. Create or open a Firebase project
Go to the Firebase Console and either create a project or open an existing one.
You do not have to adopt any other Firebase product. Using Firebase purely as a push delivery channel, while your database and auth live somewhere else entirely, is a completely normal setup.
2. Register your Android app
In Project Settings → General → Your apps, add an Android app. It asks for your package name, which has to match the applicationId in your app/build.gradle or build.gradle.kts exactly, including case.
Download the google-services.json it offers and put it in your app’s app/ directory. That is the client file, and you are done with it.
3. Generate the private key
Go to Project Settings → Service accounts, then click Generate new private key. Confirm the warning, and a .json file downloads.
That file is the one your server or push provider needs. It looks roughly like this:
{
"type": "service_account",
"project_id": "your-project-id",
"private_key_id": "...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...",
"client_email": "firebase-adminsdk-xxxxx@your-project-id.iam.gserviceaccount.com",
"client_id": "..."
}
4. Store it somewhere safe
Treat it like a database password:
- Do not commit it to version control
- Do not bundle it in your mobile app
- Do not paste it into client-side code
Anyone holding this file can send a notification to every user of your app. If it leaks, delete the key from Service accounts and generate a new one. Nothing user-facing breaks.
Common Failure Modes
“Invalid service account” or authentication errors on send. Usually google-services.json was uploaded where the service account key was expected. Check that your file has a private_key field.
SenderId mismatch. The device token was issued by a different Firebase project than the one your server is authenticating as. This normally means the app shipped with a google-services.json from project A while the server holds credentials for project B. Both sides have to be the same project.
UNREGISTERED or NotRegistered. The token is dead: the app was uninstalled, the data cleared, or the token rotated. This is expected and normal at low rates. Delete the token from your database when you see it, otherwise you accumulate dead tokens and your delivery rate quietly degrades.
Nothing arrives, no error. Check the package name matches Firebase exactly, including case. A mismatch drops the notification silently, which is the worst possible failure mode and a surprisingly common one.
Where This Fits
Getting the credential is the easy part. The work that follows is the part that becomes a project: storing device tokens, keeping them fresh, deciding who should receive a given notification, building the payload for each platform, and handling retries and dead tokens.
If your data lives in Supabase, Entrig does that half. You upload this service account JSON once, and notifications become something you configure against your existing tables rather than a service you build and maintain. It installs the Postgres trigger, resolves the recipients, and handles delivery to FCM and APNs.
You still need this file either way. Google requires your own credentials to deliver to your own app, and no service can remove that step.