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",
"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.",
"media": null,
"cta_label": "Get the voucher",
"cta_url": "https://example.com/offers/flat-white",
"questions": [],
"trigger_type": "on_enter",
"trigger_config": null,
"starts_at": null,
"ends_at": null,
"created_at": "2026-09-20T09:12:00+00:00",
"updated_at": "2026-09-20T09:12:00+00:00"
}
]
}Response fields
401The token is missing, unknown, revoked or expired.
missing_tokeninvalid_tokentoken_revokedtoken_expired
missing_tokenMissing bearer token.
invalid_tokenUnknown API token. Check it was copied in full, and that it is for this environment.
token_revokedThe token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.
token_expiredThis token has expired. Issue a new one from Configuration › Platform API.
{
"error": {
"code": "missing_token",
"message": "Missing bearer token."
}
}403The token or workspace can't do this.
missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
missing_abilityThis token doesn't have the campaigns:write scope.
platform_api_not_enabledThe full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.
wrong_environmentThis is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1
workspace_pausedThis workspace is scheduled for deletion; its API access is paused.
{
"error": {
"code": "missing_ability",
"message": "This token doesn't have the campaigns:write scope."
}
}404Nothing with that ID in this workspace.
not_found
not_foundNo such resource in this workspace.
{
"error": {
"code": "not_found",
"message": "No such resource in this workspace."
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limitedtoo_many_failed_attempts
Retry-After.rate_limitedToo many requests. Slow down and try again shortly.
too_many_failed_attemptsToo many failed attempts. Try again shortly.
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and try again shortly."
}
}curl 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications' \
-H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
-H 'Accept: application/json'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications', {
method: 'GET',
headers: {
Authorization: 'Bearer bubbl_YOUR_TOKEN',
Accept: 'application/json',
},
});
const data = await response.json();import requests
response = requests.request(
'GET',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications',
headers={
'Authorization': 'Bearer bubbl_YOUR_TOKEN',
'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' => [
'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
'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"; it comes back as "YYYY-MM-DD HH:MM" in the campaign's timezone); 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 message takes no questions; a survey needs 1–5 (OPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER or SLIDER; choice types need 2–4 choices) and carries no media. A media_asset_id is a file's id from the media library (upload one, or list them, under Media). A cta_url is fetched when you save and must answer 200 or 201, or the request is a 422.
Path parameters
The campaign ID
Headers
A unique value (e.g. a UUID) for this change. A retry with the same key within 24 hours gets the first response back, with Idempotent-Replayed: true, instead of making the change again.
Request body
application/jsonStoreCampaignNotificationRequestrequiredmessage, surveyFetched when you save: it must be a public address answering 200 or 201, or the request is a 422. Needs cta_label.
A survey's 1–5 questions: required for a survey, left out for a message.
OPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER, SLIDERA geo campaign's notification: on_enter or on_exit. A push campaign's: fixed_time, with trigger_config.send_at.
on_enter, on_exit, fixed_timeA push campaign's send time, on the campaign's day: "HH:MM", "YYYY-MM-DD HH:MM" (both in the campaign's timezone), or an ISO 8601 time with an offset. It comes back as "YYYY-MM-DD HH:MM" in the campaign's timezone. Leave it out for a geo campaign.
Responses
201
{
"data": {
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"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.",
"media": null,
"cta_label": "Get the voucher",
"cta_url": "https://example.com/offers/flat-white",
"questions": [],
"trigger_type": "on_enter",
"trigger_config": null,
"starts_at": null,
"ends_at": null,
"created_at": "2026-09-20T09:12:00+00:00",
"updated_at": "2026-09-20T09:12:00+00:00"
}
}Response fields
401The token is missing, unknown, revoked or expired.
missing_tokeninvalid_tokentoken_revokedtoken_expired
missing_tokenMissing bearer token.
invalid_tokenUnknown API token. Check it was copied in full, and that it is for this environment.
token_revokedThe token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.
token_expiredThis token has expired. Issue a new one from Configuration › Platform API.
{
"error": {
"code": "missing_token",
"message": "Missing bearer token."
}
}403The token or workspace can't do this.
missing_abilityplatform_api_not_enabledwrong_environmentworkspace_pausedlimit_exceeded
missing_abilityThis token doesn't have the campaigns:write scope.
platform_api_not_enabledThe full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.
wrong_environmentThis is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1
workspace_pausedThis workspace is scheduled for deletion; its API access is paused.
limit_exceededYou've reached the limit for max_campaigns.
{
"error": {
"code": "missing_ability",
"message": "This token doesn't have the campaigns:write scope."
}
}404Nothing with that ID in this workspace.
not_found
not_foundNo such resource in this workspace.
{
"error": {
"code": "not_found",
"message": "No such resource in this workspace."
}
}409A request with the same Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.
request_in_progress
Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.request_in_progressThis request is still being handled. Try again shortly.
{
"error": {
"code": "request_in_progress",
"message": "This request is still being handled. Try again shortly."
}
}422Something in the request isn't valid. fields says what, with the dashboard's wording.
validation_failed
fields says what, with the dashboard's wording.validation_failedThe given data was invalid.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid.",
"fields": {
"name": [
"The name field is required."
]
}
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limitedtoo_many_failed_attempts
Retry-After.rate_limitedToo many requests. Slow down and try again shortly.
too_many_failed_attemptsToo many failed attempts. Try again shortly.
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and try again shortly."
}
}curl -X POST 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications' \
-H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
-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.",
"cta_label": "Get the voucher",
"cta_url": "https://example.com/offers/flat-white",
"trigger_type": "on_enter"
}'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications', {
method: 'POST',
headers: {
Authorization: 'Bearer bubbl_YOUR_TOKEN',
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.',
cta_label: 'Get the voucher',
cta_url: 'https://example.com/offers/flat-white',
trigger_type: 'on_enter',
}),
});
const data = await response.json();import requests
response = requests.request(
'POST',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications',
headers={
'Authorization': 'Bearer bubbl_YOUR_TOKEN',
'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.',
'cta_label': 'Get the voucher',
'cta_url': 'https://example.com/offers/flat-white',
'trigger_type': 'on_enter',
},
)
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' => [
'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
'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.',
'cta_label' => 'Get the voucher',
'cta_url' => 'https://example.com/offers/flat-white',
'trigger_type' => 'on_enter',
],
]);
$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",
"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.",
"media": null,
"cta_label": "Get the voucher",
"cta_url": "https://example.com/offers/flat-white",
"questions": [],
"trigger_type": "on_enter",
"trigger_config": null,
"starts_at": null,
"ends_at": null,
"created_at": "2026-09-20T09:12:00+00:00",
"updated_at": "2026-09-20T09:12:00+00:00"
}
}Response fields
401The token is missing, unknown, revoked or expired.
missing_tokeninvalid_tokentoken_revokedtoken_expired
missing_tokenMissing bearer token.
invalid_tokenUnknown API token. Check it was copied in full, and that it is for this environment.
token_revokedThe token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.
token_expiredThis token has expired. Issue a new one from Configuration › Platform API.
{
"error": {
"code": "missing_token",
"message": "Missing bearer token."
}
}403The token or workspace can't do this.
missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
missing_abilityThis token doesn't have the campaigns:write scope.
platform_api_not_enabledThe full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.
wrong_environmentThis is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1
workspace_pausedThis workspace is scheduled for deletion; its API access is paused.
{
"error": {
"code": "missing_ability",
"message": "This token doesn't have the campaigns:write scope."
}
}404Nothing with that ID in this workspace.
not_found
not_foundNo such resource in this workspace.
{
"error": {
"code": "not_found",
"message": "No such resource in this workspace."
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limitedtoo_many_failed_attempts
Retry-After.rate_limitedToo many requests. Slow down and try again shortly.
too_many_failed_attemptsToo many failed attempts. Try again shortly.
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and try again shortly."
}
}curl 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
-H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
-H 'Accept: application/json'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', {
method: 'GET',
headers: {
Authorization: 'Bearer bubbl_YOUR_TOKEN',
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={
'Authorization': 'Bearer bubbl_YOUR_TOKEN',
'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' => [
'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
'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
Headers
A unique value (e.g. a UUID) for this change. A retry with the same key within 24 hours gets the first response back, with Idempotent-Replayed: true, instead of making the change again.
Request body
application/jsonUpdateCampaignNotificationRequestrequiredmessage, surveyFetched when you save: it must be a public address answering 200 or 201, or the request is a 422. Needs cta_label.
A survey's 1–5 questions: required for a survey, left out for a message.
OPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER, SLIDERA geo campaign's notification: on_enter or on_exit. A push campaign's: fixed_time, with trigger_config.send_at.
on_enter, on_exit, fixed_timeA push campaign's send time, on the campaign's day: "HH:MM", "YYYY-MM-DD HH:MM" (both in the campaign's timezone), or an ISO 8601 time with an offset. It comes back as "YYYY-MM-DD HH:MM" in the campaign's timezone. Leave it out for a geo campaign.
Responses
200
{
"data": {
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"name": "Morning flat white offer",
"type": "message",
"headline": "Two coffees for one",
"body": "Show this at the counter for a free flat white before 11am.",
"media": null,
"cta_label": "Get the voucher",
"cta_url": "https://example.com/offers/flat-white",
"questions": [],
"trigger_type": "on_enter",
"trigger_config": null,
"starts_at": null,
"ends_at": null,
"created_at": "2026-09-20T09:12:00+00:00",
"updated_at": "2026-09-20T09:12:00+00:00"
}
}Response fields
401The token is missing, unknown, revoked or expired.
missing_tokeninvalid_tokentoken_revokedtoken_expired
missing_tokenMissing bearer token.
invalid_tokenUnknown API token. Check it was copied in full, and that it is for this environment.
token_revokedThe token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.
token_expiredThis token has expired. Issue a new one from Configuration › Platform API.
{
"error": {
"code": "missing_token",
"message": "Missing bearer token."
}
}403The token or workspace can't do this.
missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
missing_abilityThis token doesn't have the campaigns:write scope.
platform_api_not_enabledThe full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.
wrong_environmentThis is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1
workspace_pausedThis workspace is scheduled for deletion; its API access is paused.
{
"error": {
"code": "missing_ability",
"message": "This token doesn't have the campaigns:write scope."
}
}404Nothing with that ID in this workspace.
not_found
not_foundNo such resource in this workspace.
{
"error": {
"code": "not_found",
"message": "No such resource in this workspace."
}
}409A request with the same Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.
request_in_progress
Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.request_in_progressThis request is still being handled. Try again shortly.
{
"error": {
"code": "request_in_progress",
"message": "This request is still being handled. Try again shortly."
}
}422Something in the request isn't valid. fields says what, with the dashboard's wording.
validation_failed
fields says what, with the dashboard's wording.validation_failedThe given data was invalid.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid.",
"fields": {
"name": [
"The name field is required."
]
}
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limitedtoo_many_failed_attempts
Retry-After.rate_limitedToo many requests. Slow down and try again shortly.
too_many_failed_attemptsToo many failed attempts. Try again shortly.
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and try again shortly."
}
}curl -X PATCH 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
-H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data '{
"name": "Morning flat white offer",
"type": "message",
"headline": "Two coffees for one",
"body": "Show this at the counter for a free flat white before 11am.",
"cta_label": "Get the voucher",
"cta_url": "https://example.com/offers/flat-white",
"trigger_type": "on_enter"
}'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', {
method: 'PATCH',
headers: {
Authorization: 'Bearer bubbl_YOUR_TOKEN',
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Morning flat white offer',
type: 'message',
headline: 'Two coffees for one',
body: 'Show this at the counter for a free flat white before 11am.',
cta_label: 'Get the voucher',
cta_url: 'https://example.com/offers/flat-white',
trigger_type: 'on_enter',
}),
});
const data = await response.json();import requests
response = requests.request(
'PATCH',
'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}',
headers={
'Authorization': 'Bearer bubbl_YOUR_TOKEN',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
json={
'name': 'Morning flat white offer',
'type': 'message',
'headline': 'Two coffees for one',
'body': 'Show this at the counter for a free flat white before 11am.',
'cta_label': 'Get the voucher',
'cta_url': 'https://example.com/offers/flat-white',
'trigger_type': 'on_enter',
},
)
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' => [
'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'json' => [
'name' => 'Morning flat white offer',
'type' => 'message',
'headline' => 'Two coffees for one',
'body' => 'Show this at the counter for a free flat white before 11am.',
'cta_label' => 'Get the voucher',
'cta_url' => 'https://example.com/offers/flat-white',
'trigger_type' => 'on_enter',
],
]);
$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
Headers
A unique value (e.g. a UUID) for this change. A retry with the same key within 24 hours gets the first response back, with Idempotent-Replayed: true, instead of making the change again.
Responses
200
{
"data": {
"id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
"deleted": true
}
}Response fields
401The token is missing, unknown, revoked or expired.
missing_tokeninvalid_tokentoken_revokedtoken_expired
missing_tokenMissing bearer token.
invalid_tokenUnknown API token. Check it was copied in full, and that it is for this environment.
token_revokedThe token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.
token_expiredThis token has expired. Issue a new one from Configuration › Platform API.
{
"error": {
"code": "missing_token",
"message": "Missing bearer token."
}
}403The token or workspace can't do this.
missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
missing_abilityThis token doesn't have the campaigns:write scope.
platform_api_not_enabledThe full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.
wrong_environmentThis is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1
workspace_pausedThis workspace is scheduled for deletion; its API access is paused.
{
"error": {
"code": "missing_ability",
"message": "This token doesn't have the campaigns:write scope."
}
}404Nothing with that ID in this workspace.
not_found
not_foundNo such resource in this workspace.
{
"error": {
"code": "not_found",
"message": "No such resource in this workspace."
}
}409A request with the same Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.
request_in_progress
Idempotency-Key is still running (retry after Retry-After seconds), or the change conflicts with something that depends on it.request_in_progressThis request is still being handled. Try again shortly.
{
"error": {
"code": "request_in_progress",
"message": "This request is still being handled. Try again shortly."
}
}429Too many requests. Wait for the number of seconds in Retry-After.
rate_limitedtoo_many_failed_attempts
Retry-After.rate_limitedToo many requests. Slow down and try again shortly.
too_many_failed_attemptsToo many failed attempts. Try again shortly.
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Slow down and try again shortly."
}
}curl -X DELETE 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
-H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
-H 'Accept: application/json'const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}', {
method: 'DELETE',
headers: {
Authorization: 'Bearer bubbl_YOUR_TOKEN',
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={
'Authorization': 'Bearer bubbl_YOUR_TOKEN',
'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' => [
'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
'Accept' => 'application/json',
],
]);
$data = json_decode((string) $response->getBody(), true);