Event types reference
Complete reference for event payloads generated by Blecon Network.
Common envelope
All events use the CloudEvents 1.0 specification:
{
"type": "network.device_spotted",
"source": "blecon",
"specversion": "1.0",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"time": "2026-02-11T15:34:05.123456",
"datacontenttype": "application/json",
"data": { }
}
| Field | Type | Description |
|---|---|---|
type |
string | Event type (see sections below) |
source |
string | Always "blecon" |
specversion |
string | Always "1.0" |
id |
string | Unique event identifier (UUID) |
time |
string | ISO 8601 timestamp (UTC) |
datacontenttype |
string | Always "application/json" |
data |
object | Event-specific payload |
Events are delivered in batches; a single webhook POST contains a JSON array of CloudEvent objects.
Spotted event (network.device_spotted)
Generated when a hotspot detects a tag.
{
"type": "network.device_spotted",
"source": "blecon",
"specversion": "1.0",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"time": "2026-02-11T15:34:05.123456",
"datacontenttype": "application/json",
"data": {
"device_id": "urn:uuid:9f3e2a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
"network_id": "net_abc123",
"account_id": "acc_def456",
"code": "DEVICE_SPOTTED",
"rssi": -68,
"location": "52.125039,-0.161111",
"location_accuracy_metres": 10.0,
"ap_id": "ap_789012"
}
}
| Data field | Type | Description |
|---|---|---|
device_id |
string | Tag identifier (URN format) |
network_id |
string | Network the tag belongs to |
account_id |
string | Account ID |
code |
string | Always "DEVICE_SPOTTED" |
ap_id |
string | Hotspot/access point identifier |
hotspot_id |
string | Managed hotspot ID (only if the hotspot is registered to your account) |
rssi |
integer | Signal strength in dBm |
location |
string | Hotspot GPS position as "lat,lon" |
location_accuracy_metres |
float | Hotspot position accuracy |
location_age |
float | Age of hotspot's location data in seconds |
advertising_data |
string | Raw BLE advertising data (hex encoded) |
status_data |
object | Decoded tag status data, if present (see below) |
Status data
Tags that broadcast an encrypted status payload (NeoBeacon) deliver it as a
status_data object rather than a bare string. data is always the raw payload;
the other fields appear only when the tag reports them.
| Field | Type | Description |
|---|---|---|
data |
string | Raw status payload (hex encoded) |
authenticated |
boolean | The payload's authentication code was verified |
encrypted |
boolean | The payload was encrypted |
scheme |
string | Payload format, e.g. neobeacon-v7, neobeacon-v9 |
temperature_celsius |
float | Temperature reading, if the tag has a sensor |
battery_voltage_mv |
integer | Tag supply voltage in millivolts |
battery_state |
string | ok, or low when the cell is past its end-of-life voltage |
battery_remaining_weeks |
integer | Estimated whole weeks of operation left, rounded down. 0 means less than one week. Only for tags whose cell is characterised, and only when battery_state is ok. An estimate from a discharge model, not a measurement |
First seen event (network.device_first_seen)
Generated the first time a tag is detected after registration. Same payload structure as network.device_spotted.
Position event (network.device_position)
Generated when the positioning engine calculates a tag's location.
{
"type": "network.device_position",
"source": "blecon",
"specversion": "1.0",
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"time": "2026-02-11T15:34:05.123456",
"datacontenttype": "application/json",
"data": {
"device_id": "urn:uuid:9f3e2a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
"network_id": "net_abc123",
"account_id": "acc_def456",
"code": "POSITION_UPDATED",
"sample_time": "2026-02-11T15:34:03.000000Z",
"calculated_at": "2026-02-11T15:34:05.123456Z",
"trigger": "movement",
"position": {
"coordinate_system": "local",
"x": 10.5,
"y": 20.3,
"accuracy_meters": 8.0,
"num_hotspots": 1
},
"quality": {
"has_position": true,
"accuracy_tier": "medium",
"confidence": 0.85,
"measurement_duration_seconds": 5.2,
"selected_hotspot_id": "1f0a7c2e-9b3d-4e5f-8a6b-2c4d6e8f0a1b"
},
"geojson": {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0.129528, 52.234794]
},
"properties": {
"device_id": "urn:uuid:9f3e2a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
"accuracy_meters": 8.0,
"timestamp": "2026-02-11T15:34:05.123456+00:00"
}
},
"zone": {
"zone_id": "zone_abc123",
"zone_name": "Warehouse B",
"confidence": 0.95,
"beacons_detected": 1,
"avg_rssi": -65.3,
"lat": 52.234794,
"lon": 0.129528
}
}
}
| Data field | Type | Description |
|---|---|---|
device_id |
string | Tag identifier |
network_id |
string | Network the tag belongs to |
account_id |
string | Account ID |
code |
string | Always "POSITION_UPDATED" |
sample_time |
string | When signal measurements were taken |
calculated_at |
string | When the position was calculated |
trigger |
string | Why this update was generated (see below) |
historical |
boolean | Present and true when this is a backfilled position, older than the newest already delivered. Do not update "current position" state from it (see the historical trigger below) |
position |
object | Calculated position |
quality |
object | Accuracy metadata. Includes selected_hotspot_id: the hotspot whose detection produced this position |
geojson |
object | GeoJSON Feature with GPS coordinates (when available) |
zone |
object | Zone information (when the tag is in a zone) |
explanation |
object | Why the position is where it is: every hotspot that heard the tag in this window, and what became of it (see below) |
Position object
| Field | Type | Description |
|---|---|---|
coordinate_system |
string | "local" or "gps" |
x |
float | X coordinate (local) or longitude (GPS) |
y |
float | Y coordinate (local) or latitude (GPS) |
accuracy_meters |
float | Estimated accuracy in metres |
num_hotspots |
integer | Number of hotspots used in calculation |
Quality object
| Field | Type | Description |
|---|---|---|
has_position |
boolean | Whether a position was calculated |
accuracy_tier |
string | "high", "medium", or "low" |
confidence |
float | Position confidence score (0–1) |
measurement_duration_seconds |
float | Time span of measurements used |
Zone object
| Field | Type | Description |
|---|---|---|
zone_id |
string | Zone identifier |
zone_name |
string | Human-readable zone name |
confidence |
float | Zone assignment confidence |
beacons_detected |
integer | Number of tags/hotspots detected |
avg_rssi |
float | Average signal strength in the zone |
lat |
float | Zone reference latitude |
lon |
float | Zone reference longitude |
Explanation object
The explanation lists the evidence behind one position. Each position event has its own envelope id. Quote that id when you ask us about a position: it finds the calculation that produced it.
| Field | Type | Description |
|---|---|---|
v |
integer | Format version. Currently 1 |
window |
object | sightings: detections of the tag in this window. hotspots: how many hotspots made them |
method |
string | How the position was calculated: proximity (at the chosen hotspot), device_resolved (the device reported its own position) or zone (zone only, no coordinates) |
accuracy_m |
float | Accuracy of the position, in metres |
proximity |
string | How close the tag was to the chosen hotspot: immediate (under 0.5 m), near (0.5 to 3 m), far (over 3 m) or unknown |
zone |
object | The zone evidence, with the fields of the zone object above. Also, when they apply: candidates (every zone that competed, with its score and hotspots; the highest score wins), held (true when the reported zone is kept from an earlier window because a change has not lasted long enough yet), detected (the zone this window alone detected, or none), pending and pending_since (the change being waited on, and since when) |
hotspots |
array | One entry for each hotspot that heard the tag. The chosen hotspot comes first. At most 12 entries |
more |
integer | Present when more than 12 hotspots heard the tag: the number of entries left out. Only rejected hotspots are left out |
emit |
object | Why this position was published. kind: movement (with distance_m from the last position and floor_m, the least move that counts), accuracy_improvement (from_m, to_m), zone_change (from_zone, to_zone), heartbeat (a regular update; heartbeat_s), historical (a late position), first_position, departure or state_change. reason is the same value as trigger |
Each entry in hotspots:
| Field | Type | Description |
|---|---|---|
outcome |
string | selected (the position comes from this hotspot), candidate (considered, not chosen) or rejected (not considered) |
reason |
string | For a rejected hotspot: rssi_below_cutoff (the signal was below the network's signal cutoff; only on a network that sets one, since no positioning profile does) or stale_location (the hotspot's own location was too old) |
rssi |
float | Signal strength in dBm. For a candidate, the median of its detections in the window |
sightings |
integer | Detections of the tag by this hotspot in the window |
effective_accuracy_m |
float | How well the hotspot's own location was known, in metres, allowing for its age |
score |
float | Selection score. The lowest score is chosen |
id |
string | Hotspot ID. Only for hotspots of your network |
These fields are present only for hotspots of your network:
| Field | Type | Description |
|---|---|---|
location_from |
string | Where the hotspot's location came from: configured (a position you set, or one synced from Mist), fix_stream (the hotspot's best recent location report), snapshot (the last stored location, used when no recent report exists) or device (the device reported its own position) |
fix_source |
string | The kind of report: RESOLVED_LOCATION (the Agent app's GPS), SPOTTED (a location sent with a sighting), GOOGLE_GEOLOCATE (a hub located by Wi-Fi and cell), REFBEACON (placed by an anchor it heard), mist_api or mist_site_address (a Mist access point position), or, for a configured hotspot, CONFIGURED or MIST |
fix_time |
string | When the location was measured, by the hotspot's clock |
fix_accuracy_m, fix_age_s |
float, integer | The location's stated accuracy in metres, and its age at the time of the sighting |
speed_mps, speed_basis |
float, string | The speed used to age the location, and where it came from: reported, window_travel (measured from the hotspot's recent movement, because it claimed to be still), stationary_hub or surveyed |
anchor_refined |
boolean | true when a better location report arrived after the sighting and replaced the one first used |
anchor_beacon |
string | For REFBEACON: the anchor that placed the hotspot |
zone_id, zone_source |
string | The zone this sighting counted for, and why: config (the hotspot's configured zone) or beacon (an anchor the hotspot heard at about the same time) |
zone_beacon, zone_verdict |
string | The anchor behind a beacon zone, and how its point compared with the hotspot's location: agree, zone_won (they disagreed and the anchor was the tighter evidence), location_won (they disagreed and the location was tighter, so the zone was not used) or unchecked (nothing to compare) |
zone_rejected |
string | For location_won: the zone that was not used |
external |
boolean | true for an unregistered hotspot: a phone or hub that is not registered in your workspace. It has no id and no location detail |
A hotspot is part of your network when it is an Agent registered as a device of the network, or a hub or access point that your account claimed.
Trigger values
| Value | Description |
|---|---|
first_position |
First position recorded for this tag |
movement_Xm |
Tag moved beyond the movement threshold (X = distance in metres) |
heartbeat |
Periodic update: heartbeat interval elapsed with no movement or zone change |
zone_change |
Tag entered or left a network zone |
accuracy_improvement_Xm_to_Ym |
Position accuracy improved by at least 2× vs the last emitted position (X = previous accuracy, Y = new accuracy, both in metres) |
departure |
Tag is no longer being detected |
historical |
Backfilled position: the sample is older than the newest position already delivered (for example, a hotspot uploaded queued sightings after being offline). The event also carries historical: true. If you keep your own "current position" state, do not update it from these events — use them for history only. |
Tag message (tag.message.<namespace>.<method>)
Generated when a tag sends a one-way message (no response expected). The type field is constructed from the namespace and method, for example, a temperature sensor reading uses tag.message.sensor.temperature.
{
"type": "tag.message.sensor.temperature",
"source": "blecon",
"specversion": "1.0",
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"time": "2026-02-11T15:34:05.123456",
"datacontenttype": "application/json",
"data": {
"device_id": "urn:uuid:9f3e2a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
"network_id": "net_abc123",
"account_id": "acc_def456",
"text": "Test event from console",
"payload": "1a2b3c4d",
"request_content_type": "application/octet-stream"
}
}
| Data field | Type | Description |
|---|---|---|
device_id |
string | Tag identifier |
network_id |
string | Network ID |
account_id |
string | Account ID |
payload |
string | Message payload (hex encoded) |
request_content_type |
string | Content type of the payload |
text |
string | Human-readable message text (if present) |
Tag request (tag.request.<namespace>.<method>)
Generated when a tag sends a two-way request (expecting a response). The event is routed to the first matching route, and the webhook response is forwarded back to the tag. Same data fields as tag messages.
{
"type": "tag.request.config.update",
"source": "blecon",
"specversion": "1.0",
"id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"time": "2026-02-11T15:34:05.123456",
"datacontenttype": "application/json",
"data": {
"device_id": "urn:uuid:9f3e2a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
"network_id": "net_abc123",
"account_id": "acc_def456",
"payload": "7b22666972...6d776172657d",
"request_content_type": "application/octet-stream"
}
}
Registration events
Tag registered (network.device_registered)
{
"type": "network.device_registered",
"source": "blecon",
"specversion": "1.0",
"id": "e5f6a7b8-c9d0-1234-efab-345678901234",
"time": "2026-02-11T15:34:05.123456",
"datacontenttype": "application/json",
"data": {
"device_id": "urn:uuid:9f3e2a1b-4c5d-6e7f-8a9b-0c1d2e3f4a5b",
"network_id": "net_abc123",
"account_id": "acc_def456"
}
}
Tag deregistered (network.device_deregistered)
Same structure as network.device_registered.
Error events
| Event type | Description |
|---|---|
network.request_failed |
Tag request failed at protocol level |
network.routing_failed |
No matching route for tag request |
tag.request_failed |
Tag request processing failed |
Webhook delivery format
Webhooks receive events as a JSON array (batch of 1–50 CloudEvent objects):
[
{
"type": "network.device_spotted",
"source": "blecon",
"specversion": "1.0",
"id": "event-1-uuid",
"time": "2026-02-11T15:34:05.123456",
"datacontenttype": "application/json",
"data": { }
},
{
"type": "network.device_spotted",
"source": "blecon",
"specversion": "1.0",
"id": "event-2-uuid",
"time": "2026-02-11T15:34:06.234567",
"datacontenttype": "application/json",
"data": { }
}
]
Your webhook endpoint should return a 200 status code to acknowledge receipt. Failed deliveries are retried with exponential backoff.