Use Case

IoT Anomaly Detection with Validation

Catch anomalies that static thresholds miss. Sensor readings are validated at ingress by Contact, analyzed by AI with conversation memory that spots contextual patterns over time, and alerts are scanned by Surface before reaching your dashboard and devices.

Coming soon Contact quick start

The problem with IoT anomaly detection

Threshold fatigue

Static thresholds either fire constantly or miss gradual drifts. A reading of 72 °C is fine for a motor under load but alarming at idle. Context matters.

No memory across readings

Each sensor reading is analyzed in isolation. The system can't tell whether a pattern has been developing over the last 50 readings or if this is a one-off spike.

Bad data triggers false alerts

A sensor glitch sends −999 and your pipeline generates a critical alert. Without ingress validation, garbage in means garbage alerts out.

Alert delivery is fragile

Alerts go to Slack or email, but the dashboard entity isn't updated, the device doesn't know, and there's no audit trail of what was sent or why.

How Tendrl solves it

Validate at the edge, detect with context, deliver with confidence.

1

Sensor publishes via Contact

The sensor publishes readings with tags: ["sensor-reading"]. Each message contains temperature, humidity, and a device identifier.

MicroPython SDK: publish a sensor reading
client.publish(
    data={
        "temperature": 74.2,
        "humidity": 45,
        "device_id": "hvac-unit-12"
    },
    tags=["sensor-reading"]
)
2

DynamicActions validate at ingress

Contact runs DynamicActions on every message. RequiredFields ensures temperature, humidity, and device_id are present. Rules use the between operator to enforce physical ranges: temperature between −40 and 150, humidity between 0 and 100. Validation is passive: messages always store. Failed readings get error tags injected and trigger a separate maintenance workflow.

DynamicAction configuration
{
  "name": "range_check",
  "requiredFields": ["temperature", "humidity", "device_id"],
  "rules": [
    {"field": "temperature", "operator": "between", "value": [-40, 150]},
    {"field": "humidity", "operator": "between", "value": [0, 100]}
  ],
  "tags": ["reading-valid"]
}
3

AI analyzes with conversation memory

The Strand workflow's AI node uses conversation memory configured with a 2-hour TTL and space for 50 messages. On every run, the model loads its history of recent readings for this node, a rolling window that captures trends, drift, and baseline behavior.

The context_md system prompt defines the AI as an HVAC anomaly detector. The prompt uses Jinja2 to inject the latest reading. Because the model remembers prior readings, it can detect that temperature has been climbing 0.5 degrees per reading for the last hour, something a static threshold would miss until it's too late.

System prompt: anomaly detector
You are an HVAC anomaly detector. You receive sensor
readings over time and maintain memory of recent history.

Analyze the latest reading in context of recent readings.
Flag anomalies: sudden spikes, gradual drifts, unusual
correlations between temperature and humidity.

Return JSON:
{"is_anomaly": bool, "confidence": 0-1,
  "severity": "info|warning|critical",
  "explanation": "..."}
Prompt template: inject live data
Device: {{  payload.device_id  }}
Temperature: {{  payload.temperature  }}°C
Humidity: {{  payload.humidity  }}%
4

If anomalous, Surface scans the outbound alert

An if/else node checks the AI's is_anomaly field. If true, the alert payload (including the AI's explanation) is scanned by Surface before it leaves the system. This prevents the AI from inadvertently including sensitive infrastructure details, internal IPs, or credentials in alert messages destined for external dashboards.

5

Contact delivers the alert and updates state

A Contact connector with operation message sends the alert to a dashboard entity. A second Contact connector with operation update_state patches the sensor's state table with last_anomaly_time, anomaly_severity, and status: "anomaly_detected". Non-anomalous readings update state silently with status: "healthy".

When validation fails: the maintenance path

Bad data isn't dropped; it triggers its own workflow.

×

Failed validation injects error tags

When a sensor sends −999 for temperature or omits a required field, Contact stores the message, sets validation_status: "failed", and injects the DynamicAction's failure tags into the message context. Those tags match a separate maintenance workflow in Strand.

1

Notify the ops team

A Contact connector sends a message to the ops dashboard entity with the full ActionResult: which field failed, the actual value, the expected value, and the rule that caught it.

2

Update device state

A Contact connector with operation update_state sets the sensor's state to needs_maintenance with the validation error details. The state table becomes the source of truth for device health.

3

Send a tagged diagnostic message back

A Contact connector sends a message back to the sensor device with tag diagnostic. The device routes it with @client.on(tag="diagnostic") to run self-diagnostics and report back.

MicroPython SDK: receive diagnostic request
@client.on(tag="diagnostic")
def run_diagnostics(msg):
    report = self_test(msg.get("data", {}))
    client.publish(report, tags=["diagnostic-result"])

Intelligent monitoring, not just thresholds

Contextual detection

AI with conversation memory spots patterns across 50+ readings that static rules miss entirely.

Range validation

The between operator catches sensor glitches at ingress. −999 never reaches your AI pipeline.

Passive storage

Failed validation doesn't drop the message. It stores, tags, and routes, giving you an audit trail of bad data.

Alert scanning

Surface checks outbound alerts for accidental data leaks before they reach external dashboards.

State table as dashboard

Each device's state table tracks last anomaly, severity, and maintenance status, all queryable via the Contact API.

Maintenance workflows

Validation failures trigger automatic ops notification, state updates, and tagged diagnostic messages back to the device.

Configurable memory

TTL and max messages let you tune the AI's context window. 2-hour TTL for fast equipment, 7-day TTL for slow environmental drift.

Tag-based routing

Pass and fail paths are different tag sets matching different Strand workflows. No if/else needed at the Contact level.

Example: HVAC anomaly detection

Sensor to alert to device state, with two paths for valid and invalid data.

Sensor Publishes reading
Contact Validate ranges
Claude Analyze with memory
Surface Scan alert
Contact Alert + update state
Validate

Contact checks temperature is between −40 and 150, humidity between 0 and 100, and all required fields are present. Pass triggers the anomaly detection workflow. Fail triggers the maintenance workflow: ops notified, device state set to needs_maintenance, diagnostic command sent.

Detect

Claude analyzes the reading in context of the last 50 messages (2-hour TTL). It can spot that temperature has been climbing 0.5 degrees per reading for the last hour, something a threshold would miss until it's too late.

Deliver

Anomaly alerts are scanned by Surface, then delivered via Contact to the dashboard entity and written to the sensor's state table. Non-anomalous readings update state silently.

Smarter monitoring starts here

Free tier includes 5 entities, 10 workflows, and 550 runs/month. No credit card required.

Coming soon Contact quick start