Devices

3 endpoints

The app installs behind your users. Set a correlation_id for each signed-in user in the SDK (an opaque member id, never an email or a name), then look their installs up here by it, and read what each did with your notifications. Needs the devices:read scope. Installs whose user withdrew consent are not returned.

List devices#

GET/devices
Needs devices:read

Newest first. Filter by correlation_id to find one user's installs (a phone, a tablet and a reinstall can share one). The push token, the install id and anything about where a device has been are never returned.

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
correlation_id
string

Only devices whose correlation_id is exactly this.

platform
string

Only ios or android.

updated_since
string (date-time)

Only devices seen at or after this ISO 8601 timestamp.

Responses

200
200 response
{
  "data": [
    {
      "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d8001",
      "correlation_id": "member_8841",
      "platform": "ios",
      "os_version": "18.1",
      "device_model": "iPhone16,1",
      "manufacturer": "Apple",
      "app_id": "com.example.coffee",
      "app_version": "2.4.0",
      "sdk_version": "5.1.0",
      "locale": "en-GB",
      "country": "GB",
      "timezone": "Europe/London",
      "permissions": {
        "notifications": "granted",
        "location": "always",
        "precise_location": true
      },
      "segments": [
        "vip"
      ],
      "last_seen_at": "2026-10-07T08:30:00+00:00",
      "created_at": "2026-09-20T09:12:00+00:00"
    }
  ],
  "meta": {
    "next_cursor": null
  }
}
Response fields
data
DeviceResource[]
required
data[].id
string
required
data[].correlation_id
string | null
required
data[].platform
string
required
data[].os_version
string
required
data[].device_model
string
required
data[].manufacturer
string | null
required
data[].app_id
string
required
data[].app_version
string | null
required
data[].sdk_version
string
required
data[].locale
string | null
required
data[].country
string | null
required
data[].timezone
string | null
required
data[].permissions
any[] | null
required
data[].segments
any[]
data[].last_seen_at
string | null
required
data[].created_at
string | null
required
meta
object
required
meta.next_cursor
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."
  }
}
422

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

invalid_parameterinvalid_cursor
  • invalid_parameter

    updated_since must be a valid date.

  • invalid_cursor

    That cursor is invalid or has expired.

422 response
{
  "error": {
    "code": "invalid_parameter",
    "message": "updated_since must be a valid date.",
    "fields": {
      "updated_since": [
        "Must be a valid date."
      ]
    }
  }
}
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/devices?limit=20' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/devices?limit=20', {
  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/devices?limit=20',
    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/devices?limit=20', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
    ],
]);

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

Get a device#

GET/devices/{device}
Needs devices:read

Path parameters

device
string (uuid)
required

The device ID

Responses

200
200 response
{
  "data": {
    "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d8001",
    "correlation_id": "member_8841",
    "platform": "ios",
    "os_version": "18.1",
    "device_model": "iPhone16,1",
    "manufacturer": "Apple",
    "app_id": "com.example.coffee",
    "app_version": "2.4.0",
    "sdk_version": "5.1.0",
    "locale": "en-GB",
    "country": "GB",
    "timezone": "Europe/London",
    "permissions": {
      "notifications": "granted",
      "location": "always",
      "precise_location": true
    },
    "segments": [
      "vip"
    ],
    "last_seen_at": "2026-10-07T08:30:00+00:00",
    "created_at": "2026-09-20T09:12:00+00:00"
  }
}
Response fields
data
object
required
data.id
string
required
data.correlation_id
string | null
required
data.platform
string
required
data.os_version
string
required
data.device_model
string
required
data.manufacturer
string | null
required
data.app_id
string
required
data.app_version
string | null
required
data.sdk_version
string
required
data.locale
string | null
required
data.country
string | null
required
data.timezone
string | null
required
data.permissions
any[] | null
required
data.segments
any[]
required
data.last_seen_at
string | null
required
data.created_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/devices/{device}' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/devices/{device}', {
  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/devices/{device}',
    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/devices/{device}', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
    ],
]);

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

List a device's activity#

GET/devices/{device}/activity
Needs devices:read

What this install did with your notifications and geofences, newest first: a notification delivered, displayed, opened or dismissed (with its campaign_id and notification_id), and geofence entries and exits. Raw position updates are not returned, and nor is any coordinate. Geofence events carry no location, because Bubbl records where, not which.

Path parameters

device
string (uuid)
required

The device ID

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
type
string

Only this kind of activity, e.g. notification or geofence.

since
string (date-time)

Only activity that happened at or after this ISO 8601 timestamp.

Responses

200
200 response
{
  "data": [
    {
      "id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d9001",
      "type": "notification",
      "activity": "opened",
      "occurred_at": "2026-10-07T08:31:12+00:00",
      "campaign_id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d4001",
      "notification_id": "0192f0a4-6b1e-7c3a-9d2f-5e8b1a4d5001"
    }
  ],
  "meta": {
    "next_cursor": null
  }
}
Response fields
data
object[]
required
data[].id
string
required
data[].type
string
required
data[].activity
string
required
data[].occurred_at
string
required
data[].campaign_id
string | null
required
data[].notification_id
string | null
required
meta
object
required
meta.next_cursor
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."
  }
}
422

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

invalid_parameterinvalid_cursor
  • invalid_parameter

    updated_since must be a valid date.

  • invalid_cursor

    That cursor is invalid or has expired.

422 response
{
  "error": {
    "code": "invalid_parameter",
    "message": "updated_since must be a valid date.",
    "fields": {
      "updated_since": [
        "Must be a valid date."
      ]
    }
  }
}
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/devices/{device}/activity?limit=20' \
  -H 'Authorization: Bearer bubbl_YOUR_TOKEN' \
  -H 'Accept: application/json'
Request
const response = await fetch('https://api.bubbl.tech/platform/v1/devices/{device}/activity?limit=20', {
  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/devices/{device}/activity?limit=20',
    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/devices/{device}/activity?limit=20', [
    'headers' => [
        'Authorization' => 'Bearer bubbl_YOUR_TOKEN',
        'Accept' => 'application/json',
    ],
]);

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