API reference

The complete, normative reference for the Runnev HTTP API. Base URL https://runnev.dev/v1. Every endpoint, its parameters, its responses, and a working example.

Conventions

  • Base URL. https://runnev.dev/v1. HTTP/2 and HTTP/3 are supported; HTTP/1.1 works for streaming too.
  • Content type. Requests and responses are JSON (application/json) except the subscribe stream (text/event-stream) and raw publish/replay (application/octet-stream).
  • Timestamps are RFC 3339 in UTC with millisecond precision, e.g. 2026-08-04T11:02:13.418Z.
  • Ids are bare base62 (22 chars), e.g. cBczepiZSW8GJaCae0xIj7. No prefixes.
  • Sequences are non-negative integers. A stream's cursor is the sequence of its newest batch, starting at 0 when empty.
  • Caching. Every /v1 response sends cache-control: no-store.
  • Unknown query parameters are ignored, never rejected. See Forward compatibility.
  • Authentication. Authorization: Bearer <key> on every endpoint except /v1/health, /v1/version, and the demo stream. See Authentication.

Universal response headers

Every /v1 response carries:

HeaderExampleMeaning
x-request-idreq_0Kj2wq8ULn4mAe1sUnique per request; quote it in support.
x-ratelimit-limit1000Requests allowed in the current window.
x-ratelimit-remaining998Requests left in the window.
x-ratelimit-reset1785931200Unix time when the window resets.
cache-controlno-storeNever cache API responses.
serverrunnevService identifier.

A 429 additionally sends retry-after in seconds. Publish responses add x-runnev-cursor.

Pagination

List endpoints accept ?limit= (1-100, default 20) and a ?cursor= pagination token. The response includes has_more and, when there is more, a next_cursor to pass back. This pagination cursor is an opaque token and is unrelated to a stream's sequence cursor.

Endpoints

GET/v1/healthliveness, no auth

Liveness probe. No authentication.

curl https://runnev.dev/v1/health
200 OK
{"status":"ok","uptime_s":1843302}
GET/v1/versionbuild info, no auth
200 OK
{"version":"1.6.2","commit":"9d3c1af","built":"2026-07-15T08:14:02Z"}
POST/v1/streamscreate a stream

Create a stream. Requires auth.

Body

FieldTypeNotes
name requiredstring1-128 chars. Not required to be unique.
retention_secondsinteger60-2592000. Default 86400.
max_bytesinteger1 MiB - 4 GiB. Default 268435456.
modestringjson (default) or raw.
idstringOptional client-supplied base62 id for idempotent creation.
curl https://runnev.dev/v1/streams \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -d '{"name":"orders-eu","retention_seconds":86400}'
201 Created
{
  "id": "cBczepiZSW8GJaCae0xIj7",
  "name": "orders-eu",
  "created_at": "2026-06-11T09:22:31Z",
  "retention_seconds": 86400,
  "max_bytes": 268435456,
  "cursor": 0,
  "bytes_stored": 0,
  "mode": "json"
}

Errors: 400 invalid_request_error (bad body), 409 stream_name_taken (supplied id already exists), 422 retention_out_of_range, 403 stream_limit_reached.

GET/v1/streamslist streams

List streams in the project. Query: limit, cursor.

200 OK
{
  "data": [
    {"id":"cBczepiZSW8GJaCae0xIj7","name":"orders-eu","cursor":41823,"mode":"json","created_at":"2026-06-11T09:22:31Z","retention_seconds":86400,"max_bytes":268435456,"bytes_stored":118293011}
  ],
  "has_more": false,
  "next_cursor": null
}
GET/v1/streams/{id}metadata or subscribe

Dual-mode on the Accept header.

  • With Accept: text/event-stream → subscribe (SSE). Query: cursor (head, 0, or an integer). See Subscribing.
  • Otherwise → return the stream metadata object (same shape as create).
subscribe
curl -N "https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7?cursor=head" \
  -H "Accept: text/event-stream" -H "Authorization: Bearer $RUNNEV_API_KEY"

Errors: 404 stream_not_found, 400 invalid_stream_id, 403 project_forbidden.

DELETE/v1/streams/{id}delete a stream

Delete a stream and all its batches. Open subscribers receive event: end. Returns 204 with no body.

curl -X DELETE https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7 \
  -H "Authorization: Bearer $RUNNEV_API_KEY"   # 204
POST/v1/streams/{id}/{seq}publish at a sequence

The primary write path. seq is a non-negative integer, at most 1024 ahead of the cursor. Idempotent on (id, seq). Accepts application/json ({"events":[...]}) or, for raw streams, application/octet-stream. Max body 8 MiB.

curl https://runnev.dev/v1/streams/cBczepiZSW8GJaCae0xIj7/41823 \
  -H "Authorization: Bearer $RUNNEV_API_KEY" \
  -d '{"events":[{"type":"order.paid","id":"o_5521"}]}'
202 Accepted
{"stream_id":"cBczepiZSW8GJaCae0xIj7","seq":41823,"accepted":1,"cursor":41823,"duplicate":false}

Extra response header: x-runnev-cursor: 41823. A repeat of a stored sequence returns 200 with "duplicate": true.

Errors: 413 payload_too_large, 422 sequence_too_far_ahead, 409 invalid_sequence, 415 unsupported_media_type, 429 rate_limited.

POST/v1/streams/{id}/publishpublish, server-assigned seq

Append at the next sequence, chosen by the server. Not idempotent unless you send an Idempotency-Key header. Same body and limits as above.

202 Accepted
{"stream_id":"cBczepiZSW8GJaCae0xIj7","seq":41824,"accepted":1,"cursor":41824,"duplicate":false}
GET/v1/streams/{id}/cursorcurrent cursor
200 OK
{"cursor":41823,"updated_at":"2026-08-04T11:02:13.418Z"}
GET/v1/streams/{id}/batches/{seq}replay one batch

Fetch a single batch by sequence. For json streams the response is the batch object; for raw streams it is the stored bytes with their original content type.

200 OK
{"seq":41823,"ts":"2026-08-04T11:02:13.418Z","events":[{"type":"order.paid","id":"o_5521"}]}

Errors: 404 stream_not_found or a 404 when the sequence has been evicted or never existed.

Forward compatibility

Unknown query parameters are ignored on every endpoint. Appending, say, ?trace=abc&shard=3 to any Runnev URL changes nothing about how the request is handled. This is a permanent guarantee so that clients can safely add their own tracing, sharding, or cache-busting parameters; new API behaviour is always introduced through parameters we define, and never by making a previously ignored parameter into an error.