Push architecture
How a campaign reaches a device — from the dashboard, through FCM/APNs, into your app, and back as a delivery / click ACK.
A Swan push notification is a round trip: campaign → FCM/APNs → device → user tap → click ACK back to Swan. The SDK is responsible for the last two legs.
End-to-end flow
┌────────────────────┐
│ Campaigns engine │ (you author the message + audience here)
└────────┬───────────┘
│
▼
┌────────────────────┐
│ Swan backend │ resolves audience → device tokens
│ │
│ POST to FCM/APNs │ one request per device
└────────┬───────────┘
│
┌────────▼───────────┐
│ FCM / APNs │
└────────┬───────────┘
│ data-only payload
▼
┌────────────────────┐
│ Swan SDK │ - receives payload in messaging service / delegate
│ │ - renders notification (basic or carousel)
│ (your app) │ - posts to OS notification tray
│ │ - on tap: routes to deep-link handler
└────────┬───────────┘
│ POST /events { type: "push_clicked", messageId }
▼
┌────────────────────┐
│ Swan backend │ click ACK reconciles with delivery report
└────────────────────┘Why data-only payloads
The SDK uses data-only FCM payloads (no notification block), which means:
- The OS always wakes the app to handle the message — the SDK renders the notification itself
- Background message handlers fire reliably (including for
silent: 'true'payloads where we suppress the user-visible notification entirely) - The SDK can render carousel templates, custom sounds, and channel-specific
styling that pure
notificationpayloads can't express
The trade-off: you must keep the SDK's messaging service registered. The Android getting-started guide covers the manifest entries.
Notification channels (Android)
The SDK declares five default channels at first init:
| Channel ID | Importance | Use |
|---|---|---|
swan_transactional | High | Order confirmations, OTPs |
swan_alerts | High | Service alerts, account changes |
swan_promotional | Default | Marketing campaigns |
swan_general | Default | Catch-all |
swan_notifications | Default | Legacy / unspecified |
The campaign payload picks the channel by ID. Host apps can register additional
channels (e.g. one per category) via Swan.createNotificationChannel(...) —
typically used for custom-sound channels, since the sound is bound to the
channel at creation.
Click acknowledgement
When the user taps a notification, the SDK posts a push_clicked event back to
/events with the originating messageId. The campaigns engine reconciles
this with its delivery report to compute open rates, attribution, and trigger
follow-on journeys (e.g. "if user clicked promo X, send promo Y after 24h").
You don't wire this up — the SDK does it automatically when the notification
is tapped. You only need to handle the deep-link payload via
Swan.addNotificationOpenedListener and Swan.addDeepLinkOpenedListener.
Silent push
A payload with silent: 'true' fires the background message handler without
posting a user-visible notification. The SDK does not emit a delivery ACK
or click ACK for silent payloads — by definition there is no user-visible
interaction to report.
Use silent push for: cache invalidation, lightweight refresh nudges, or triggering an in-app action without bothering the user.