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.
Bubbl.setSegments(listOf("vip", "newsletter"))Bubbl.setSegments(["vip", "newsletter"])await Bubbl.setSegments(['vip', 'newsletter']);Bubbl.setSegments(['vip', 'newsletter']);setSegmentsreplaces 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
segmentsinBubblOptionsatstart. - 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.
Bubbl.setCorrelationId(memberId) // after sign-in
Bubbl.clearCorrelationId() // on sign-outBubbl.setCorrelationId(memberId) // after sign-in
Bubbl.clearCorrelationId() // on sign-outawait Bubbl.setCorrelationId(memberId); // after sign-in
await Bubbl.clearCorrelationId(); // on sign-outBubbl.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()orsetConsent(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 callsetConsent(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.
Bubbl.track("purchase.completed", mapOf("total" to 12.5, "currency" to "GBP"))Bubbl.track("purchase.completed", properties: ["total": 12.5, "currency": "GBP"])await Bubbl.track('purchase.completed', {'total': 12.5, 'currency': 'GBP'});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)withrequireConsent, 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 happened | Android (BubblEvent.) | iOS (BubblEvent.) | Flutter | React Native (type) |
|---|---|---|---|---|
| A notification arrived, before it's shown | NotificationReceived | .notificationReceived | BubblNotificationReceived | notification.received |
| It's on screen | NotificationDisplayed | .notificationDisplayed | BubblNotificationDisplayed | notification.displayed |
| Opened from its system notification | NotificationOpened | .notificationOpened | BubblNotificationOpened | notification.opened |
| Call to action tapped | NotificationCtaClicked | .notificationCtaClicked | BubblNotificationCtaClicked | notification.cta_clicked |
| Closed without acting | NotificationDismissed | .notificationDismissed | BubblNotificationDismissed | notification.dismissed |
| Media shown or started | MediaViewed | .mediaViewed | BubblMediaViewed | media.viewed |
| Video or audio played to the end | MediaCompleted | .mediaCompleted | BubblMediaCompleted | media.completed |
| Survey started | SurveyStarted | .surveyStarted | BubblSurveyStarted | survey.started |
| Survey answers sent | SurveySubmitted | .surveySubmitted | BubblSurveySubmitted | survey.submitted |
| Entered a geofence | GeofenceEntered | .geofenceEntered | BubblGeofenceEntered | geofence.entered |
| Left a geofence | GeofenceExited | .geofenceExited | BubblGeofenceExited | geofence.exited |
| Something went wrong that Bubbl couldn't fix | Error | .error | BubblError | error |
| The device credential was refused | CredentialRejected | .credentialRejected | BubblCredentialRejected | credential.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.
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.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)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)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);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.