Skip to main content

Content

A notification's content is a map of locale → copy, plus a _default pointer:

{
"en": {
"title": "Weekend flash sale ⚡",
"body": "{{firstName|there}}, 30% off everything until Sunday night.",
"image": "https://cdn.acme.example/push/weekend.jpg",
"url": "acme://sale/weekend",
"actions": [
{ "id": "shop", "title": "Shop now" },
{ "id": "later", "title": "Remind me later", "url": "acme://remind?sale=weekend" }
]
},
"tr": { "title": "Hafta sonu indirimi ⚡", "body": "…", "url": "acme://sale/weekend" },
"_default": "en"
}

Locale resolution

For each subscriber the worker picks, in order:

  1. the exact language (en-GB),
  2. its base (en),
  3. _default.

_default must name a locale that exists in the map. Keys are BCP 47-ish (tr, en, en-GB).

Fields

FieldLimitsNotes
title1–200 charsRequired. Placeholders allowed.
body1–1000 charsRequired. Placeholders allowed.
imageURLHTTPS. Android big picture, web image, iOS attachment via the NSE. Not shown on the iOS lock screen preview.
url≤ 2048Deep link or URL opened on tap. Placeholders allowed.
actions≤ 3, unique ids ^[a-z0-9_-]{1,32}$, titles ≤ 40Buttons. url overrides the notification's own for that button.

Drafts in the wizard are held to a looser schema (empty fields allowed) so autosave never loses text; the strict rules above apply at send time.

Presentation

Locale-independent options in presentation:

FieldiOSAndroidWeb
soundaps.sound ("default", or a file in the app bundle); "none" → no key = silentdata.sound → the SDK's channel/setSilentsilent: true for "none"
badgeaps.badgesetNumberbadgeCount (Badging API)
threadIdaps.thread-id (groups in Notification Center)notification group key
iconlarge icon when there is no imagenotification icon
interruptionLevelaps.interruption-level: passive, active (default), time-sensitive, critical
relevanceScoreaps.relevance-score, 0–1, orders the notification summary
channelIdthe Android channel the SDK posts to

Anything a platform cannot show is ignored by that platform's transport.

Interruption level is what lets a push break through Focus (time-sensitive, needs the Time Sensitive Notifications capability in the app) or silent mode (critical, needs an entitlement from Apple). The server passes the value through and does not check the entitlement; without it iOS downgrades to active. passive drops into the list without a sound or banner.

Android channels are defined once in the dashboard and created on the device by the SDK the first time a push names one, so a new channel never needs an app release. A channel the device does not have and the push does not describe falls back to the SDK's default channel.

Silent pushes

silent: true on a campaign, notification or template sends a background push: nothing is drawn, the app is woken briefly and gets data.

PlatformWhat goes outWhat the SDK does
iOSapns-push-type: background, priority 5, aps: { "content-available": 1 } — no alert, sound or mutable-content (Apple discards a background push that carries any of them)didReceiveBackground(userInfo:) reports delivered and calls onReceive with message.silent == true
Androidthe usual data message with silent: "1"draws nothing, calls onReceive
Webskipped at fan-out — a browser cannot take a push without showing something

A silent campaign needs no content; data is the whole message (the wizard's Data card). iOS gives the app about 30 seconds and throttles apps that send too many — a few per hour is safe, a few per minute is not. A/B tests are not available on silent campaigns.

Data payload

data is a flat string → string map (max 64-char keys, 1024-char values) that rides along untouched and reaches your onOpen handler as message.data. Use it for routing (screen, ids). It is flat because FCM allows nothing else in data.

Platform rendering cheat sheet

FeatureiOSAndroidChrome/EdgeFirefoxSafari (mac/iOS)
Title / body
Image✅ (NSE)
Buttons✅ ≤4 (NSE)✅ ≤3✅ ≤2
Soundsystemsystemsystem
Badge✅ PWA✅ PWA
Group / threadvia tagvia tag
No sound (sound: "none")
Background (silent: true)skippedskippedskipped
Interruption level✅ iOS 15+
Channels✅ Android 8+

Size

Web push payloads are encrypted and capped at 4 KB by the push services (3 993 bytes of plaintext with the record size used). APNs allows 4 KB; FCM 4 KB of data. The wizard's byte meter shows the largest locale. Keep data lean; if you must attach more, send an id and fetch on open.

Emoji and Unicode

Everything is UTF-8 end to end. Emoji in titles and bodies are fine on all platforms; counts are in characters, not bytes.