Skip to content

Document Management

Once a document is ingested into a Grill project, three admin endpoints let you manage it:

EndpointPurpose
GET /grill/docsList all documents in the project namespace.
GET /grill/docs/{docId}Get metadata for one document.
DELETE /grill/docs/{docId}Remove a document's vectors and storage from the namespace.

All three require a project API key (Authentication).

List documents

bash
curl -sS "$GRILL/grill/docs" \
  -H "authorization: Bearer $GRILL_KEY" | jq .

Response (ListDocsResponse):

json
{
  "namespace": "project_docs_rag_4f2",
  "total_documents": 3,
  "documents": [
    {
      "doc_id": "annual-report-2025",
      "title": "Annual Report 2025",
      "language": "en",
      "filename": "annual-report.pdf",
      "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-…"
    },

  ]
}

The documents array is the authoritative source for doc_id values. Use the exact doc_id string in doc_filter (on /grill/search) and as the {docId} path parameter on the per-doc endpoints.

source_job_id ties the document back to the /grill/ingest job that created it — useful for audit and incident replay.

Inspect one document

bash
curl -sS "$GRILL/grill/docs/annual-report-2025" \
  -H "authorization: Bearer $GRILL_KEY" | jq .

Response (DocInfo):

json
{
  "doc_id": "annual-report-2025",
  "title": "Annual Report 2025",
  "language": "en",
  "filename": "annual-report.pdf",
  "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-…"
}

The same shape as a single entry from GET /grill/docs. Field semantics:

FieldMeaning
doc_idStable identifier within this project namespace.
titleDetected from document metadata when present, otherwise from filename.
languageDetected primary language (BCP-47 short code).
filenameThe original Content-Disposition filename at ingest.
pagesSource page count (when applicable).
chunkset_count / chunk_countHow many chunksets and chunks were extracted. Useful for sanity-checking the pipeline.
image_count / table_countNumber of figures and tables Grill identified.
ingested_atUTC timestamp the ingest job completed.
source_job_idThe job_id returned by the original POST /grill/ingest.

Delete a document

bash
curl -sS -X DELETE "$GRILL/grill/docs/annual-report-2025" \
  -H "authorization: Bearer $GRILL_KEY" | jq .

Response (DeleteDocResponse):

json
{
  "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 /grill/search calls will not surface its content. There is no soft-delete or recycle bin.
  • 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 Grill project.

Common patterns

Garbage-collect old docs by ingest date.

bash
curl -sS "$GRILL/grill/docs" -H "authorization: Bearer $GRILL_KEY" \
| jq -r '.documents
         | map(select(.ingested_at < "2026-01-01T00:00:00Z"))
         | .[].doc_id' \
| xargs -I{} curl -sS -X DELETE "$GRILL/grill/docs/{}" \
                -H "authorization: Bearer $GRILL_KEY"

Replace a doc with a fresh version.

bash
# 1. delete the old version
curl -sS -X DELETE "$GRILL/grill/docs/annual-report-2025" \
  -H "authorization: Bearer $GRILL_KEY"

# 2. ingest the new file
curl -sS -X POST "$GRILL/grill/ingest" \
  -H "authorization: Bearer $GRILL_KEY" \
  -H "content-type: application/octet-stream" \
  -H 'content-disposition: attachment; filename="annual-report-2025.pdf"' \
  --data-binary @annual-report-2025.pdf

This 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 Grill client:

python
from poma import Grill

g = Grill()

# 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:

python
from datetime import datetime, timezone
from poma import Grill

cutoff = datetime(2026, 1, 1, tzinfo=timezone.utc)
with Grill() as g:
    for d in g.list_docs():
        if datetime.fromisoformat(d.ingested_at) < cutoff:
            g.delete_doc(d.doc_id)

Full surface: Grill.list_docs / Grill.get_doc / Grill.delete_doc. Async equivalents on AsyncGrill.

Errors

StatusCauseNotes
400Empty docId path parameterProvide the exact doc_id from DocInfo.
401Missing/invalid Bearer tokenUse a project API key.
403Project lacks Grill accessCreate or switch to a Grill project.
404Doc is not in this project namespaceConfirm with GET /grill/docs. Document is project-scoped.
502 / 503Upstream Grill / proxy errorRetry with backoff.

Next

  • API reference — full request/response shapes for every /grill/* endpoint.