ChangelogOpen dashboard

Campaigns

7 endpoints

When and to whom your notifications go out. A geo campaign targets locations (directly or via location groups) and fires when a device enters or leaves one; a push campaign sends at fixed times to an audience of segments (or everyone). Notifications are added afterwards — see Notifications, nested under a campaign.

List campaigns#

GET/campaigns
Needs campaigns:readNo token needed

Newest first, with their targeting (location, group and segment ids).

Query parameters

cursor
string

An opaque value from a previous page's meta.next_cursor. Omit for the first page.

limit
integer

How many to return, up to 100.

Default: 20
updated_since
string (date-time)

Only campaigns updated at or after this ISO 8601 timestamp.

Responses

200
200 response
{
  "data": [
    {
      "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
      "name": "Morning coffee offer",
      "type": "geo",
      "status": "live",
      "starts_at": "2026-10-01T07:00:00Z",
      "ends_at": "2026-10-31T11:00:00Z",
      "timezone": "Europe/London",
      "paused": false,
      "frequency_cap_count": null,
      "frequency_cap_period": null,
      "targeting": {
        "location_ids": [
          "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001"
        ],
        "location_group_ids": [],
        "segmentation_ids": [],
        "segment_match": null
      },
      "created_at": "2026-09-20T09:12:00Z",
      "updated_at": "2026-09-27T14:03:00Z"
    }
  ],
  "meta": {
    "next_cursor": null
  }
}
Response fields
data
CampaignResource[]
required
data[].id
string
required
data[].name
string
required
data[].type
string
required
data[].status
string
required
data[].starts_at
string
required
data[].ends_at
string | null
required
data[].timezone
string
required
data[].paused
boolean
required
data[].frequency_cap_count
integer | null
required
data[].frequency_cap_period
string | null
required
data[].targeting
object
required
data[].targeting.location_ids
any[]
data[].targeting.location_group_ids
any[]
data[].targeting.segmentation_ids
any[]
data[].targeting.segment_match
string
required
data[].created_at
string | null
required
data[].updated_at
string | null
required
meta
object
required
meta.next_cursor
string | null
required
401

The token is missing, expired or revoked.

unauthenticated
401 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
403

The token can't do this.

missing_abilityplatform_api_not_enabledwrong_environment
403 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
422
422 response
{
  "error": [
    null
  ]
}
429

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

