Skip to main content

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:

  1. Broadcasts: One-time messages to a segment of users.
  2. Campaigns: Automated messages triggered by user behavior or attributes.
  3. 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.

  1. Navigate to Broadcasts > Create Broadcast.
  2. Define your audience (e.g., "All users in London").
  3. Select Push notifications as your channel.
  4. 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.

  1. Create or edit a Campaign Workflow.
  2. Click the + (plus) icon to add a new step.
  3. From the dropdown menu, select Push Notification.
Select Push Notification Channel
  1. Once added, click Add push content to open the editor.
Campaign Node

3. Sending via Transactionals​

Transactional notifications are triggered programmatically via the API but must be defined in the dashboard first.

  1. Navigate to Transactionals > Create Message.
  2. Enter a Name and Description for your internal reference.
  3. Choose your Subscription Preference (usually "All subscribed people").
  4. Select Push Notification as the channel.
  5. Set a Trigger Name (e.g., order_shipped). This is the event name that will be used to trigger the notification.
  6. Click Proceed (or Add content) to design your notification.
Create Transactional Message

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.

Push Notification Editor

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_id your 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_core and firebase_messaging are configured.
  • iOS: App Group and Notification Service Extension are configured.
  • Android: Background/terminated notifications with actions use the SDK native bridge (NotificationCompat, PendingIntent, SDK BroadcastReceiver) 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.Builder notification using payload title / body.
  • Parses data.actions and adds up to three action buttons.
  • Uses PendingIntent and an SDK BroadcastReceiver for 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

  • delivered when the notification is received.
  • opened when the user taps the notification body.
  • Action tap handling receives the expected action_id.

6) QA checklist (release readiness)​

  • Android: POST_NOTIFICATIONS permission granted on Android 13+.
  • Device token registered after identify (registerDeviceToken with 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 correct action_id for action taps.
  • iOS: CDP_ACTIONS category registered before actionable pushes arrive.
  • iOS: Action identifiers exactly match campaign action_id values.
  • 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 that data.actions is a valid JSON string array.
  • Clicks not tracked: Confirm API key is set during SDK init, and delivery_message_id is present and non-empty.
  • Wrong action_id tracked: Confirm each button uses the exact action_id from 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 OpenCDP action_id.
  • In manual mode, adding a new action_id requires 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.

  1. Click the Test push notification button in the top right.
  2. Select the platform (Android or iOS).
  3. 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.

Test by User ID

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

Test by Token

Technical Setup​

Ensure your app is configured to receive notifications:

User ID = Person ID

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.

Rich push images

Image layout differs by platform and Android OS version (thumbnail right on Android 12+, left on older devices). See Rich push images.