Quickstart

From zero to a live subscriber in about five minutes. You will create a stream, publish a batch to it, subscribe and watch it arrive, then disconnect and resume from a cursor without losing anything.

Get an API key

Every request except the health and version endpoints needs a key in the Authorization header. For this walkthrough you can use the public demo key, which is read-only and rate limited, or your own key from the dashboard once you have an account.

bash
# your own key, from the dashboard
export RUNNEV_API_KEY="rnv_live_00112233445566778899aabbccddeeff"

# check connectivity - no auth required
curl https://runnev.dev/v1/health
# {"status":"ok","uptime_s":1843302}

1. Create a stream

Streams are identified by a base62 id. Let the server assign one:

bash
curl https://runnev.dev/v1/streams \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"quickstart","retention_seconds":3600}'
201 Created
{
  "id": "RqrkHXc9bOQs9VlDT836jx",
  "name": "quickstart",
  "created_at": "2026-08-04T11:00:00Z",
  "retention_seconds": 3600,
  "max_bytes": 268435456,
  "cursor": 0,
  "bytes_stored": 0,
  "mode": "json"
}

Keep that id; every other call uses it. Export it for convenience:

bash
export STREAM="RqrkHXc9bOQs9VlDT836jx"

2. Publish a batch

The primary write path puts the sequence number in the URL. Start at 1. Choosing the sequence yourself is what makes publishes idempotent, which the Publishing page covers in depth.

bash
curl https://runnev.dev/v1/streams/$STREAM/1 \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events":[{"type":"hello","msg":"first batch"}]}'
202 Accepted
{"stream_id":"RqrkHXc9bOQs9VlDT836jx","seq":1,"accepted":1,"cursor":1,"duplicate":false}

3. Subscribe

Open a second terminal and hold a subscription open. The -N flag tells curl not to buffer, so you see batches as they arrive. By default a subscription starts at the head, so publish again from the first terminal and watch it show up.

terminal 2 - subscribe
curl -N "https://runnev.dev/v1/streams/$STREAM?cursor=0" \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer $RUNNEV_API_KEY"
server-sent events
: runnev

id: 1
event: batch
data: {"seq":1,"ts":"2026-08-04T11:00:12.418Z","events":[{"type":"hello","msg":"first batch"}]}

: keepalive

The line beginning with a colon is a comment frame. Runnev sends one every 15 seconds when the stream is idle so that intermediaries and clients can tell a healthy quiet connection from a dead one.

4. Resume from a cursor

Stop the subscriber with Ctrl-C. Publish a couple more batches while it is down:

terminal 1
curl https://runnev.dev/v1/streams/$STREAM/2 \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -d '{"events":[{"type":"tick","n":2}]}'
curl https://runnev.dev/v1/streams/$STREAM/3 \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -d '{"events":[{"type":"tick","n":3}]}'

Now reconnect starting after sequence 1. You get 2 and 3 immediately, then live:

terminal 2 - resume
curl -N "https://runnev.dev/v1/streams/$STREAM?cursor=1" \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer $RUNNEV_API_KEY"

That is the whole loop: publish at a sequence, read from a cursor, resume from the cursor after any disconnection. The SDKs do the reconnection for you.

The same thing with an SDK

subscribe.mjs
import { Runnev } from "@runnev/client";

const runnev = new Runnev({ apiKey: process.env.RUNNEV_API_KEY });

const stream = await runnev.createStream({ name: "quickstart", retentionSeconds: 3600 });
await runnev.publish(stream.id, 1, [{ type: "hello", msg: "first batch" }]);

for await (const batch of runnev.subscribe(stream.id, { cursor: 0 })) {
  console.log(batch.seq, batch.events);
}

Next steps