rate_limited
429 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
Request
curl 'https://api.bubbl.tech/platform/v1/campaigns?limit=20' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns?limit=20', {
  method: 'GET',
  headers: {
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'GET',
    'https://api.bubbl.tech/platform/v1/campaigns?limit=20',
    headers={
        '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?limit=20', [
    'headers' => [
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Create a campaign#

POST/campaigns
Needs campaigns:writeNo token needed

A geo campaign needs at least one of location_ids/location_group_ids and can't take segmentation_ids; a push campaign is the other way round. starts_at/ends_at are Y-m-d (a push campaign runs for one day) or Y-m-d H:i in the campaign's own timezone. No notifications yet — add them afterwards (see Notifications).

Request body

application/jsonStoreCampaignRequestrequired
name
string
required
type
string
required
One of: geo, push
timezone
string
required
One of: Africa/Abidjan, Africa/Accra, Africa/Addis_Ababa, Africa/Algiers, Africa/Asmara, Africa/Bamako, Africa/Bangui, Africa/Banjul, Africa/Bissau, Africa/Blantyre, Africa/Brazzaville, Africa/Bujumbura, Africa/Cairo, Africa/Casablanca, Africa/Ceuta, Africa/Conakry, Africa/Dakar, Africa/Dar_es_Salaam, Africa/Djibouti, Africa/Douala, Africa/El_Aaiun, Africa/Freetown, Africa/Gaborone, Africa/Harare, Africa/Johannesburg, Africa/Juba, Africa/Kampala, Africa/Khartoum, Africa/Kigali, Africa/Kinshasa, Africa/Lagos, Africa/Libreville, Africa/Lome, Africa/Luanda, Africa/Lubumbashi, Africa/Lusaka, Africa/Malabo, Africa/Maputo, Africa/Maseru, Africa/Mbabane, Africa/Mogadishu, Africa/Monrovia, Africa/Nairobi, Africa/Ndjamena, Africa/Niamey, Africa/Nouakchott, Africa/Ouagadougou, Africa/Porto-Novo, Africa/Sao_Tome, Africa/Tripoli, Africa/Tunis, Africa/Windhoek, America/Adak, America/Anchorage, America/Anguilla, America/Antigua, America/Araguaina, America/Argentina/Buenos_Aires, America/Argentina/Catamarca, America/Argentina/Cordoba, America/Argentina/Jujuy, America/Argentina/La_Rioja, America/Argentina/Mendoza, America/Argentina/Rio_Gallegos, America/Argentina/Salta, America/Argentina/San_Juan, America/Argentina/San_Luis, America/Argentina/Tucuman, America/Argentina/Ushuaia, America/Aruba, America/Asuncion, America/Atikokan, America/Bahia, America/Bahia_Banderas, America/Barbados, America/Belem, America/Belize, America/Blanc-Sablon, America/Boa_Vista, America/Bogota, America/Boise, America/Cambridge_Bay, America/Campo_Grande, America/Cancun, America/Caracas, America/Cayenne, America/Cayman, America/Chicago, America/Chihuahua, America/Ciudad_Juarez, America/Costa_Rica, America/Coyhaique, America/Creston, America/Cuiaba, America/Curacao, America/Danmarkshavn, America/Dawson, America/Dawson_Creek, America/Denver, America/Detroit, America/Dominica, America/Edmonton, America/Eirunepe, America/El_Salvador, America/Fort_Nelson, America/Fortaleza, America/Glace_Bay, America/Goose_Bay, America/Grand_Turk, America/Grenada, America/Guadeloupe, America/Guatemala, America/Guayaquil, America/Guyana, America/Halifax, America/Havana, America/Hermosillo, America/Indiana/Indianapolis, America/Indiana/Knox, America/Indiana/Marengo, America/Indiana/Petersburg, America/Indiana/Tell_City, America/Indiana/Vevay, America/Indiana/Vincennes, America/Indiana/Winamac, America/Inuvik, America/Iqaluit, America/Jamaica, America/Juneau, America/Kentucky/Louisville, America/Kentucky/Monticello, America/Kralendijk, America/La_Paz, America/Lima, America/Los_Angeles, America/Lower_Princes, America/Maceio, America/Managua, America/Manaus, America/Marigot, America/Martinique, America/Matamoros, America/Mazatlan, America/Menominee, America/Merida, America/Metlakatla, America/Mexico_City, America/Miquelon, America/Moncton, America/Monterrey, America/Montevideo, America/Montserrat, America/Nassau, America/New_York, America/Nome, America/Noronha, America/North_Dakota/Beulah, America/North_Dakota/Center, America/North_Dakota/New_Salem, America/Nuuk, America/Ojinaga, America/Panama, America/Paramaribo, America/Phoenix, America/Port-au-Prince, America/Port_of_Spain, America/Porto_Velho, America/Puerto_Rico, America/Punta_Arenas, America/Rankin_Inlet, America/Recife, America/Regina, America/Resolute, America/Rio_Branco, America/Santarem, America/Santiago, America/Santo_Domingo, America/Sao_Paulo, America/Scoresbysund, America/Sitka, America/St_Barthelemy, America/St_Johns, America/St_Kitts, America/St_Lucia, America/St_Thomas, America/St_Vincent, America/Swift_Current, America/Tegucigalpa, America/Thule, America/Tijuana, America/Toronto, America/Tortola, America/Vancouver, America/Whitehorse, America/Winnipeg, America/Yakutat, Antarctica/Casey, Antarctica/Davis, Antarctica/DumontDUrville, Antarctica/Macquarie, Antarctica/Mawson, Antarctica/McMurdo, Antarctica/Palmer, Antarctica/Rothera, Antarctica/Syowa, Antarctica/Troll, Antarctica/Vostok, Arctic/Longyearbyen, Asia/Aden, Asia/Almaty, Asia/Amman, Asia/Anadyr, Asia/Aqtau, Asia/Aqtobe, Asia/Ashgabat, Asia/Atyrau, Asia/Baghdad, Asia/Bahrain, Asia/Baku, Asia/Bangkok, Asia/Barnaul, Asia/Beirut, Asia/Bishkek, Asia/Brunei, Asia/Chita, Asia/Colombo, Asia/Damascus, Asia/Dhaka, Asia/Dili, Asia/Dubai, Asia/Dushanbe, Asia/Famagusta, Asia/Gaza, Asia/Hebron, Asia/Ho_Chi_Minh, Asia/Hong_Kong, Asia/Hovd, Asia/Irkutsk, Asia/Jakarta, Asia/Jayapura, Asia/Jerusalem, Asia/Kabul, Asia/Kamchatka, Asia/Karachi, Asia/Kathmandu, Asia/Khandyga, Asia/Kolkata, Asia/Krasnoyarsk, Asia/Kuala_Lumpur, Asia/Kuching, Asia/Kuwait, Asia/Macau, Asia/Magadan, Asia/Makassar, Asia/Manila, Asia/Muscat, Asia/Nicosia, Asia/Novokuznetsk, Asia/Novosibirsk, Asia/Omsk, Asia/Oral, Asia/Phnom_Penh, Asia/Pontianak, Asia/Pyongyang, Asia/Qatar, Asia/Qostanay, Asia/Qyzylorda, Asia/Riyadh, Asia/Sakhalin, Asia/Samarkand, Asia/Seoul, Asia/Shanghai, Asia/Singapore, Asia/Srednekolymsk, Asia/Taipei, Asia/Tashkent, Asia/Tbilisi, Asia/Tehran, Asia/Thimphu, Asia/Tokyo, Asia/Tomsk, Asia/Ulaanbaatar, Asia/Urumqi, Asia/Ust-Nera, Asia/Vientiane, Asia/Vladivostok, Asia/Yakutsk, Asia/Yangon, Asia/Yekaterinburg, Asia/Yerevan, Atlantic/Azores, Atlantic/Bermuda, Atlantic/Canary, Atlantic/Cape_Verde, Atlantic/Faroe, Atlantic/Madeira, Atlantic/Reykjavik, Atlantic/South_Georgia, Atlantic/St_Helena, Atlantic/Stanley, Australia/Adelaide, Australia/Brisbane, Australia/Broken_Hill, Australia/Darwin, Australia/Eucla, Australia/Hobart, Australia/Lindeman, Australia/Lord_Howe, Australia/Melbourne, Australia/Perth, Australia/Sydney, Europe/Amsterdam, Europe/Andorra, Europe/Astrakhan, Europe/Athens, Europe/Belgrade, Europe/Berlin, Europe/Bratislava, Europe/Brussels, Europe/Bucharest, Europe/Budapest, Europe/Busingen, Europe/Chisinau, Europe/Copenhagen, Europe/Dublin, Europe/Gibraltar, Europe/Guernsey, Europe/Helsinki, Europe/Isle_of_Man, Europe/Istanbul, Europe/Jersey, Europe/Kaliningrad, Europe/Kirov, Europe/Kyiv, Europe/Lisbon, Europe/Ljubljana, Europe/London, Europe/Luxembourg, Europe/Madrid, Europe/Malta, Europe/Mariehamn, Europe/Minsk, Europe/Monaco, Europe/Moscow, Europe/Oslo, Europe/Paris, Europe/Podgorica, Europe/Prague, Europe/Riga, Europe/Rome, Europe/Samara, Europe/San_Marino, Europe/Sarajevo, Europe/Saratov, Europe/Simferopol, Europe/Skopje, Europe/Sofia, Europe/Stockholm, Europe/Tallinn, Europe/Tirane, Europe/Ulyanovsk, Europe/Vaduz, Europe/Vatican, Europe/Vienna, Europe/Vilnius, Europe/Volgograd, Europe/Warsaw, Europe/Zagreb, Europe/Zurich, Indian/Antananarivo, Indian/Chagos, Indian/Christmas, Indian/Cocos, Indian/Comoro, Indian/Kerguelen, Indian/Mahe, Indian/Maldives, Indian/Mauritius, Indian/Mayotte, Indian/Reunion, Pacific/Apia, Pacific/Auckland, Pacific/Bougainville, Pacific/Chatham, Pacific/Chuuk, Pacific/Easter, Pacific/Efate, Pacific/Fakaofo, Pacific/Fiji, Pacific/Funafuti, Pacific/Galapagos, Pacific/Gambier, Pacific/Guadalcanal, Pacific/Guam, Pacific/Honolulu, Pacific/Kanton, Pacific/Kiritimati, Pacific/Kosrae, Pacific/Kwajalein, Pacific/Majuro, Pacific/Marquesas, Pacific/Midway, Pacific/Nauru, Pacific/Niue, Pacific/Norfolk, Pacific/Noumea, Pacific/Pago_Pago, Pacific/Palau, Pacific/Pitcairn, Pacific/Pohnpei, Pacific/Port_Moresby, Pacific/Rarotonga, Pacific/Saipan, Pacific/Tahiti, Pacific/Tarawa, Pacific/Tongatapu, Pacific/Wake, Pacific/Wallis, UTC
frequency_cap_count
integer | null
frequency_cap_period
string | null
One of: day, week, month, null
location_ids
(string (uuid))[]
location_group_ids
(string (uuid))[]
segmentation_ids
(string (uuid))[]
segment_match
string | null
One of: any, all, null
starts_at
string
required

Only the campaign's own bounds — the trait's attachments.*.* rules are the dashboard form's, for a field this endpoint doesn't accept (notifications are added afterwards, through the nested resource).

ends_at
string | null

Responses

201
201 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
    "name": "Morning coffee offer",
    "type": "geo",
    "status": "live",
    "starts_at": "2026-10-01T07:00:00Z",
    "ends_at": "2026-10-31T11:00:00Z",
    "timezone": "Europe/London",
    "paused": false,
    "frequency_cap_count": null,
    "frequency_cap_period": null,
    "targeting": {
      "location_ids": [
        "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001"
      ],
      "location_group_ids": [],
      "segmentation_ids": [],
      "segment_match": null
    },
    "created_at": "2026-09-20T09:12:00Z",
    "updated_at": "2026-09-27T14:03:00Z"
  }
}
Response fields
data
object
required
data.id
string
required
data.name
string
required
data.type
string
required
data.status
string
required
data.starts_at
string
required
data.ends_at
string | null
required
data.timezone
string
required
data.paused
boolean
required
data.frequency_cap_count
integer | null
required
data.frequency_cap_period
string | null
required
data.targeting
object
required
data.targeting.location_ids
any[]
data.targeting.location_group_ids
any[]
data.targeting.segmentation_ids
any[]
data.targeting.segment_match
string
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, expired or revoked.

unauthenticated
401 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
403

The token can't do this.

missing_abilityplatform_api_not_enabledwrong_environment
403 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
422

Some fields aren't valid. fields says which, with the dashboard's wording.

validation_failed
422 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
429

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

rate_limited
429 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
Request
curl -X POST 'https://api.bubbl.tech/platform/v1/campaigns' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  --data '{
  "name": "Morning coffee offer",
  "type": "geo",
  "timezone": "Europe/London",
  "schedule": {
    "starts_at": "2026-10-01T07:00:00Z",
    "ends_at": "2026-10-31T11:00:00Z"
  },
  "targeting": {
    "location_ids": [
      "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001"
    ],
    "location_group_ids": [],
    "segmentation_ids": []
  }
}'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Morning coffee offer',
    type: 'geo',
    timezone: 'Europe/London',
    schedule: {
      starts_at: '2026-10-01T07:00:00Z',
      ends_at: '2026-10-31T11:00:00Z',
    },
    targeting: {
      location_ids: [
        '0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001',
      ],
      location_group_ids: [],
      segmentation_ids: [],
    },
  }),
});

