Refractions
A Refraction is an event-driven, no-code workflow graph. A trigger starts it; Stages run in sequence, wired by pass / fail edges; and the run is recorded as an execution.
Triggers
A Refraction starts from one or more trigger nodes:
| Trigger | Fires when |
|---|---|
| On Signal | A matching Signal type arrives (optionally from specific source Facets). |
| On schedule | A fixed interval, cron expression (in the org timezone), or one-time time. |
| On API call | An inbound HTTP webhook hits the trigger's key. |
Multiple triggers on one Refraction are OR — each starts its own run.
The API trigger exposes an authenticated webhook: POST /api/v1/triggers/<key> with a bearer
key managed (create / reveal / rotate) on the Refraction. It supports a "learning mode" that locks
the expected payload shape.
Stage catalog
Stages come from a rich, shipped set. Grouped by purpose:
Control flow — filter, conditional branch, for-each, delay, wait-until, wait-for-signal, wait-for-approval (a human gate → the Approvals inbox), assert, dedupe, rate-limit, stop.
Data — transform payload (expression DSL), set variables, format template, lookup.
Capture Sets — save to capture set, enrich from capture set, query capture set, update row, export capture set.
Device — facet command (typed parameters from the device's capability manifest; fails cleanly if the device is offline).
Integrations — call API (HTTP), send webhook (HMAC-signed), GraphQL, publish to an external MQTT broker, put an object to storage.
Notifications — email, Slack, Telegram, Discord, Microsoft Teams, PagerDuty, SMS, WhatsApp.
AI / ML — call LLM (Anthropic / OpenAI-compatible / Ollama), classify, extract, OCR.
Integration and notification stages let a Refraction reach external systems your org configures — treat destinations as you would any outbound webhook target.
Conditions & expressions
A filter stage evaluates conditions against the Signal and the run's data. Operators are typed to the field:
- String — equals, not-equals, contains, starts-with, regex.
- Number —
=,!=,>,<,>=,<=, in-range. - Boolean — is-true / is-false.
- List — in-list / not-in-list (great for EPC allow/blocklists).
- Datetime — before / after / between.
- Time-of-day — before / after / between a clock time, with a per-condition timezone; overnight windows wrap.
Conditions in a filter are combined with AND. A fail edge out of a filter or branch means condition not met — that is a normal outcome, not an error, and does not raise a failure alert.
Fields can reference a reserved envelope in any input or {{template}}: $facet.* (id, label,
agent name, hardware id, type, status, site, org) and $signal.* (id, type, facet id, received
at). The transform stage exposes a sandboxed function library (string ops like substr /
upper / concat, encoding helpers like hex_to_ascii / hex_to_int, and numeric coercion).
Test, enable, disable
- Test runs the graph against a chosen real trigger payload. It is a live run — side effects fire — and is always confirmed with an itemized summary first.
- Enable activates the Refraction so it processes live Signals; Disable stops it. Both are admin actions. The graph is validated on save (no unknown stages, no dangling edges).
Executions
Every run is an execution with a status of running, completed, failed, or partial,
plus per-stage results, duration, and the count of Capture Set rows written. The Executions
view shows the stage chain like a CI pipeline, with live status and click-to-inspect panels, and
links to the rows a run produced.
Long-running stages (an API call, a delay, waiting for a signal or an approval) pause and resume rather than blocking. A Refraction can raise a failure alert (email / Slack) on failed executions.