---
title: "Delete a human (company timelines keep their evidence)"
method: DELETE
path: "/api/v1/ackdb/persons/{personId}"
tags: ["ackDB"]
---

# Delete a human (company timelines keep their evidence)

`DELETE /api/v1/ackdb/persons/{personId}`

## Request

- Base URL: `http://localhost:3009`
- URL: `http://localhost:3009/api/v1/ackdb/persons/{personId}`
- Auth: HTTP bearer

Hard-deletes the person, archived and restorable, with their durable keys frozen as a tombstone.

**Events are DETACHED, never deleted.** A Slack message in a customer's channel is that COMPANY's history — deleting the human must not rewrite it. Every `person_id`/`contact_id` pointer is nulled and archived, so a restore re-attaches them. Embedding chunks and conversations are untouched: the text did not change, only its attribution.

**Goes:** the person row, its durable keys, person attributes, merge candidates naming it, its merge tombstones, every affiliation edge (with links, traits and relationship ownership), and its segment memberships (each preceded by an `exit`).

## Path parameters

- `personId` string, required

## Query parameters

- `dryRun` boolean

## Request body

- object
  - `reason` string: Why — stored on the archive header.
  - `actor` string: Who — console user email; null for service calls.
  - `dryRun` boolean: Same as ?dryRun=true, for clients that cannot send query params.

## Response `200`

Deleted (or previewed). Returns what was deleted, what was detached, what was deliberately retained, and the tombstone that now guards the record's identifiers.

- AckdbRecordDeletionResponse
  - `data` object
    - `dryRun` boolean
    - `deletionId` string, nullable: Null on a dry run — no header is written.
    - `kind` 'company' | 'person' | 'contact'
    - `record` object
      - `id` string
      - `label` string, nullable
    - `deleted` AckdbRecordDeletionCounts: Per-table row counts a delete removed. Every field is always present (0 when untouched).
      - `events` integer: Deleted. 0 for person/contact deletes — those DETACH instead.
      - `rawPayloads` integer
      - `files` integer
      - `contentChunks` integer: Derived: deleted, never archived; rebuilt by `npm run embed`.
      - `conversations` integer: Derived: rebuilt by `npm run conversations`.
      - `contacts` integer
      - `persons` integer: The record itself (person delete) or husks left behind.
      - `entities` integer: The record itself plus any merge tombstones pointing at it.
      - `identityLinks` integer
      - `contactIdentityLinks` integer
      - `personIdentityLinks` integer
      - `traits` integer: entity_traits + contact_traits + person_traits (attributes ride the same rails).
      - `memberAssignments` integer
      - `mergeCandidates` integer
      - `segmentExits` integer: Memberships removed, each preceded by an `exit` in segment_changes.
      - `relationships` integer: Explicit company relationship episodes deleted.
      - `contactRelationships` integer: Contact hierarchy episodes deleted.
    - `detached` object: Pointers nulled on rows that SURVIVE — events kept on company timelines, agent sessions.
      - `events` integer
      - `agentSessions` integer
    - `tombstone` object
      - `active` boolean
      - `identifiers` object[]: The frozen `(type, value)` pairs, stored exactly as the resolvers store them (domains as ingested, emails lowercased, LinkedIn canonical) — the guard compares with plain equality.
        - `type` string
        - `value` string
    - `retained` object[]: What the delete deliberately left standing, and why (the #419 honesty trio).
      - `kind` string
      - `count` integer
      - `reason` string
    - `pendingRebuild` object[]: Always empty on a delete (a deleted company has nothing left to rebuild content for, and a person/contact delete does not touch content). A RESTORE is what populates it.

## Other responses

- `404`: No such record.
- `409`: `MERGED_ENTITY` / `MERGED_PERSON` — the reference names a merge tombstone (delete the survivor; its tombstones go with it). `PENDING_INGEST_CONFLICT` — a queued ingest would resurrect the data or dead-letter on the missing record; retry once 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)