const data = await response.json();
Request
import requests

response = requests.request(
    'POST',
    'https://api.bubbl.tech/platform/v1/campaigns',
    headers={
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    json={
        'name': 'Morning coffee offer',
        'type': 'geo',
        'timezone': 'Europe/London',
        'schedule': {
            'starts_at': '2026-10-01T07:00:00Z',
            'ends_at': '2026-10-31T11:00:00Z',
        },
        'targeting': {
            'location_ids': [
                '0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001',
            ],
            'location_group_ids': [],
            'segmentation_ids': [],
        },
    },
)
response.raise_for_status()
data = response.json()
Request
<?php

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.bubbl.tech/platform/v1/campaigns', [
    'headers' => [
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
    'json' => [
        'name' => 'Morning coffee offer',
        'type' => 'geo',
        'timezone' => 'Europe/London',
        'schedule' => [
            'starts_at' => '2026-10-01T07:00:00Z',
            'ends_at' => '2026-10-31T11:00:00Z',
        ],
        'targeting' => [
            'location_ids' => [
                '0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001',
            ],
            'location_group_ids' => [],
            'segmentation_ids' => [],
        ],
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Get a campaign#

GET/campaigns/{campaign}
Needs campaigns:readNo token needed

Path parameters

campaign
string (uuid)
required

The campaign ID

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
    "name": "Morning coffee offer",
    "type": "geo",
    "status": "live",
    "starts_at": "2026-10-01T07:00:00Z",
    "ends_at": "2026-10-31T11:00:00Z",
    "timezone": "Europe/London",
    "paused": false,
    "frequency_cap_count": null,
    "frequency_cap_period": null,
    "targeting": {
      "location_ids": [
        "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001"
      ],
      "location_group_ids": [],
      "segmentation_ids": [],
      "segment_match": null
    },
    "created_at": "2026-09-20T09:12:00Z",
    "updated_at": "2026-09-27T14:03:00Z"
  }
}
Response fields
data
object
required
data.id
string
required
data.name
string
required
data.type
string
required
data.status
string
required
data.starts_at
string
required
data.ends_at
string | null
required
data.timezone
string
required
data.paused
boolean
required
data.frequency_cap_count
integer | null
required
data.frequency_cap_period
string | null
required
data.targeting
object
required
data.targeting.location_ids
any[]
data.targeting.location_group_ids
any[]
data.targeting.segmentation_ids
any[]
data.targeting.segment_match
string
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, expired or revoked.

unauthenticated
401 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
403

The token can't do this.

missing_abilityplatform_api_not_enabledwrong_environment
403 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
404

Nothing with that ID in this workspace.

not_found
404 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
429

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

rate_limited
429 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
Request
curl 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}', {
  method: 'GET',
  headers: {
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'GET',
    'https://api.bubbl.tech/platform/v1/campaigns/{campaign}',
    headers={
        '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}', [
    'headers' => [
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Update a campaign#

PATCH/campaigns/{campaign}
Needs campaigns:writeNo token needed

The whole campaign is resubmitted (not a partial patch) — the same rules as creating one apply again.

Path parameters

campaign
string (uuid)
required

The campaign ID

Request body

application/jsonUpdateCampaignRequestrequired
name
string
required
type
string
required
One of: geo, push
timezone
string
required
One of: Africa/Abidjan, Africa/Accra, Africa/Addis_Ababa, Africa/Algiers, Africa/Asmara, Africa/Bamako, Africa/Bangui, Africa/Banjul, Africa/Bissau, Africa/Blantyre, Africa/Brazzaville, Africa/Bujumbura, Africa/Cairo, Africa/Casablanca, Africa/Ceuta, Africa/Conakry, Africa/Dakar, Africa/Dar_es_Salaam, Africa/Djibouti, Africa/Douala, Africa/El_Aaiun, Africa/Freetown, Africa/Gaborone, Africa/Harare, Africa/Johannesburg, Africa/Juba, Africa/Kampala, Africa/Khartoum, Africa/Kigali, Africa/Kinshasa, Africa/Lagos, Africa/Libreville, Africa/Lome, Africa/Luanda, Africa/Lubumbashi, Africa/Lusaka, Africa/Malabo, Africa/Maputo, Africa/Maseru, Africa/Mbabane, Africa/Mogadishu, Africa/Monrovia, Africa/Nairobi, Africa/Ndjamena, Africa/Niamey, Africa/Nouakchott, Africa/Ouagadougou, Africa/Porto-Novo, Africa/Sao_Tome, Africa/Tripoli, Africa/Tunis, Africa/Windhoek, America/Adak, America/Anchorage, America/Anguilla, America/Antigua, America/Araguaina, America/Argentina/Buenos_Aires, America/Argentina/Catamarca, America/Argentina/Cordoba, America/Argentina/Jujuy, America/Argentina/La_Rioja, America/Argentina/Mendoza, America/Argentina/Rio_Gallegos, America/Argentina/Salta, America/Argentina/San_Juan, America/Argentina/San_Luis, America/Argentina/Tucuman, America/Argentina/Ushuaia, America/Aruba, America/Asuncion, America/Atikokan, America/Bahia, America/Bahia_Banderas, America/Barbados, America/Belem, America/Belize, America/Blanc-Sablon, America/Boa_Vista, America/Bogota, America/Boise, America/Cambridge_Bay, America/Campo_Grande, America/Cancun, America/Caracas, America/Cayenne, America/Cayman, America/Chicago, America/Chihuahua, America/Ciudad_Juarez, America/Costa_Rica, America/Coyhaique, America/Creston, America/Cuiaba, America/Curacao, America/Danmarkshavn, America/Dawson, America/Dawson_Creek, America/Denver, America/Detroit, America/Dominica, America/Edmonton, America/Eirunepe, America/El_Salvador, America/Fort_Nelson, America/Fortaleza, America/Glace_Bay, America/Goose_Bay, America/Grand_Turk, America/Grenada, America/Guadeloupe, America/Guatemala, America/Guayaquil, America/Guyana, America/Halifax, America/Havana, America/Hermosillo, America/Indiana/Indianapolis, America/Indiana/Knox, America/Indiana/Marengo, America/Indiana/Petersburg, America/Indiana/Tell_City, America/Indiana/Vevay, America/Indiana/Vincennes, America/Indiana/Winamac, America/Inuvik, America/Iqaluit, America/Jamaica, America/Juneau, America/Kentucky/Louisville, America/Kentucky/Monticello, America/Kralendijk, America/La_Paz, America/Lima, America/Los_Angeles, America/Lower_Princes, America/Maceio, America/Managua, America/Manaus, America/Marigot, America/Martinique, America/Matamoros, America/Mazatlan, America/Menominee, America/Merida, America/Metlakatla, America/Mexico_City, America/Miquelon, America/Moncton, America/Monterrey, America/Montevideo, America/Montserrat, America/Nassau, America/New_York, America/Nome, America/Noronha, America/North_Dakota/Beulah, America/North_Dakota/Center, America/North_Dakota/New_Salem, America/Nuuk, America/Ojinaga, America/Panama, America/Paramaribo, America/Phoenix, America/Port-au-Prince, America/Port_of_Spain, America/Porto_Velho, America/Puerto_Rico, America/Punta_Arenas, America/Rankin_Inlet, America/Recife, America/Regina, America/Resolute, America/Rio_Branco, America/Santarem, America/Santiago, America/Santo_Domingo, America/Sao_Paulo, America/Scoresbysund, America/Sitka, America/St_Barthelemy, America/St_Johns, America/St_Kitts, America/St_Lucia, America/St_Thomas, America/St_Vincent, America/Swift_Current, America/Tegucigalpa, America/Thule, America/Tijuana, America/Toronto, America/Tortola, America/Vancouver, America/Whitehorse, America/Winnipeg, America/Yakutat, Antarctica/Casey, Antarctica/Davis, Antarctica/DumontDUrville, Antarctica/Macquarie, Antarctica/Mawson, Antarctica/McMurdo, Antarctica/Palmer, Antarctica/Rothera, Antarctica/Syowa, Antarctica/Troll, Antarctica/Vostok, Arctic/Longyearbyen, Asia/Aden, Asia/Almaty, Asia/Amman, Asia/Anadyr, Asia/Aqtau, Asia/Aqtobe, Asia/Ashgabat, Asia/Atyrau, Asia/Baghdad, Asia/Bahrain, Asia/Baku, Asia/Bangkok, Asia/Barnaul, Asia/Beirut, Asia/Bishkek, Asia/Brunei, Asia/Chita, Asia/Colombo, Asia/Damascus, Asia/Dhaka, Asia/Dili, Asia/Dubai, Asia/Dushanbe, Asia/Famagusta, Asia/Gaza, Asia/Hebron, Asia/Ho_Chi_Minh, Asia/Hong_Kong, Asia/Hovd, Asia/Irkutsk, Asia/Jakarta, Asia/Jayapura, Asia/Jerusalem, Asia/Kabul, Asia/Kamchatka, Asia/Karachi, Asia/Kathmandu, Asia/Khandyga, Asia/Kolkata, Asia/Krasnoyarsk, Asia/Kuala_Lumpur, Asia/Kuching, Asia/Kuwait, Asia/Macau, Asia/Magadan, Asia/Makassar, Asia/Manila, Asia/Muscat, Asia/Nicosia, Asia/Novokuznetsk, Asia/Novosibirsk, Asia/Omsk, Asia/Oral, Asia/Phnom_Penh, Asia/Pontianak, Asia/Pyongyang, Asia/Qatar, Asia/Qostanay, Asia/Qyzylorda, Asia/Riyadh, Asia/Sakhalin, Asia/Samarkand, Asia/Seoul, Asia/Shanghai, Asia/Singapore, Asia/Srednekolymsk, Asia/Taipei, Asia/Tashkent, Asia/Tbilisi, Asia/Tehran, Asia/Thimphu, Asia/Tokyo, Asia/Tomsk, Asia/Ulaanbaatar, Asia/Urumqi, Asia/Ust-Nera, Asia/Vientiane, Asia/Vladivostok, Asia/Yakutsk, Asia/Yangon, Asia/Yekaterinburg, Asia/Yerevan, Atlantic/Azores, Atlantic/Bermuda, Atlantic/Canary, Atlantic/Cape_Verde, Atlantic/Faroe, Atlantic/Madeira, Atlantic/Reykjavik, Atlantic/South_Georgia, Atlantic/St_Helena, Atlantic/Stanley, Australia/Adelaide, Australia/Brisbane, Australia/Broken_Hill, Australia/Darwin, Australia/Eucla, Australia/Hobart, Australia/Lindeman, Australia/Lord_Howe, Australia/Melbourne, Australia/Perth, Australia/Sydney, Europe/Amsterdam, Europe/Andorra, Europe/Astrakhan, Europe/Athens, Europe/Belgrade, Europe/Berlin, Europe/Bratislava, Europe/Brussels, Europe/Bucharest, Europe/Budapest, Europe/Busingen, Europe/Chisinau, Europe/Copenhagen, Europe/Dublin, Europe/Gibraltar, Europe/Guernsey, Europe/Helsinki, Europe/Isle_of_Man, Europe/Istanbul, Europe/Jersey, Europe/Kaliningrad, Europe/Kirov, Europe/Kyiv, Europe/Lisbon, Europe/Ljubljana, Europe/London, Europe/Luxembourg, Europe/Madrid, Europe/Malta, Europe/Mariehamn, Europe/Minsk, Europe/Monaco, Europe/Moscow, Europe/Oslo, Europe/Paris, Europe/Podgorica, Europe/Prague, Europe/Riga, Europe/Rome, Europe/Samara, Europe/San_Marino, Europe/Sarajevo, Europe/Saratov, Europe/Simferopol, Europe/Skopje, Europe/Sofia, Europe/Stockholm, Europe/Tallinn, Europe/Tirane, Europe/Ulyanovsk, Europe/Vaduz, Europe/Vatican, Europe/Vienna, Europe/Vilnius, Europe/Volgograd, Europe/Warsaw, Europe/Zagreb, Europe/Zurich, Indian/Antananarivo, Indian/Chagos, Indian/Christmas, Indian/Cocos, Indian/Comoro, Indian/Kerguelen, Indian/Mahe, Indian/Maldives, Indian/Mauritius, Indian/Mayotte, Indian/Reunion, Pacific/Apia, Pacific/Auckland, Pacific/Bougainville, Pacific/Chatham, Pacific/Chuuk, Pacific/Easter, Pacific/Efate, Pacific/Fakaofo, Pacific/Fiji, Pacific/Funafuti, Pacific/Galapagos, Pacific/Gambier, Pacific/Guadalcanal, Pacific/Guam, Pacific/Honolulu, Pacific/Kanton, Pacific/Kiritimati, Pacific/Kosrae, Pacific/Kwajalein, Pacific/Majuro, Pacific/Marquesas, Pacific/Midway, Pacific/Nauru, Pacific/Niue, Pacific/Norfolk, Pacific/Noumea, Pacific/Pago_Pago, Pacific/Palau, Pacific/Pitcairn, Pacific/Pohnpei, Pacific/Port_Moresby, Pacific/Rarotonga, Pacific/Saipan, Pacific/Tahiti, Pacific/Tarawa, Pacific/Tongatapu, Pacific/Wake, Pacific/Wallis, UTC
frequency_cap_count
integer | null
frequency_cap_period
string | null
One of: day, week, month, null
location_ids
(string (uuid))[]
location_group_ids
(string (uuid))[]
segmentation_ids
(string (uuid))[]
segment_match
string | null
One of: any, all, null
starts_at
string
required

Only the campaign's own bounds — the trait's attachments.*.* rules are the dashboard form's, for a field this endpoint doesn't accept (notifications are added afterwards, through the nested resource).

ends_at
string | null

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
    "name": "Morning coffee offer",
    "type": "geo",
    "status": "live",
    "starts_at": "2026-10-01T07:00:00Z",
    "ends_at": "2026-10-31T11:00:00Z",
    "timezone": "Europe/London",
    "paused": false,
    "frequency_cap_count": null,
    "frequency_cap_period": null,
    "targeting": {
      "location_ids": [
        "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001"
      ],
      "location_group_ids": [],
      "segmentation_ids": [],
      "segment_match": null
    },
    "created_at": "2026-09-20T09:12:00Z",
    "updated_at": "2026-09-27T14:03:00Z"
  }
}
Response fields
data
object
required
data.id
string
required
data.name
string
required
data.type
string
required
data.status
string
required
data.starts_at
string
required
data.ends_at
string | null
required
data.timezone
string
required
data.paused
boolean
required
data.frequency_cap_count
integer | null
required
data.frequency_cap_period
string | null
required
data.targeting
object
required
data.targeting.location_ids
any[]
data.targeting.location_group_ids
any[]
data.targeting.segmentation_ids
any[]
data.targeting.segment_match
string
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, expired or revoked.

unauthenticated
401 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
403

The token can't do this.

missing_abilityplatform_api_not_enabledwrong_environment
403 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
404

Nothing with that ID in this workspace.

not_found
404 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
422

Some fields aren't valid. fields says which, with the dashboard's wording.

validation_failed
422 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
429

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

rate_limited
429 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
Request
curl -X PATCH 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  --data '{
  "name": "Morning coffee offer (October)"
}'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}', {
  method: 'PATCH',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Morning coffee offer (October)',
  }),
});

