Rules: writers
Last updated 29 October 2026
Check these rules before you rely on a program that appends: each keeps a message from being lost or doubled, or a stream from costing more than it must.
The requests keep the stream’s URL in STREAM, a key in TONBO_API_KEY, offsets in NEXT and OFFSET, and the stream’s incarnation in INCARNATION, as Append, Read and tail and Snapshots and retention do.
Send producer headers with every append
Without Producer-Id, Producer-Epoch and Producer-Seq, an append is not deduplicated, so a retry after a lost answer appends the message twice (see Append).
curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-H "Producer-Id: quickstart" \
-H "Producer-Epoch: 0" \
-H "Producer-Seq: 0" \
-d '{"n":0}'curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"n":0}'When an answer is lost, send the same request again
The same append sent again answers 204 with the first one’s offsets and appends nothing. The same message under a new sequence number is a new append, and a second copy.
curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-H "Producer-Id: quickstart" \
-H "Producer-Epoch: 0" \
-H "Producer-Seq: 1" \
-d '{"n":1}'curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-H "Producer-Id: quickstart" \
-H "Producer-Epoch: 0" \
-H "Producer-Seq: 2" \
-d '{"n":1}'Give each writer its own producer id, and stop when refused
Sequence numbers go up by exactly one per producer, so two writers under one id refuse each other’s appends. A writer that restarted and lost its count moves to a higher Producer-Epoch from sequence 0, and a writer refused with 403 for an older epoch stops: a newer instance has taken its place (see Troubleshooting).
Batch small messages into one append
A JSON array appends one message per element, in one request, and an append costs one write per 10 KiB of its body and at least one (see the worked example).
curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-H "Producer-Id: quickstart" \
-H "Producer-Epoch: 0" \
-H "Producer-Seq: 0" \
-d '[{"n":0},{"n":1},{"n":2}]'curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-H "Producer-Id: quickstart" \
-H "Producer-Epoch: 0" \
-H "Producer-Seq: 0" \
-d '{"n":0}'
curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-H "Producer-Id: quickstart" \
-H "Producer-Epoch: 0" \
-H "Producer-Seq: 1" \
-d '{"n":1}'
curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Content-Type: application/json" \
-H "Producer-Id: quickstart" \
-H "Producer-Epoch: 0" \
-H "Producer-Seq: 2" \
-d '{"n":2}'Close a stream when its writer is done, and let runs expire
A closed stream tells every reader at the end that it is over (see Close), and a stream with an expiry deletes itself, so a stream per run stops costing storage once its readers are done (see the worked example).
tonbo stream create run-42 --content-type application/json --expires-in 24h
curl -sS -i -X POST "$STREAM" \
-H "Authorization: Bearer $TONBO_API_KEY" \
-H "Stream-Closed: true"# Never closed, never deleted: kept, and billed, for good.
tonbo stream create run-42 --content-type application/json