Quick Start Guide
Get up and running with OpenPushAPI in under 5 minutes.
Quick Start
OpenPushAPI lets you send push notifications to web, Android, and iOS users from one unified API. This guide walks you through setup in five steps.
Step 1 — Create an account and app
Create a free account at the console. Once logged in, create a new App. You'll receive two credentials:
app_key— Public key, used in your JavaScript SDK. Safe to embed in frontend code.app_secret— Private key, used for server-side API calls. Never expose this in client-side code.
Step 2 — Install the JavaScript SDK
Add these two lines to the <head> or end of <body> on every page of your website:
<!-- Add to your website -->
<script src="https://sdk.openpushapi.com/v1/sdk.js"></script>
<script>
OpenPush.init({
appKey: 'your-app-key-here',
autoPrompt: true,
promptDelay: 3000
});
</script>
Step 3 — Users opt in automatically
With autoPrompt: true, the SDK will show the browser's native push permission dialog to new visitors after promptDelay milliseconds. Once a user accepts, they're automatically registered as a subscriber in your dashboard.
Step 4 — Send your first notification
Use the REST API to send a push notification to all your subscribers:
curl -X POST https://api.openpushapi.com/v1/notifications \
-H "Content-Type: application/json" \
-H "X-App-Key: your-app-key" \
-H "X-App-Secret: your-app-secret" \
-d '{
"title": "Hello World!",
"body": "Your first push notification from OpenPushAPI.",
"url": "https://yoursite.com",
"target_type": "all"
}'
Response
{
"notification_id": 12345,
"status": "queued",
"total_recipients": 847,
"is_ab_test": false,
"message": "Notification queued successfully"
}
Step 5 — Check delivery stats
Check delivery status and click-through rates for any notification:
curl https://api.openpushapi.com/v1/stats/notifications/12345 \
-H "X-App-Key: your-app-key" \
-H "X-App-Secret: your-app-secret"
Authentication
OpenPushAPI uses two header-based credentials for authentication:
| Header | Usage | Where to find |
|---|---|---|
X-App-Key |
All requests (public & private) | Console → App → Settings |
X-App-Secret |
Server-side API requests only | Console → App → Settings |
SDK requests (subscriber registration from the browser) only need X-App-Key. The app key is safe to include in frontend JavaScript.
API requests (sending notifications, managing subscribers) require both headers. The X-App-Secret must never be exposed in frontend code — keep it server-side only.
Security notice: Treat X-App-Secret like a password. Rotate it immediately if you suspect it has been exposed. You can regenerate it from the console.
Your First Notification
Here's a more complete notification with all the commonly used fields:
{
"title": "New Feature: Dark Mode",
"body": "We've just launched dark mode. Update your settings to try it.",
"url": "https://yourapp.com/settings/appearance",
"icon_url": "https://yourapp.com/icons/feature-icon.png",
"image_url": "https://yourapp.com/images/dark-mode-preview.jpg",
"target_type": "tags",
"target_tags": {
"plan": "pro"
},
"target_platforms": ["web", "android"],
"actions": [
{ "title": "Try Dark Mode", "url": "https://yourapp.com/settings/appearance" },
{ "title": "Maybe Later" }
],
"ttl_seconds": 86400
}
Targeting options
target_type |
Description | Required field |
|---|---|---|
"all" |
Send to all active subscribers | — |
"segment" |
Send to a saved segment | target_segment_id |
"tags" |
Send to subscribers matching tags | target_tags |
"external_ids" |
Send to specific user IDs | target_ids |
"subscriber_ids" |
Send to specific subscriber IDs | target_ids |
SDK Installation
The OpenPushAPI JavaScript SDK handles Web Push subscription on your website. It registers a Service Worker, manages the subscription lifecycle, and handles all communication with our API.
/v1/).
Breaking changes will be released as /v2/ — existing /v1/ URLs remain stable indefinitely.
Current version: v1
Option 1 — CDN (recommended)
<!-- Standard (readable) -->
<script src="https://sdk.openpushapi.com/v1/sdk.js"></script>
<!-- Minified (production, ~30% smaller) -->
<script src="https://sdk.openpushapi.com/v1/sdk.min.js"></script>
Option 2 — Self-hosted
Download the SDK files and host them yourself. You'll also need to serve the Service Worker file (openpush-sw.js) from the root of your domain.
# Download SDK + Service Worker and place in your web root
curl -O https://sdk.openpushapi.com/v1/sdk.min.js
curl -O https://sdk.openpushapi.com/v1/openpush-sw.js
# Serve openpush-sw.js from https://yourdomain.com/openpush-sw.js
SDK Configuration
Pass a configuration object to OpenPush.init():
OpenPush.init({
appKey: 'your-app-key', // Required
autoPrompt: false, // Show permission prompt automatically
promptDelay: 3000, // Delay in ms before auto-prompt (if autoPrompt: true)
serviceWorkerUrl: '/openpush-sw.js', // Custom Service Worker path
softPrompt: { // Optional: show a custom UI before the browser dialog
title: 'Get notified',
body: 'Stay up to date with the latest news.',
acceptText: 'Yes, notify me',
denyText: 'No thanks',
icon: '/icon.png', // Optional icon URL
},
topics: ['news', 'deals'], // Auto-subscribe to these topics on registration
onSubscribe: function(subscriberId) {
console.log('Subscribed:', subscriberId);
},
onError: function(err) {
console.error('Push error:', err);
}
});
| Option | Type | Default | Description |
|---|---|---|---|
appKey |
string | — | Required. Your public app key. |
autoPrompt |
boolean | false |
Trigger the permission flow automatically on page load. |
promptDelay |
number | 0 |
Delay in ms before auto-prompt fires (requires autoPrompt: true). |
serviceWorkerUrl |
string | '/openpush-sw.js' |
Path to the Service Worker file. Must be served from your domain root. |
softPrompt |
object | null | null |
Show a custom UI card before the native browser permission dialog. Pass null to skip and go straight to the browser dialog. Fields: title, body, acceptText, denyText, icon. |
topics |
string[] | [] |
Topic slugs to subscribe to automatically on first registration. |
onSubscribe |
function | null |
Called with subscriberId after successful registration. |
onError |
function | null |
Called with an error object if subscription fails. |
SDK Methods
// Manually trigger the permission flow (shows softPrompt if configured)
await OpenPush.prompt();
// Set tags for the current subscriber
await OpenPush.setTags({ plan: 'pro', cohort: 'beta-users' });
// Remove specific tag keys
await OpenPush.removeTags(['cohort', 'source']);
// Set external ID (your own user ID)
await OpenPush.setExternalId('user_12345');
// Subscribe to topics
await OpenPush.subscribeToTopics(['breaking-news', 'deals']);
// Unsubscribe from topics
await OpenPush.unsubscribeFromTopics(['deals']);
// Get current subscribed topics
const topics = await OpenPush.getTopics();
// Check if user is subscribed
const subscribed = OpenPush.isSubscribed(); // synchronous, returns boolean
// Get current subscriber ID
const subscriberId = OpenPush.getSubscriberId(); // synchronous, returns string | null
// Unsubscribe the current user (removes from API + browser)
await OpenPush.unsubscribe();
Flutter SDK — Installation
The openpushapi_flutter package has no mandatory Google/Firebase dependency. iOS uses Apple's native APNs directly. For Android, you provide the FCM token yourself — using whichever push client you prefer.
1. Download and add the SDK
Download the Flutter SDK package from the console and place it in your project:
# pubspec.yaml — add as a local path dependency
dependencies:
openpushapi_flutter:
path: ./packages/openpushapi_flutter
2. iOS — enable Push Notifications capability
In Xcode: open Runner → Signing & Capabilities → click + Capability → add Push Notifications. No other iOS setup is needed — the SDK handles APNs registration automatically.
3. Android — add your push provider (optional)
Android push requires FCM (Google). Add it only if you need Android support:
dependencies:
firebase_core: ^3.0.0
firebase_messaging: ^15.0.0
Follow the FlutterFire setup guide to add google-services.json to your Android project. This dependency lives in your app, not in the SDK.
Flutter SDK — Initialization
iOS: one line — the SDK requests APNs permission and registers the device token automatically.
Android: call registerToken() after obtaining the FCM token yourself.
import 'dart:io';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:openpushapi_flutter/openpushapi_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// iOS: APNs handled automatically — nothing extra needed
await OpenPushAPI.init(
config: const OpenPushConfig(
appKey: 'YOUR_APP_KEY',
debug: true, // set false in production
),
);
// Android: provide FCM token yourself
if (!kIsWeb && Platform.isAndroid) {
// Uses firebase_messaging — add it to YOUR pubspec, not required by the SDK
// final token = await FirebaseMessaging.instance.getToken();
// if (token != null) {
// await OpenPushAPI.registerToken(token, platform: 'android');
// }
// FirebaseMessaging.instance.onTokenRefresh.listen((t) {
// OpenPushAPI.registerToken(t, platform: 'android');
// });
}
runApp(const MyApp());
}
OpenPushConfig options
| Option | Type | Default | Description |
|---|---|---|---|
appKey | String | — | Required. Your public app key. |
apiUrl | String | https://api.openpushapi.com/v1 | API base URL. Override for testing. |
autoRequestPermission | bool | true | Request notification permission on init (iOS). |
vapidKey | String? | null | VAPID public key — required for Flutter Web push. |
debug | bool | false | Print SDK logs to console. |
Flutter SDK — API Reference
| Method | Description |
|---|---|
OpenPushAPI.init(config:) | Initialize the SDK. On iOS, automatically registers APNs token. |
OpenPushAPI.registerToken(token, platform:) | Register a push token. Use for Android ('android') and Web ('web'). |
OpenPushAPI.setExternalId(id) | Link subscriber to your own user ID (call after login). |
OpenPushAPI.setTags(map) | Set or update segmentation tags. Merged server-side. |
OpenPushAPI.unsubscribe() | Delete subscriber from OpenPushAPI and clear local state. |
OpenPushAPI.trackClick(notifId) | Track a notification click event. |
OpenPushAPI.subscriberId | Current subscriber ID, or null if not yet registered. |
OpenPushAPI.isInitialized | Whether SDK has been successfully initialized. |
// Link to your user after login
await OpenPushAPI.setExternalId(currentUser.id);
// Segment by plan and country
await OpenPushAPI.setTags({'plan': 'pro', 'country': 'US'});
// Unsubscribe on logout
await OpenPushAPI.unsubscribe();
// Read the subscriber ID
print(OpenPushAPI.subscriberId);
Flutter Web — Web Push
Web Push uses the standard browser Push API with VAPID — no Firebase required. Get your VAPID public key from the OpenPushAPI console (App → Platform Settings → Web Push), obtain a push subscription in your service worker, and pass the endpoint to registerToken.
// Web — register a VAPID push endpoint
if (kIsWeb) {
// Obtain the push subscription endpoint from the browser Push API
// using your service worker, then:
await OpenPushAPI.registerToken(pushEndpointUrl, platform: 'web');
}
Service worker: place openpush-sw.js at the root of your domain. It handles background message display without any third-party push SDK.
REST API — Authentication
The base URL for all API requests is https://api.openpushapi.com/v1.
Include both headers in every server-side request:
curl https://api.openpushapi.com/v1/subscribers \
-H "X-App-Key: ak_live_xxxxxxxxxxxxx" \
-H "X-App-Secret: as_live_xxxxxxxxxxxxx"
Apps API
Manage your applications programmatically.
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/apps | Create a new app |
GET | /v1/apps | List all apps |
GET | /v1/apps/{id} | Get app details |
PUT | /v1/apps/{id} | Update app settings |
DELETE | /v1/apps/{id} | Delete an app |
Subscribers API
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/subscribers | Register a subscriber (from SDK) |
GET | /v1/subscribers | List subscribers (filterable) |
GET | /v1/subscribers/{id} | Get subscriber details |
PUT | /v1/subscribers/{id} | Update subscriber (tags, external_id) |
DELETE | /v1/subscribers/{id} | Unsubscribe |
POST | /v1/subscribers/{id}/tags | Add tags |
DELETE | /v1/subscribers/{id}/tags | Remove tags |
Filter subscribers
# Filter by platform
GET /v1/subscribers?platform=web
# Filter by active status
GET /v1/subscribers?is_active=1
# Filter by tag
GET /v1/subscribers?tags[plan]=pro
# Pagination
GET /v1/subscribers?limit=50&offset=100
Notifications API
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/notifications | Send a notification |
GET | /v1/notifications | List notifications |
GET | /v1/notifications/{id} | Get notification + stats |
POST | /v1/notifications/{id}/cancel | Cancel a scheduled notification |
Full notification payload
{
"title": "Hi {{first_name}}, Flash Sale — 24 hours only!",
"body": "50% off all Pro plans. Offer ends tonight.",
"url": "https://yourapp.com/sale",
"icon_url": "https://yourapp.com/icons/sale.png",
"image_url": "https://yourapp.com/images/sale-banner.jpg",
"badge_url": "https://yourapp.com/icons/badge.png",
"target_type": "segment",
"target_segment_id": 42,
"target_platforms": ["web", "android", "ios"],
"target_countries": ["US", "GB", "DE"],
"actions": [
{ "title": "Claim Offer", "url": "https://yourapp.com/sale" },
{ "title": "Remind Me Later" }
],
"ttl_seconds": 43200,
"send_at": "2026-04-24T09:00:00Z",
"timezone_delivery": true,
"send_time_optimized": false,
"is_ab_test": false
}
Segments API
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/segments | Create a segment |
GET | /v1/segments | List segments |
PUT | /v1/segments/{id} | Update segment rules |
DELETE | /v1/segments/{id} | Delete segment |
GET | /v1/segments/{id}/count | Count matching subscribers |
// Create a segment: Pro users in the US on web
{
"name": "Pro Web US",
"rules": {
"platform": "web",
"country_code": "US",
"tags.plan": "pro"
}
}
Stats API
| Method | Endpoint | Description |
|---|---|---|
GET | /v1/stats/overview | Overall subscriber & delivery stats |
GET | /v1/stats/notifications/{id} | Per-notification stats |
Tracking API
Use the tracking endpoint to record notification click events from mobile apps and custom integrations. The JavaScript SDK and Flutter SDK call this automatically.
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/track/click | Record a notification click |
curl -X POST https://api.openpushapi.com/v1/track/click \
-H "Content-Type: application/json" \
-H "X-App-Key: your-app-key" \
-d '{
"notification_id": "12345",
"subscriber_id": "98765"
}'
The click is deduplicated per subscriber — calling this endpoint multiple times for the same subscriber and notification only increments the counter once.
Click tracking in mobile apps
// Flutter — track click when user taps notification
FirebaseMessaging.onMessageOpenedApp.listen((message) {
final notifId = message.data['opn_notification_id'];
if (notifId != null) OpenPushAPI.trackClick(notifId);
});
Webhooks
Webhooks allow OpenPushAPI to push delivery events to your server in real time. You can use them to sync delivery stats, trigger workflows, or log events.
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/webhooks | Register a webhook endpoint |
GET | /v1/webhooks | List registered webhooks |
PUT | /v1/webhooks/{id} | Update a webhook |
DELETE | /v1/webhooks/{id} | Delete a webhook |
Registering a webhook
curl -X POST https://api.openpushapi.com/v1/webhooks \
-H "Content-Type: application/json" \
-H "X-App-Key: your-app-key" \
-H "X-App-Secret: your-app-secret" \
-d '{
"url": "https://yourserver.com/hooks/push",
"events": ["delivered", "clicked", "failed"],
"is_active": true
}'
Available events
| Event | Triggered when |
|---|---|
delivered | A push notification is successfully delivered to a device |
clicked | A subscriber clicks/taps the notification |
failed | Delivery fails (invalid token, device unreachable, etc.) |
Webhook payload
{
"event": "clicked",
"notification_id": 12345,
"subscriber_id": 98765,
"platform": "android",
"timestamp": "2026-04-06T10:23:11Z"
}
Webhook requests are signed with an X-OpenPush-Signature header (HMAC-SHA256 of the raw body using your webhook secret). Always verify this signature before processing the payload.
Error Handling
All error responses follow a consistent JSON format:
{
"error": "Subscriber not found"
}
HTTP status codes
| Code | Meaning |
|---|---|
200 | OK — request succeeded |
201 | Created — resource created successfully |
204 | No Content — deleted successfully |
400 | Bad Request — missing or invalid parameters |
401 | Unauthorized — missing or invalid X-App-Key / X-App-Secret |
403 | Forbidden — action not allowed (e.g. plan limit reached) |
404 | Not Found — resource does not exist |
422 | Unprocessable Entity — validation failed |
429 | Too Many Requests — rate limit exceeded |
500 | Internal Server Error — contact support if this persists |
Rate limits
API rate limits are returned in response headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per minute |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Personalization
Use {{variable}} placeholders in the title, body, or url of any notification. Each subscriber receives their own version with the placeholders replaced.
Supported variables
| Variable | Source |
|---|---|
{{first_name}} | Subscriber's first_name field |
{{last_name}} | Subscriber's last_name field |
{{external_id}} | Your own user ID linked to this subscriber |
{{platform}} | web, android, or ios |
{{country}} | 2-letter country code (US, DE, …) |
{{city}} | Subscriber's city |
{{language}} | Subscriber's browser/device language |
{{tags.KEY}} | Any tag value — e.g. {{tags.plan}} |
Missing or unknown variables are silently replaced with an empty string.
{
"title": "Hey {{first_name}}, your {{tags.plan}} trial ends soon!",
"body": "Upgrade today and keep all your {{tags.plan}} features.",
"url": "https://yourapp.com/upgrade?ref={{external_id}}",
"target_type": "all"
}
Personalization works on all platforms — Web Push, Android (FCM), and iOS (APNs). The substitution happens in the delivery engine, so your original notification record stores the template, not the resolved text.
A/B Testing
Test two notification variants against each other. Subscribers are randomly split according to the ab_split_percent you specify. The winner (highest CTR) is automatically selected after both variants have been delivered for at least 4 hours and each has reached 100+ deliveries.
Sending an A/B test
Set is_ab_test: true and include the variant B fields in the payload:
{
"title": "New Feature Available",
"body": "We just launched something you'll love.",
"url": "https://yourapp.com/new",
"target_type": "all",
"is_ab_test": true,
"ab_split_percent": 50,
"title_b": "You're going to love this",
"body_b": "Check out the latest update — it's live now."
}
A/B test fields
| Field | Type | Description |
|---|---|---|
is_ab_test | boolean | Enable A/B split for this notification |
ab_split_percent | integer | % of subscribers who receive variant A (10–90, default 50) |
title_b | string | Title for variant B |
body_b | string | Body text for variant B |
Variants A and B share the same targeting, URL, icon, schedule, and TTL. Stats for both variants appear side by side in the console notification detail page.
A/B testing requires the Pro plan or higher.
Geo-targeting
Restrict delivery to subscribers in specific countries using the target_countries field. Pass an array of ISO 3166-1 alpha-2 country codes.
{
"title": "Black Friday — US & Canada only",
"body": "Exclusive deal available in your region.",
"url": "https://yourapp.com/black-friday",
"target_type": "all",
"target_countries": ["US", "CA"]
}
Geo-targeting can be combined with any target_type — it acts as an additional filter on top of segment, tag, or ID targeting. If target_countries is omitted or empty, the notification is delivered to all countries.
Country codes are matched against the country_code field on the subscriber, which is automatically detected from the subscriber's IP address at registration time.
Smart Send Time
When send_time_optimized: true, the delivery engine sends the notification to each subscriber at their personal optimal hour — the time of day when that subscriber has historically clicked push notifications most often.
{
"title": "Weekly digest is ready",
"body": "Here's what happened this week.",
"target_type": "all",
"send_time_optimized": true
}
The preferred hour per subscriber is computed nightly from their delivery and click history. Subscribers without enough history receive the notification immediately.
How it works: The engine delivers only to subscribers whose preferred_hour matches the current local hour (±1). Subscribers not yet in their window are skipped in the current run and picked up in subsequent runs. Subscribers without a recorded preferred hour receive the notification immediately.
In-App Notification Bell
The notification center widget adds a bell icon to your web app that shows a dropdown inbox of recent push notifications. It does not require browser permission — it fetches from our API and is ideal for logged-in app experiences.
Installation
<!-- Add a target element where the bell should appear -->
<div id="openpush-center"></div>
<!-- Load the widget -->
<script src="https://sdk.openpushapi.com/v1/notification-center.js"></script>
<script>
OpenPushCenter.init({
appKey: 'your-app-key',
subscriberId: currentUser.opnSubscriberId, // your subscriber's ID
targetSelector: '#openpush-center', // where to mount the bell
maxItems: 20, // max notifications to show
});
</script>
In-App API endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /v1/inapp?subscriber_id={id} | List unread in-app notifications |
PUT | /v1/inapp/{id}/read | Mark single notification as read |
POST | /v1/inapp/read-all | Mark all as read for a subscriber |
# Fetch unread in-app notifications
curl "https://api.openpushapi.com/v1/inapp?subscriber_id=98765&limit=20" \
-H "X-App-Key: your-app-key"
# Mark all as read
curl -X POST https://api.openpushapi.com/v1/inapp/read-all \
-H "Content-Type: application/json" \
-H "X-App-Key: your-app-key" \
-d '{"subscriber_id": 98765}'
The widget polls for new notifications every 60 seconds automatically. The unread badge count updates in real time when new items arrive.
Automation (Journeys)
Journeys let you build multi-step automated notification sequences triggered by subscriber events. For example: send a welcome push immediately after signup, wait 3 days, then send a follow-up only if the subscriber hasn't clicked anything.
Journey triggers
| Trigger | When it fires |
|---|---|
subscriber_created | A new subscriber registers |
tag_added | A specific tag is added to a subscriber |
segment_enter | A subscriber enters a segment |
event | A custom event is tracked via the API |
manual | Manually enrolled via the console or API |
Step types
| Type | Description |
|---|---|
send_push | Send a push notification |
wait | Pause for N minutes / hours / days |
condition | Branch based on subscriber field value |
ab_split | Randomly split enrollment into A/B paths |
Journeys are configured and managed from the console at Automation → Journeys. The engine processes active journey enrollments every minute.
Console only: Journeys are currently managed exclusively from the console builder at Automation → Journeys. REST API support for journeys is coming soon.
Unsubscribe Reason Tracking
When a subscriber opts out, you can capture why they unsubscribed. This data appears in the Analytics section of the console and helps you improve retention.
Passing a reason
Include a reason field when calling the unsubscribe endpoint:
curl -X DELETE https://api.openpushapi.com/v1/subscribers/98765 \
-H "Content-Type: application/json" \
-H "X-App-Key: your-app-key" \
-d '{"reason": "too_many"}'
Valid reason values
| Value | Meaning |
|---|---|
too_many | Receiving too many notifications |
not_relevant | Notifications are not relevant |
never_signed_up | Subscriber didn't intentionally opt in |
other | Other / not specified |
The Web SDK can also show a built-in reason dialog before unsubscribing:
// Show reason dialog, then unsubscribe
await OpenPush.unsubscribe({ showReasonDialog: true });
// Or unsubscribe with a known reason directly
await OpenPush.unsubscribe({ reason: 'too_many' });
Web Push (VAPID)
Web Push uses the VAPID (Voluntary Application Server Identification) protocol. OpenPushAPI automatically generates VAPID keys for each app — you don't need to manage them yourself.
Browser support: Chrome 50+, Firefox 44+, Edge 17+, Safari 16+ (macOS Ventura), Opera 42+.
The SDK registers a Service Worker in the browser, which handles incoming push messages even when the page is closed. The Service Worker is automatically generated and versioned per your app.
Custom Service Worker integration
If you already have a Service Worker, you can import ours from within it:
// your-service-worker.js
importScripts('https://sdk.openpushapi.com/v1/openpush-sw.js');
// Your existing SW code below...
self.addEventListener('fetch', (event) => {
// ...
});
Android — Firebase Cloud Messaging (FCM)
To send push notifications to Android apps, you need to configure FCM credentials in your OpenPushAPI app settings.
- Create a Firebase project at console.firebase.google.com
- Go to Project Settings → Cloud Messaging and copy your Server Key and Project ID
- In OpenPushAPI console, navigate to your App → Platform Settings → Android and enter the credentials
- In your Android app, use the Firebase SDK to get the FCM registration token and register it via our API
// Android — Register FCM token with OpenPushAPI
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
val token = task.result
// Register with OpenPushAPI
val client = OkHttpClient()
val body = JSONObject().apply {
put("platform", "android")
put("subscription_token", token)
}.toString()
val request = Request.Builder()
.url("https://api.openpushapi.com/v1/subscribers")
.addHeader("X-App-Key", "your-app-key")
.post(body.toRequestBody("application/json".toMediaType()))
.build()
client.newCall(request).enqueue(...)
}
iOS — Apple Push Notification Service (APNs)
To send push notifications to iOS apps, you need an APNs .p8 key from your Apple Developer account. The key file is uploaded directly in the console — no server-side configuration needed.
Step 1 — Get your APNs key from Apple
- Log in to developer.apple.com
- Navigate to Certificates, Identifiers & Profiles → Keys
- Click + to create a new key, enable Apple Push Notifications service (APNs)
- Download the
.p8file — you can only download it once - Note your Key ID (10 characters) and Team ID (found under Membership)
Step 2 — Upload in OpenPushAPI Console
- Go to Console → Apps → select your app → Platform Settings → iOS (APNs)
- Fill in Key ID, Team ID, and Bundle ID (e.g.
com.yourcompany.yourapp) - Click Choose file, select your
.p8file, and click Save APNs Settings - A green checkmark confirms the key is stored. You can replace it at any time by uploading a new file, or remove it with the Remove checkbox.
.p8 key file is stored securely on the server and is never exposed via the API. It cannot be set through the REST API — only through the Console UI.
// iOS — Register APNs token with OpenPushAPI
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
// Register with OpenPushAPI
var request = URLRequest(url: URL(string: "https://api.openpushapi.com/v1/subscribers")!)
request.httpMethod = "POST"
request.setValue("your-app-key", forHTTPHeaderField: "X-App-Key")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: [
"platform": "ios",
"subscription_token": token
])
URLSession.shared.dataTask(with: request).resume()
}