const data = await response.json();
Request
import requests

response = requests.request(
    'PATCH',
    'https://api.bubbl.tech/platform/v1/campaigns/{campaign}',
    headers={
        'Accept': 'application/json',
        'Content-Type': 'application/json',
    },
    json={
        'name': 'Morning coffee offer (October)',
    },
)
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}', [
    'headers' => [
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
    'json' => [
        'name' => 'Morning coffee offer (October)',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Delete a campaign#

DELETE/campaigns/{campaign}
Needs campaigns:writeNo token needed

Path parameters

campaign
string (uuid)
required

The campaign ID

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
    "deleted": true
  }
}
Response fields
data
object
required
data.id
string
required
data.deleted
boolean
required
401

The token is missing, expired or revoked.

unauthenticated
401 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
403

The token can't do this.

missing_abilityplatform_api_not_enabledwrong_environment
403 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
404

Nothing with that ID in this workspace.

not_found
404 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
429

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

rate_limited
429 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
Request
curl -X DELETE 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}', {
  method: 'DELETE',
  headers: {
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'DELETE',
    'https://api.bubbl.tech/platform/v1/campaigns/{campaign}',
    headers={
        '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}', [
    'headers' => [
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Publish a campaign#

POST/campaigns/{campaign}/publish
Needs campaigns:writeNo token needed

Path parameters

campaign
string (uuid)
required

The campaign ID

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
    "name": "Morning coffee offer",
    "type": "geo",
    "status": "live",
    "starts_at": "2026-10-01T07:00:00Z",
    "ends_at": "2026-10-31T11:00:00Z",
    "timezone": "Europe/London",
    "paused": false,
    "frequency_cap_count": null,
    "frequency_cap_period": null,
    "targeting": {
      "location_ids": [
        "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001"
      ],
      "location_group_ids": [],
      "segmentation_ids": [],
      "segment_match": null
    },
    "created_at": "2026-09-20T09:12:00Z",
    "updated_at": "2026-09-27T14:03:00Z"
  }
}
Response fields
data
object
required
data.id
string
required
data.name
string
required
data.type
string
required
data.status
string
required
data.starts_at
string
required
data.ends_at
string | null
required
data.timezone
string
required
data.paused
boolean
required
data.frequency_cap_count
integer | null
required
data.frequency_cap_period
string | null
required
data.targeting
object
required
data.targeting.location_ids
any[]
data.targeting.location_group_ids
any[]
data.targeting.segmentation_ids
any[]
data.targeting.segment_match
string
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, expired or revoked.

unauthenticated
401 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
403

The token can't do this.

missing_abilityplatform_api_not_enabledwrong_environment
403 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
404

Nothing with that ID in this workspace.

not_found
404 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
409

The campaign can't be published yet.

campaign_incompletecampaign_ended
409 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
429

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

rate_limited
429 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
Request
curl -X POST 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/publish' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/publish', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'POST',
    'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/publish',
    headers={
        'Accept': 'application/json',
    },
)
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}/publish', [
    'headers' => [
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);

