---
title: "Withdraw a document"
method: POST
path: "/v1/projects/{project_id}/documents/{document_id}/withdraw"
tags: ["Documents"]
---

# Withdraw a document

`POST /v1/projects/{project_id}/documents/{document_id}/withdraw`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/documents/{document_id}/withdraw`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

Takes a document out of every default read while keeping its history. The withdrawal is archived as a **tombstone version**, so one mechanism answers what a document holds now and there is no second lifecycle flag for a reader to miss.

Its chunks are dropped from the knowledge index, so a withdrawn document costs a live search nothing. The content is not lost with them: it is in the version before the tombstone, which [`POST /v1/projects/{project_id}/documents/{document_id}/versions/{version}/restore`](/docs/api/documents/restore-document-version) re-chunks from.

`DELETE` stays what it is — permanent, and it removes the backing file. Withdrawal is the reversible act. It does not apply under `/.system/`: a platform-written document keeps the owning module's lifecycle.

## Path parameters

- `document_id` string, required

## Headers

- `If-Match` string

## Request body

- object
  - `version_label` string, nullable: Optional tag for the tombstone version this write archives.
  - `expected_version` integer, nullable: The version the caller believes the resource holds. When the resource is at any other version the write is refused with `409 VERSION_CONFLICT` and nothing is written; `meta.current_version` on that response names the version in force. Omit it to write unconditionally. Omitting it does not make the write unordered: two writes that reach the server together are still serialized, and the one whose version was taken first is refused the same way. What the field adds is refusing a write whose author read the resource some time ago and has not seen what happened since. The `If-Match` header carries the same precondition for a client that prefers the HTTP spelling. Sending both with different versions is `400 VALIDATION_FAILED`.

## Response `200`

The withdrawn document

- DocumentRecord
  - `id` string: Document ID
  - `file_id` string: Underlying file ID
  - `project_id` string: Project ID
  - `path` string, nullable: Logical path of the document within the project (e.g. /reports/q1.txt)
  - `filename` string: Original filename
  - `content_type` string: Media type of the source file the document was ingested from. Absent when the underlying file is gone.
  - `size` integer: File size in bytes
  - `status` 'pending' | 'processing' | 'ready' | 'failed' | 'withdrawn': Ingestion lifecycle state. `pending` — enqueued; `processing` — chunks being extracted and embedded; `ready` — fully indexed; `failed` — processing error (see the `error` field on `GET /documents/{id}/status`); `withdrawn` — taken out of listings and search, with its chunks dropped, and restorable from an earlier version.
  - `version` integer: The document's content version, starting at 1. Every write that changes the content or its annotations archives the state it replaced and increments this.
  - `content` string, nullable: Text content (only present on getDocument, and only when status is ready)
  - `chunk_strategy` 'page' | 'whole' | 'size': The chunk strategy the document was last (re-)ingested with. Absent when the default (`whole`) was used — the mapper omits the key rather than sending `null`.
  - `chunk_size` integer: Window size in characters used when `chunk_strategy=size`. Absent otherwise.
  - `chunk_overlap` integer: Overlap in characters between consecutive windows used when `chunk_strategy=size`. Absent otherwise.
  - `created_at` string, date-time
  - `updated_at` string, date-time

## Other responses

- `400`: Bad Request — the document is filed under the reserved root
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Document not found
- `409`: The document is already withdrawn (`DOCUMENT_ALREADY_WITHDRAWN`), or it has moved past the version this write read (`VERSION_CONFLICT`, with `meta.current_version`).

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
