Notifications

5 endpoints

A 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#

GET/campaigns/{campaign}/notifications
Needs notifications:read

Path parameters

campaign
string (uuid)
required

The campaign ID

Responses

200
200 response
{
  "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
data
CampaignNotificationResource[]
required
data[].id
string
required
data[].name
string
required
data[].type
string
required
data[].headline
string
required
data[].body
string
required
data[].media
object | null
data[].media.id
string
required
data[].media.url
string | null
required
data[].media.type
string
required
data[].cta_label
string | null
required
data[].cta_url
string | null
required
data[].questions
NotificationQuestionResource[]
data[].questions[].id
string
required
data[].questions[].question_text
string
required
data[].questions[].type
string
required
data[].questions[].required
boolean
required
data[].questions[].choices
string[]
data[].trigger_type
string | null
required
data[].trigger_config
string | null
required
data[].starts_at
string | null
required
data[].ends_at
string | null
required
data[].created_at
string | null
required
data[].updated_at
string | null
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
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();
Request
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()
Request
<?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#

POST/campaigns/{campaign}/notifications
Needs notifications:write

Creates 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

campaign
string (uuid)
required

The campaign ID

Headers

Idempotency-Key
string

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/jsonStoreCampaignNotificationRequestrequired
name
string
required
type
string
required
One of: message, survey
headline
string
required
body
string
required
media_asset_id
string (uuid) | null
cta_label
string | null
cta_url
string (uri) | null

Fetched when you save: it must be a public address answering 200 or 201, or the request is a 422. Needs cta_label.

questions
object[]

A survey's 1–5 questions: required for a survey, left out for a message.

questions[].id
string (uuid) | null
questions[].question_text
string
required
questions[].type
string
required
One of: OPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER, SLIDER
questions[].required
boolean
questions[].choices
object[]
questions[].choices[].id
string (uuid) | null
questions[].choices[].text
string
required
trigger_type
string
required

A geo campaign's notification: on_enter or on_exit. A push campaign's: fixed_time, with trigger_config.send_at.

One of: on_enter, on_exit, fixed_time
trigger_config
object
trigger_config.send_at
string | null

A 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.

starts_at
string | null
ends_at
string | null

Responses

201
201 response
{
  "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
data
CampaignNotificationResource
required
data.id
string
required
data.name
string
required
data.type
string
required
data.headline
string
required
data.body
string
required
data.media
object | null
data.media.id
string
required
data.media.url
string | null
required
data.media.type
string
required
data.cta_label
string | null
required
data.cta_url
string | null
required
data.questions
NotificationQuestionResource[]
data.questions[].id
string
required
data.questions[].question_text
string
required
data.questions[].type
string
required
data.questions[].required
boolean
required
data.questions[].choices
string[]
data.trigger_type
string | null
required
data.trigger_config
string | null
required
data.starts_at
string | null
required
data.ends_at
string | null
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_pausedlimit_exceeded
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

  • limit_exceeded

    You've reached the limit for max_campaigns.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
409

A 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
  • request_in_progress

    This request is still being handled. Try again shortly.

409 response
{
  "error": {
    "code": "request_in_progress",
    "message": "This request is still being handled. Try again shortly."
  }
}
422

Something in the request isn't valid. fields says what, with the dashboard's wording.

validation_failed
  • validation_failed

    The given data was invalid.

422 response
{
  "error": {
    "code": "validation_failed",
    "message": "The given data was invalid.",
    "fields": {
      "name": [
        "The name field is required."
      ]
    }
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
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"
}'
Request
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();
Request
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()
Request
<?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#

GET/campaigns/{campaign}/notifications/{notification}
Needs notifications:read

Path parameters

campaign
string (uuid)
required

The campaign ID

notification
string (uuid)
required

The notification ID

Responses

200
200 response
{
  "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
data
CampaignNotificationResource
required
data.id
string
required
data.name
string
required
data.type
string
required
data.headline
string
required
data.body
string
required
data.media
object | null
data.media.id
string
required
data.media.url
string | null
required
data.media.type
string
required
data.cta_label
string | null
required
data.cta_url
string | null
required
data.questions
NotificationQuestionResource[]
data.questions[].id
string
required
data.questions[].question_text
string
required
data.questions[].type
string
required
data.questions[].required
boolean
required
data.questions[].choices
string[]
data.trigger_type
string | null
required
data.trigger_config
string | null
required
data.starts_at
string | null
required
data.ends_at
string | null
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
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();
Request
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()
Request
<?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#

PATCH/campaigns/{campaign}/notifications/{notification}
Needs notifications:write

The whole notification (content and trigger) is resubmitted, same rules as adding one.

Path parameters

campaign
string (uuid)
required

The campaign ID

notification
string (uuid)
required

The notification ID

Headers

Idempotency-Key
string

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/jsonUpdateCampaignNotificationRequestrequired
name
string
required
type
string
required
One of: message, survey
headline
string
required
body
string
required
media_asset_id
string (uuid) | null
cta_label
string | null
cta_url
string (uri) | null

Fetched when you save: it must be a public address answering 200 or 201, or the request is a 422. Needs cta_label.

questions
object[]

A survey's 1–5 questions: required for a survey, left out for a message.

questions[].id
string (uuid) | null
questions[].question_text
string
required
questions[].type
string
required
One of: OPEN_ENDED, SINGLE_CHOICE, MULTIPLE_CHOICE, RATING, BOOLEAN, NUMBER, SLIDER
questions[].required
boolean
questions[].choices
object[]
questions[].choices[].id
string (uuid) | null
questions[].choices[].text
string
required
trigger_type
string
required

A geo campaign's notification: on_enter or on_exit. A push campaign's: fixed_time, with trigger_config.send_at.

One of: on_enter, on_exit, fixed_time
trigger_config
object
trigger_config.send_at
string | null

A 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.

starts_at
string | null
ends_at
string | null

Responses

200
200 response
{
  "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
data
CampaignNotificationResource
required
data.id
string
required
data.name
string
required
data.type
string
required
data.headline
string
required
data.body
string
required
data.media
object | null
data.media.id
string
required
data.media.url
string | null
required
data.media.type
string
required
data.cta_label
string | null
required
data.cta_url
string | null
required
data.questions
NotificationQuestionResource[]
data.questions[].id
string
required
data.questions[].question_text
string
required
data.questions[].type
string
required
data.questions[].required
boolean
required
data.questions[].choices
string[]
data.trigger_type
string | null
required
data.trigger_config
string | null
required
data.starts_at
string | null
required
data.ends_at
string | null
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
409

A 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
  • request_in_progress

    This request is still being handled. Try again shortly.

409 response
{
  "error": {
    "code": "request_in_progress",
    "message": "This request is still being handled. Try again shortly."
  }
}
422

Something in the request isn't valid. fields says what, with the dashboard's wording.

validation_failed
  • validation_failed

    The given data was invalid.

422 response
{
  "error": {
    "code": "validation_failed",
    "message": "The given data was invalid.",
    "fields": {
      "name": [
        "The name field is required."
      ]
    }
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
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"
}'
Request
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();
Request
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()
Request
<?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#

DELETE/campaigns/{campaign}/notifications/{notification}
Needs notifications:write

Detaches it; its content is also deleted, but only once it's no longer attached to any other campaign.

Path parameters

campaign
string (uuid)
required

The campaign ID

notification
string (uuid)
required

The notification ID

Headers

Idempotency-Key
string

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
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001",
    "deleted": true
  }
}
Response fields
data
object
required
data.id
string
required
data.removed
boolean
required
401

The token is missing, unknown, revoked or expired.

missing_tokeninvalid_tokentoken_revokedtoken_expired
  • missing_token

    Missing bearer token.

  • invalid_token

    Unknown API token. Check it was copied in full, and that it is for this environment.

  • token_revoked

    The token "Zapier" was revoked on 2026-10-06 at 18:27 UTC. Issue a new one from Configuration › Platform API.

  • token_expired

    This token has expired. Issue a new one from Configuration › Platform API.

401 response
{
  "error": {
    "code": "missing_token",
    "message": "Missing bearer token."
  }
}
403

The token or workspace can't do this.

missing_abilityplatform_api_not_enabledwrong_environmentworkspace_paused
  • missing_ability

    This token doesn't have the campaigns:write scope.

  • platform_api_not_enabled

    The full Platform API isn't enabled for this workspace yet. Contact customer services to turn it on.

  • wrong_environment

    This is a Sandbox token: use the Sandbox API address. https://api.sandbox.bubbl.tech/platform/v1

  • workspace_paused

    This workspace is scheduled for deletion; its API access is paused.

403 response
{
  "error": {
    "code": "missing_ability",
    "message": "This token doesn't have the campaigns:write scope."
  }
}
404

Nothing with that ID in this workspace.

not_found
  • not_found

    No such resource in this workspace.

404 response
{
  "error": {
    "code": "not_found",
    "message": "No such resource in this workspace."
  }
}
409

A 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
  • request_in_progress

    This request is still being handled. Try again shortly.

409 response
{
  "error": {
    "code": "request_in_progress",
    "message": "This request is still being handled. Try again shortly."
  }
}
429

Too many requests. Wait for the number of seconds in Retry-After.

rate_limitedtoo_many_failed_attempts
  • rate_limited

    Too many requests. Slow down and try again shortly.

  • too_many_failed_attempts

    Too many failed attempts. Try again shortly.

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and try again shortly."
  }
}
Request
curl -X DELETE 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/notifications/{notification}' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
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();
Request
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()
Request
<?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);