Delivery credentials
What you need in hand before the first push goes out — the Firebase service account for Android and the APNs .p8 key for iOS, field by field, including Apple's environment trap.
Delivery credentials
PushMesh does not deliver a push on its own: the notification is put on the device by Google (FCM) on Android and by Apple (APNs) on iOS. To speak to them on behalf of your application, the service needs a credential of yours.
This is the step that stalls most integrations on day one, and it has a
distinctive symptom: the send returns 200, recipients looks right, and the
result reads successful: 0 with remaining: 0. The audience existed; the
message had no way out.
Credentials are registered in the dashboard, at
https://app.pushmesh.io. No public route accepts a delivery credential — neither to store one nor to read one back. A push signing key is the kind of secret that should not travel through an automated integration, so it only goes in through the screen.
This page exists so you know what to have in hand before creating an account, and so you recognise the right credential when you see it.
What each platform requires, in one table
| Platform | What you provide | Where it comes from |
|---|---|---|
| Android (and WebPush) | the Firebase service account .json file | Firebase Console |
| Android (optional) | the Android app’s google-services.json | Firebase Console |
| iOS | the APNs .p8 key + Key ID + Team ID + Bundle ID + environment | Apple Developer |
You only need the platform you actually serve. An Android-only application never has to touch APNs.
1. Android — the Firebase service account
What it is (and what does not work)
What the service accepts is the service account JSON file from your Firebase project. It is the file the console generates under:
Firebase Console → Project settings → Service accounts → “Generate new private key”
The legacy server key does not work. That long string older applications kept in an environment variable belongs to a retired protocol. If what you have is a single line of text rather than a JSON file with keys and values, it is the wrong credential — generate a new one through the path above.
The fields that are checked
The file is validated at registration time, field by field, and every problem comes back named instead of turning into a delivery failure weeks later:
| Field | What it must be | Typical failure |
|---|---|---|
project_id | a non-empty string | missing ⇒ the file is not a service account |
client_email | an e-mail address (contains @; ends in .iam.gserviceaccount.com) | missing or hand-edited |
private_key | a PEM containing the BEGIN PRIVATE KEY block | line breaks (\n) stripped while copying |
Do not edit the file. The most common failure here is pasting the
private_keywithout its line breaks, or running it through an editor that “tidies up” the text. Paste the whole content exactly as Google delivered it.
After registering
- The credential is encrypted on the server. It never comes back through any screen or any route.
- The dashboard offers a connection test: it requests an access token from Google using your credential and shows which project answered. Run it before your first send — it is the difference between finding the problem now and finding it mid-campaign.
- Revoking stops delivery immediately. Deleting the credential from PushMesh halts this application’s sends until you configure another one; it does not delete anything in Firebase. If the key leaked, revoke it in both places.
Optional: the google-services.json
Besides the service account (which sends), there is a second file that only
identifies the project to the Android application. Pasting the
google-services.json into the dashboard once means your app no longer has to
ship the file: the SDK fetches those values from the server at boot.
Firebase Console → Project settings → Your apps → download the
google-services.jsonfor the Android app
There are five values, and they are public by design — they already ship inside every published APK. They are not a credential, which is why the route that serves them asks for none:
curl https://api.pushmesh.io/api/v1/apps/APP_ID/firebase_params
{
"android": {
"project_id": "my-firebase-project",
"sender_id": "123456789012",
"app_id": "1:123456789012:android:abcdef0123456789",
"api_key": "AIza…",
"package_name": "com.example.myapp"
},
"ios": null
}
Until they are configured, the same route answers 200 with "android": null.
Mind the name. The
api_keyinside theandroidblock is the Firebase key, public, taken from Google’s file. It is not your application’s PushMesh key (pm_live_…), which must never leave your server. They are different things with similar names, and mixing them up is the most expensive mistake on this page.
If your Firebase project has more than one Android app, the dashboard asks
for the package_name to pick which one to use — without it, storing the wrong
app would make the SDK initialise against a different project. The Android app
identifier always has the form 1:<number>:android:<hash>.
Route details in Applications and keys.
2. iOS — the APNs .p8 key
iOS requires five pieces of information, and all of them are shape-checked before being stored.
| Field | Required format | Where to get it |
|---|---|---|
.p8 | the file contents, with the BEGIN PRIVATE KEY block | Apple Developer → Certificates, IDs & Profiles → Keys (check “Apple Push Notifications service”) |
| Key ID | exactly 10 alphanumeric characters | the same Keys screen |
| Team ID | exactly 10 alphanumeric characters | Apple Developer → Membership |
| Bundle ID | reverse-dns (com.yourcompany.app) | Xcode / App Store Connect |
| Environment | sandbox or production | your choice — read the next section |
The
.p8can only be downloaded once. Apple hands the file over when the key is created and never again. If you do not have it, there is no way to recover it: generate another key.
On save, the credential is encrypted on the server. The dashboard offers a connection test that validates the key against Apple itself and shows the accepted bundle and environment.
Revoking deletes the credential from PushMesh and stops iOS deliveries immediately, without touching anything in Apple Developer.
The environment trap — read this before blaming the platform
Apple keeps two separate worlds, and a push token from one is worthless in the other.
The build decides, not the server:
| How the app reached the device | Token environment |
|---|---|
| run straight from Xcode | sandbox |
| published to the App Store | production |
| distributed through TestFlight | production |
TestFlight being production is the most common mistake of all: the person is testing, therefore assumes sandbox, and no push ever arrives.
The same .p8 key works in both worlds — all that changes is where the
service sends. That is why the environment is an application setting you can
switch at any time in the dashboard, without re-uploading the key, and the
switch takes effect immediately.
When the key is not valid in the chosen environment, Apple answers
BadEnvironmentKeyIdInToken, and the error that reaches you says exactly that:
check whether the application’s environment matches the environment the APNs key
was created for.
3. How to know it worked
Once registered, send to one real device and read the result. The reading table is short:
| What you see | What it means |
|---|---|
good recipients, successful: 0, remaining: 0 | the delivery credential is missing or wrong |
good successful, high failed | the tokens are dead, or (on iOS) they were issued in the other environment |
good successful, recebidos: 0 | the push went out; what did not come back is the proof — notification permission, device offline, or a custom client that never calls the receipts route |
good successful, good recebidos | you are done. |
The full walkthrough, with commands, is in Getting started.
Checklist before creating an account
Android
- Access to the Firebase Console of the project that already issues your app’s tokens
- Permission to generate a service account private key
- (Optional) The Android app’s
google-services.json
iOS
- An Apple Developer account with access to Keys
- The APNs
.p8file — or permission to generate a new one - Key ID, Team ID and Bundle ID at hand
- The answer to “does this build come from Xcode, the App Store or TestFlight?”
See also
| Topic | Page |
|---|---|
| from zero to your first push with a receipt | Getting started |
| the public route for Firebase parameters | Applications and keys |
| what changes when you switch providers | Migrating from another provider |
| retention and personal data | Data and privacy |