Thinger.io emits an event whenever something happens in your account: a device connects, a record reaches a Data Bucket, an alarm fires. Open a WebSocket to receive the events you subscribe to as they happen, to keep a dashboard, an app or an integration current without polling.
Connect
Open a WebSocket to the events endpoint of your account:
wss://YOUR_SERVER/v2/users/YOUR_USER/events?authorization=YOUR_TOKENYOUR_SERVER is iot.thinger.io on the free Cloud account, or your own domain on a private instance. Here YOUR_TOKEN is an access token of your account, or the session token of the console. The REST reference lists the endpoint as GET /v2/users/{user}/events.
| Query parameter | Required | Content |
|---|---|---|
authorization | Yes, unless the token travels in an Authorization: Bearer header or an authorization cookie | The token. Browsers cannot set headers on a WebSocket, so they pass it here. |
project | No | A project ID. The connection works inside that project: for project members, and for access tokens bound to projects, which must use it. |
The server refuses the connection with 401 when the token is not valid or has expired, and with 403 when the token cannot subscribe to events of that account or project.
One connection carries any number of subscriptions. You send JSON messages to subscribe and unsubscribe; the server answers each one, and then sends the matching events.
Subscribe
Send a register message with the event name and its filters:
{
"action": "register",
"event": "device_state_change",
"filters": { "device": "YOUR_DEVICE" }
}| Field | Content |
|---|---|
action | register. |
event | An event name from the catalog. |
filters | Fields of the event and the values to match, for example { "device": "YOUR_DEVICE", "state": "connected" }. Always send it; use {} to receive every event of that name. See Filters. |
params | Optional. { "interval": <milliseconds> } limits how often stream data events (such as device_resource_stream with signal data) reach this subscription. |
update | Optional. true replaces the filters and params of an existing subscription with the same filters on this connection, instead of adding another one. |
The server answers with success and the message it received in request. registered maps the event name to the ID of the new subscription:
{
"success": true,
"request": {
"action": "register",
"event": "device_state_change",
"filters": { "device": "YOUR_DEVICE" }
},
"registered": { "device_state_change": 3 }
}Unsubscribe
Send unregister with the event name. With filters, only the subscriptions with those same filters are removed; without them, every subscription of this connection to that event:
{
"action": "unregister",
"event": "device_state_change",
"filters": { "device": "YOUR_DEVICE" }
}{
"success": true,
"request": { "action": "unregister", "event": "device_state_change", "filters": { "device": "YOUR_DEVICE" } },
"unregistered": { "device_state_change": 3 }
}To remove every subscription of the connection, send clear. The answer lists what was removed, also under unregistered:
{ "action": "clear" }{
"success": true,
"request": { "action": "clear" },
"unregistered": { "device_state_change": [3], "bucket_write": [4] }
}Errors
A refused message gets success: false and a message:
{
"success": false,
"request": { "action": "register", "event": "bucket_write", "filters": { "bucket": "climate" } },
"message": "action not allowed"
}message | Cause |
|---|---|
invalid JSON input | The message is not valid JSON (request is null). |
required a JSON object | The message is JSON, but not an object. |
invalid action | action is not register, unregister or clear. |
invalid event name | event is missing or empty. |
requested event name cannot be mapped to permissions | The token or project member subscribes to an event that is not in the permissions table, including unknown event names. |
not enabled, no actions allowed, action not allowed | The token or project member lacks the permission the event needs. |
subscriptions over a project require a '…' filter | Inside a project, the event needs a device, bucket or rule filter. |
the '…' filter must be a resource identifier, or several separated by '|' | Inside a project, the filter is not a plain resource ID. |
… '…' does not belong to the project | Inside a project, the device, bucket or rule of the filter is not in the project, or not in the member's groups. |
When the account owner subscribes to an event name that does not exist, the answer is success: false with registered: null and no message.
Event messages
Each event arrives as one JSON message: the fields of the event, plus event, ts (milliseconds since the epoch) and user. For example, a device_state_change:
{
"device": "YOUR_DEVICE",
"state": "connected",
"event": "device_state_change",
"ts": 1760700000000,
"user": "YOUR_USER"
}Lifecycle events (device_create, bucket_update…) carry the document of the resource; _delete events carry its ID. Events of the Server administrator scope do not carry user.
Filters
Filter values are regular expressions that must match the whole value of the field: "device": "sensor_.*" matches every device whose ID starts with sensor_. An event without the filtered field does not match. When the field is a list, the event matches if any element does.
Escape the regular expression characters \ ^ $ . | ? * + ( ) [ ] { } of a literal ID: a filter "device": "sensor.1" also matches sensor_1. Inside a project, filters take only plain resource IDs, or several joined with | ("device": "sensor_1|sensor_2").
Permissions
What a connection can subscribe to depends on its token:
| Connection | Can subscribe to |
|---|---|
| The account owner, with the console session token | Every event of the Account scope. Events of the Server administrator scope need a server administrator. |
| An access token | Only the events below, with the permission each one needs. |
A project member, or an access token with project | Only the events below, with the member's permissions (and the token's, for a member's token), and the filter they require inside the project. |
| Event | Permission | Filter required inside a project |
|---|---|---|
bucket_write | Bucket:ReadBucket on the bucket of the bucket filter, or Device:ReadDeviceBucket on the device of the device filter | device or bucket |
device_property_update | Device:ReadDeviceProperty on the device of the device filter | device |
device_state_change | Device:ReadDeviceConfig on the device of the device filter | device |
alarm_instance_activate, alarm_instance_normalize, alarm_instance_create, alarm_instance_update, alarm_instance_delete | Alarm:ListAlarmInstances on all alarms (*) | None: the events are limited to the project |
alarm_rule_execute | Alarm:ListAlarmRules on all alarm rules (*) | rule |
A permission on one resource needs the matching filter: bucket_write with Bucket:ReadBucket on the bucket climate works with "filters": { "bucket": "climate" }, not with {}. Access tokens and their permissions are managed in Access Tokens.
Catalog
The public catalog GET /v1/server/events returns the name, filter fields and scope of every event the server can send. It needs no token and is not a stream: read it to discover the events and their filter values.
This is the catalog of Thinger.io 7.4. It lists 123 events: 24 activity events and the lifecycle events of 33 resource types.
Scope says who receives the event: Account events belong to the account that subscribes; Server administrator events are about the whole server and need a server administrator.
Activity events
Values lists the values a filter field takes. Each one is described in Activity events in detail.
| Event | Fires when | Filters | Values | Scope |
|---|---|---|---|---|
alarm_instance_activate | An alarm instance becomes active. | instance | Account | |
alarm_instance_normalize | The condition of an active alarm instance returns to normal. | instance | Account | |
alarm_rule_execute | An alarm rule is evaluated. | rule | Account | |
bucket_export_completed | An export of a Data Bucket finishes and its file is ready. | bucket, file | Account | |
bucket_export_failed | An export of a Data Bucket fails. | bucket, file | Account | |
bucket_export_started | An export of a Data Bucket starts. | bucket, file | Account | |
bucket_write | A record is written to a Data Bucket. | bucket | Account | |
claim_completed | A device is claimed. | claim | Account | |
device_authentication_failed | A device tries to connect with wrong credentials. | device | Account | |
device_callback_call | An HTTP device sends data to its callback. | device, product | Account | |
device_geofence_event | A device enters or leaves a geofence, or stays inside or outside it. | device, geofence, geofence_event, product | geofence_event: onEnter, onExit, whileInside, whileOutside | Account |
device_location_update | The location of a device changes. | device, product | Account | |
device_resource_request_failed | A call to a device resource fails. | device, product, resource | Account | |
device_resource_stream | A stream of a device resource starts, stops, sends data or fails. | device, product, resource, signal | signal: start, stop, data, error | Account |
device_state_change | A device connects, disconnects, resumes, wakes up or goes to sleep. | device, product, state | state: connected, disconnected, resumed, awake, asleep | Account |
device_stats_data | The connection statistics of a connected device change (traffic, connection time). | device, product | Account | |
device_topic_publish | An MQTT device publishes to a topic. | device, product, topic | Account | |
device_topic_subscribe | An MQTT device subscribes to a topic. | device, product, topic | Account | |
device_topic_unsubscribe | An MQTT device unsubscribes from a topic. | device, product, topic | Account | |
endpoint_call | An endpoint is called. | endpoint, type | type: email, http_request, virtual | Account |
endpoint_call_response | An endpoint call finishes, successfully or not. | endpoint, type, status, status_code | type: email, http_request, virtualstatus: success, failure | Account |
plugin_action | A plugin is started, stopped, upgraded or changes its state. | plugin, action | action: start, restart, pause, unpause, stop, kill, create, upgrade, remove, public, private | Account |
product_script_log | A script of a product writes to its log. | product | Account | |
product_stream | Data is sent to a stream of a product. | product, stream | Account |
Lifecycle events
Each resource type emits <type>_create, <type>_update and <type>_delete when a resource of that type is created, changed or deleted. Their filters are the IDs of the resource.
| Resource type | Events | Filters | Scope |
|---|---|---|---|
alarm_instance | alarm_instance_create, alarm_instance_update, alarm_instance_delete | instance | Account |
alarm_rule | alarm_rule_create, alarm_rule_update, alarm_rule_delete | rule | Account |
brand | brand_create, brand_update, brand_delete | brand | Server administrator |
bucket | bucket_create, bucket_update, bucket_delete | bucket | Account |
claim | claim_create, claim_update, claim_delete | claim | Account |
dashboard | dashboard_create, dashboard_update, dashboard_delete | dashboard | Account |
device | device_create, device_update, device_delete | device | Account |
device_bucket | device_bucket_create, device_bucket_update, device_bucket_delete | device, bucket | Account |
device_property | device_property_create, device_property_update, device_property_delete | device, property | Account |
domain | domain_create, domain_update, domain_delete | domain | Server administrator |
endpoint | endpoint_create, endpoint_update, endpoint_delete | endpoint | Account |
group | group_create, group_update, group_delete | group | Account |
group_property | group_property_create, group_property_update, group_property_delete | group, property | Account |
host | host_create, host_update, host_delete | host | Server administrator |
identity_provider | identity_provider_create, identity_provider_update, identity_provider_delete | identity_provider | Server administrator |
oauth_client | oauth_client_create, oauth_client_update, oauth_client_delete | oauth_client | Server administrator |
plugin_property | plugin_property_create, plugin_property_update, plugin_property_delete | plugin, property | Account |
product | product_create, product_update, product_delete | product | Account |
product_property | product_property_create, product_property_update, product_property_delete | product, property | Account |
project | project_create, project_update, project_delete | project | Account |
project_member | project_member_create, project_member_update, project_member_delete | project, member | Account |
project_property | project_property_create, project_property_update, project_property_delete | project, property | Account |
project_role | project_role_create, project_role_update, project_role_delete | project, role | Account |
property | property_create, property_update, property_delete | property | Account |
proxy | proxy_create, proxy_update, proxy_delete | proxy | Server administrator |
role | role_create, role_update, role_delete | role | Account |
storage | storage_create, storage_update, storage_delete | storage | Account |
sync | sync_create, sync_update, sync_delete | sync | Account |
sync_lock | sync_lock_create, sync_lock_update, sync_lock_delete | sync, lock | Account |
token | token_create, token_update, token_delete | token | Account |
type | type_create, type_update, type_delete | type | Account |
type_property | type_property_create, type_property_update, type_property_delete | type, property | Account |
user | user_create, user_update, user_delete | user | Server administrator |
Activity events in detail
alarm_instance_activate
An alarm instance becomes active.
Filters: instance.
alarm_instance_normalize
The condition of an active alarm instance returns to normal.
Filters: instance.
alarm_rule_execute
An alarm rule is evaluated.
Filters: rule.
bucket_export_completed
An export of a Data Bucket finishes and its file is ready.
Filters: bucket, file.
bucket_export_failed
An export of a Data Bucket fails.
Filters: bucket, file.
bucket_export_started
An export of a Data Bucket starts.
Filters: bucket, file.
bucket_write
A record is written to a Data Bucket.
Filters: bucket.
claim_completed
A device is claimed.
Filters: claim.
device_authentication_failed
A device tries to connect with wrong credentials.
Filters: device.
device_callback_call
An HTTP device sends data to its callback.
Filters: device, product.
device_geofence_event
A device enters or leaves a geofence, or stays inside or outside it.
Filters: device, geofence, geofence_event, product.
Values of geofence_event: onEnter, onExit, whileInside, whileOutside.
device_location_update
The location of a device changes.
Filters: device, product.
device_resource_request_failed
A call to a device resource fails.
Filters: device, product, resource.
device_resource_stream
A stream of a device resource starts, stops, sends data or fails.
Filters: device, product, resource, signal.
Values of signal: start, stop, data, error.
device_state_change
A device connects, disconnects, resumes, wakes up or goes to sleep.
Filters: device, product, state.
Values of state: connected, disconnected, resumed, awake, asleep.
device_stats_data
The connection statistics of a connected device change (traffic, connection time).
Filters: device, product.
device_topic_publish
An MQTT device publishes to a topic.
Filters: device, product, topic.
device_topic_subscribe
An MQTT device subscribes to a topic.
Filters: device, product, topic.
device_topic_unsubscribe
An MQTT device unsubscribes from a topic.
Filters: device, product, topic.
endpoint_call
An endpoint is called.
Filters: endpoint, type.
Values of type: email, http_request, virtual.
endpoint_call_response
An endpoint call finishes, successfully or not.
Filters: endpoint, type, status, status_code.
Values of type: email, http_request, virtual.
Values of status: success, failure.
plugin_action
A plugin is started, stopped, upgraded or changes its state.
Filters: plugin, action.
Values of action: start, restart, pause, unpause, stop, kill, create, upgrade, remove, public, private.
product_script_log
A script of a product writes to its log.
Filters: product.
product_stream
Data is sent to a stream of a product.
Filters: product, stream.
Reconnect
The server does not keep events for disconnected clients: events emitted while the socket is closed are lost, and the subscriptions end with the connection. After a reconnection, send the register messages again and read the current state (device status, last bucket records) through the REST API to fill the gap.
The server sends a WebSocket ping after 60 seconds without messages from the client and closes the connection if the client does not answer. Standard WebSocket clients answer pings on their own.
Examples
YOUR_USER is your username and YOUR_DEVICE the ID of one of your devices.
const url = 'wss://YOUR_SERVER/v2/users/YOUR_USER/events?authorization=YOUR_TOKEN';
function connect() {
const ws = new WebSocket(url);
ws.onopen = () => {
ws.send(JSON.stringify({
action: 'register',
event: 'device_state_change',
filters: { device: 'YOUR_DEVICE' },
}));
};
ws.onmessage = (message) => {
const data = JSON.parse(message.data);
if ('success' in data) {
if (!data.success) console.error('Refused:', data.message ?? data);
return;
}
console.log(`${data.device} is ${data.state}`);
};
// Subscriptions end with the connection: connect and register again
ws.onclose = () => setTimeout(connect, 5000);
}
connect();echo '{"action":"register","event":"device_state_change","filters":{"device":"YOUR_DEVICE"}}' \
| websocat -n "wss://YOUR_SERVER/v2/users/YOUR_USER/events?authorization=YOUR_TOKEN"The first message you receive is the answer with registered; connect or disconnect the device to receive the event. The command-line example uses websocat.
Device resource streams
A separate WebSocket streams the resources of one connected device, such as a sensor reading every few seconds:
wss://YOUR_SERVER/v3/users/YOUR_USER/devices/YOUR_DEVICE/resources/<resource>?authorization=YOUR_TOKEN&interval=5000The token needs Device:AccessDeviceResources on the device. Without /<resource>, one socket streams several resources of the device, which you choose with messages.
| Query parameter | Content |
|---|---|
interval | Sampling interval in milliseconds, for a single resource. 0 or none: the device sends its data when it produces it. |
raw | Present (no value): the socket carries only the resource data, as text or binary, without the event fields. Needs a resource in the URL. |
signals | Comma-separated signals to receive: start, stop, data, error. By default data,error. |
Each message is a device_resource_stream event (device, resource, signal, payload, ts…), or only the payload in raw mode. On a socket for several resources, send { "resource": "<resource>", "interval": <seconds> } to start or change a stream (in these messages interval is in seconds, not milliseconds as in the URL), { "resource": "<resource>", "enabled": false } to stop it, and { "resource": "<resource>", "payload": … } to send input to it.
Problems with a message arrive as { "error": "<message>" }, for example { "error": "device is not connected" } or { "error": "cannot access this device resource with the supplied credentials" }. If the device is not connected when you open the socket, the server answers 404 with device not available, and the socket closes when the device disconnects.
Deprecated endpoints
| Endpoint | Use instead |
|---|---|
GET /v1/users/{user}/events (WebSocket) | GET /v2/users/{user}/events. The v1 socket sends no answers and has no unregister or clear. |
GET /v1/users/{user}/devices/{device}/stats (server-sent events with the device status) | The device_state_change event on GET /v2/users/{user}/events. |
See also
- Dashboard SDK:
Thinger.events.subscribeand the live functions of the SDK use this endpoint. - REST API
- Access Tokens