---
title: "Delete specific events by id (with cascade)"
method: DELETE
path: "/api/v1/ackdb/data/events"
tags: ["ackDB"]
---

# Delete specific events by id (with cascade)

`DELETE /api/v1/ackdb/data/events`

## Request

- Base URL: `http://localhost:3009`
- URL: `http://localhost:3009/api/v1/ackdb/data/events`
- Auth: HTTP bearer

Surgically delete events by id. Events are append-only - this is the supported way to remove bad data (test / duplicate / wrong-entity events) and to correct a wrong-field event: **delete it, then re-ingest the corrected one.**

**Why delete enables correction:** the idempotency key lives on the event's raw payload. Re-pushing a corrected event with the same key is normally deduplicated (silently dropped). Deleting the event also deletes its raw payload, which frees the idempotency key - so the corrected re-push is accepted as a new event.

**What gets deleted per event (atomic, one transaction):** the event row, its raw payload (frees the idempotency key), its attached files (rows + stored blobs), the affected entity+source pairs' embedding chunks (except chunks a surviving file still references), and every conversation built from a deleted payload — plus an audit row in `event_deletions`.

**Derived content is invalidated, never left stale:** deleted text must not stay semantically searchable or citable. Surviving payloads of the affected pairs are queued for re-embedding (`pendingRebuild` reports each pair); until the embed/conversations pipelines next run, search for those pairs is missing — deliberately.

**Refusals (409, whole request, fail-closed):** `SYSTEM_EVENT_NOT_DELETABLE` when any id is an ackDB bookkeeping event (source `system`/`member` — the audit log of state that still stands; undo the state via its inverse API instead). `PENDING_INGEST_CONFLICT` when a queued ingest shares an idempotency key with a to-be-deleted payload and would resurrect it (retry after the queue drains).

**Deliberately retained (reported in `retained`):** the affected entities' identity links — they are why a corrected re-ingest lands on the SAME entity — and any entity the delete leaves without source events/payloads (entity delete does not exist; the row stays, resolvable via its links).

**IDs-only by design** - no filter/query selectors. Look up ids first via the read API. Unknown ids are reported in `notFound`, never fatal. Use `dryRun: true` to preview the identical report without mutating. **This is a destructive, irreversible operation** (re-ingest from the source to recover).

## Request body

- AckdbDeleteEventsRequest: Request body for DELETE /data/events. IDs-only by design (no filter selectors) to keep the blast radius minimal - look up ids via the read API first.
  - `ids` string[], required: Event ids (ULIDs) to delete. Duplicates are collapsed; unknown ids are reported in `notFound`.
  - `dryRun` boolean: When true, resolve and count what WOULD be deleted without mutating anything.
  - `reason` string: Optional reason, stored in the event_deletions audit row.

## Response `200`

Delete (or dry-run preview) completed. Returns per-table counts, what was deliberately retained, and the per-pair pending-rebuild report.

- AckdbDeleteEventsResponse: Response from DELETE /data/events. A committed delete returns `deleted` (per-table counts); a dry-run returns `resolved` + `wouldDelete` with the identical report. `retained` says what was deliberately left standing; `pendingRebuild` reports, per entity+source pair, the derived content removed now and the surviving payloads queued for re-embedding.
  - `data` object, required
    - `dryRun` boolean, required: Whether this was a preview (true) or a committed delete (false).
    - `deleted` object: Per-table counts actually deleted (committed delete only).
      - `events` integer
      - `rawPayloads` integer
      - `files` integer
      - `contentChunks` integer: Embedding chunks deleted — the affected pairs' message-derived chunks plus the deleted files' chunks (chunks a surviving file references are kept).
      - `conversations` integer: Conversation rows deleted because they contained a deleted payload.
      - `blobs` integer: File blobs deleted from the file store (best-effort, post-commit).
    - `wouldDelete` object: Per-table counts that WOULD be deleted (dry-run only). Same keys as `deleted`; `blobs` equals `files` (one stored blob per attached file).
      - `events` integer
      - `rawPayloads` integer
      - `files` integer
      - `contentChunks` integer
      - `conversations` integer
      - `blobs` integer
    - `resolved` object[]: Per-id resolution of the matched events (dry-run only).
      - `id` string
      - `entityId` string
      - `source` string
      - `eventType` string
      - `summary` string
      - `timestamp` string, date-time
      - `linkedRawPayloadId` string, nullable
      - `fileCount` integer
    - `notFound` string[], required: Requested ids that matched no event (reported, never fatal).
    - `retained` object[], required: What the delete deliberately left standing: `identity_links` are kept so a corrected re-ingest resolves to the same entity; an `entity` entry flags a company the delete left without source events/payloads (entity delete does not exist — the row stays, resolvable via its links).
      - `kind` 'identity_links' | 'entity', required
      - `entityId` string, required
      - `count` integer: identity_links only — how many links the entity keeps.
      - `reason` string, required
    - `pendingRebuild` object[], required: Per affected entity+source pair: derived content removed in this transaction and the surviving payloads queued for re-embedding. Until the embed/conversations pipelines next run, semantic search and conversation answers for the pair are missing — deliberately (missing is honest; stale is not).
      - `entityId` string, required
      - `source` string, required
      - `contentChunksDeleted` integer, required
      - `conversationsDeleted` integer, required
      - `payloadsQueuedForReembed` integer, required
    - `rebuildNote` string: Present when pendingRebuild is non-empty — what is missing and how it comes back.

## Other responses

- `400`: Invalid request body - empty `ids`, more than 1000 ids, or malformed JSON.
- `409`: Refused fail-closed (no partial deletes): `SYSTEM_EVENT_NOT_DELETABLE` — a requested id is an ackDB bookkeeping event (source `system`/`member`); the offending ids are in `error.details.events`, undo the state via its inverse API instead. `PENDING_INGEST_CONFLICT` — a queued ingest shares an idempotency key with a to-be-deleted payload; retry after the queue drains.

---

[API](https://skmtc.dev/floqer/apis/floqer-public-api.md) - [All operations](https://skmtc.dev/floqer/apis/floqer-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/floqer/apis/floqer-public-api/revisions/fce50dc07b20?raw)
