Choose your platform, prepare its credentials and connect the SDK. Android uses Firebase Cloud Messaging. Apple uses APNs directly — Firebase is not required for iOS.
01 / Start here
Choose a platform
One PushPort application can contain Android, iOS and Web settings. The App ID identifies it in PushPort. Android applicationId and Apple Bundle ID identify the native apps. Signing in to the PushPort dashboard does not require you to configure your own Google OAuth client.
Choose a platform
Platform
Push provider
Current availability
Android
Firebase Cloud Messaging (FCM)
Maven Central · dev.pushport:android-sdk:0.0.1
iOS / iPadOS
Apple APNs
Swift SDK 0.0.1 via Swift Package Manager; APNs settings per environment in the application console.
Web
Web Push / VAPID
Web SDK 0.0.1 via the GitHub npm archive. Configure the website origin and enable Web Push.
Create an application and choose its platforms. Save the Android package name, Apple Bundle ID or exact HTTPS website origin. Open Settings to configure Firebase, APNs or Web Push, then follow the SDK guide for that platform.
Copy the App ID from the application settings. It is a PushPort identifier, not a Firebase project ID or your Android package name.
Before you begin
Apple SDK 0.0.1 is public. In application settings, upload your APNs .p8 key with Team ID, Key ID and the correct Sandbox or Production environment. The server sends through APNs directly. Verify a real device before starting a campaign.
Web Push uses the browser’s native push service and VAPID keys. You do not need a Firebase project. Supported provider routes cover Chrome/Edge, Firefox and Safari.
Give employees access to your team’s applications.
Choose all current and future team applications, or selected applications with a separate role for each.
View: audience and reports. Send: also campaigns and segments. Manage: also settings and deletion. Creating applications requires team-wide management access.
Only the owner manages team membership, employee permissions and API keys.
Invite a teammate
An invitation email is sent automatically. You can also copy the link. Sign in or register with the invited email to accept. Invitations expire after 7 days.
For this PushPort integration you need a Firebase project and one service-account JSON key. You do not need to add Firebase dependencies or a Google configuration file to your Android app just for PushPort.
Select the right project
Open Firebase Console and create a project or select the project that will send Android notifications. Google Analytics can stay disabled. You do not need Firestore, Realtime Database or Firebase Authentication for push delivery.
Check the Android package
Read applicationId in the app module’s build.gradle.kts and enter that value in the Android package field of your PushPort application. Registering a separate Android app in Firebase and supplying SHA fingerprints are not required by this PushPort connection. Keep any existing Firebase setup used for other features.
Enable the APIs
In the same Google Cloud project, open APIs & Services → Library. Ensure Firebase Cloud Messaging API (HTTP v1) and Firebase Management API are enabled. PushPort uses the first to send and the second to read the project’s sender number.
Download the private JSON key
In Firebase: Project settings → Service accounts → Firebase Admin SDK → Generate new private key. The downloaded JSON has type service_account and contains project_id, client_email and private_key. The language tab shown in Firebase does not change this key.
Upload the key to PushPort
Open your application in the PushPort dashboard → Settings → Firebase. Choose the downloaded JSON and upload it. Check that Firebase is shown as configured. This file goes to the corresponding Android application settings only.
Connect and test the SDK
Follow the Android SDK section below. Initialize with the PushPort App ID, open the app, grant permission and check the installation in Audience. Then send a test to that installation before a campaign.
The private JSON is a server credential. Do not put it in app/src, commit it to Git, bundle it in an APK or paste it into the contact form. For PushPort itself, google-services.json and the Google Services Gradle plugin are not required.
Do not mix up the files and identifiers
Do not mix up the files and identifiers
File or value
Where it belongs
What it is for
Service-account JSON
PushPort → application → Settings → Firebase
Authorizes the server to send through the selected Firebase project.
google-services.json
Not required for this PushPort Android integration
Client configuration for an app’s own Firebase services, if it uses them separately.
PushPort App ID
SDK initialization in your app’s code
Public application identifier; it is not a provider secret or a device token.
APNs .p8 key
PushPort → application → Settings → Apple APNs
Apple sender credential. Never add it to the iOS app or Firebase for PushPort.
If Firebase setup fails
Access denied / 403
Verify the project and active key. In Google Cloud IAM, the service account needs Firebase Viewer (roles/firebase.viewer) to read project data and Firebase Cloud Messaging API Admin (roles/firebasecloudmessaging.admin) to send. Check both APIs above.
Wrong JSON file
Upload the service-account key, not google-services.json or a Google OAuth client file.
No installation or no notification
Check the App ID, package restriction, Google Play services, network, system permission and the SDK subscription. Firebase Analytics does not control locale collection.
Android SDK 0.0.2 is published on Maven Central. Add google() and mavenCentral() to Gradle repositories, then implementation("dev.pushport:android-sdk:0.0.2"). Gradle downloads the SDK and its transitive dependencies.
Initialize the SDK in your Application class and register that class in AndroidManifest.xml. If your application already has an Application class, initialize it there. In apps with multiple processes, initialize only in the main process.
MyApplication.kt
import android.app.Application
import dev.pushport.sdk.PushPort
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
PushPort.initWithContext(this, "PUSHPORT_APP_ID")
}
}
AndroidManifest.xml
<application android:name=".MyApplication" />
Activity
PushPort.requestNotificationPermission(this)
Request notification permission from a visible Activity after explaining its purpose.
Firebase Messaging, WorkManager and manifest components arrive through the dependency.
Open the app, grant permission, then check its installation in Audience.
The PushPort Apple SDK talks to APNs directly. Do not create an iOS app in Firebase for PushPort, add Firebase Messaging or download GoogleService-Info.plist. Firebase can remain in an iOS app if you use it independently for other features.
Apple SDK 0.0.1 is public. In application settings, upload your APNs .p8 key with Team ID, Key ID and the correct Sandbox or Production environment. The server sends through APNs directly. Verify a real device before starting a campaign.
Apple Developer membership
Downloading the SDK requires no Apple account. Real APNs testing needs an active Apple Developer Program membership, a signed app and a matching provisioning profile.
Create or check the Bundle ID
In Apple Developer → Certificates, Identifiers & Profiles → Identifiers, select or register the explicit App ID matching the app’s Bundle Identifier in Xcode. Enable Push Notifications.
Prepare an APNs key
Open Keys, create a key with Apple Push Notification service enabled and configure the available environment/topic scope for the intended app. Download the .p8 file; Apple offers this download once.
Keep the four required values
Record the Team ID, Key ID, Bundle ID and .p8 file. Check the key’s permitted environment and topics; do not assume that every new key covers both development and production.
Enable the Xcode capability
Select the main app target → Signing & Capabilities → + Capability → Push Notifications. Use a signing team/profile that includes the entitlement.
Select the correct environment
Use .sandbox for a development aps-environment entitlement and .production for production, TestFlight and App Store builds. The app, server endpoint and key scope must agree. Then follow the SDK integration below.
Install the public Swift package on a Mac with Xcode. Minimum iOS version: 15.0. No CocoaPods account, Firebase file or manual framework download is required.
Apple SDK 0.0.1 is public. In application settings, upload your APNs .p8 key with Team ID, Key ID and the correct Sandbox or Production environment. The server sends through APNs directly. Verify a real device before starting a campaign.
Add the package
Xcode → File → Add Package Dependencies → paste the repository URL below → Exact Version → 0.0.1. Add the PushPort product to the main application target.
Use the PushPort App ID from your application settings. The SwiftUI example below forwards both APNs callbacks; in a UIKit app, add the same calls to the existing AppDelegate.
Use .sandbox for a development aps-environment entitlement and .production for production, TestFlight and App Store builds. The app, server endpoint and key scope must agree. Then follow the SDK integration below.
Request permission after explaining the benefit, for example from a button. Initialization does not show the system dialog. After a denial, the user changes permission in Settings.
NotificationSettings.swift
import SwiftUI
import PushPort
@MainActor
struct NotificationSettings: View {
@State private var result = ""
@State private var requesting = false
var body: some View {
VStack {
Button("Enable notifications") {
requesting = true
Task { @MainActor in
defer { requesting = false }
do {
let granted = try await PushPort.shared.requestPermission()
result = granted ? "Notifications allowed" : "Check notification settings"
} catch {
result = "Unable to request permission"
}
}
}
.disabled(requesting)
Text(result)
}
}
}
System permission and the PushPort subscription are separate. setSubscribed(false) saves an opt-out; true does not override a system denial. The SDK captures app/system languages and timezone automatically. setLocale(nil) restores automatic locale selection. Already-sent APNs notifications may still arrive after an opt-out.
Swift
// Call from your app's settings action.
@MainActor
func updateNotifications(subscribed: Bool, locale: String?) async throws {
try await PushPort.shared.setSubscribed(subscribed)
try await PushPort.shared.setLocale(locale)
}
// locale: "uk-UA" for an override, nil for automatic selection.
// Handle errors in the calling view and update its displayed state.
For images, add a Notification Service Extension in Xcode and link only the PushPortNotificationService product to that extension. The main app keeps the PushPort product.
Use the extension class
Replace the generated extension class with the subclass below. Keep NSExtensionPrincipalClass as $(PRODUCT_MODULE_NAME).NotificationService.
Prepare an image payload
The payload needs aps.mutable-content = 1, pushport_app_id and an HTTPS image_url. Supported image types: JPEG, PNG and GIF, up to 5 MiB. The download resource timeout is 20 seconds.
Handle a tap in the app
Use onNotificationOpened to receive the message ID and optional safe HTTPS link. The application decides how to navigate. Image failure or extension timeout keeps the original text.
NotificationService.swift
import PushPortNotificationService
final class NotificationService: PushPortNotificationService {}
The SDK forwards notification callbacks to an existing UNUserNotificationCenterDelegate. Install your own delegate before SDK initialization, or pass manageNotificationDelegate: false and forward responses to handleNotificationResponse(_:).
Web Push uses the browser’s native push service and VAPID keys. You do not need a Firebase project. Supported provider routes cover Chrome/Edge, Firefox and Safari.
Save your website origin
Create an application or enable Web on an existing one. Save the exact HTTPS origin, for example https://example.com, without a path. Subdomains are separate origins.
Enable Web Push
Application → Settings → Web Push → Enable Web Push. PushPort generates a stable key pair and stores the private key securely. The SDK downloads only the public key. Do not regenerate keys while subscriptions use them.
Serve the worker
Install the SDK below and copy its worker into your website. It must return JavaScript from the same origin; never return an HTML fallback for this URL.
Ask from a user action
Initialize after the page loads and request permission directly from a button click. On iPhone/iPad, a supported Home Screen web app is required. Permission denial is changed in browser settings.
Version 0.0.1 is available as a regular npm dependency from the GitHub release archive. npm registry publication is pending publisher setup. Copy node_modules/@pushport/web-sdk/dist/pushport-sw.js to public/pushport-sw.js during your application build.
import { PushPort } from '@pushport/web-sdk';
if (PushPort.isSupported()) {
const push = await PushPort.initialize({
appId: 'YOUR_PUSHPORT_APP_ID',
serviceWorkerUrl: '/pushport-sw.js',
});
document.querySelector('#enable-push')?.addEventListener('click', () => {
void push.requestPermission().catch(error => {
const status = document.querySelector('#push-status');
if (status) status.textContent = error.message;
});
});
}
Already have a service worker? Import the copied PushPort worker in the existing worker and pass serviceWorkerRegistration to initialize. The host owns scope and worker updates. The SDK will not replace another worker. HTTPS links in notifications stay within your website origin.
setSubscribed(false) unsubscribes and synchronizes the preference. setLocale(null) restores automatic locale detection. Review status().lastSyncedAt and syncError after changing permissions, languages or connectivity.
KMP reuses the native Android, Apple and Web SDKs. It creates no second installation and needs no separate application or provider credentials in PushPort.
KMP SDK 0.0.1 is published on Maven Central. Add google() and mavenCentral(), then dev.pushport:kmp-sdk:0.0.1 to commonMain. The Android SDK is transitive. Apple and Web hosts also connect their native SDK as described below.
Add dev.pushport:kmp-sdk:0.0.1 to commonMain. The Android SDK is transitive. Keep one client per application and call it on the UI/main thread.
Android host
Create the client with createAndroidPushPort(applicationContext) and a provider for the current foreground Activity. Do not retain a destroyed Activity.
Apple host
Add PushPort 0.0.1 through Swift Package Manager, instantiate PushPortKMPBridge() in Swift and forward APNs callbacks. The common client uses createApplePushPort(); initialization must use the same App ID and APNs environment.
Web host
Install the Web SDK in the host project, serve its worker from the same origin and pass the imported module to createWebPushPort. Kotlin/Wasm and native desktop delivery are not included.
The release build passed on macOS, including an independent Kotlin framework linked into a Swift application with the public Apple SDK. Real device/browser delivery is checked separately.
Next Android SDK release — these APIs are not part of the published 0.0.1. The compatible backend must be deployed first.
Automatically: installation UUID, separate PushPort user ID, Android ID where available, FCM token, notification permission and opt-in, language and locales, timezone, package/app/OS/SDK versions, manufacturer/model, carrier, foreground sessions and usage time. The backend records last IP and approximate country from trusted ingress. Background synchronization is not an app visit.
Each new installation receives a new PushPort userId. Restarts and updates preserve it. Android ID links history only; users are not merged and tags are not copied after reinstall. Existing user IDs remain unchanged. Explicit verified login can still link an account.
Tags, email, E.164 phone, coordinates and custom events are supplied explicitly by your app. PushPort never reads contacts, IMEI, advertising ID or GPS automatically. Email/phone metadata does not enable email/SMS delivery.
Your authenticated backend requests a five-minute proof using POST /api/v1/apps/{appId}/identity-tokens with installationId and externalId, then returns token to the app. Use the existing owner session bearer on the backend only. A failed or expired login needs a new proof. SDK calls queue changes; PushPort.user(context) is the last server-confirmed profile and status().syncError reports failures.
When required, setConsentRequired must run before initialization. Consent and Android notification permission are separate. Revocation stops future PushPort collection/sync and presentation; it cannot recall in-flight requests or delete server history.
Android · Kotlin
PushPort.setConsentRequired(this, true) // before init
PushPort.initWithContext(this, appId)
// After your consent flow:
PushPort.setConsentGiven(this, true)
// After your backend verifies the account:
PushPort.login(context, externalId, identityToken)
PushPort.setTags(context, mapOf("plan" to "pro"))
val confirmedUser = PushPort.user(context)
PushPort.logout(context)
Audience lists installations. One person with two devices can appear twice. Filters use language, locale, approximate country and subscription state; save useful combinations as segments.
The SDK synchronizes the app locale and system locales, timezone, version and notification state. A locale such as de-DE is different from a language such as de. Country is estimated from the IP and may be unknown.
The server API is available on pushport.dev. PushPort.user(context) is prepared in the Android SDK and requires the next release; published version 0.0.1 does not include it.
Open your application → Settings → API keys. Create a key for the partner’s server and copy it immediately: it cannot be displayed again. Never embed the key in an app or URL. Applications are resolved by userId; no App ID is required. Account keys cover owned apps and team apps after invitation acceptance. Key scopes are limited by current membership permissions: viewers can read, senders can send, managers can also update tags. Selected-app grants apply; removal or role changes immediately change key access. Application keys remain restricted to one app.
The API accepts the original PushPort userId UUID without prefixes or suffixes. It is neither an FCM token nor Android ID. The application developer chooses the link parameter and any ID decoration. The partner removes that decoration and sends the original UUID in API requests.
Link from your app
Read the confirmed userId after successful synchronization. If no profile is available yet, wait for synchronization and retry. The host application builds and opens its own links; use a URL builder to encode values. When switching accounts, wait for the new profile to be confirmed.
Android · Kotlin
val userId = PushPort.user(context)?.userId
if (userId != null) {
val offerUrl = android.net.Uri.parse("https://example.com/offer")
.buildUpon()
.appendQueryParameter("sub_id_10", userId + "::|00030")
.build().toString()
// Open offerUrl using your application flow.
} else {
PushPort.sync(context) // async; retry after synchronization
}
Look up one installation
Lookup accepts one ID only. It returns installId, appId (Android package name), customAppName, locale (language), countryCode (nullable), timezone and type: api. Applications are resolved by userId; no App ID is required. Account keys cover owned apps and team apps after invitation acceptance. Key scopes are limited by current membership permissions: viewers can read, senders can send, managers can also update tags. Selected-app grants apply; removal or role changes immediately change key access. Application keys remain restricted to one app.
HTTP
POST /api/v1/partner/getInstall/
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{"id":"11111111-2222-4333-8444-555555555555"}
Send to a list
Send 1–10,000 IDs. Duplicates are removed; unknown and foreign IDs appear in excludedUserIds. No matches returns 422. click_uuid accepts an array, or one string for compatibility. Title, body and link apply to the entire list. The generic API uses userIds, title, body, imageUrl and clickUrl. For users from multiple apps, the response contains messages: one job with messageId per app, plus aggregate requestedCount, queuedCount and excludedUserIds. Read the status of each messageId. A single app retains the original response format.
Idempotency-Key is optional. Without it, every request creates a new send, including retries. If supplied, reuse the same key when retrying. Different content under the same key returns 409. 202 means queued, not delivered. The worker selects one current Android subscription per userId and skips disabled subscriptions. Read status with messageId using the same API key. Revoking a key does not cancel accepted jobs.
60 requests/minute per key; send body up to 2 MiB. scheduledAt is Unix milliseconds, at most 366 days ahead. Honor Retry-After on 429. accepted means provider acceptance, opened means a tap, and unknown means an uncertain outcome without blind retries.
HTTP
GET /api/v1/public/notifications/MESSAGE_ID
Authorization: Bearer YOUR_API_KEY
Registration, purchase and user tags
Pass the same PushPort userId in the WebView link and to your partner server. When the partner receives confirmation of a registration or purchase, their server updates tags through the API. PushPort does not detect actions on third-party sites automatically.
Tags are isolated by application. Even when one person uses several apps, send the intended App ID and that app’s PushPort userId. Copy App ID from Overview → App ID or Settings → API keys, or request GET /api/v1/public/app with the application key. Each app needs its own key. An App ID/key mismatch returns 403. Legacy URLs without App ID still resolve the application from the key.
Create an application key with USER_TAGS_WRITE (Update user tags). Existing keys do not gain this permission automatically. PATCH merges only the supplied tags: the string "true" sets a flag, null removes a tag. Repeating the same patch is safe. GET on the same path requires INSTALLATIONS_READ. Unknown or foreign userId returns 404 without creating a profile.
HTTP
PATCH /api/v1/public/apps/APP_ID/users/USER_ID/tags
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{"tags":{"registration":"true"}}
// After a confirmed purchase:
// {"tags":{"purchase":"true"}}
// Remove a tag: {"tags":{"purchase":null}}
Up to 50 tags per user, keys up to 64 characters and values up to 256. Keys and values are case-sensitive strings, not booleans. If a postback arrives before the device registers with PushPort, retain it and retry after synchronization. Keep the API key on your server.
Audience and the campaign editor show tags actually received for the selected application. Click a tag to add an Exists condition; choose Equals and a value to match it exactly. All conditions use AND, up to 10. Does not equal includes missing tags. Save filters as segments. The system tag install="true" is added automatically regardless of push permission, including for existing users. It cannot be removed or changed and does not count toward the 50 custom tags.
Scheduled campaigns select recipients when they start and recheck tags before each send. A user who no longer matches is skipped with audience_changed. A push already handed to the provider cannot be recalled. Tags do not trigger campaigns automatically: create and send or schedule a campaign.
Audience updates before every send. This is a shared sequence, not a journey starting at installation. Pausing cancels unstarted sends; an active send may finish.
After downtime, the elapsed slot is recorded as skipped, the sequence advances one message and resumes at the next future slot.
Pause the automation to edit its schedule and audience. After saving, click Resume. Sends already started use the previous settings.
Open Campaigns in your application. Save a draft with a title, message and audience. Check the eligible count and test on your own installation before sending to the full audience.
Choose immediate sending or a future date. The time shown uses your browser’s timezone and is stored as UTC. Schedules continue on the server after you close the page.
Add translations: exact locale → language → default message.
One-time schedules are available; recurring and recipient-local schedules are not implemented yet.
Cancellation stops pending work. It cannot recall notifications already passed to FCM.
Paste a public HTTPS image URL, or open Application images below it. Upload a PNG/JPEG or choose an existing image: its URL is inserted automatically. You can also manage the library in application settings.
Uploads: up to 1 MB, 4096 pixels per side and 8 million pixels total. The library holds up to 100 images / 50 MB per application. Images are public to anyone with their link.
Deleting an image breaks its link in existing and scheduled messages. Application deletion removes all its images. Copies already downloaded on devices remain.
Check the preview and test on a device before sending.
A successful send means that FCM, APNs or the browser push service accepted the request. It does not prove display on the device. Reported notification opens, failures and uncertain outcomes are counted separately.
Review the campaign result and installation state together. An invalid token differs from a temporary provider error. An uncertain sending result is not retried blindly because that could create a duplicate.
Check subscription and system permission before retrying.
A draft preview and the audience at sending time can differ.
Compare the timestamp and installation ID when investigating a report.
If an installation is missing, launch the app and check the App ID, backend connectivity and SDK initialization. If it appears but cannot receive notifications, check permission, subscription and token status.
If a message is accepted but not visible, check the device connection, notification settings and the exact target installation. For image-only issues, open the image URL without signing in and check the size and format.
Missing code email: check spam, confirm the address and wait one minute before requesting another code.
Firebase error: confirm that the sender credentials belong to the same application project.
Still stuck? Send the App ID, approximate time and expected result through Contacts.