Tether

Forget versus delete

Two different operations, two different guarantees. Choosing the wrong one is either a compliance problem or a data-loss problem, so this guide is mostly about the distinction.

Both require the delete operation on your grant — not write. Hiding a fact is destructive from a reader's point of view.

Every response below was captured from a real run.

The distinction

forget delete
Intent stop recalling this erase this
Rows preserved purged
Status 204 202 with a progress id
Reversible no endpoint for it in v0.1 no
Use when a fact is stale or unwanted in recall a subject asks for erasure

Forget is suppression. Delete is erasure. If someone exercises a right to be forgotten, forget is the wrong call — the content is still in the database.

Forget

curl -X POST "$TETHER/v1/anchors/$ID/forget" \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: your-unique-key' \
  -d '{"space":"default"}'
HTTP 204

Afterwards the anchor is gone from reads:

GET /v1/anchors/$ID  →  HTTP 404

One nuance that surprises people. Forget applies to the current version, not the whole chain. A superseded earlier version stays readable by direct id:

GET /v1/anchors/$OLDER_VERSION  →  HTTP 200

That is deliberate — forget removes a fact from recall while preserving history — but it means forget alone does not make a chain's content unreachable. If that is what you need, use delete.

Delete

curl -X DELETE "$TETHER/v1/anchors/$ID" \
  -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: your-unique-key' \
  -d '{"space":"default"}'
{
  "deletionId": "del_01M2Y02ED979FQG90FQY8J3R2F",
  "state": "pending",
  "stages": { "d1": "done", "vectors": "pending" }
}

202, not 204. This is not evasion. Deletion spans two systems: rows in D1 and vectors in the search index. The D1 half is synchronous and already done by the time you get this response. The vector half is asynchronous, and claiming otherwise would be a compliance statement Tether cannot support.

Delete targets the entire chain, not one version.

Progress

curl "$TETHER/v1/deletions/$DELETION_ID?space=default" -H "Authorization: Bearer $KEY"
{
  "id": "del_01M2Y02ED979FQG90FQY8J3R2F",
  "state": "pending",
  "stages": { "d1": "done", "vectors": "pending" },
  "createdAt": 1789860592041,
  "completedAt": null
}

Poll until state is done and stages.vectors is done. Only then is the content gone from both systems. completedAt is null until it is.

Backup expiry is reported separately and is not part of this response.

The marker, and why deletion sticks

Deletion writes a deletion_marker keyed on the chain, and the marker outlives the rows. Three things consult it:

That last one is the subtle case, and it is the reason deletion is safe despite being asynchronous.

Idempotency

Both operations require an Idempotency-Key. Two copies of one delete request produce one deletion job with one id — never two ids reporting on the same work.