Blackline Real-Time Event Streaming API allows you to get live events in real-time.
This API provides WebSocket-based streaming of real-time events from Blackline Safety devices. Connect once and receive live updates as events occur.
Authenticate each WebSocket connection by passing a bearer token in the Sec-WebSocket-Protocol header during the handshake. Browser WebSocket APIs do not allow custom headers on the upgrade request, and the subprotocol field is the only client-settable value available.
Token source: Obtain a bearer token from Connect V4 using the standard OAuth 2.0 client credentials flow. The same token is valid for this streaming API.
Server handshake contract: The server echoes the supplied subprotocol value back in the handshake response. Reverse proxies and load balancers in front of the streaming endpoint must preserve the Sec-WebSocket-Protocol header on both the request and response.
Example (Browser, JavaScript):
// 1. Obtain a bearer token from Connect V4
const tokenResponse = await fetch("https://api.blacklinesafety.com/live/v4/oauth2/token", {
method: "POST",
headers: {
"x-api-key": "<your-api-key>",
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "<your-client-id>",
client_secret: "<your-client-secret>",
}),
});
const { access_token } = await tokenResponse.json();
// 2. Open the WebSocket connection, passing the token as the subprotocol
const ws = new WebSocket("wss://streaming.blacklinesafety.com/v1/ws", access_token);
// 3. Register for topics once the connection is open
const orgId = 123;
const topics = ["sensor_readings", "device_presence", "text_message", "device_event", "location"];
ws.addEventListener("open", () => {
for (const topic of topics) {
ws.send(JSON.stringify({
type: "register",
orgId,
token: access_token,
data: { topic },
}));
}
});
// 4. Handle incoming events
ws.addEventListener("message", (event) => {
const msg = JSON.parse(event.data);
console.log(`I received ${JSON.stringify(msg)} from ${msg.meta.topic}`);
});
Example (Python, websockets + requests):
import asyncio
import json
import requests
import websockets
async def main():
# 1. Obtain a bearer token from Connect V4
token_response = requests.post(
"https://api.blacklinesafety.com/live/v4/oauth2/token",
headers={"x-api-key": "<your-api-key>"},
data={
"grant_type": "client_credentials",
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>",
},
)
access_token = token_response.json()["access_token"]
# 2. Open the WebSocket connection, passing the token as the subprotocol
async with websockets.connect(
"wss://streaming.blacklinesafety.com/v1/ws",
subprotocols=[access_token],
) as ws:
# 3. Register for topics
org_id = 123
for topic in ["sensor_readings", "device_presence", "text_message", "device_event", "location"]:
await ws.send(json.dumps({
"type": "register",
"orgId": org_id,
"token": access_token,
"data": {"topic": topic},
}))
# 4. Handle incoming events
async for raw in ws:
msg = json.loads(raw)
print(f"I received {msg} from {msg['meta']['topic']}")
asyncio.run(main())
Failure modes: Missing or invalid tokens cause the upgrade to fail with 401 Unauthorized.
Production server - North America
Bearer token obtained from Connect V4, passed as the WebSocket subprotocol during the handshake. The server echoes this value back in the handshake response.
Production server - Europe
Bearer token obtained from Connect V4, passed as the WebSocket subprotocol during the handshake. The server echoes this value back in the handshake response.
Production server - United Arab Emirates
Bearer token obtained from Connect V4, passed as the WebSocket subprotocol during the handshake. The server echoes this value back in the handshake response.
Register to receive events from a specific topic for all authorized devices in specified organization
Available only on servers:
Accepts the following message:
Registration request payload, describing the topic which the client wants to register for
Sent by the client to subscribe to a topic within a specific organization. Once registered, the client receives events on that topic for every device the authenticated user is authorized to access in the given organization. Individual devices cannot be targeted; device access is determined server-side from the token.
Send a separate register message for each topic the client wants to
receive. The token field must match the token used during the WebSocket
handshake.
When at least one device is successfully registered, the server replies
with a success message that lists the device IDs the client is now
subscribed to and any devices that failed to register. When every device
fails to register, the server replies with an error message and closes
the connection. When the registration request itself is invalid (bad
topic, missing fields), the server replies with an error message and
keeps the connection open so the client can retry.
Only events generated after a successful registration are delivered. No historical or buffered events are sent; use the companion REST API to fetch current state.
Register to receive sensor readings from devices in specified organization
{
"type": "register",
"orgId": 123,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"data": {
"topic": "sensor_readings"
}
}
Register to receive device presence status updates (online/offline) from devices in specified organization
{
"type": "register",
"orgId": 123,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"data": {
"topic": "device_presence"
}
}
Unregister from a specific topic. If topic is null, disconnects the client entirely.
Available only on servers:
Accepts the following message:
Deregistration request to stop receiving events from a specific topic
Sent by the client to stop receiving events on a previously registered
topic. After deregistration the client no longer receives events for
that topic on this connection. The server does not send a success or
error response to a deregistration request; the effect is silent.
Setting data.topic to null deregisters the client from every topic
it is currently subscribed to. Once a client has no remaining
subscriptions (either through a null deregister or after the last
individual topic is removed), the server closes the WebSocket
connection. This is the recommended way to cleanly disconnect.
Deregistering from a topic the client is not subscribed to is a no-op.
Unregister from sensor_readings topic
{
"type": "de-register",
"orgId": 123,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"data": {
"topic": "sensor_readings"
}
}
Unregister from device_presence topic
{
"type": "de-register",
"orgId": 123,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"data": {
"topic": "device_presence"
}
}
Unregister from all topics and close the connection
{
"type": "de-register",
"orgId": 123,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"data": {
"topic": null
}
}
Send an echo message to verify the WebSocket is working
Available only on servers:
Accepts the following message:
Echo request to verify WebSocket functionality
Sent by the client to verify the WebSocket connection is alive. The
server replies with an echoResponse carrying the same data value.
Echo messages do not require any topic registration and can be sent at any point during the connection's lifetime.
Send an echo request to test the connection
{
"type": "echo",
"orgId": 123,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"data": "test-ping-001"
}
Success response for registration/deregistration requests
Available only on servers:
Accepts the following message:
Success response payload sent when operations complete successfully
Delivered by the server in response to a register request that
succeeded for at least one device. The data.message field is a
human-readable summary describing how many devices were successfully
registered and which devices, if any, failed to register.
A registration that succeeds for some devices but fails for others is
still reported as a success; the failed device IDs are listed inside
data.message. A registration where every device fails is reported
as an error, not as a success.
Deregistration requests and echo requests do not produce a success
message.
The content of data.message is intended for logging and diagnostics
and should not be parsed for programmatic logic.
Successful registration to sensor readings
{
"type": "success",
"orgId": 123,
"data": {
"message": "All device registrations successful: 5 devices registered (devices: [1001, 1002, 1003, 1004, 1005])"
}
}
Some devices registered successfully, others failed
{
"type": "success",
"orgId": 123,
"data": {
"message": "Device registration completed: 3 successful (devices: [1001, 1002, 1003]), 2 failed (devices: [1004, 1005])"
}
}
Error response for failed requests
Available only on servers:
Accepts the following message:
Error response payload sent when operations fail
Delivered by the server when a client request cannot be processed.
Common triggers include malformed JSON, failed validation of a
register or de-register payload, unknown message types, and
registration attempts where every device fails to register.
The data.msg field is a human-readable explanation of the failure
and is intended for logging and operator visibility, not for
programmatic parsing.
Most errors are recoverable: the server keeps the connection open
after sending an error message, and the client may continue to
send other requests. The connection is closed immediately after the
error in only one case: when a register request fails for every
authorized device in the organization.
Message validation failed
{
"type": "error",
"orgId": 123,
"data": {
"msg": "Message validation failed: Token is required"
}
}
Invalid or expired token
{
"type": "error",
"orgId": 123,
"data": {
"msg": "Authentication failed: Invalid token"
}
}
Unsupported topic specified in registration
{
"type": "error",
"orgId": 123,
"data": {
"msg": "Invalid Topic value found in message with value invalid_topic"
}
}
Failed to parse message JSON
{
"type": "error",
"orgId": 123,
"data": {
"msg": "Failed to parse message"
}
}
Echo response with timing information
Available only on servers:
Accepts the following message:
Echo response from server for connection health check
Delivered in response to an echoRequest. Used to verify the
WebSocket connection is alive.
Server response to an echo request with timing information
{
"type": "echo",
"data": "test-ping-001",
"meta": {
"receivedAt": "2024-01-15T10:30:00.123Z",
"sentAt": "2024-01-15T10:30:00.125Z"
}
}
Sensor readings event, emitted by a device
Available only on servers:
Accepts the following message:
Sensor reading values from a device. Each message can include multiple sensor readings, as devices have multiple sensors.
Delivered to clients registered to the sensor_readings topic when a
device publishes new sensor measurements.
Sensor readings are forwarded to subscribers as they are received from the upstream event pipeline; they are not aggregated, sampled, or deduplicated by the streaming API. Clients that need historical data or roll-ups should use the companion REST API.
{
"data": {
"createdByDeviceAt": "2019-08-24T14:15:22Z",
"device": {
"unitId": 42301
},
"organization": {
"id": 0
},
"location": {
"latitude": 51.1657,
"longitude": 10.4515,
"readAt": "2024-01-01T00:00:00.000Z"
},
"sensorReadings": [
{
"unit": "string",
"value": 0,
"readAt": "2019-08-24T14:15:22Z",
"sensor": {
"type": "CO"
},
"isErroneous": true,
"inlet": 1,
"thresholdBreach": {
"high": true,
"low": true,
"overLimit": true
}
}
],
"status": {
"battery": {
"internalLevel": 60,
"charging": true
},
"signalStrength": 10
}
},
"meta": {
"type": "sensor_readings",
"topic": "sensor_readings",
"correlationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
Device presence status update event
Available only on servers:
Accepts the following message:
Device presence status updates including online/offline state and the trigger that caused the change.
Delivered to clients registered to the device_presence topic when a
device's presence state changes.
The eventTimestamp field reflects when the presence change actually
occurred, not when the message was delivered. Clients should order
presence events by eventTimestamp, not by receive time.
{
"data": {
"device": {
"unitId": 42301
},
"presence": {
"state": "ONLINE",
"eventTimestamp": "2024-01-01T00:00:00.000Z",
"trigger": "DEVICE"
},
"status": {
"battery": {
"internalLevel": 60,
"charging": true
},
"signalStrength": 10
}
},
"meta": {
"type": "device_presence",
"topic": "device_presence",
"correlationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
Read receipt emitted when a device acknowledges a text message
Available only on servers:
Accepts the following message:
A read receipt emitted when a device acknowledges a text message
Delivered to clients registered to the text_message topic when a
device user reads a text message previously sent to the device.
Only read receipts are delivered on this topic. Outbound text message sends and non-read delivery events are not part of this stream and must be handled through the messaging REST API.
Read receipts carry meta.type of text_message_read_receipt.
{
"data": {
"device": {
"unitId": 42301
},
"createdByDeviceAt": "2024-01-01T00:00:00.000Z",
"message": {
"uid": 7391285046123749
},
"status": {
"battery": {
"internalLevel": 60,
"charging": true
},
"signalStrength": 10
}
},
"meta": {
"type": "text_message_read_receipt",
"topic": "text_message",
"correlationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
Non-sensor event for a device
Available only on servers:
Accepts the following message:
Safety events emitted by a device, such as falls, no motion, emergencies, or low battery.
Delivered to clients registered to the device_event topic when a
device reports a safety alert. Current alert types are:
EMERGENCYFALL_DETECTEDNO_MOTIONSILENT_EMERGENCYDEVICE_TIPPED_OVERPUMP_BLOCK_DETECTEDMISSED_CHECK_INLOW_BATTERYTWA_ALERT_DETECTEDSTEL_ALERT_DETECTEDNew alert types may be added over time; clients should tolerate unknown values without failing.
{
"data": {
"createdByDeviceAt": "2019-08-24T14:15:22Z",
"device": {
"unitId": 42301
},
"organization": {
"id": 0
},
"location": {
"latitude": 51.1657,
"longitude": 10.4515,
"readAt": "2024-01-01T00:00:00.000Z"
},
"eventData": {
"eventType": "EMERGENCY"
},
"status": {
"battery": {
"internalLevel": 60,
"charging": true
},
"signalStrength": 10
}
},
"meta": {
"type": "device_event",
"topic": "device_event",
"correlationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
Device location update with all location sources
Available only on servers:
Accepts the following message:
Location update for a device, including all location sources and the selected best location.
Delivered to clients registered to the location topic when a
device publishes a new location update.
Each message contains the full array of location entries (GPS and/or
beacon) from the source event, along with a bestLocationIndex
indicating which entry was selected as the best location. Clients
that only need the best location can index directly into the array;
clients that need all location sources can iterate the full array.
Location updates are forwarded as they are received from the upstream event pipeline; they are not aggregated or deduplicated by the streaming API.
{
"data": {
"device": {
"unitId": 42301
},
"organization": {
"id": 0
},
"bestLocationIndex": 0,
"locations": [
{
"latLong": {
"latitude": 51.0447,
"longitude": -114.0719
},
"datestamp": "2024-01-01T00:00:00.000Z",
"locationType": "gps",
"gps": {
"altitude": 1045,
"speed": 0,
"direction": 180,
"averageSnr": 35,
"numSatellites": 8
},
"beacon": {
"beaconId": 12345,
"beaconRssi": -65,
"beaconBatteryLevel": "NORMAL",
"beaconSignalLevel": 3
}
}
],
"deviceTimestamp": "2024-01-01T00:00:00.000Z"
},
"meta": {
"type": "location",
"topic": "location",
"correlationId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
Registration request payload, describing the topic which the client wants to register for
Sent by the client to subscribe to a topic within a specific organization. Once registered, the client receives events on that topic for every device the authenticated user is authorized to access in the given organization. Individual devices cannot be targeted; device access is determined server-side from the token.
Send a separate register message for each topic the client wants to
receive. The token field must match the token used during the WebSocket
handshake.
When at least one device is successfully registered, the server replies
with a success message that lists the device IDs the client is now
subscribed to and any devices that failed to register. When every device
fails to register, the server replies with an error message and closes
the connection. When the registration request itself is invalid (bad
topic, missing fields), the server replies with an error message and
keeps the connection open so the client can retry.
Only events generated after a successful registration are delivered. No historical or buffered events are sent; use the companion REST API to fetch current state.
Deregistration request to stop receiving events from a specific topic
Sent by the client to stop receiving events on a previously registered
topic. After deregistration the client no longer receives events for
that topic on this connection. The server does not send a success or
error response to a deregistration request; the effect is silent.
Setting data.topic to null deregisters the client from every topic
it is currently subscribed to. Once a client has no remaining
subscriptions (either through a null deregister or after the last
individual topic is removed), the server closes the WebSocket
connection. This is the recommended way to cleanly disconnect.
Deregistering from a topic the client is not subscribed to is a no-op.
Success response payload sent when operations complete successfully
Delivered by the server in response to a register request that
succeeded for at least one device. The data.message field is a
human-readable summary describing how many devices were successfully
registered and which devices, if any, failed to register.
A registration that succeeds for some devices but fails for others is
still reported as a success; the failed device IDs are listed inside
data.message. A registration where every device fails is reported
as an error, not as a success.
Deregistration requests and echo requests do not produce a success
message.
The content of data.message is intended for logging and diagnostics
and should not be parsed for programmatic logic.
Error response payload sent when operations fail
Delivered by the server when a client request cannot be processed.
Common triggers include malformed JSON, failed validation of a
register or de-register payload, unknown message types, and
registration attempts where every device fails to register.
The data.msg field is a human-readable explanation of the failure
and is intended for logging and operator visibility, not for
programmatic parsing.
Most errors are recoverable: the server keeps the connection open
after sending an error message, and the client may continue to
send other requests. The connection is closed immediately after the
error in only one case: when a register request fails for every
authorized device in the organization.
Echo request to verify WebSocket functionality
Sent by the client to verify the WebSocket connection is alive. The
server replies with an echoResponse carrying the same data value.
Echo messages do not require any topic registration and can be sent at any point during the connection's lifetime.
Echo response from server for connection health check
Delivered in response to an echoRequest. Used to verify the
WebSocket connection is alive.
Sensor reading values from a device. Each message can include multiple sensor readings, as devices have multiple sensors.
Delivered to clients registered to the sensor_readings topic when a
device publishes new sensor measurements.
Sensor readings are forwarded to subscribers as they are received from the upstream event pipeline; they are not aggregated, sampled, or deduplicated by the streaming API. Clients that need historical data or roll-ups should use the companion REST API.
Device presence status updates including online/offline state and the trigger that caused the change.
Delivered to clients registered to the device_presence topic when a
device's presence state changes.
The eventTimestamp field reflects when the presence change actually
occurred, not when the message was delivered. Clients should order
presence events by eventTimestamp, not by receive time.
Safety events emitted by a device, such as falls, no motion, emergencies, or low battery.
Delivered to clients registered to the device_event topic when a
device reports a safety alert. Current alert types are:
EMERGENCYFALL_DETECTEDNO_MOTIONSILENT_EMERGENCYDEVICE_TIPPED_OVERPUMP_BLOCK_DETECTEDMISSED_CHECK_INLOW_BATTERYTWA_ALERT_DETECTEDSTEL_ALERT_DETECTEDNew alert types may be added over time; clients should tolerate unknown values without failing.
A read receipt emitted when a device acknowledges a text message
Delivered to clients registered to the text_message topic when a
device user reads a text message previously sent to the device.
Only read receipts are delivered on this topic. Outbound text message sends and non-read delivery events are not part of this stream and must be handled through the messaging REST API.
Read receipts carry meta.type of text_message_read_receipt.
Location update for a device, including all location sources and the selected best location.
Delivered to clients registered to the location topic when a
device publishes a new location update.
Each message contains the full array of location entries (GPS and/or
beacon) from the source event, along with a bestLocationIndex
indicating which entry was selected as the best location. Clients
that only need the best location can index directly into the array;
clients that need all location sources can iterate the full array.
Location updates are forwarded as they are received from the upstream event pipeline; they are not aggregated or deduplicated by the streaming API.