Pause a campaign#

POST/campaigns/{campaign}/pause
Needs campaigns:writeNo token needed

Path parameters

campaign
string (uuid)
required

The campaign ID

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
    "name": "Morning coffee offer",
    "type": "geo",
    "status": "live",
    "starts_at": "2026-10-01T07:00:00Z",
    "ends_at": "2026-10-31T11:00:00Z",
    "timezone": "Europe/London",
    "paused": false,
    "frequency_cap_count": null,
    "frequency_cap_period": null,
    "targeting": {
      "location_ids": [
        "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d3001"
      ],
      "location_group_ids": [],
      "segmentation_ids": [],
      "segment_match": null
    },
    "created_at": "2026-09-20T09:12:00Z",
    "updated_at": "2026-09-27T14:03:00Z"
  }
}
Response fields
data
object
required
data.id
string
required
data.name
string
required
data.type
string
required
data.status
string
required
data.starts_at
string
required
data.ends_at
string | null
required
data.timezone
string
required
data.paused
boolean
required
data.frequency_cap_count
integer | null
required
data.frequency_cap_period
string | null
required
data.targeting
object
required
data.targeting.location_ids
any[]
data.targeting.location_group_ids
any[]
data.targeting.segmentation_ids
any[]
data.targeting.segment_match
string
required
data.created_at
string | null
required
data.updated_at
string | null
required
401

The token is missing, expired or revoked.

unauthenticated
401 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
403

The token can't do this.

missing_abilityplatform_api_not_enabledwrong_environment
403 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
404

Nothing with that ID in this workspace.

not_found
404 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
429

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

rate_limited
429 response
{
  "error": {
    "code": "string",
    "message": "string",
    "fields": {}
  }
}
Request
curl -X POST 'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/pause' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/campaigns/{campaign}/pause', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
  },
});

const data = await response.json();
Request
import requests

response = requests.request(
    'POST',
    'https://api.bubbl.tech/platform/v1/campaigns/{campaign}/pause',
    headers={
        'Accept': 'application/json',
    },
)
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}/pause', [
    'headers' => [
        'Accept' => 'application/json',
    ],
]);

$data = json_decode((string) $response->getBody(), true);