← All lessons/Working with Records
7
Working with Records

Soft Delete & Tombstones

How deletion works in an append-only system, what tombstones are, and how to list deleted records.

Prerequisite: Lesson 6 complete

What you'll learn

  • DELETE /v1/agents/:id/records/:hash — writes a tombstone record
  • flags & 0x02 — the tombstone bit
  • Scan without vs with include_tombstones: true
  • as_of before the tombstone — deleted record reappears
  • TTL sweeper vs manual delete — comparison
Challenge

Write 3 records. Delete the middle one. Scan without tombstones — how many? With? as_of before delete — how many?

Interactive Walkthrough

What you'll learn

How deletion works in an append-only system, what tombstones are, and how to list and skip deleted records.

Why deletion is different

SapixDB never removes bytes from disk. A "delete" appends a tombstone record — a special record with flags & 0x02 = 1 that marks a content_hash as logically deleted. The original record remains forever (required for audit trails and time travel).

Delete a record by hash

curl -s -X DELETE \
  "http://localhost:7475/v1/agents/users/records/<HASH>" \
  -H "Authorization: Bearer spx_root_YOUR_ROOT_KEY" \
  | python3 -m json.tool

Response: the tombstone record's own content_hash.

What happens after deletion

  • GET /v1/agents/users/records/<HASH> — returns 404 (logically deleted)
  • POST /v1/agents/users/query with type: "scan" — by default excludes tombstoned records
  • type: "as_of" at a timestamp before the tombstone — the record is visible (history preserved)

Include tombstoned records in a scan

curl -s -X POST http://localhost:7475/v1/agents/users/query \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer spx_root_YOUR_ROOT_KEY" \
  -d '{"type": "scan", "limit": 100, "include_tombstones": true}' \
  | python3 -m json.tool

Records with "tombstone": true in the response are logically deleted. The original payload is still in the tombstone record for audit.

TTL vs manual tombstone

TTL (ttl_ms)Manual delete
Who creates tombstoneBackground sweeperYour API call
WhenAfter ttl_ms msImmediately
Use caseSessions, caches, temp dataUser-initiated deletes
ReversibleNoNo

Challenge

Write 3 records. Delete the middle one. Scan without include_tombstones — how many records? Scan with it — how many? Use as_of (timestamp before the delete) — does the deleted record appear?

---

← Previous
Lesson 6: Reading Records in Depth
Next →
Lesson 8: Batch Writes