Document Management
Once a document is ingested into an index4.ai project, three admin endpoints let you manage it:
| Endpoint | Purpose |
|---|---|
GET /docs | List all documents in the project namespace. |
GET /docs/{docId} | Get metadata for one document. |
DELETE /docs/{docId} | Remove a document's vectors and storage from the namespace. |
All three accept a project API key; account-level credentials work too when you send X-Project-ID, unless the project is protected (Authentication).
List documents
curl -sS "https://api.index4.ai/v1/docs" \
-H "authorization: Bearer $INDEX4AI_API_KEY" | jq .Response (ListDocsResponse):
{
"namespace": "project_docs_rag_4f2",
"total_documents": 3,
"documents": [
{
"doc_id": "annual-report-2025",
"title": "Annual Report 2025",
"language": "en",
"filename": "annual-report.pdf",
"canonical_url": "https://investors.example.com/2025/annual-report",
"pages": 84,
"chunkset_count": 142,
"chunk_count": 612,
"image_count": 38,
"table_count": 14,
"ingested_at": "2026-04-30T10:00:00Z",
"source_job_id": "550e8400-…",
"bm25_state": "ready"
},
…
]
}The documents array is the authoritative source for doc_id values. Use the exact doc_id string in doc_filter (on /search) and as the {docId} path parameter on the per-doc endpoints.
source_job_id ties the document back to the /ingest job that created it — useful for audit and incident replay.
Inspect one document
curl -sS "https://api.index4.ai/v1/docs/annual-report-2025" \
-H "authorization: Bearer $INDEX4AI_API_KEY" | jq .Response (DocInfo):
{
"doc_id": "annual-report-2025",
"title": "Annual Report 2025",
"language": "en",
"filename": "annual-report.pdf",
"canonical_url": "https://investors.example.com/2025/annual-report",
"pages": 84,
"chunkset_count": 142,
"chunk_count": 612,
"image_count": 38,
"table_count": 14,
"ingested_at": "2026-04-30T10:00:00Z",
"source_job_id": "550e8400-…",
"bm25_state": "ready"
}The same shape as a single entry from GET /docs. Field semantics:
| Field | Meaning |
|---|---|
doc_id | Stable identifier within this project namespace. |
title | Detected from document metadata when present, otherwise from filename. |
language | Detected primary language (BCP-47 short code). |
filename | The original Content-Disposition filename at ingest. |
canonical_url | Citation URL stamped by Core at ingest — e.g. the resolved target when ingesting from a short link / redirect. Empty when not applicable. |
pages | Source page count (when applicable). |
chunkset_count / chunk_count | How many chunksets and chunks were extracted. Useful for sanity-checking the pipeline. |
image_count / table_count | Number of figures and tables index4.ai identified. |
ingested_at | UTC timestamp the ingest job completed. |
source_job_id | The job_id returned by the original POST /ingest. |
bm25_state | State of the document's BM25 (lexical) index. |
attributes / attribute_schema | The typed attributes attached at ingest, and their declared types (see Ingestion). |
Delete a document
curl -sS -X DELETE "https://api.index4.ai/v1/docs/annual-report-2025" \
-H "authorization: Bearer $INDEX4AI_API_KEY" | jq .Response (DeleteDocResponse):
{
"doc_id": "annual-report-2025",
"vectors_deleted": 612,
"storage_deleted": true
}DELETE is destructive and scoped:
- Destructive. All vectors and storage for this document are removed. Subsequent
/searchcalls will not surface its content. - Scoped. Only the document is removed. The project, project API key, and all other documents are left untouched.
If you want to reset the entire project namespace at once, delete and recreate the project — see Create a project. (Deleted projects can be restored; see that page.)
Common patterns
Garbage-collect old docs by ingest date.
curl -sS "https://api.index4.ai/v1/docs" -H "authorization: Bearer $INDEX4AI_API_KEY" \
| jq -r '.documents
| map(select(.ingested_at < "2026-01-01T00:00:00Z"))
| .[].doc_id' \
| xargs -I{} curl -sS -X DELETE "https://api.index4.ai/v1/docs/{}" \
-H "authorization: Bearer $INDEX4AI_API_KEY"Replace a doc with a fresh version.
# 1. delete the old version
curl -sS -X DELETE "https://api.index4.ai/v1/docs/annual-report-2025" \
-H "authorization: Bearer $INDEX4AI_API_KEY"
# 2. ingest the new file
curl -sS -X POST "https://api.index4.ai/v1/ingest" \
-H "authorization: Bearer $INDEX4AI_API_KEY" \
-H "content-type: application/octet-stream" \
-H 'content-disposition: attachment; filename="annual-report-2025.pdf"' \
--data-binary @annual-report-2025.pdfThis pattern is safer than relying on re-ingest replacement, because the docId derivation is filename-sensitive — explicit delete-then-ingest avoids surprises if the filename changes between versions.
From the Python SDK
All three admin operations are typed methods on the Index4AI client:
from index4ai import Index4AI
g = Index4AI()
# List
docs = g.list_docs() # list[DocInfo]
for d in docs:
print(d.doc_id, d.filename, d.pages, d.source_job_id)
# Inspect one
info = g.get_doc("annual-report-2025") # DocInfo
# Delete one
result = g.delete_doc("annual-report-2025") # DeleteDocResponse
print(result.vectors_deleted, result.storage_deleted)DocInfo and DeleteDocResponse are dataclasses — the fields match the JSON shapes documented above. To garbage-collect old docs:
from datetime import datetime, timezone
from index4ai import Index4AI
cutoff = datetime(2026, 1, 1, tzinfo=timezone.utc)
g = Index4AI()
try:
for d in g.list_docs():
if datetime.fromisoformat(d.ingested_at) < cutoff:
g.delete_doc(d.doc_id)
finally:
g.close()Full surface: Index4AI.list_docs / Index4AI.get_doc / Index4AI.delete_doc. Async equivalents on AsyncIndex4AI.
Errors
| Status | Cause | Notes |
|---|---|---|
400 | Empty docId path parameter | Provide the exact doc_id from DocInfo. |
401 | Missing/invalid Bearer token | Use a project API key. |
404 | Doc is not in this project namespace | Confirm with GET /docs. Document is project-scoped. |
502 / 503 | Upstream / proxy error | Retry with backoff. |
Next
- API reference — full request/response shapes for every endpoint.