---
title: "Delete Child Document"
method: DELETE
path: "/documents/{child_id}"
tags: ["documents"]
---

# Delete Child Document

`DELETE /documents/{child_id}`

## Request

- Base URL: `https://api.projectindi.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.projectindi.com/documents/{child_id}`
- Auth: none declared

Delete a document. Storage, the timeline card, embeddings and extracted notes follow the row.

Storage cleanup, the L4 ``timeline_entries`` projection and the
``child_notes`` extracted from the document are DB triggers on
``documents``; embeddings and ingestion_jobs are FK CASCADE; chat_messages /
email_attachments references are FK SET NULL. Migration 20261007060803
lists what the delete does and does not reach.
The timeline cleanup used to live here instead, and this was one of three
handlers that delete a documents row - the other two never got it, including
the ``deleteDocumentsBatch`` mutation every client actually calls, which is
how a deleted record kept an undeletable card on the child's timeline.
``trigger_cleanup_document_timeline_entry`` (20260831072639) owns it now, in
the same transaction as the row.

Idempotent: a delete for a row that's already gone (or never visible to this
caller via RLS) returns 200 with ``deleted: False`` plus ``already_deleted: True``
when the caller is a family member. The client's stated intent - "this should
not exist" - is satisfied either way. This protects callers whose previous
attempt timed out server-side but actually committed (the prod 2026-05-27
case for document 1e23ffad-bbe3-43b3-b2c0-f7bc3fe6b939). A ``document_id``
that is not a syntactic uuid reaches that same answer rather than a 500 -
it addresses no row by construction (#8840).

Authentication: JWT required
Authorization: RLS - family members of the child can delete (see
    ``Family members can delete documents`` policy, migration
    ``20251212110001_family_unit_invites.sql``). On the idempotent retry
    path the user-client DELETE matched nothing and so proves nothing about
    the caller, hence the explicit family check before answering anything
    other than "not found".

Body:
    document_id: UUID of the document to delete

Response:
    ``{"success": True, "deleted": True}`` when the document row was
    deleted, or ``{"success": True, "deleted": False, "already_deleted":
    True}`` on the idempotent retry path. Existing clients only check
    ``success``; the extra markers are purely informational for debugging.

## Path parameters

- `child_id` string, required

## Request body

- DeleteDocumentRequest: Request body for deleting a document. Accepts both camelCase and snake_case.
  - `documentId` string, required

## Response `200`

Successful Response

- unknown

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/projectindi/apis/indi-agent-api-first-principles.md) - [All operations](https://skmtc.dev/projectindi/apis/indi-agent-api-first-principles/llms.txt) - [OpenAPI document](https://skmtc.dev/projectindi/apis/indi-agent-api-first-principles/revisions/25a7813e21fd?raw)
