Notifications
5 endpointsA campaign's notifications — nested under it, since a notification has no meaning until a campaign decides when it fires. Creating one here writes its content (headline, body, media, CTA, survey questions) and its trigger together; there's no separate endpoint for notification content alone.
List a campaign's notifications#
/campaigns/{campaign}/notificationsPath parameters
The campaign ID
Responses
200
{
"data": [
{
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"campaign_id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
"headline": "Coffee's on us ☕",
"body": "Show this at the counter for a free flat white before 11am.",
"media_asset_id": null,
"cta_label": "Get the voucher",
"cta_url": "https://harbour.coffee/free",
"trigger_type": "on_enter",
"created_at": "2026-09-20T09:12:00Z"
}
],
"meta": {
"next_cursor": null
}
}Response fields
401The token is missing, expired or revoked.
unauthenticated
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}403The token can't do this.
missing_abilityplatform_api_not_enabledwrong_environment
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}404Nothing with that ID in this workspace.
not_found
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limited
Retry-After.{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}curl 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications' \
-H 'Accept: application/json'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications', {
method: 'GET',
headers: {
Accept: 'application/json',
},
});
const data = await response.json();import requests
response = requests.request(
'GET',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications',
headers={
'Accept': 'application/json',
},
)
response.raise_for_status()
data = response.json()<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications', [
'headers' => [
'Accept' => 'application/json',
],
]);
$data = json_decode((string) $response->getBody(), true);Add a notification to a campaign#
/campaigns/{campaign}/notificationsCreates the notification's content and attaches it with a trigger in one call. A push campaign's notification needs trigger_type: fixed_time and trigger_config.send_at (a time on the campaign's day, e.g. "10:00"); a geo campaign's needs trigger_type: on_enter or on_exit, with an optional starts_at/ ends_at window inside the campaign's own. A survey needs 1–5 questions (OPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER or SLIDER; choice types need 2–4 choices) and carries no media.
Path parameters
The campaign ID
Request body
application/jsonStoreCampaignNotificationRequestrequiredmessage, surveyOPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER, SLIDERon_enter, on_exitResponses
201
{
"data": {
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"campaign_id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
"headline": "Coffee's on us ☕",
"body": "Show this at the counter for a free flat white before 11am.",
"media_asset_id": null,
"cta_label": "Get the voucher",
"cta_url": "https://harbour.coffee/free",
"trigger_type": "on_enter",
"created_at": "2026-09-20T09:12:00Z"
}
}Response fields
401The token is missing, expired or revoked.
unauthenticated
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}403The token can't do this.
missing_abilityplatform_api_not_enabledwrong_environment
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}404Nothing with that ID in this workspace.
not_found
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}422Some fields aren't valid. fields says which, with the dashboard's wording.
validation_failed
fields says which, with the dashboard's wording.{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limited
Retry-After.{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}curl -X POST 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data '{
"name": "Morning flat white offer",
"type": "message",
"headline": "Coffee'\''s on us ☕",
"body": "Show this at the counter for a free flat white before 11am.",
"trigger_type": "on_enter",
"cta_label": "Get the voucher",
"cta_url": "https://harbour.coffee/free"
}'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Morning flat white offer',
type: 'message',
headline: 'Coffee\'s on us ☕',
body: 'Show this at the counter for a free flat white before 11am.',
trigger_type: 'on_enter',
cta_label: 'Get the voucher',
cta_url: 'https://harbour.coffee/free',
}),
});
const data = await response.json();import requests
response = requests.request(
'POST',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications',
headers={
'Accept': 'application/json',
'Content-Type': 'application/json',
},
json={
'name': 'Morning flat white offer',
'type': 'message',
'headline': 'Coffee\'s on us ☕',
'body': 'Show this at the counter for a free flat white before 11am.',
'trigger_type': 'on_enter',
'cta_label': 'Get the voucher',
'cta_url': 'https://harbour.coffee/free',
},
)
response.raise_for_status()
data = response.json()<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications', [
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'json' => [
'name' => 'Morning flat white offer',
'type' => 'message',
'headline' => 'Coffee\'s on us ☕',
'body' => 'Show this at the counter for a free flat white before 11am.',
'trigger_type' => 'on_enter',
'cta_label' => 'Get the voucher',
'cta_url' => 'https://harbour.coffee/free',
],
]);
$data = json_decode((string) $response->getBody(), true);Get a campaign notification#
/campaigns/{campaign}/notifications/{notification}Path parameters
The campaign ID
The notification ID
Responses
200
{
"data": {
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"campaign_id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
"headline": "Coffee's on us ☕",
"body": "Show this at the counter for a free flat white before 11am.",
"media_asset_id": null,
"cta_label": "Get the voucher",
"cta_url": "https://harbour.coffee/free",
"trigger_type": "on_enter",
"created_at": "2026-09-20T09:12:00Z"
}
}Response fields
401The token is missing, expired or revoked.
unauthenticated
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}403The token can't do this.
missing_abilityplatform_api_not_enabledwrong_environment
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}404Nothing with that ID in this workspace.
not_found
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limited
Retry-After.{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}curl 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
-H 'Accept: application/json'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', {
method: 'GET',
headers: {
Accept: 'application/json',
},
});
const data = await response.json();import requests
response = requests.request(
'GET',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}',
headers={
'Accept': 'application/json',
},
)
response.raise_for_status()
data = response.json()<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', [
'headers' => [
'Accept' => 'application/json',
],
]);
$data = json_decode((string) $response->getBody(), true);Update a campaign notification#
/campaigns/{campaign}/notifications/{notification}The whole notification (content and trigger) is resubmitted, same rules as adding one.
Path parameters
The campaign ID
The notification ID
Request body
application/jsonUpdateCampaignNotificationRequestrequiredmessage, surveyOPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER, SLIDERon_enter, on_exitResponses
200
{
"data": {
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"campaign_id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
"headline": "Coffee's on us ☕",
"body": "Show this at the counter for a free flat white before 11am.",
"media_asset_id": null,
"cta_label": "Get the voucher",
"cta_url": "https://harbour.coffee/free",
"trigger_type": "on_enter",
"created_at": "2026-09-20T09:12:00Z"
}
}Response fields
401The token is missing, expired or revoked.
unauthenticated
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}403The token can't do this.
missing_abilityplatform_api_not_enabledwrong_environment
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}404Nothing with that ID in this workspace.
not_found
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}422Some fields aren't valid. fields says which, with the dashboard's wording.
validation_failed
fields says which, with the dashboard's wording.{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limited
Retry-After.{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}curl -X PATCH 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data '{
"headline": "Two coffees for one"
}'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', {
method: 'PATCH',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
headline: 'Two coffees for one',
}),
});
const data = await response.json();import requests
response = requests.request(
'PATCH',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}',
headers={
'Accept': 'application/json',
'Content-Type': 'application/json',
},
json={
'headline': 'Two coffees for one',
},
)
response.raise_for_status()
data = response.json()<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('PATCH', 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', [
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'json' => [
'headline' => 'Two coffees for one',
],
]);
$data = json_decode((string) $response->getBody(), true);Remove a notification from a campaign#
/campaigns/{campaign}/notifications/{notification}Detaches it; its content is also deleted, but only once it's no longer attached to any other campaign.
Path parameters
The campaign ID
The notification ID
Responses
200
{
"data": {
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"deleted": true
}
}Response fields
401The token is missing, expired or revoked.
unauthenticated
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}403The token can't do this.
missing_abilityplatform_api_not_enabledwrong_environment
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}404Nothing with that ID in this workspace.
not_found
{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limited
Retry-After.{
"error": {
"code": "string",
"message": "string",
"fields": {}
}
}curl -X DELETE 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
-H 'Accept: application/json'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', {
method: 'DELETE',
headers: {
Accept: 'application/json',
},
});
const data = await response.json();import requests
response = requests.request(
'DELETE',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}',
headers={
'Accept': 'application/json',
},
)
response.raise_for_status()
data = response.json()<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('DELETE', 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', [
'headers' => [
'Accept' => 'application/json',
],
]);
$data = json_decode((string) $response->getBody(), true);