> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# 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](https://tonbo.dev/docs/streams/append.md), [Read and tail](https://tonbo.dev/docs/streams/read.md) and [Snapshots and retention](https://tonbo.dev/docs/streams/snapshots.md) 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](https://tonbo.dev/docs/streams/append.md#append)).

**Do**

```shell
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}'
```

**Don’t**

```shell
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.

**Do**

```shell
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}'
```

**Don’t**

```shell
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](https://tonbo.dev/docs/streams/troubleshooting/stream.md#producer-replaced)).

### 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](https://tonbo.dev/docs/streams/pricing.md#batch-messages)).

**Do**

```shell
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}]'
```

**Don’t**

```shell
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](https://tonbo.dev/docs/streams/append.md#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](https://tonbo.dev/docs/streams/pricing.md#stream-per-run)).

**Do**

```shell
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"
```

**Don’t**

```shell
# Never closed, never deleted: kept, and billed, for good.
tonbo stream create run-42 --content-type application/json
```
