Deletion
Permanently remove records from a topic by seq range (before_seq) and/or by tag
match (match). A delete removes records that exist right now: it is permanent,
effective immediately for every reader, silent (never a tombstone), and point-in-time —
future records with the same tag are never affected.
This page covers the delete request grammar, the semantics of each before_seq/match
combination, the response, and the errors. Deletion is the voluntary-removal half of the
data model; for why it never produces a tombstone, see
the deletion guarantee.
POST /v0/topics/:topic/delete — remove records
Permanently remove records by seq range, tag match, or both. Deletion is:
- Permanent — deleted records are gone for good; there is no un-delete in
/v0. - Effective immediately — invisible to all reads at once (diff, topic-state
count/bytes, and SSE); the reader’s cursor simply advances past the deleted seqs. - Asynchronous, no compaction / no reclaim — records are logically gone instantly (work runs off the call path), but a deleted record stays on disk, just marked: on disk a delete flips an in-place delete-flag byte in segment files (the WAL stays append-only), and the only space released is a whole segment dropped when a delete clears it entirely.
- Silent — a delete never produces a tombstone. Tombstones are reserved for involuntary cap/TTL loss.
- Point-in-time — it is not a standing filter. It removes only records present at call time; future records, even with a matching tag, are never affected.
A delete advances the topic’s earliest_seq (its first live seq) but not its
evict_floor (the involuntary cap/TTL floor), so reading across a purely-deleted gap is
silent — there is no way for a lagging consumer to “miss a deletion,” because the record is
gone for all readers at the same instant.
Request body
At least one of before_seq / match is required (else 400 invalid_request); supply
both to AND them.
# Cancel one order by exact tag match.
curl -X POST $TOPICS/v0/topics/orders/delete \
-H 'content-type: application/json' \
-d '{ "match": ["tag", "Eq", "order-7731"] }'# → 200 OK
{ "topic": "orders", "deleted": 1, "earliest_seq": 480001, "head_seq": 480234,
"count": 233, "bytes": 478342,
"performance": { "server_total_ms": 0.12 } }| Field | Type | Req? | Meaning |
|---|---|---|---|
before_seq | u64 | one of | Delete every record with $seq < before_seq (a snapshot / drop-prefix by seq — logical removal, not physical compaction). |
match | predicate | one of | A tag predicate: ["tag","Eq","X"] exact match, or ["tag","Glob","X*"] trailing-prefix only (a literal prefix followed by a single *). A bare string is shorthand for Eq: "match": "order-7731" == ["tag","Eq","order-7731"]. |
Glob is deliberately limited to a trailing * — a literal prefix plus one *. Full
glob/regex is excluded so a tag delete is always either a point lookup or a bounded
range scan over the per-topic tag index, never a full-log scan. Records with no tag are
never matched by match.
Semantics by combination
| Body | Effect |
|---|---|
before_seq only | Delete every record with $seq < before_seq (a snapshot / drop-prefix by seq). |
match only | Delete every existing record whose tag matches, bounded by the current head at call time. Point-in-time: future records with that tag are not deleted (e.g. revoke a kicked user’s chat history with match ["tag","Glob","user-1042:*"]). |
match + before_seq | Delete records with $seq < before_seq AND a matching tag (e.g. publish v2 of a message then delete its prior versions, keeping the new one: match ["tag","Eq","user-1042:msg-5"], before_seq = <seq of v2>). |
Response
{ "topic": "orders",
"deleted": 14,
"earliest_seq": 480001,
"head_seq": 480234,
"count": 234,
"bytes": 478820,
"performance": { "server_total_ms": 0.12 } }| Field | Type | Meaning |
|---|---|---|
topic | string | The topic name. |
deleted | int | Count of records removed by this call. |
earliest_seq | u64 | New first live seq (advanced past any deleted prefix). |
head_seq | u64 | Topic log end (unchanged by a delete). |
count | int | Records currently retained, already excluding the deleted ones. |
bytes | int | Currently retained payload bytes, already excluding the deleted ones. |
performance | object | Timing block. |
Errors
error.code | HTTP | When |
|---|---|---|
invalid_request | 400 | Neither before_seq nor match supplied; a malformed match tuple; a match op outside {Eq, Glob}; or a Glob pattern without a trailing *. |
topic_not_found | 404 | The topic does not exist (delete never auto-creates). |
See also
- Deletion guarantee — why a delete is silent, permanent, and point-in-time.
- Tombstones — the dual watermark and why deletes never tombstone.
- Reading (diff) — how a deleted gap reads on the consume path.
- Writing Records — the
tagfield a tag delete matches against.