Sending Push Notifications
Push notifications allow you to send direct messages to your users' mobile devices. They are perfect for timely alerts, transaction updates, and re-engagement campaigns.
In Conviso, you can send push notifications in three different ways:
- Broadcasts: One-time messages to a segment of users.
- Campaigns: Automated messages triggered by user behavior or attributes.
- Transactionals: System-triggered alerts (like password resets or shipping updates).
While the method of triggering the notification varies, the Content Editor remains consistent across all modes.
1. Sending via Broadcasts
A broadcast is ideal for announcements or alerts you want to send immediately (or schedule for a specific time) to a group of people.
- Navigate to Broadcasts > Create Broadcast.
- Define your audience (e.g., "All users in London").
- Select Push notifications as your channel.
- Compose your message in the editor.
(For a full guide on Broadcast settings, see the Broadcasts page).
2. Sending via Campaigns
Campaigns allow you to automate push notifications based on user journeys. For example, sending a welcome notification 5 minutes after a user signs up.
- Create or edit a Campaign Workflow.
- Click the + (plus) icon to add a new step.
- From the dropdown menu, select Push Notification.

- Once added, click Add push content to open the editor.

3. Sending via Transactionals
Transactional notifications are triggered programmatically via the API but must be defined in the dashboard first.
- Navigate to Transactionals > Create Message.
- Enter a Name and Description for your internal reference.
- Choose your Subscription Preference (usually "All subscribed people").
- Select Push Notification as the channel.
- Set a Trigger Name (e.g.,
order_shipped). This is the event name that will be used to trigger the notification. - Click Proceed (or Add content) to design your notification.

Once created, use the Trigger Name to send messages via the API. See the Node.js API Guide for implementation details.
The Push Notification Editor
Regardless of how you trigger the notification (Campaign, Broadcast, or Transactional), you will use the same editor to craft your message.

