Concepts
Signal keys and types, units and groups, display names, and push vs pull ingestion.
Signal keys & types
A signal is identified by its key — a string like daily_sales or
machine_temp_c, unique within a workspace. The key is the identity
everywhere: segments, trainings, and stored data all reference the raw key.
Values are typed automatically as they arrive. Each key accumulates one or
more observed types, surfaced on list rows as value_types — numeric
values (continuous, discrete, boolean) power charts and models; text
values are treated as categorical states.
Signals also carry lightweight metadata, all managed through the API:
| Metadata | What it is | Set with |
|---|---|---|
| Display name | A friendly label ("o" → "Open"); presentation-only | POST /v1/signal/display-name |
| Unit | A measurement unit ("$/MWh", "kWh", "%") | POST /v1/signal/units/assign |
| Group | A named bucket for organizing many signals | POST /v1/signal/groups/assign |
Signals vs events
A signal is a continuous series — a value every tick. An event is a
discrete occurrence: a launch, an outage, a headline. Events live under
named streams (their own keys), get scored automatically for sentiment,
relevance, and impact, and show up in your signal list alongside regular
signals. They have their own page: Events.
Units & groups
Both units and groups are resolved in layers: your explicit assignment
always wins; otherwise the platform infers one automatically (from key
namespaces, data sources, or shared naming patterns). Every resolved value
carries an origin field — user for your assignments, llm for
inferred units, and namespace / source / inferred / fallback for
automatic grouping — so you always know where a label came from.
Clearing an assignment (empty unit, or groups/unassign) reverts the
key to automatic resolution; it never deletes data.
Display names
Display names are purely cosmetic. Setting display_name: "Open" on key
o changes what UIs and list responses show — the key stays o in every
API call, segment, and training. Fetch the full {key: display_name} map
with GET /v1/signal/display-names.
Push vs pull
Data reaches a signal two ways:
- Push —
POST /v1/signal/push, one value per call. Best for live data: an IoT reading, a completed order, a sensor tick. Each push counts as one streaming event against the workspace's monthly allowance (see Billing). - Pull — Ingestion jobs: database connectors, file uploads, API polls, and webhooks that map columns to signal keys on a schedule. Best for history and bulk.
Both land in the same place. A signal doesn't know or care how its values arrived, and you can freely mix the two — backfill history through an ingestion job, then keep the signal fresh with pushes.
Signals shared into your workspace by subscription are read-only — you can list them, chart them, and build segments from them, but you can't push to or delete them.

