Segments and events

Tag devices with segments for campaign targeting, give Bubbl your own user ID, record your own events, and listen to what Bubbl does as it happens.

Segments#

Segments are tags your app puts on a device, such as vip or newsletter, so campaigns in the dashboard can target them. They're created by your app through the SDK, never in the dashboard.

KOTLIN
Bubbl.setSegments(listOf("vip", "newsletter"))
SWIFT
Bubbl.setSegments(["vip", "newsletter"])
DART
await Bubbl.setSegments(['vip', 'newsletter']);
TS
Bubbl.setSegments(['vip', 'newsletter']);
  • setSegments replaces the device's segments: pass the whole list each time, and an empty list to clear them.
  • Up to 100 segments, each up to 255 characters. Bubbl trims them and drops empty ones and duplicates.
  • To set them from the first launch, pass segments in BubblOptions at start.
  • Bubbl keeps them on the device and sends them when it can, so it's fine to call offline.

In the dashboard, a segment appears under Company › Segments the first time a device reports it, with its device count and the campaigns that use it. Owners and admins can give it a readable label or hide it there. See Segments.

Your own user ID#

If your app has accounts, tell Bubbl your own id for the person using the device, so Bubbl's data can be joined to your user or CRM records. It starts with 5.1.0: on 5.0.x the calls don't exist.

KOTLIN
Bubbl.setCorrelationId(memberId)   // after sign-in
Bubbl.clearCorrelationId()         // on sign-out
SWIFT
Bubbl.setCorrelationId(memberId)   // after sign-in
Bubbl.clearCorrelationId()         // on sign-out
DART
await Bubbl.setCorrelationId(memberId);   // after sign-in
await Bubbl.clearCorrelationId();         // on sign-out
TS
Bubbl.setCorrelationId(memberId);   // after sign-in
Bubbl.clearCorrelationId();         // on sign-out
  • Send an opaque id (a member number or a UUID), never an email address, a name or anything else that identifies the person to someone who sees Bubbl's data. Your privacy notice should say that your app shares its own user id with Bubbl.
  • Call it after start, and after each sign-in. Bubbl keeps it between launches and sends it with the next device sync, so there's nothing to do at every launch. Setting the same id again changes nothing.
  • It's sent exactly as you give it, never trimmed or lowercased. It must be 1 to 255 characters and not blank; otherwise it's ignored with a warning in the log.
  • It isn't unique: one person with a phone and a tablet gives two devices with the same id, and a reinstall is a new device. Join on your side by this id, not by the install id.
  • Clear it when the person signs out, so the next person to use the device isn't tied to the last.
  • deleteMyData() and a start that began afresh (a different API key or credential) forget it along with everything else: set it again afterwards.
  • When the person withdraws consent (optOut() or setConsent(false)), Bubbl clears the id and ignores one sent while consent is withdrawn. The SDK keeps the last id you set and sends it again with the consent when you call setConsent(true), so you don't have to.

On production the dashboard doesn't show it yet: it's the External user ID on a device in Active users, which is a Sandbox feature. Showing it in other exports, and looking people up by it through the Platform API, aren't available yet.

Custom events#

track records an event of your app's own, with optional properties, and sends it with Bubbl's event queue.

KOTLIN
Bubbl.track("purchase.completed", mapOf("total" to 12.5, "currency" to "GBP"))
SWIFT
Bubbl.track("purchase.completed", properties: ["total": 12.5, "currency": "GBP"])
DART
await Bubbl.track('purchase.completed', {'total': 12.5, 'currency': 'GBP'});
TS
Bubbl.track('purchase.completed', { total: 12.5, currency: 'GBP' });
  • Name: 1 to 100 characters from A–Z a–z 0–9 . _ : -. An event with any other name isn't sent, and Bubbl logs a warning.
  • Properties: up to 50, flat: text, numbers, true or false, or null. Anything else (a list, a nested object) is left out.
  • Events are only recorded while Bubbl is active: not before setConsent(true) with requireConsent, and not after an opt-out.

Where your events show in the dashboard#

