Skip to main content

What are signals?

A signal is an event emitted by the state machine when something changes about a track or a zone. Every item that arrives at your workflow’s trigger node includes a signal field indicating what happened. There are two families of signals depending on your trigger configuration:
  • Track state signalstrack_created, track_updated, track_expired
  • Zone state signalszone_occupied, zone_updated, zone_empty

Track state signals

Track state signals fire for individual track lifecycle events. There are three types:

track_created

Fired once when a new track is first detected. This is the first time the state machine has seen this object. At this point the track state is minimal — there’s only one detection, so velocity is zero, dwell time is zero, and zone data may be sparse. Common use: Most workflows do not act on track_created because there isn’t enough data to make meaningful decisions yet.

track_updated

Fired on every subsequent detection after the track is created. This is by far the most common signal — a track that lasts 5 minutes at 1 detection/second generates ~300 track_updated signals. Each update includes the fully enriched track state with current velocity, dwell times, zone intersections, and more. The data gets richer over time as more detections accumulate. Common use: This is where most business logic runs. Your checks evaluate the current state on each update and the Event Orchestrator decides whether conditions warrant creating or closing an event.

track_expired

Fired once when the state machine hasn’t received a detection for this track within the TTL window (default: 15 seconds). The track is considered gone. The expired signal includes the final track state — the last known position, total dwell times, complete zone history, and final velocity measurements. Common use: Used for “track summary” workflows that only care about the complete lifecycle. Also used by the Event Orchestrator to close events when a track disappears. Batch processing workflows typically emit data only on track expiration.

Signal flow example

Time 0s:   Object detected     → signal: "track_created"
Time 1s:   Object re-detected  → signal: "track_updated"  (velocity: 5 px/s)
Time 2s:   Object enters zone  → signal: "track_updated"  (zone active, dwell: 0s)
...
Time 300s: Object still in zone → signal: "track_updated" (dwell: 298s, velocity: 0.5 px/s)
Time 301s: Object still in zone → signal: "track_updated" (dwell: 299s) ← check threshold met!
...
Time 500s: Object leaves frame  → (no more detections)
Time 515s: TTL expires          → signal: "track_expired"  (final state)

Filtering signals in your workflow

The Detection Webhook Trigger delivers all three signal types by default. You can use the signal type in your workflow logic — for example, the Event Orchestrator uses track_expired to know when to close events. If you only want to process certain signals, you can add an n8n IF node after the trigger to filter:
// Only process updates (skip created and expired)
{{ $json.signal === "track_updated" }}
Most workflows process all signals and let the check nodes and Event Orchestrator handle the logic. You typically don’t need to manually filter signals.

Zone state signals

Zone state signals fire when the occupancy of a monitored zone changes. They are available only in streaming mode with the signal type set to Zone State.

zone_occupied

Fired once when the first track enters a previously empty zone. The zone transitions from 0 to 1+ active tracks. Common use: Triggers the start of zone monitoring. The Zone Activity Check may not pass yet if only one track is present.

zone_updated

Fired on every change to the zone’s active track list — a track enters, a track exits, or an existing track’s state updates (new detection, changed position). This is the most common zone signal. A busy zone can generate many zone_updated signals as tracks move in and out. Common use: This is where zone-level business logic runs. The Zone Activity Check evaluates the current track count and thresholds on each update.

zone_empty

Fired once when the last track leaves the zone. The zone transitions back to 0 active tracks. Common use: Used by the Event Orchestrator to close zone-scoped events when the zone clears.

Zone signal flow example

Time 0s:   First forklift enters zone  → signal: "zone_occupied"  (track_count: 1)
Time 10s:  Second forklift enters      → signal: "zone_updated"   (track_count: 2)
Time 25s:  Third forklift enters       → signal: "zone_updated"   (track_count: 3)
Time 30s:  Fourth forklift enters      → signal: "zone_updated"   (track_count: 4) ← congestion threshold met!
Time 60s:  First forklift leaves       → signal: "zone_updated"   (track_count: 3) ← below threshold, close event
Time 90s:  All forklifts leave         → signal: "zone_empty"     (track_count: 0)

Zone state payload

Each zone signal includes the full list of active tracks in the zone:
{
  "signal": "zone_updated",
  "zone_state": {
    "zone_id": "92",
    "zone_name": "Aisle-Zone-West",
    "datasource_id": "2ba0f384-...",
    "datasource_name": "Shortblock_LB1 - West View",
    "active_tracks": {
      "track-uuid-1": {
        "tag": "forklift",
        "entered_at": "2026-02-19T18:35:59.803Z",
        "dwell_time": 25.0,
        "detection_count": 7,
        "intersection": { "current_percent": 45.3, "avg_percent": 82.3 }
      }
    },
    "track_count": 4,
    "updated_at": "2026-02-19T18:36:40.464Z",
    "trigger_track_id": "track-uuid-2",
    "trigger_tag": "tugger"
  }
}
FieldDescription
active_tracksMap of all tracks currently in the zone, with per-track dwell time, intersection, and motion data
track_countTotal number of active tracks
trigger_track_idThe specific track that caused this zone update (the one that entered, exited, or updated)
trigger_tagObject type of the trigger track