Data model
Three shapes carry all customer data through the platform. This page is the conceptual map; the full field-by-field contracts live in the API reference.
TelemetryRecord (what you write)
The ingest unit is a flat, self-describing record:
| Field | Type | Notes |
|---|---|---|
measurement | string | What kind of thing this sample describes; see measurement families |
time | ISO 8601 datetime | The sample's own timestamp, not arrival time |
tags | map of string to string | Identity and dimensions: node_id, link_id, node_type, and whatever else you index by |
fields | map of string to float, int, or string | The measured values |
POST /telemetry takes a bare JSON array of these records, up to 1,000 per batch inside a 1 MB body. Your tenant is stamped on every record server-side; a record cannot be written into someone else's tenant no matter what the body claims. The write is acknowledged with accepted and rejected counts and the indices of any rejected records. There is no measurement allowlist on write, so derived and custom metrics are fine; put them under the telemetry measurement if you want to read them back through topology. Full contract: Telemetry API.
Topology entities (what you read back)
GET /topology is a projection, not a separate database: for a given measurement family and freshness window it returns the latest sample per entity, shaped as:
| Field | Meaning |
|---|---|
measurement | The family this entity was projected from |
entity_id | The entity's identity, derived from its tags |
observed_at | Timestamp of the newest sample that produced this entity |
tags | The tags of that sample |
fields | The fields of that sample |
The response wraps entities with as_of_utc (the evaluation instant, defaulting to now) and your tenant_key. Because it is a latest-sample projection, "empty topology" almost always means "no telemetry inside the freshness window", not "no data ever". Reads are capped at 50,000 rows and there is no pagination; scope by measurement and freshness instead. Full contract: Topology API.
Prediction outputs (cached and live)
GET /predictions serves the snr model family, the only family in production today, in two shapes:
Cached (default)
A PredictionSet: the model family, when the set was captured, and one item per link with link_id, horizon_minutes, predicted_at, the model version, and a value map of contract-driven floats. For SNR the value carries p50, p10, and p90, a percentile band rather than a single point.
Live (live=true)
An InferencePayload: predictions computed on request, each with the same value contract, plus a provenance block that makes the answer auditable:
| Provenance field | Tells you |
|---|---|
served_from | sagemaker, local, or registry |
model_version | Exactly which model answered |
feature_source | Whether features came from stored telemetry (influx) or a packaged feature set (npz) |
input_window_start / input_window_end | The telemetry window the features were built from |
telemetry_points_used | How much data backed the inference |
fill_policy | How gaps were handled |
feature_schema_version | The feature contract version |
The rule of thumb: cached is cheap and right for dashboards and polling; live is per-request inference with receipts, right when the forecast will drive a decision. Requests are capped at 100 link ids. Full contract: Predictions API.
What is not in the data model
No orgs or per-user roles on the API surface (the key is the principal), no webhook or event shapes, and no server-side prediction tiers: bronze, silver, and gold are console plan concepts that control what the console requests, not fields on the wire. See Plans and billing.