API Reference
topics is a persistent event log exposed as a JSON-first HTTP API: every operation is a
plain request with a JSON body and a JSON response, so curl is a complete client. No SDK,
no binary framing, no connection modes.
This page is built to be read top to bottom, getting more detailed as you go — a one-sentence model, then the six everyday operations, then the complete endpoint surface and the conventions that hold across all of it.
Level 1 — the one-sentence model
A topic is a log you
POSTevents to and read back by number. The server assigns each event a monotonicseq; reading is “give me everything afterfrom_seq,” and advancing your storedfrom_seqis the ack.
If you remember only that, you can use topics. Everything below is that idea expanded.
Level 2 — the six everyday operations
Nearly everything you’ll do is one of these six calls. Each links to its full reference page.
| Operation | Call | What it does |
|---|---|---|
| Create | PUT /v0/topics/:topic | Configure a topic up front — or skip it; a topic is created lazily on first write. |
| Write | POST /v0/topics/:topic | Append a batch of records; the server numbers each and returns the assigned seqs. |
| Read | POST /v0/topics/:topic/diff | Get the records after from_seq, the next cursor, and any tombstone. |
| Watch | POST /v0/watch → GET /v0/watch/:wid | Follow one or many topics live over one resumable SSE stream. |
| Delete | POST /v0/topics/:topic/delete | Permanently remove records by seq range and/or tag — immediate, point-in-time. |
| Fan out | PUT /v0/routers/:router | Copy every record from one topic into another, server-side, preserving origin. |
Need lease-based job delivery instead of a shared cursor? Create the topic with
type: "queue" and the same surface gains claim / ack / nack / extend / work —
visibility-timeout leases, redelivery, and dead-lettering. See Queues.
Level 3 — the complete surface
Base URL & versioning
http://{host}:{port}/v0/...The API version is the first path segment (/v0). The default bind is
127.0.0.1:4000, so a local server lives at http://localhost:4000/v0/.... There is no
region or tenant in the host — topics is single-machine.
Breaking changes would ship as a new prefix (/v1) that may run concurrently. Within a
version, changes are strictly additive: new optional request fields, new response
fields, new endpoints. Clients must ignore unknown response fields so an older client
keeps working against a newer server.
The JSON-first, curl-friendly ethos
A few conventions hold across every endpoint and make the surface predictable:
- Bodies are JSON in, JSON out.
POST/PUTcarryapplication/json; responses areapplication/json, except the SSE stream (text/event-stream). Yourdatapayload is stored and returned verbatim — never parsed, indexed, or reshaped. POSTdescribes the operation in the body, even for reads likediff, because the operation is too rich for a query string. Reads are still safe to retry.- Success returns bare data — no
{"status":"ok"}envelope. The presence of a top-levelerrorkey is the only success/failure discriminator. - The monotonic
$seqis the cursor. There is no opaque consumer token on the core read path; you advance your own storedfrom_seq. - Every response carries a
performanceblock so latency observability lives in the response, not a side channel.
Data loss is always explicit, never silent. Capacity loss (cap eviction, TTL expiry)
surfaces as an in-band 200 tombstone in a diff or an event: tombstone SSE frame —
never as an HTTP error. See Tombstones.
These rules — the error body, the status-code table, the $-metadata convention,
pagination, idempotency, and the Topic config object — are specified once on
Conventions.
Endpoint index
The complete /v0 surface. Auth scopes and per-route requirements are covered on
Conventions and Security.
| Method | Path | Purpose |
|---|---|---|
PUT | /v0/topics/:topic | Create / configure a topic (idempotent upsert) |
GET | /v0/topics/:topic | Get topic state (head / earliest / count / config) |
GET | /v0/topics | List topics (opaque-cursor paginated) |
DELETE | /v0/topics/:topic | Delete a topic (cascades routers) |
POST | /v0/topics/:topic | Append record(s); returns assigned seqs + head |
POST | /v0/topics/:topic/diff | Read difference from a cursor (batched + tombstones) |
POST | /v0/topics/:topic/delete | Permanently delete records by before_seq and/or tag match |
PUT | /v0/routers/:router | Create / configure a router (idempotent upsert) |
GET | /v0/routers/:router | Get a router |
GET | /v0/routers | List routers (opaque-cursor paginated) |
DELETE | /v0/routers/:router | Delete a router |
POST | /v0/watch | Create a multiplexed SSE watch session |
GET | /v0/watch/:wid | Open the SSE stream for a session |
POST | /v0/topics/:q/claim | Queue: lease up to N claimable jobs to a node |
POST | /v0/topics/:q/ack | Queue: complete jobs (ack == permanent delete) |
POST | /v0/topics/:q/nack | Queue: release leased jobs for (delayed) reclaim |
POST | /v0/topics/:q/extend | Queue: extend lease deadlines (heartbeat) |
GET | /v0/topics/:q/work | Queue: SSE auto-claim / push (PUSH mode) |
GET | /v0/health (/healthz) | Liveness |
GET | /v0/ready (/readyz) | Readiness (WAL-replay / drain aware) |
GET | /v0/metrics | Prometheus / JSON metrics |
The five queue endpoints (claim/ack/nack/extend/work) are only available on a topic
created with type:"queue"; calling them on a plain log topic returns 409 not_a_queue. See
Queues.
Reference pages
Base URL, auth, error body, status codes, cursors, idempotency, and the Topic config object.
ConventionsCreate, configure, inspect, list, and delete topics — the control plane.
TopicsAppend one or many records atomically; seq assignment and idempotency.
Writing RecordsThe core consume operation: cursor reads, tombstones, and node filtering.
Reading (diff)Multiplexed Server-Sent Events over a single resumable connection.
Watch (SSE)Lease-based at-least-once job delivery: claim, ack, nack, extend, and work.
Queues