Hypeline Docs

Quickstart

From an API key to a live event stream in minutes.

Get from zero to a live, authenticated event stream with a single curl. The stream is Server-Sent Events (SSE) over plain HTTP, so anything that can read an HTTP response can consume it.

Get an API key

Mint a scoped key with the CLI. The plaintext key is shown once at creation, so copy it immediately.

livestrom apikey create --name my-app --scopes stream:read
# → lstr_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  (shown once)

Export it so the snippets below can read it:

export LIVESTROM_KEY="lstr_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

Stream live events

Point curl -N (no buffering) at /v1/stream with your key in the X-API-Key header. Events arrive as SSE frames as soon as the Engine detects them.

curl -N -H "X-API-Key: $LIVESTROM_KEY" https://<host>/v1/stream

You can also present the key as a bearer token:

curl -N -H "Authorization: Bearer $LIVESTROM_KEY" https://<host>/v1/stream

Each frame carries an id (also the resume cursor), an event label (the source_type), and a data line with one StreamEvent JSON object:

id: 018f9c2e-1a2b-7c3d-8e4f-5a6b7c8d9e0f
event: rss
data: {"id":"018f9c2e-1a2b-7c3d-8e4f-5a6b7c8d9e0f","schema_version":1,"source_id":"svt-nyheter","source_type":"rss","url":"https://example.se/artikel","title":"...","content":"...","tags":["kommun"],"fetched_at":"2026-07-01T12:00:00Z","emitted_at":"2026-07-01T12:00:01Z"}

Filter server-side

Narrow the stream to just the slice you care about with repeatable query parameters: source, source_type, tag, and keyword.

curl -N -H "X-API-Key: $LIVESTROM_KEY" \
  "https://<host>/v1/stream?source_type=rss&tag=kommun"

A trailing .* on a source value is a prefix match (for example ?source=kommun.*). Filters are capped per key; an over-cap filter returns a documented 400.

Resume without gaps

SSE reconnects automatically and replays from the last id it saw via the Last-Event-ID request header, so a dropped connection resumes gap-free:

curl -N \
  -H "X-API-Key: $LIVESTROM_KEY" \
  -H "Last-Event-ID: 018f9c2e-1a2b-7c3d-8e4f-5a6b7c8d9e0f" \
  https://<host>/v1/stream

To backfill from a known point on a fresh connection, use ?since=<event-id>:

curl -N -H "X-API-Key: $LIVESTROM_KEY" \
  "https://<host>/v1/stream?since=018f9c2e-1a2b-7c3d-8e4f-5a6b7c8d9e0f"

Expired cursors

A Last-Event-ID older than the retained backfill window returns 410 Gone. Resync from now (drop the cursor) and continue tailing live.

Consume from JavaScript

The browser's native EventSource API cannot set request headers, so a bare new EventSource("/v1/stream") gets a 401 from our auth middleware. Read the stream with fetch() and a streaming body reader instead.

Primary: fetch() with a streaming reader

// Node 18+ / modern browser
const res = await fetch("https://<host>/v1/stream", {
  headers: { "X-API-Key": process.env.LIVESTROM_KEY }
});
const reader = res.body.getReader();          // a ReadableStream reader
const dec = new TextDecoder();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  process.stdout.write(dec.decode(value));
}

For automatic reconnect and Last-Event-ID handling that matches native EventSource behaviour, use the @microsoft/fetch-event-source npm package (a convenience wrapper for your own project, not a dependency of these docs):

import { fetchEventSource } from "@microsoft/fetch-event-source";

await fetchEventSource("https://<host>/v1/stream", {
  headers: { "X-API-Key": process.env.LIVESTROM_KEY },
  onmessage(ev) {
    console.log(ev.id, JSON.parse(ev.data));
  },
});

Why not plain EventSource?

The browser's EventSource API cannot set request headers. Use fetch() with a streaming reader, or a wrapper like @microsoft/fetch-event-source, for authenticated access.

Fallback: query-param key (less secure)

If you must use bare EventSource, pass the key as a query parameter. Note the key then appears in server access logs and browser history, so prefer the header-based approaches above.

// Less secure: key visible in logs and history
const es = new EventSource(`https://<host>/v1/stream?api_key=${key}`);
es.onmessage = (ev) => console.log(JSON.parse(ev.data));

Latency by source tier

Expected latency depends on the source_type of each event:

  • Firehose sources arrive in seconds.
  • RSS / JSON feeds arrive in minutes.
  • Feed-less HTML-diff sources arrive in minutes to tens of minutes.

Errors

Every error on the boundary is an RFC 9457 application/problem+json document with type, title, status, and detail fields. The statuses you will see on /v1/stream:

StatusMeaning
400Malformed or over-cap filter (too many source/tag/keyword terms for your key).
401Missing or invalid API key.
403The key lacks the required stream:read scope.
410The Last-Event-ID cursor is older than the retained window. Resync from now.
429Per-key rate limit exceeded. Honour the Retry-After header.

See the API Reference for the full operation and the StreamEvent schema.

On this page