Manage topics¶
Create, inspect, change and delete the topics your services produce to and consume from.
Before you start: to create a topic you need a create grant that matches its name; to change or delete one you must own it or hold admin. Access model and grants has the full rules.
Create a topic¶
curl -i -u "$AUTH" -X POST "$NARAD/v1/topics" \
-H "Content-Type: application/json" \
-d '{"name": "orders", "partitions": 6, "retention_ms": 86400000}'
HTTP/1.1 201 Created
Content-Length: 233
Content-Type: application/json
Date: Mon, 28 Sep 2026 19:31:25 GMT
{
"name": "orders",
"id": "244de9d4ac35abae",
"partitions": 6,
"retention_ms": 86400000,
"visibility_timeout_ms": 30000,
"max_in_flight_per_partition": 1024,
"max_acked_ahead_per_partition": 1024,
"created_at": 1790623885,
"owner": "billing-service"
}
topic, err := client.CreateTopic(ctx, "orders",
narad.WithPartitionCount(6),
narad.WithRetention(24*time.Hour))
CreateTopic returns narad.ErrExists when the topic is already there. A service that creates its topics at startup should call EnsureTopic with the same options, which treats an existing topic as success.
narad topic add orders --partitions 6 --retention 24h
The CLI prints the created topic as JSON, with the same fields as the curl response.
$NARADand$AUTHare the base URL and credentials from Connect and authenticate.$AUTHneeds acreategrant that matchesorders.ordersis the topic name: 1 to 200 letters, digits,.,_or-; a longer name gets400. A name that is taken gets409, and so does a name that differs from an existing topic's only in letter case (Ordersnext toorders), because on a case-insensitive filesystem the two would share one directory. Topics an earlier release created with longer names (up to 255 bytes) keep working, but one whose name is over 230 bytes cannot become a fan-out child: the name of its fan-out cursor file would pass the filesystem's 255-byte limit.- The user who creates a topic becomes its owner, shown in
owner.
Every field the request accepts is in the HTTP API reference. The ones worth deciding up front:
Partitions¶
A topic is split into partitions, which spread its storage and its consumers across the cluster. The count is at least 3 and at most 108 by default (the operator's topic.max_partitions), and it defaults to 3. Pick about as many as the consumers you expect to run in parallel. You can raise the count later but never lower it, so start modest.
Retention¶
Narad deletes a message retention_ms after it was written, whether or not anyone consumed it. The minimum is one hour. 0 keeps messages forever, and leaving the field out gives the operator's default: 7 days for the binary, 12 hours for a cluster installed with the Helm chart. A negative value gets 400. A message that is still unacked when retention removes it is never delivered, so size retention for your slowest consumer plus a margin for replay. The delivery contract has the details.
Visibility timeout¶
visibility_timeout_ms is how long a consumed message stays hidden from other consumers before Narad hands it out again. It defaults to 30 seconds and cannot be changed after the topic is created. Set it above your usual processing time; a slow job can extend its lease instead of forcing a long timeout on every message.
The same request can also give the topic a schema, make it a fan-out or delay child of another topic, and set the two per-partition limits described under flow control.
Inspect a topic¶
curl -u "$AUTH" "$NARAD/v1/topics/orders"
The answer is the topic's settings plus partition_stats: for each partition, the oldest and next offsets, its size on disk, and owner_node, the node that stores it. A topic with a schema also carries schema_version and the current schema.
Full response
{
"name": "orders",
"id": "244de9d4ac35abae",
"partitions": 6,
"retention_ms": 86400000,
"visibility_timeout_ms": 30000,
"max_in_flight_per_partition": 1024,
"max_acked_ahead_per_partition": 1024,
"created_at": 1790623885,
"owner": "billing-service",
"role": "standalone",
"schema_version": 0,
"partition_stats": [
{
"index": 0,
"segments": 0,
"oldest_offset": 0,
"next_offset": 0,
"high_watermark": 0,
"size_bytes": 0,
"owner_node": "narad-0"
},
{
"index": 1,
"segments": 0,
"oldest_offset": 0,
"next_offset": 0,
"high_watermark": 0,
"size_bytes": 0,
"owner_node": "narad-0"
},
{
"index": 2,
"segments": 0,
"oldest_offset": 0,
"next_offset": 0,
"high_watermark": 0,
"size_bytes": 0,
"owner_node": "narad-0"
},
{
"index": 3,
"segments": 0,
"oldest_offset": 0,
"next_offset": 0,
"high_watermark": 0,
"size_bytes": 0,
"owner_node": "narad-0"
},
{
"index": 4,
"segments": 0,
"oldest_offset": 0,
"next_offset": 0,
"high_watermark": 0,
"size_bytes": 0,
"owner_node": "narad-0"
},
{
"index": 5,
"segments": 0,
"oldest_offset": 0,
"next_offset": 0,
"high_watermark": 0,
"size_bytes": 0,
"owner_node": "narad-0"
}
]
}
- You can read a topic if you hold any grant that matches its name, own it, or are an admin. Otherwise the answer is
403, or404when the topic does not exist. - How far behind consumers are is not in this response; the metrics reference has the lag gauges.
List topics¶
GET /v1/topics returns up to limit topics (100 by default, 1000 at most) and a next_page_token. Pass the token back as page_token to get the next page. The list holds only topics you may read, and that filter runs after each page is cut, so a page can be short or even empty while the token is still set. Keep paging until next_page_token is empty. The parameters are in the HTTP API reference.
Change a topic¶
curl -i -u "$AUTH" -X PATCH "$NARAD/v1/topics/orders" \
-H "Content-Type: application/json" \
-d '{"retention_ms": 172800000}'
HTTP/1.1 200 OK
Content-Length: 254
Content-Type: application/json
Date: Mon, 28 Sep 2026 19:31:25 GMT
{
"name": "orders",
"id": "244de9d4ac35abae",
"partitions": 6,
"retention_ms": 172800000,
"visibility_timeout_ms": 30000,
"max_in_flight_per_partition": 1024,
"max_acked_ahead_per_partition": 1024,
"created_at": 1790623885,
"owner": "billing-service",
"role": "standalone"
}
What can change after creation:
| Field | Can change | Notes |
|---|---|---|
retention_ |
yes | 0 keeps messages forever; to go back to the operator's default, send its value |
max_, max_acked_ahead_per_partition |
yes | send one or both; the other keeps its value |
partitions |
raise only | a lower or equal count gets 400 |
schema |
yes | registers a new version; see Evolve a schema |
visibility_ |
no | the field is refused with 400 |
- A request with several fields applies them one at a time, in a fixed order: retention, the per-partition limits, partitions, then schema. There is no transaction across them. The first failure stops the sequence and answers its error, and the fields before it stay applied, so send one field per request when you need all or nothing.
- A topic with delay children keeps at least each child's delay plus one hour of retention. A change below that gets
409. - A retention or per-partition limit change applies on every node that owns a partition of the topic, not only on the node that ran it. Each owner applies it to the partitions it has open within about a second of its copy of the metadata receiving the change, idle ones included, and partitions it opens later start under the new values. A raised retention therefore protects the backlog on every partition, and a lowered one frees space on every partition at the next retention pass.
Delete a topic¶
curl -i -u "$AUTH" -X DELETE "$NARAD/v1/topics/orders"
HTTP/1.1 204 No Content
Date: Mon, 28 Sep 2026 19:25:15 GMT
A delete cannot be undone
Deleting a topic removes its settings and every message it holds, on every node, and detaches its fan-out children. There is no way to get it back.
- The
204comes once the topic's metadata is deleted. From then on the topic is gone for every client, even if a node that was down still has its files; that node removes them when it next starts. - Consumers waiting in a long poll on the topic are answered at once, with
204or404. - Deleting a topic that is already gone gets
404. - With the CLI,
narad topic rm ordersasks for confirmation first;--forceskips the question.
Next steps¶
- Produce messages: send messages to the topic you created.
- Enforce schemas on a topic: refuse payloads that do not match a JSON Schema.
- Fan out and delay messages: copy a topic's messages to other topics, now or later.