Skip to Content
API ReferenceOverview

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 POST events to and read back by number. The server assigns each event a monotonic seq; reading is “give me everything after from_seq,” and advancing your stored from_seq is 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.

OperationCallWhat it does
CreatePUT /v0/topics/:topicConfigure a topic up front — or skip it; a topic is created lazily on first write.
WritePOST /v0/topics/:topicAppend a batch of records; the server numbers each and returns the assigned seqs.
ReadPOST /v0/topics/:topic/diffGet the records after from_seq, the next cursor, and any tombstone.
WatchPOST /v0/watchGET /v0/watch/:widFollow one or many topics live over one resumable SSE stream.
DeletePOST /v0/topics/:topic/deletePermanently remove records by seq range and/or tag — immediate, point-in-time.
Fan outPUT /v0/routers/:routerCopy 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/PUT carry application/json; responses are application/json, except the SSE stream (text/event-stream). Your data payload is stored and returned verbatim — never parsed, indexed, or reshaped.
  • POST describes the operation in the body, even for reads like diff, 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-level error key is the only success/failure discriminator.
  • The monotonic $seq is the cursor. There is no opaque consumer token on the core read path; you advance your own stored from_seq.
  • Every response carries a performance block 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.

MethodPathPurpose
PUT/v0/topics/:topicCreate / configure a topic (idempotent upsert)
GET/v0/topics/:topicGet topic state (head / earliest / count / config)
GET/v0/topicsList topics (opaque-cursor paginated)
DELETE/v0/topics/:topicDelete a topic (cascades routers)
POST/v0/topics/:topicAppend record(s); returns assigned seqs + head
POST/v0/topics/:topic/diffRead difference from a cursor (batched + tombstones)
POST/v0/topics/:topic/deletePermanently delete records by before_seq and/or tag match
PUT/v0/routers/:routerCreate / configure a router (idempotent upsert)
GET/v0/routers/:routerGet a router
GET/v0/routersList routers (opaque-cursor paginated)
DELETE/v0/routers/:routerDelete a router
POST/v0/watchCreate a multiplexed SSE watch session
GET/v0/watch/:widOpen the SSE stream for a session
POST/v0/topics/:q/claimQueue: lease up to N claimable jobs to a node
POST/v0/topics/:q/ackQueue: complete jobs (ack == permanent delete)
POST/v0/topics/:q/nackQueue: release leased jobs for (delayed) reclaim
POST/v0/topics/:q/extendQueue: extend lease deadlines (heartbeat)
GET/v0/topics/:q/workQueue: SSE auto-claim / push (PUSH mode)
GET/v0/health (/healthz)Liveness
GET/v0/ready (/readyz)Readiness (WAL-replay / drain aware)
GET/v0/metricsPrometheus / 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

Last updated on