Skip to main content

When to Use Action Models

Action models (COLLAPSED models) are templates for structured data that you publish explicitly from within actions. They’re ideal when you need:
  • Control over timing - Publish only when specific conditions are met
  • Dynamic routing - Send data to different topics based on logic
  • Data transformation - Process and enrich data before structuring
Unlike basic models that trigger automatically on topic changes, action models let you decide exactly when and where structured data is created.

Example Use Case

Imagine you receive raw alarm data and need to:
  1. Extract relevant fields from JSON
  2. Add a unique ID and timestamp
  3. Route critical alarms to a special topic
  4. Publish a structured record
DEFINE MODEL AlarmRecord COLLAPSED
    ADD STRING "alarm_id"
    ADD STRING "equipment_id"
    ADD STRING "severity"
    ADD STRING "timestamp"

DEFINE ACTION ProcessAlarm
ON TOPIC "alarms/+/input" DO
    SET "equipment_id" WITH TOPIC POSITION 2
    SET "severity" WITH (GET JSON "severity" IN PAYLOAD AS STRING)
    
    PUBLISH MODEL AlarmRecord TO "alarms/structured/" + {equipment_id} WITH
        alarm_id = (RANDOM UUID)
        equipment_id = {equipment_id}
        severity = {severity}
        timestamp = TIMESTAMP "UTC"
    
    IF {severity} EQUALS "CRITICAL" THEN
        PUBLISH TOPIC "alerts/critical/" + {equipment_id} WITH PAYLOAD

Step-by-Step Guide

1

Define a COLLAPSED Model

Create a template with all required fields. The COLLAPSED keyword indicates this model has no automatic trigger:
DEFINE MODEL SensorRecord COLLAPSED
    ADD STRING "sensor_id"
    ADD STRING "sensor_type"
    ADD DOUBLE "temperature"
    ADD DOUBLE "humidity"
    ADD STRING "quality_status"
    ADD STRING "timestamp"
2

Create the Processing Action

Define an action that extracts data and publishes the model:
DEFINE ACTION ProcessSensorJSON
ON TOPIC "sensors/+/json_data" DO
    SET "sensor_id" WITH TOPIC POSITION 2
    
    // Extract from JSON payload
    SET "temp" WITH (GET JSON "temperature" IN PAYLOAD AS DOUBLE)
    SET "hum" WITH (GET JSON "humidity" IN PAYLOAD AS DOUBLE)
    SET "type" WITH (GET JSON "type" IN PAYLOAD AS STRING)
    SET "quality" WITH (GET JSON "quality" IN PAYLOAD AS STRING)
    
    // Publish structured record
    PUBLISH MODEL SensorRecord TO "sensors/processed/" + {sensor_id} WITH
        sensor_id = {sensor_id}
        sensor_type = {type}
        temperature = {temp}
        humidity = {hum}
        quality_status = {quality}
        timestamp = TIMESTAMP "UTC"
3

Test the Flow

Publish to: sensors/temp001/json_data
{
  "temperature": 23.5,
  "humidity": 65,
  "type": "environmental",
  "quality": "GOOD"
}
Result at: sensors/processed/temp001
{
  "sensor_id": "temp001",
  "sensor_type": "environmental",
  "temperature": 23.5,
  "humidity": 65,
  "quality_status": "GOOD",
  "timestamp": "2025-10-25T14:30:15Z"
}

Use Case Examples

Route alarms by severity while creating structured records:
DEFINE MODEL AlarmRecord COLLAPSED
    ADD STRING "alarm_id"
    ADD STRING "equipment_id"
    ADD STRING "alarm_type"
    ADD DOUBLE "trigger_value"
    ADD STRING "severity"
    ADD STRING "timestamp"

DEFINE ACTION ProcessAlarmJSON
ON TOPIC "alarms/+/input" DO
    SET "equipment_id" WITH TOPIC POSITION 2
    
    PUBLISH MODEL AlarmRecord TO "alarms/structured/" + {equipment_id} WITH
        alarm_id = (RANDOM UUID)
        equipment_id = {equipment_id}
        alarm_type = (GET JSON "type" IN PAYLOAD AS STRING)
        trigger_value = (GET JSON "value" IN PAYLOAD AS DOUBLE)
        severity = (GET JSON "severity" IN PAYLOAD AS STRING)
        timestamp = TIMESTAMP "UTC"
    
    // Route critical alarms
    SET "sev" WITH (GET JSON "severity" IN PAYLOAD AS STRING)
    IF {sev} EQUALS "CRITICAL" THEN
        PUBLISH TOPIC "alerts/critical/" + {equipment_id} WITH PAYLOAD

Best Practices

Explicit type casting prevents runtime errors:
// Good
SET "temp" WITH (GET JSON "temperature" IN PAYLOAD AS DOUBLE)

// Risky
SET "temp" WITH (GET JSON "temperature" IN PAYLOAD)
Check data before publishing:
SET "value" WITH (GET JSON "value" IN PAYLOAD AS DOUBLE)

IF {value} >= 0 AND {value} <= 100 THEN
    PUBLISH MODEL ValidData TO "output" WITH value = {value}
ELSE
    PUBLISH TOPIC "errors/validation" WITH "Invalid value: " + {value}
Include identifiers in output topics for easy filtering:
// For topic "sensors/temp001/data" - wildcard is at position 2
SET "sensor_id" WITH TOPIC POSITION 2
PUBLISH MODEL SensorData TO "processed/" + {sensor_id} + "/data" WITH ...
Use RANDOM UUID for record identification:
PUBLISH MODEL Record TO "records/new" WITH
    record_id = (RANDOM UUID)
    ...

Next Steps