Designing Your Content
- Title: The headline of your notification. Keep it punchy (under 40 characters is best).
- Message: The main body text.
- Deep Link (Optional): A URL scheme (e.g.,
myapp://promo/123) to take the user to a specific screen. - Image (Optional): A URL for a rich media image (banner) to reach users visually.
- Actions (Optional): Quick action buttons on the notification (e.g., “View offer”, “Dismiss”). Each action has an
action_idyour app must recognize. iOS and Android handle actionable taps differently in code—see Actionable push notifications (manual mode) below. For full SDK steps (Firebase, App Group, NSE), use the Flutter push guide.
Personalization with Attributes
You can make your notifications dynamic using Liquid syntax. On the left sidebar of the editor, you will see a list of available Attributes.
Clicking an attribute (like first_name) will copy the variable to your clipboard, or you can type it manually:
"Hey
{{ first_name }}, your order is ready!"
If the user's name is "Jane", she will see: "Hey Jane, your order is ready!"
You can also use attributes to provide fallback logic:
"Hey
{{ first_name | default: 'there' }}, check this out!"
Actionable push notifications (manual mode)
This section is for mobile apps using OpenCDP push payloads that include data.actions. Campaign authors should only use action_id values that your app team has approved and implemented.
For now, iOS action registration is manual: your team maintains a fixed allowlist of action_id values, registers matching identifiers in native iOS code, and keeps Android behavior aligned with the same ids (Android actionable rendering is handled via the Flutter SDK helper; ids still must match the payload).
Prerequisites
- Base push tracking is set up from the SDK README (
handleForegroundPushDelivery,handleBackgroundPushDelivery,handlePushNotificationOpen). firebase_coreandfirebase_messagingare configured.- iOS: App Group and Notification Service Extension are configured.
- Android: Background/terminated notifications with actions use the SDK native bridge (
NotificationCompat,PendingIntent, SDKBroadcastReceiver) when you call the helper below.
1) iOS: register CDP_ACTIONS category at launch
action_id in OpenCDP must match UNNotificationAction.identifier.
import UIKit
import Flutter
import UserNotifications
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
registerCDPActionCategory()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func registerCDPActionCategory() {
let openAction = UNNotificationAction(
identifier: "view_offer", // MUST match CDP action_id
title: "View Offer",
options: [.foreground]
)
let dismissAction = UNNotificationAction(
identifier: "dismiss_offer", // MUST match CDP action_id
title: "Dismiss",
options: []
)
let category = UNNotificationCategory(
identifier: "CDP_ACTIONS",
actions: [openAction, dismissAction],
intentIdentifiers: [],
options: [.customDismissAction]
)
UNUserNotificationCenter.current().setNotificationCategories([category])
}
}
2) iOS: action tap handling
Forward action taps so Flutter (or your native layer) receives the tapped action_id and can route the user. Ensure the app eventually calls OpenCDPSDK.handlePushNotificationOpen(...) with payload data that includes the tapped action id.
import UserNotifications
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let actionId = response.actionIdentifier
if actionId != UNNotificationDefaultActionIdentifier &&
actionId != UNNotificationDismissActionIdentifier {
// Forward to Flutter side if needed.
// Ensure Flutter eventually calls OpenCDPSDK.handlePushNotificationOpen(...)
// with payload data that includes the tapped action id.
}
completionHandler()
}
}
3) Android: render actions for background and terminated state
When actions are present, Android push is often data-focused. Use the SDK helper to render a native notification from payload data (this path is a no-op on iOS).
// lib/push_actions_service.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:open_cdp_flutter_sdk/open_cdp_flutter_sdk.dart';
class PushActionsService {
@pragma('vm:entry-point')
static Future<void> onBackgroundMessage(RemoteMessage message) async {
await Firebase.initializeApp();
// 1) Track delivery
await OpenCDPSDK.handleBackgroundPushDelivery(message.data);
// 2) Render native Android notification + actions (no-op on iOS)
await OpenCDPSDK.showAndroidActionableNotification(
message.data,
channelName: 'CDP Notifications',
channelDescription: 'Push notifications from CDP',
);
}
}
Wire the handler in main.dart:
FirebaseMessaging.onBackgroundMessage(PushActionsService.onBackgroundMessage);
What the helper does on Android
- Builds a
NotificationCompat.Buildernotification using payloadtitle/body. - Parses
data.actionsand adds up to three action buttons. - Uses
PendingIntentand an SDKBroadcastReceiverfor body and action taps. - Forwards taps through the SDK receiver and opens the app with payload extras (
opencdp_payload_json,opencdp_action_id,opencdp_link).
4) Tap and open callback in Flutter
If you also show your own local notifications in Flutter, use this pattern so tap tracking includes the action:
import 'dart:convert';
Future<void> onLocalNotificationTap(NotificationResponse response) async {
final payload = response.payload;
if (payload == null || payload.isEmpty) return;
final data = Map<String, dynamic>.from(jsonDecode(payload));
final actionId = response.actionId;
if (actionId != null && actionId.isNotEmpty) {
data['action_id'] = actionId;
}
await OpenCDPSDK.handlePushNotificationOpen(data);
}
5) Example payload (smoke test)
{
"delivery_message_id": "msg_123",
"delivery_send_context": "campaign",
"delivery_send_context_id": "cmp_123",
"title": "Weekend offer",
"body": "Choose an action",
"android_channel_id": "marketing",
"actions": "[{\"action_id\":\"view_offer\",\"label\":\"View\"},{\"action_id\":\"dismiss_offer\",\"label\":\"Dismiss\"}]"
}
Expected behavior
deliveredwhen the notification is received.openedwhen the user taps the notification body.- Action tap handling receives the expected
action_id.
6) QA checklist (release readiness)
- Android:
POST_NOTIFICATIONSpermission granted on Android 13+. - Device token registered after identify (
registerDeviceTokenwith a fresh token). - Background handler wired:
FirebaseMessaging.onBackgroundMessage(...). - App opens correctly on body tap and on each action tap.
- Dashboard or events show
delivered,opened, and correctaction_idfor action taps. - iOS:
CDP_ACTIONScategory registered before actionable pushes arrive. - iOS: Action identifiers exactly match campaign
action_idvalues. - iOS: Notification Service Extension installed; App Group matches across Runner, extension, and SDK config.
7) Troubleshooting
- No buttons on Android: Confirm the notification was rendered with
OpenCDPSDK.showAndroidActionableNotification(...), and thatdata.actionsis a valid JSON string array. - Clicks not tracked: Confirm API key is set during SDK init, and
delivery_message_idis present and non-empty. - Wrong
action_idtracked: Confirm each button uses the exactaction_idfrom the payload.
Notes
- On iOS, button labels come from
UNNotificationAction(title:)in your registered category, not from the payload alone. - Identifier matching is strict: native
action_id/ identifiers must equal the OpenCDPaction_id. - In manual mode, adding a new
action_idrequires an app update (and coordinated dashboard use).
Testing Your Notification
Before sending a message or activating a campaign, it is critical to test your notification on a real device.
- Click the Test push notification button in the top right.
- Select the platform (Android or iOS).
- Choose how you want to identify the test device:
Option A: Select by User ID
Useful if you want to test how the notification looks for a specific real user in your database.

Option B: Enter Device Token
Useful for developers testing a specific device directly.

Technical Setup
Ensure your app is configured to receive notifications:
- Start here (all mobile SDKs): Mobile E2E guide
- Flutter: Push setup
- Android: Push setup
- iOS: Push setup
- React Native: Push setup
- Node.js (server send): API examples
When testing by User ID, enter the same value you passed to identify() in your app — not the device ID or FCM/APNs token. See Identity and devices.
Image layout differs by platform and Android OS version (thumbnail right on Android 12+, left on older devices). See Rich push images.