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

FieldNotes
titleRequired.
bodyThe main line.
subtitleiOS only. Rendered between title and body; other platforms ignore it.
urlOpened when the notification is tapped.
imageAndroid and web render it natively. iOS needs a Notification Service Extension โ€” see below.
dataArbitrary key/values handed to your app. Values are coerced to strings before send.
buttonsUp 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.

FieldEffect
collapseIdA 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.
ttlSeconds the provider keeps retrying an offline device. Omit for the provider default. 0 means "deliver now or discard".
priorityhigh wakes the device immediately. normal may be batched to save battery โ€” the right default for anything not time-critical.

Apple iOS โ€” ios

FieldNotes
badgeAbsolute count. APNs has no increment operation โ€” a relative change has to be computed on-device, so only absolute values are sent.
soundFile name in your app bundle, or default.
interruptionLevelpassive ยท 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.
relevanceScore0โ€“1. Ranks the notification inside a summary. Clamped.
categoryA UNNotificationCategory your app registers. This is how iOS knows which buttons to draw.
threadIdGroups related notifications in Notification Center.
targetContentIdWhich window or scene the notification relates to.
contentAvailableWakes the app for a silent background refresh.
mediaAttachment URL. Needs a Notification Service Extension โ€” see below.
Setting 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

FieldNotes
channelIdMust already exist on the device (Android 8+). A missing channel means the notification is dropped silently.
smallIconDrawable resource name for the status bar.
bigPictureExpanded image. FCM renders this natively.
largeIconFCM 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.
visibilitypublic ยท private ยท secret โ€” how much shows on the lockscreen.
groupKeyStacks related notifications into one tray entry.
soundDefaults to the device's own.

Web push โ€” web

FieldNotes
iconShown beside the notification text.
badgeMonochrome glyph for the Android status bar.
requireInteractionKeeps 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:

PlatformWhat happens
WebRendered 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.
iOSDelivered as nb_buttons. iOS cannot declare buttons in a payload โ€” your app registers a UNNotificationCategory, and you must also set ios.category to match.
AndroidDelivered 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.

RejectedWhy
ios.relevanceScore: 2Outside 0โ€“1.
android.accentColor: "blue"Must be #RRGGBB.
advanced.priority: "urgent"Only normal or high.
6 buttonsCapped 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" }
}
โ† REST APIImporting an audience โ†’