Message content
Every field a notification can carry, what each platform does with it, and which ones need code in your app before they do anything.
One content object covers every platform. Shared fields apply everywhere; the
ios, android and web blocks customize a single one. Nothing you
leave out changes the payload.
POST /v1/messages
authorization: Bearer sk_live_โฆ
{
"name": "Autumn winback",
"audience": { "segment_id": "โฆ" },
"content": {
"title": "We saved your cart ๐",
"subtitle": "Darlivo",
"body": "Two items are still waiting.",
"image": "https://cdn.yourco.com/cart.jpg",
"url": "https://yourco.com/cart",
"data": { "cart_id": "88213" },
"buttons": [{ "id": "checkout", "text": "Checkout" }],
"ios": { "badge": 3, "interruptionLevel": "time-sensitive" },
"android": { "channelId": "cart", "accentColor": "#6366F1" },
"web": { "requireInteraction": true },
"advanced": { "collapseId": "cart-winback", "ttl": 43200, "priority": "high" }
}
}
name is internal. It never reaches a recipient โ it exists so the
Sent messages list is readable when three campaigns all open with "Your order".Shared fields
| Field | Notes |
|---|---|
title | Required. |
body | The main line. |
subtitle | iOS only. Rendered between title and body; other platforms ignore it. |
url | Opened when the notification is tapped. |
image | Android and web render it natively. iOS needs a Notification Service Extension โ see below. |
data | Arbitrary key/values handed to your app. Values are coerced to strings before send. |
buttons | Up to 5. See Action buttons. |
Delivery policy โ advanced
These map onto transport-level controls, not payload keys: APNs headers, FCM's
android config, RFC 8030 headers for web push.
| Field | Effect |
|---|---|
collapseId | A newer message with the same id replaces an older one that hasn't been delivered yet. Use it for anything self-superseding โ score updates, cart reminders. |
ttl | Seconds the provider keeps retrying an offline device. Omit for the provider default. 0 means "deliver now or discard". |
priority | high wakes the device immediately. normal may be batched to save battery โ the right default for anything not time-critical. |
Apple iOS โ ios
| Field | Notes |
|---|---|
badge | Absolute count. APNs has no increment operation โ a relative change has to be computed on-device, so only absolute values are sent. |
sound | File name in your app bundle, or default. |
interruptionLevel | passive ยท active ยท time-sensitive ยท critical. Critical pierces Focus and silent mode and requires an entitlement from Apple; we also promote the sound to the critical form for you. |
relevanceScore | 0โ1. Ranks the notification inside a summary. Clamped. |
category | A UNNotificationCategory your app registers. This is how iOS knows which buttons to draw. |
threadId | Groups related notifications in Notification Center. |
targetContentId | Which window or scene the notification relates to. |
contentAvailable | Wakes the app for a silent background refresh. |
media | Attachment URL. Needs a Notification Service Extension โ see below. |
image or ios.media automatically sets
mutable-content. Without it the Notification Service Extension never runs and the
attachment silently does not appear โ which is a miserable thing to debug.Google Android โ android
| Field | Notes |
|---|---|
channelId | Must already exist on the device (Android 8+). A missing channel means the notification is dropped silently. |
smallIcon | Drawable resource name for the status bar. |
bigPicture | Expanded image. FCM renders this natively. |
largeIcon | FCM has no field for this โ it is delivered as nb_large_icon in data and drawn by the Notibase SDK. |
accentColor | #RRGGBB. Tints the small icon and your app name. |
ledColor | #RRGGBB, converted to FCM's float RGBA light settings. Ignored on devices without an LED. |
visibility | public ยท private ยท secret โ how much shows on the lockscreen. |
groupKey | Stacks related notifications into one tray entry. |
sound | Defaults to the device's own. |
Web push โ web
| Field | Notes |
|---|---|
icon | Shown beside the notification text. |
badge | Monochrome glyph for the Android status bar. |
requireInteraction | Keeps the notification on screen until the user acts on it. |
Web push needs no client work for any of this โ the hosted service worker already handles it, and hosted delivery means you never redeploy to pick up a fix.
Action buttons
"buttons": [
{ "id": "checkout", "text": "Checkout", "url": "https://yourco.com/cart" },
{ "id": "dismiss", "text": "Not now" }
]
Support differs by platform, and it is worth being precise about it:
| Platform | What happens |
|---|---|
| Web | Rendered directly. Browsers show two; extras are dropped, so we trim to two rather than letting them vanish silently. A button's url wins over the notification's. |
| iOS | Delivered as nb_buttons. iOS cannot declare buttons in a payload โ your app registers a UNNotificationCategory, and you must also set ios.category to match. |
| Android | Delivered as nb_buttons for the SDK to draw. |
Validation
Platform blocks are validated strictly. An unknown key is a
400, not a silent no-op:
{ "content": { "title": "hi", "ios": { "relevence_score": 0.5 } } }
โ 400 unrecognized key "relevence_score" in ios
This is deliberate. A typo'd option would otherwise send successfully, do nothing, and leave no evidence anywhere that it was dropped.
| Rejected | Why |
|---|---|
ios.relevanceScore: 2 | Outside 0โ1. |
android.accentColor: "blue" | Must be #RRGGBB. |
advanced.priority: "urgent" | Only normal or high. |
| 6 buttons | Capped at 5 โ no platform renders more. |
Per-channel overrides
When one channel needs entirely different copy, replace the shared fields with a per-channel map. Each branch takes the same shape as above.
"content": {
"apns": { "title": "Short for iOS", "ios": { "badge": 1 } },
"fcm": { "title": "Longer line for Android users" }
}