Bubbl keeps each event against the device that sent it, with its name, its properties and when it happened. In the dashboard you see them in the activity log: View full log → on a campaign's page opens it. It lists every device event, and a custom event has its name in the Type column. (In a Sandbox workspace, a device's Recent activity under Active users shows them too, as custom. followed by the name. Sandbox isn't available on production yet.)

An event also counts its device as active for Active users. Properties are stored but not shown anywhere in the dashboard, and there's no report, chart, segment or campaign rule built on custom events. Export workspace data (Company settings) includes them, without their properties. Keep location events for doesn't delete them; it only removes any position they carry.

Listening to events#

addEventListener tells your app what Bubbl does as it happens, on the main thread: each step of a notification's life, geofences entered and left, and errors. These are for your own use, such as your analytics or UI; Bubbl records its own for your campaign reports.

What happenedAndroid (BubblEvent.)iOS (BubblEvent.)FlutterReact Native (type)
A notification arrived, before it's shownNotificationReceived.notificationReceivedBubblNotificationReceivednotification.received
It's on screenNotificationDisplayed.notificationDisplayedBubblNotificationDisplayednotification.displayed
Opened from its system notificationNotificationOpened.notificationOpenedBubblNotificationOpenednotification.opened
Call to action tappedNotificationCtaClicked.notificationCtaClickedBubblNotificationCtaClickednotification.cta_clicked
Closed without actingNotificationDismissed.notificationDismissedBubblNotificationDismissednotification.dismissed
Media shown or startedMediaViewed.mediaViewedBubblMediaViewedmedia.viewed
Video or audio played to the endMediaCompleted.mediaCompletedBubblMediaCompletedmedia.completed
Survey startedSurveyStarted.surveyStartedBubblSurveyStartedsurvey.started
Survey answers sentSurveySubmitted.surveySubmittedBubblSurveySubmittedsurvey.submitted
Entered a geofenceGeofenceEntered.geofenceEnteredBubblGeofenceEnteredgeofence.entered
Left a geofenceGeofenceExited.geofenceExitedBubblGeofenceExitedgeofence.exited
Something went wrong that Bubbl couldn't fixError.errorBubblErrorerror
The device credential was refusedCredentialRejected.credentialRejectedBubblCredentialRejectedcredential.rejected

The notification events carry the BubblMessage (message). The geofence events carry locationId, the dashboard location's ID. The error event carries a message string, which diagnostics also keep as lastError.

Always handle unknown eventsNew event types arrive in minor releases, so give your when an else branch and your switch a default case, and ignore events you don't know. The Kotlin and Dart event types are open, so the compiler insists on it in a when or switch expression; elsewhere, add it yourself.
KOTLIN
val listener = BubblEventListener { event ->
    when (event) {
        is BubblEvent.NotificationDisplayed -> analytics.log("bubbl_shown", event.message.id)
        is BubblEvent.GeofenceEntered -> analytics.log("bubbl_entered", event.locationId)
        else -> {}
    }
}
Bubbl.addEventListener(listener)
// later
Bubbl.removeEventListener(listener)
SWIFT
let token = Bubbl.addEventListener { event in
    switch event {
    case .notificationDisplayed(let message): analytics.log("bubbl_shown", message.id)
    case .geofenceEntered(let locationId): analytics.log("bubbl_entered", locationId)
    default: break
    }
}
// later
Bubbl.removeEventListener(token)
DART
final subscription = Bubbl.addEventListener((event) {
  switch (event) {
    case BubblNotificationDisplayed(:final message):
      analytics.log('bubbl_shown', message.id);
    case BubblGeofenceEntered(:final locationId):
      analytics.log('bubbl_entered', locationId);
    default:
      break;
  }
});
// later
await Bubbl.removeEventListener(subscription);
TS
const subscription = Bubbl.addEventListener((event) => {
  switch (event.type) {
    case 'notification.displayed': analytics.log('bubbl_shown', event.message.id); break;
    case 'geofence.entered': analytics.log('bubbl_entered', event.locationId); break;
    default: break;
  }
});
// later
subscription.remove();

In Flutter, Bubbl.events is a broadcast Stream<BubblEvent> of the same events, if you prefer a stream. On Android, keep the listener object to remove it; on iOS, keep the BubblEventToken that addEventListener returns (Swift closures have no identity to remove them by).

See BubblEvent for each event's fields.