Devices
One read endpoint for every line in the account.
One read endpoint for every line in the account.
Every route takes Authorization: Bearer <ACCESS_TOKEN> unless the auth line says otherwise. Ids are numbers. Errors use the shape on the errors page.
| Route | Scope | What it does |
|---|---|---|
GET /v1/devices | devices.read | Every device the installer owns — across all channels — in one list. |
GET /v1/devices/{id} | devices.read | Look up a device by id alone — the channel is resolved automatically. |
GET /v1/devices/{channel}/{id} | devices.read | Fetch one device by channel + id. |
PATCH /v1/devices/{channel}/{id} | devices.write | Rename a device, assign/unassign it to a workspace, or set it as default. |
DELETE /v1/devices/{channel}/{id} | devices.write | Remove a device connection. |
List devices
GET https://api.bluereacher.com/v1/devicesAuth: Bearer access token. Scope: devices.read.
Every device the installer owns — across all channels — in one list.
Each channel (iMessage) is stored separately. This single endpoint queries all four sources, normalizes each record into a common structure with a channel field, and returns them together. Requires the devices.read scope. Each channel is queried independently — if one source is briefly unavailable, the others are still returned. The response also includes a summary.by_channel count.
The channel-native values also remain flattened at the top level for backwards compatibility.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
channel | query | enum | no | Filter to one channel: "imessage". Omit for all. |
Request
curl -X GET "https://api.bluereacher.com/v1/devices" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"data": [
{
"id": 4,
"channel": "imessage",
"name": "MacBook Pro",
"status": "Active-S",
"health_status": "healthy",
"last_seen_at": "2026-05-21T09:10:00.000Z",
"last_ping_at": "2026-05-21T09:10:00.000Z",
"health": {
"battery_level": null,
"battery_charging": null,
"internet_connection": null,
"battery_optimized": null,
"service_uptime": null,
"is_default_sms_app": null,
"missing_permissions": null,
"rate_limit": 100,
"limit_exceed_action": "queue",
"last_state": null,
"last_probe_ok_at": null,
"last_probe_ms": null,
"auto_refresh": null
},
"channel_details": {
"device_model": "MacBookPro18,3",
"os_version": "14.4",
"app_version": "1.2.0",
"last_ping": "2026-05-21T09:10:00.000Z",
"user_name": "Ada Lovelace",
"rate_limit": 100,
"limit_exceed_action": "queue"
},
"workspace_id": 7,
"workspace_name": "Acme Co",
"is_default": false,
"device_model": "MacBookPro18,3",
"os_version": "14.4",
"app_version": "1.2.0",
"last_ping": "2026-05-21T09:10:00.000Z",
"user_name": "Ada Lovelace",
"rate_limit": 100,
"limit_exceed_action": "queue",
"created_at": "2026-05-20T08:00:00.000Z",
"updated_at": "2026-05-21T09:10:00.000Z"
}
],
"summary": {
"total": 4,
"by_channel": {
"imessage": 1
}
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): devices.read. |
Response fields
device: A normalized device across any channel. Fields:
| Field | Type | Description |
|---|---|---|
id | number | Device id within its channel. Example: 12 |
channel | enum | Which channel the device belongs to: "imessage". Example: imessage |
name | string, nullable | Display name (falls back to the device model / imessage user when unnamed). |
status | string, nullable | |
workspace_id | number, nullable | The workspace the device is assigned to. null if unassigned. |
workspace_name | string, nullable | Name of the subaccount (workspace) the device is assigned to. null if unassigned. |
is_default | boolean, nullable | True if this is the default device for its workspace/channel. |
created_at | string (ISO 8601), nullable | When the device connection was created. |
updated_at | string (ISO 8601), nullable | When the device connection was last updated. |
health_status | enum | |
last_seen_at | string (ISO 8601), nullable | |
last_ping_at | string (ISO 8601), nullable | |
health | object | Uniform telemetry bag with the SAME keys on every channel — { battery_level, battery_charging, internet_connection, battery_optimized, service_uptime, is_default_sms_app, missing_permissions, rate_limit, limit_exceed_action, last_state, last_probe_ok_at, last_probe_ms, auto_refresh }. |
channel_details | object | The full channel-native field set (same values as the flattened extras below), grouped into one object so callers have a stable place to read channel-specific data. Empty object {} when the channel exposes none. |
…channel extras | varies |
Get device by id (channel-less)
GET https://api.bluereacher.com/v1/devices/{id}Auth: Bearer access token. Scope: devices.read.
Look up a device by id alone — the channel is resolved automatically.
Convenience endpoint for callers that already know the device id but not its channel. The API resolves the device across all channels and returns the device when exactly one channel matches.
When the same id exists on multiple channels (rare — device ids are only unique within a channel), the response is 409 Conflict with a channels array, and the caller should re-issue the request against /devices/{channel}/{id} to disambiguate. Use the channel-qualified endpoint directly when you already know the channel — it skips the cross-channel scan.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | Device id within any channel. |
Request
curl -X GET "https://api.bluereacher.com/v1/devices/7022" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK — exactly one channel matched):
{
"success": true,
"data": {
"id": 7022,
"channel": "imessage",
"name": null,
"status": "active",
"health_status": "healthy",
"last_seen_at": "2026-06-09T11:08:43.000Z",
"last_ping_at": "2026-06-09T11:08:43.000Z",
"health": {
"battery_level": 100,
"battery_charging": true,
"internet_connection": "wifi",
"battery_optimized": false,
"service_uptime": 172800,
"is_default_sms_app": true,
"missing_permissions": [],
"rate_limit": null,
"limit_exceed_action": null,
"last_state": null,
"last_probe_ok_at": null,
"last_probe_ms": null,
"auto_refresh": null
},
"channel_details": {
"device_model": "SM-A065F",
"app_version": "10.9.5",
"battery_level": 100,
"battery_charging": true,
"internet_connection": "wifi",
"is_default_sms_app": true,
"service_uptime": 172800,
"battery_optimized": false,
"missing_permissions": [],
"mms_config": "auto",
"last_ping": "2026-06-09T11:08:43.000Z",
"sims": [
{
"slot": 1,
"carrier": "Example Mobile",
"number": "+15551234567",
"name": "Example Mobile",
"country": "US",
"enabled": true
},
{
"slot": 2,
"carrier": "Example Mobile",
"number": "+15557654321",
"name": "Example Mobile",
"country": "US",
"enabled": true
}
]
},
"workspace_id": 7,
"workspace_name": "Acme Co",
"is_default": null,
"device_model": "SM-A065F",
"app_version": "10.9.5",
"battery_level": 100,
"battery_charging": true,
"internet_connection": "wifi",
"is_default_sms_app": true,
"service_uptime": 172800,
"battery_optimized": false,
"missing_permissions": [],
"mms_config": "auto",
"last_ping": "2026-06-09T11:08:43.000Z",
"sims": [
{
"slot": 1,
"carrier": "Example Mobile",
"number": "+15551234567",
"name": "Example Mobile",
"country": "US",
"enabled": true
},
{
"slot": 2,
"carrier": "Example Mobile",
"number": "+15557654321",
"name": "Example Mobile",
"country": "US",
"enabled": true
}
],
"created_at": null,
"updated_at": "2026-06-09T11:08:43.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — exactly one channel matched | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): devices.read. |
| 404 | No channel has a device with this id | Device not found |
Response fields
device: The matched device. Fields:
| Field | Type | Description |
|---|---|---|
id | number | Device id within its channel. Example: 12 |
channel | enum | Which channel the device belongs to: "imessage". Example: imessage |
name | string, nullable | Display name (falls back to the device model / imessage user when unnamed). |
status | string, nullable | |
workspace_id | number, nullable | The workspace the device is assigned to. null if unassigned. |
workspace_name | string, nullable | Name of the subaccount (workspace) the device is assigned to. null if unassigned. |
is_default | boolean, nullable | True if this is the default device for its workspace/channel. |
created_at | string (ISO 8601), nullable | When the device connection was created. |
updated_at | string (ISO 8601), nullable | When the device connection was last updated. |
health_status | enum | |
last_seen_at | string (ISO 8601), nullable | |
last_ping_at | string (ISO 8601), nullable | |
health | object | Uniform telemetry bag with the SAME keys on every channel — { battery_level, battery_charging, internet_connection, battery_optimized, service_uptime, is_default_sms_app, missing_permissions, rate_limit, limit_exceed_action, last_state, last_probe_ok_at, last_probe_ms, auto_refresh }. |
channel_details | object | The full channel-native field set (same values as the flattened extras below), grouped into one object so callers have a stable place to read channel-specific data. Empty object {} when the channel exposes none. |
…channel extras | varies |
Get device
GET https://api.bluereacher.com/v1/devices/{channel}/{id}Auth: Bearer access token. Scope: devices.read.
Fetch one device by channel + id.
A device is addressed by both its channel and its id, since device ids are only unique within a channel. A URL like /v1/devices/{id} without a channel segment will return 404. The response is the same normalized data structure as the list endpoint.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
channel | path | enum | yes | |
id | path | number | yes | Device id within that channel. |
Request
curl -X GET "https://api.bluereacher.com/v1/devices/imessage/12" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"data": {
"id": 12,
"channel": "imessage",
"name": "Sales iMessage",
"status": "ready",
"health_status": "healthy",
"last_seen_at": "2026-05-21T09:12:00.000Z",
"last_ping_at": "2026-05-21T09:13:00.000Z",
"health": {
"battery_level": null,
"battery_charging": null,
"internet_connection": null,
"battery_optimized": null,
"service_uptime": null,
"is_default_sms_app": null,
"missing_permissions": null,
"rate_limit": null,
"limit_exceed_action": null,
"last_state": "CONNECTED",
"last_probe_ok_at": "2026-05-21T09:13:00.000Z",
"last_probe_ms": 5,
"auto_refresh": true
},
"channel_details": {
"number": "+15551234567",
"client_id": "wa_8f21",
"user_name": "Ada Lovelace",
"last_state": "CONNECTED",
"last_activity_at": "2026-05-21T09:12:00.000Z",
"last_probe_ok_at": "2026-05-21T09:13:00.000Z",
"last_probe_ms": 5,
"auto_refresh": true
},
"workspace_id": 7,
"workspace_name": "Acme Co",
"is_default": true,
"number": "+15551234567",
"client_id": "wa_8f21",
"user_name": "Ada Lovelace",
"last_state": "CONNECTED",
"last_activity_at": "2026-05-21T09:12:00.000Z",
"last_probe_ok_at": "2026-05-21T09:13:00.000Z",
"last_probe_ms": 5,
"auto_refresh": true,
"created_at": "2026-05-21T09:14:22.000Z",
"updated_at": "2026-05-21T09:14:22.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): devices.read. |
| 404 | Device does not exist or is not owned by the installer | Device not found |
Response fields
device: The device requested. Fields:
| Field | Type | Description |
|---|---|---|
id | number | Device id within its channel. Example: 12 |
channel | enum | Which channel the device belongs to: "imessage". Example: imessage |
name | string, nullable | Display name (falls back to the device model / imessage user when unnamed). |
status | string, nullable | |
workspace_id | number, nullable | The workspace the device is assigned to. null if unassigned. |
workspace_name | string, nullable | Name of the subaccount (workspace) the device is assigned to. null if unassigned. |
is_default | boolean, nullable | True if this is the default device for its workspace/channel. |
created_at | string (ISO 8601), nullable | When the device connection was created. |
updated_at | string (ISO 8601), nullable | When the device connection was last updated. |
health_status | enum | |
last_seen_at | string (ISO 8601), nullable | |
last_ping_at | string (ISO 8601), nullable | |
health | object | Uniform telemetry bag with the SAME keys on every channel — { battery_level, battery_charging, internet_connection, battery_optimized, service_uptime, is_default_sms_app, missing_permissions, rate_limit, limit_exceed_action, last_state, last_probe_ok_at, last_probe_ms, auto_refresh }. |
channel_details | object | The full channel-native field set (same values as the flattened extras below), grouped into one object so callers have a stable place to read channel-specific data. Empty object {} when the channel exposes none. |
…channel extras | varies |
Update device
PATCH https://api.bluereacher.com/v1/devices/{channel}/{id}Auth: Bearer access token. Scope: devices.write.
Rename a device, assign/unassign it to a workspace, or set it as default.
Editable fields: name, workspace_id (send null to unassign), and is_default.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
channel | path | enum | yes | |
id | path | number | yes | Device id. |
name | body | string | no | New display name. Max 255. |
workspace_id | body | number | no | Assign to a workspace (must belong to the installer). Send null to unassign. |
is_default | body | boolean | no | Mark/unmark as the default device for its channel. |
Request
curl -X PATCH "https://api.bluereacher.com/v1/devices/imessage/12" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Sales iMessage (renamed)",
"workspace_id": 7,
"is_default": true
}'Responses
A 200 reply (Updated):
{
"success": true,
"data": {
"id": 12,
"channel": "imessage",
"name": "Sales iMessage (renamed)",
"status": "ready",
"health_status": "healthy",
"last_seen_at": "2026-05-21T09:12:00.000Z",
"last_ping_at": "2026-05-21T09:13:00.000Z",
"health": {
"battery_level": null,
"battery_charging": null,
"internet_connection": null,
"battery_optimized": null,
"service_uptime": null,
"is_default_sms_app": null,
"missing_permissions": null,
"rate_limit": null,
"limit_exceed_action": null,
"last_state": "CONNECTED",
"last_probe_ok_at": "2026-05-21T09:13:00.000Z",
"last_probe_ms": 5,
"auto_refresh": true
},
"channel_details": {
"number": "+15551234567",
"client_id": "wa_8f21",
"user_name": "Ada Lovelace",
"last_state": "CONNECTED",
"last_activity_at": "2026-05-21T09:12:00.000Z",
"last_probe_ok_at": "2026-05-21T09:13:00.000Z",
"last_probe_ms": 5,
"auto_refresh": true
},
"workspace_id": 7,
"workspace_name": "Acme Co",
"is_default": true,
"number": "+15551234567",
"client_id": "wa_8f21",
"user_name": "Ada Lovelace",
"last_state": "CONNECTED",
"last_activity_at": "2026-05-21T09:12:00.000Z",
"last_probe_ok_at": "2026-05-21T09:13:00.000Z",
"last_probe_ms": 5,
"auto_refresh": true
}
}| Status | When | Message |
|---|---|---|
| 200 | Updated | |
| 400 | Empty body — no editable fields provided | No editable fields provided. Editable: name, workspace_id, is_default. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): devices.write. |
| 404 | Device does not exist or is not owned by the installer | Device not found |
Response fields
device: The updated device. Fields:
| Field | Type | Description |
|---|---|---|
id | number | Device id within its channel. Example: 12 |
channel | enum | Which channel the device belongs to: "imessage". Example: imessage |
name | string, nullable | Display name (falls back to the device model / imessage user when unnamed). |
status | string, nullable | |
workspace_id | number, nullable | The workspace the device is assigned to. null if unassigned. |
workspace_name | string, nullable | Name of the subaccount (workspace) the device is assigned to. null if unassigned. |
is_default | boolean, nullable | True if this is the default device for its workspace/channel. |
created_at | string (ISO 8601), nullable | When the device connection was created. |
updated_at | string (ISO 8601), nullable | When the device connection was last updated. |
health_status | enum | |
last_seen_at | string (ISO 8601), nullable | |
last_ping_at | string (ISO 8601), nullable | |
health | object | Uniform telemetry bag with the SAME keys on every channel — { battery_level, battery_charging, internet_connection, battery_optimized, service_uptime, is_default_sms_app, missing_permissions, rate_limit, limit_exceed_action, last_state, last_probe_ok_at, last_probe_ms, auto_refresh }. |
channel_details | object | The full channel-native field set (same values as the flattened extras below), grouped into one object so callers have a stable place to read channel-specific data. Empty object {} when the channel exposes none. |
…channel extras | varies |
Delete device
DELETE https://api.bluereacher.com/v1/devices/{channel}/{id}Auth: Bearer access token. Scope: devices.write.
Remove a device connection.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
channel | path | enum | yes | |
id | path | number | yes | Device id. |
Request
curl -X DELETE "https://api.bluereacher.com/v1/devices/imessage/12" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (Deleted):
{
"success": true,
"message": "Device deleted.",
"data": {
"channel": "imessage",
"id": 12,
"deleted": true
}
}| Status | When | Message |
|---|---|---|
| 200 | Deleted | |
| 400 | Device still assigned to a workspace/subaccount — unassign first | This device is assigned to a workspace. Unassign it (set workspace_id to null) before deleting. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): devices.write. |
| 404 | Device does not exist or is not owned by the installer | Device not found |

