Push Notifications API
Complete reference for the push notification endpoints. Push is delivered via Firebase Cloud Messaging (FCM) to Android, iOS, and web devices.
Authentication
All push endpoints require an API key with the SEND_PUSH scope in the X-API-Key header.
Connect Firebase Project
Save your Firebase service account credentials. MsGine uses these to send push notifications through your own FCM sender ID. If you skip this step, the shared MsGine sender is used.
Endpoint
PUT https://api.msgine.net/api/v1/developers/push/configRequest
curl -X PUT https://api.msgine.net/api/v1/developers/push/config \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectId": "your-firebase-project-id",
"clientEmail": "firebase-adminsdk@your-project.iam.gserviceaccount.com",
"privateKey": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n"
}'Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
projectId | string | Yes | Firebase project ID |
clientEmail | string | Yes | Service account email |
privateKey | string | Yes | Service account private key (full PEM block) |
How to get these values: Firebase Console → Project Settings → Service accounts → Generate new private key. Copy
project_id,client_email, andprivate_keyfrom the downloaded JSON.
Response
{
"projectId": "your-firebase-project-id",
"clientEmail": "firebase-adminsdk@your-project.iam.gserviceaccount.com",
"isActive": true,
"createdAt": "2026-05-28T10:00:00.000Z",
"updatedAt": "2026-05-28T10:00:00.000Z"
}Credentials are validated on save. An invalid private key returns
400 Bad Request.
Get Firebase Config
GET https://api.msgine.net/api/v1/developers/push/configReturns the currently connected Firebase project. The private key is never returned.
{
"projectId": "your-firebase-project-id",
"clientEmail": "firebase-adminsdk@your-project.iam.gserviceaccount.com",
"isActive": true,
"createdAt": "2026-05-28T10:00:00.000Z",
"updatedAt": "2026-05-28T10:00:00.000Z"
}Returns null if no config is set.
Remove Firebase Config
DELETE https://api.msgine.net/api/v1/developers/push/configRemoves your Firebase credentials. Pushes fall back to the shared MsGine sender.
Register Device Token
Register an FCM device token for a user. Call this on every app launch — tokens can rotate.
Endpoint
POST https://api.msgine.net/api/v1/developers/push/deviceRequest
curl -X POST https://api.msgine.net/api/v1/developers/push/device \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"token": "fcm-token-from-firebase-sdk",
"platform": "android",
"externalUserId": "user_abc123"
}'Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | FCM registration token from Firebase SDK |
platform | "android" | "ios" | "web" | Yes | Device platform |
externalUserId | string | No | Your own user identifier. Used when sending with externalUserIds. |
Response
{
"id": "uuid-...",
"platform": "android",
"isActive": true,
"createdAt": "2026-05-28T10:00:00.000Z"
}If the same token is re-registered (e.g. a different user logs in on the same device), it is reassigned automatically.
Remove Device Token
Deactivate a device token — call this on logout.
DELETE https://api.msgine.net/api/v1/developers/push/devicecurl -X DELETE https://api.msgine.net/api/v1/developers/push/device \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "token": "fcm-token-to-remove" }'Send Push Notification
Endpoint
POST https://api.msgine.net/api/v1/developers/pushTargeting
| Goal | Field to use |
|---|---|
| Send to specific users (by your own IDs) | externalUserIds |
| Send to specific users (by MsGine UUIDs) | userIds |
| Send to all devices on a topic | topic |
| Broadcast to all registered devices | (leave targeting fields empty) |
Basic request
curl -X POST https://api.msgine.net/api/v1/developers/push \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Your order shipped",
"body": "Estimated arrival: tomorrow.",
"externalUserIds": ["user_abc123", "user_def456"],
"data": { "screen": "orders", "orderId": "ORD-999" }
}'Full request with all options
{
"title": "Flash Sale",
"body": "50% off everything — today only!",
"type": "notification",
"priority": "high",
"ttl": 86400,
"imageUrl": "https://cdn.example.com/banner.jpg",
"collapseKey": "flash-sale",
"topic": "promotions",
"externalUserIds": ["user_1", "user_2"],
"data": {
"screen": "shop",
"promo": "FLASH50"
},
"android": {
"channelId": "promotions",
"sound": "default",
"color": "#4F46E5",
"clickAction": "OPEN_PROMO"
},
"apns": {
"badge": 1,
"sound": "default",
"category": "PROMO"
},
"webpush": {
"icon": "https://cdn.example.com/icon.png",
"link": "https://app.example.com/shop",
"requireInteraction": true
},
"scheduledFor": "2026-06-01T09:00:00.000Z",
"idempotencyKey": "campaign_june_push_001"
}Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Notification title (max 100 chars) |
body | string | Yes | Notification body (max 500 chars) |
type | "notification" | "data" | "both" | No | notification = visible banner. data = silent background payload. Default: notification |
priority | "high" | "normal" | No | high wakes the device immediately. Default: high |
ttl | number | No | Message TTL in seconds (max 2419200 = 28 days). If device is offline past TTL, message is discarded. |
imageUrl | string | No | Image URL shown in the notification body |
collapseKey | string | No | Replaces pending undelivered notifications with the same key on the device |
data | Record<string, string> | No | Silent key-value payload delivered to the app |
topic | string | No | FCM topic (broadcast to all subscribed devices) |
externalUserIds | string[] | No | Your own user identifiers |
userIds | string[] | No | MsGine user UUIDs |
scheduledFor | string (ISO 8601) | No | Schedule a future send |
idempotencyKey | string | No | Prevents duplicate sends (max 64 chars) |
android | AndroidConfig | No | Android-specific overrides |
apns | ApnsConfig | No | iOS-specific overrides |
webpush | WebPushConfig | No | Web Push overrides |
AndroidConfig
| Field | Type | Description |
|---|---|---|
channelId | string | Notification channel (required for Android 8.0+) |
sound | string | Sound file name |
clickAction | string | Intent action when notification is tapped |
color | string | Accent color in #RRGGBB |
icon | string | Drawable resource name |
priority | "min" | "low" | "default" | "high" | "max" | Notification display priority |
ApnsConfig
| Field | Type | Description |
|---|---|---|
badge | number | App icon badge count (0 = clear) |
sound | string | Sound file name or "default" |
contentAvailable | boolean | Silent background notification |
mutableContent | boolean | Allow notification service extension to modify before display |
category | string | Action category identifier |
threadId | string | Thread ID for grouping notifications |
WebPushConfig
| Field | Type | Description |
|---|---|---|
icon | string | Notification icon URL |
link | string | URL to open on click |
badge | string | Badge image URL |
requireInteraction | boolean | Keep notification visible until dismissed |
Response
{
"id": "uuid-...",
"channel": "push",
"title": "Your order shipped",
"body": "Estimated arrival: tomorrow.",
"status": "pending",
"sandbox": false,
"recipientCount": 2,
"scheduledAt": null,
"createdAt": "2026-05-28T10:00:00.000Z"
}Status values
| Status | Meaning |
|---|---|
pending | Queued for delivery |
sent | All recipients delivered successfully |
partial | Some recipients delivered, some failed |
failed | All recipients failed |
Confirm Delivery or Open
Report when a notification is received or tapped. Updates deliveredAt, openedAt, and deliveryConfirmedCount on the notification record.
Endpoint
POST https://api.msgine.net/api/v1/developers/push/deliverycurl -X POST https://api.msgine.net/api/v1/developers/push/delivery \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"notificationId": "uuid-from-send-response",
"event": "delivered"
}'Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
notificationId | string | Yes | UUID returned by the send endpoint |
event | "delivered" | "opened" | Yes | delivered = device received it. opened = user tapped it. |
Get Push History
GET https://api.msgine.net/api/v1/developers/push/history?page=1&limit=20Response
{
"notifications": [
{
"id": "uuid-...",
"channel": "push",
"title": "Your order shipped",
"body": "Estimated arrival: tomorrow.",
"recipientCount": 2,
"successCount": 2,
"failureCount": 0,
"deliveryConfirmedCount": 1,
"status": "sent",
"sentAt": "2026-05-28T10:00:05.000Z",
"deliveredAt": "2026-05-28T10:00:06.000Z",
"openedAt": null,
"createdAt": "2026-05-28T10:00:00.000Z"
}
],
"total": 47,
"page": 1,
"pages": 3
}Sandbox mode
API keys with environment: "sandbox" skip actual FCM delivery. The notification is saved with status: "sent" and sandbox: true immediately. Use this for testing without sending real notifications.
Webhook events
Configure webhooks at PUT /developers/webhooks to receive push delivery events on your server:
| Event | When |
|---|---|
push.sent | All recipients delivered |
push.partial | Some delivered, some failed |
push.failed | All failed |
Event payload:
{
"eventType": "push.sent",
"channel": "push",
"messageId": "uuid-...",
"data": {
"title": "Your order shipped",
"body": "Estimated arrival: tomorrow.",
"successCount": 2,
"failureCount": 0,
"recipientCount": 2
}
}Error responses
| Code | Reason |
|---|---|
400 | No active devices found, FCM not configured, or invalid credentials |
401 | Missing or invalid API key |
403 | API key lacks SEND_PUSH scope |
404 | Notification not found (delivery confirm) |
429 | Rate limit exceeded |