OrdinateDB

Documentation

product Pre-releaseunwritten sections are marked

API guide · live reads

Streaming

Overview

OrdinateDB ships two live-read transports. GET /v1/stream is a direct series WebSocket. POST /v1/read/subscribe is a model-resolved Server-Sent Events stream with optional snapshots and explicit lag recovery. Neither replaces durable historical reads.

Choose the WebSocket when you already know one series UUID and want its live edge. Choose resolved-read SSE when a model selector should follow attributes and optionally seed the client with snapshots.

Quick example

This browser example opens the direct series WebSocket and handles the two documented live frame types.

Connect to /v1/stream?series=<uuid>. Read authorization and the selected series are fixed when the connection opens; reconnect after permission changes. Authentication may use the session cookie or a bearer credential supported by the WebSocket client.

Browser JavaScript

const url = new URL("/v1/stream", ordinateUrl);
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
url.searchParams.set("series", seriesId);

const socket = new WebSocket(url);
socket.addEventListener("message", (event) => {
  const update = JSON.parse(event.data);
  if (update.type === "point") appendPoint(update);
  if (update.type === "gap") markGap(update);
});

The server sends JSON text frames for the selected series:

Point frame

{
  "type": "point",
  "series": "00000000-0000-4000-8000-000000000001",
  "t_ms": 1721476800000.0,
  "ts_ns": "1721476800000000000",
  "value": 42.5,
  "quality": 192
}

Gap frame

{
  "type": "gap",
  "series": "00000000-0000-4000-8000-000000000001",
  "start_ns": "1721476800000000000",
  "end_ns": "1721476860000000000",
  "cause": "GAP_SOURCE_DISCONNECTED"
}

ts_ns, start_ns, and end_ns are exact decimal-string identities. t_ms is a display coordinate. Point values retain their number, Boolean, string, or digital-ordinal JSON shape.

The WebSocket is best-effort. If the client falls behind the in-process broadcast buffer, missed frames are skipped and streaming continues. No replay or synthetic lag frame is sent.

How it works

Both transports pin authorization and resolution when the connection opens. Live delivery can move ahead of a slow client, so durable HTTP reads are the source for recovering a complete historical viewport.

Recover a complete viewport through raw/render/gaps HTTP reads. A reconnect starts at the then-current live edge.

Reference

Resolved-read SSE

POST /v1/read/subscribe is fetch-based SSE because the subscription selector is a JSON body. The body contains resolve, optional include_snapshot, and optional timestamp_format. Resolution and read filtering are pinned when the connection opens.

JavaScript stream reader

const response = await fetch(`${ordinateUrl}/v1/read/subscribe`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
    Accept: "text/event-stream"
  },
  body: JSON.stringify({
    resolve: {
      select: { kind: "attributes", content: [attributeId] },
      scope: {}
    },
    include_snapshot: true,
    timestamp_format: "decimal-string"
  })
});

if (!response.ok || !response.body) {
  throw new Error(await response.text());
}

const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  consumeSseText(value); // retain incomplete trailing records between reads
}

The event order is contractual:

  1. one resolved event;
  2. when requested, one current frame per attribute with a snapshot;
  3. live frame, gap, and laggedevents.

After lagged, the server emits current snapshot frames to reseed the live edge. That reseed is not durable history. Re-read the viewport with POST /v1/read/render before resuming append if completeness matters. A gap is a durable half-open interval, not a fabricated point; never draw through it.

Exact nanoseconds

Send "timestamp_format":"decimal-string" to /v1/read/subscribe and /v1/read/render. All nested fields ending in _ns become base-10 strings, including resolved segments, annotations, episodes, gaps, lineage, and provenance. Malformed strings, signed-64-bit overflow, and unknown format tokens return 400.

Common patterns

Recover after lag

Pause appending live frames, re-read the visible window through POST /v1/read/render, then resume from the reseeded live edge. This prevents a snapshot reseed from being mistaken for complete missed history.

Preserve render quality

Exact render results include initial_quality and exact t_ns coordinates. The quality seed is the first recorded numeric point inside the requested [start, end) window, not a look-back or interpolation. It may be non-null even when decimated value arrays are empty, such as a homogeneous Bad-only window.

Quality transitions are strictly ordered and take effect at their exact t_ns. In significant-points mode, the parallel t_ns array identifies each recorded sample. In minmax-band mode it identifies bucket starts, not extrema. Legacy mode omits this exact metadata.

Related topics

See the exact schemas for GET /v1/stream and POST /v1/read/subscribe.

Read the wire conventions for half-open ranges, decimal-string timestamps, provenance, and error handling.