Api / Memory / Entries

POST/v1/memory/entries/:id/invalidate

Auth: tenant — Status: stable

Soft-delete an entry by stamping its valid_to field. The row stays in the database and remains reachable through historical reads (GET/v1/memory/entries?as_of=<epoch>); it disappears from the default active set used by /v1/memory/entries, /v1/memory/entries/:id, and the agent-facing /mem search / /mem entries.

Distinct from DELETE — invalidation preserves the row for audit and replay. See Structured memory → Temporal model for the rationale.

Request

Path paramTypeDescription
idintEntry id.

Body

The body is optional. When present, it's a JSON object with one optional field:

FieldTypeDescription
whenintegerEpoch seconds to stamp into valid_to. Default = wall-clock now(). Must be ≥ 0.

Pass an explicit when for replay scenarios (importing a fact that was already retired at a known historical moment). Most live invalidations omit it.

curl -X POST \
  -H "Authorization: Bearer atr_…" \
  http://arbiter.example.com/v1/memory/entries/42/invalidate
curl -X POST \
  -H "Authorization: Bearer atr_…" \
  -H "Content-Type: application/json" \
  -d '{"when": 1735689600}' \
  http://arbiter.example.com/v1/memory/entries/42/invalidate

Response

200 OK

{ "invalidated": true, "id": 42 }

Failure modes

StatusWhenBody
400Invalid JSON; negative when.{"error": "..."}
401Missing / invalid bearer.{"error": "..."}
404Id doesn't exist for this tenant or the row was already invalidated. The two cases collapse here because they're indistinguishable to a default reader (an invalidated row isn't visible via GET/v1/memory/entries/:id).{"error": "entry not found or already invalidated"}
409A concurrent request invalidated the row between the active-state probe and the UPDATE. Rare; benign — the after-state matches the caller's intent.{"error": "entry was already invalidated by a concurrent request"}

The merge of "not found" and "already invalidated" into a single 404 is intentional. The runtime doesn't expose a "fetch invalidated row by id" point read — that path would let callers trivially bypass the active-only filter; instead, historical reads happen via as_of on the list endpoint, which scopes the audit window deliberately.

Idempotency

Invalidation is one-directional and idempotent under repeated calls. Once a row has valid_to set, subsequent invalidate requests return 404 without changing the timestamp. To "un-invalidate" a row, hard-delete it (DELETE/v1/memory/entries/:id) and re-create it with the desired content.

Ordering vs. update_entry

PATCH/v1/memory/entries/:id (and the agent's /mem add entry repair path) only modify rows where valid_to IS NULL. An invalidated row is immutable through the normal write surface — historical content stays as it was at the moment of invalidation. To correct a historical record, hard-delete and re-create.

Effect on relations

Memory relations whose source or target was invalidated stay in the database. They cascade-DELETE only when their endpoint is hard-deleted via DELETE/v1/memory/entries/:id. Consumers that want "active relations only" semantics should check both endpoint entries' valid_to IS NULL at read time.

See also