Managing Documents
Upload, list, inspect, and delete RAG documents using the project token or user JWT.
Authentication
Section titled “Authentication”All RAG endpoints accept either a project API token or a user JWT. Using a project token automatically scopes requests to that project — no project_id parameter required.
# Project token (recommended from notebooks/services)-H "X-API-Key: <project-token>"
# User JWT-H "Authorization: Bearer <jwt>"Uploading a Document
Section titled “Uploading a Document”Upload a file (multipart)
Section titled “Upload a file (multipart)”Send the file as multipart/form-data. The platform detects the MIME type automatically.
curl -X POST https://<domain>/api/rag/documents \ -H "X-API-Key: <project-token>" \ -F "file=@/path/to/document.pdf" \ -F "name=My Document" # optional — defaults to filenameFor requests authenticated with a user JWT, also include project_id:
curl -X POST https://<domain>/api/rag/documents \ -H "Authorization: Bearer <jwt>" \ -F "file=@/path/to/document.pdf" \ -F "project_id=<uuid>"Upload raw text (JSON)
Section titled “Upload raw text (JSON)”curl -X POST https://<domain>/api/rag/documents \ -H "X-API-Key: <project-token>" \ -H "Content-Type: application/json" \ -d '{ "name": "Release Notes v2.1", "text": "Version 2.1 introduces support for GPU scheduling..." }'Response
Section titled “Response”{ "id": "3f8a1b2c-0000-0000-0000-000000000000", "project_id": "a1b2c3d4-...", "name": "document.pdf", "content_type": "application/pdf", "chunk_count": 12, "status": "ready", "error": "", "created_by": "admin", "created_at": "2026-04-10T09:00:00Z"}Indexing is synchronous — the response is only returned once the document has been fully embedded and stored. Status will be "ready" on success or "failed" on error (with an error message).
Document statuses
Section titled “Document statuses”| Status | Meaning |
|---|---|
indexing | Extraction, chunking, and embedding in progress |
ready | Document is indexed and searchable |
failed | Indexing failed — check the error field |
Listing Documents
Section titled “Listing Documents”curl "https://<domain>/api/rag/documents?project_id=<uuid>" \ -H "X-API-Key: <project-token>"With a project token, project_id is inferred automatically:
curl https://<domain>/api/rag/documents \ -H "X-API-Key: <project-token>"Response
Section titled “Response”{ "documents": [ { "id": "3f8a1b2c-...", "project_id": "a1b2c3d4-...", "name": "document.pdf", "content_type": "application/pdf", "chunk_count": 12, "status": "ready", "error": "", "created_by": "admin", "created_at": "2026-04-10T09:00:00Z" } ]}Results are ordered newest first.
Getting a Single Document
Section titled “Getting a Single Document”curl https://<domain>/api/rag/documents/<document-id> \ -H "X-API-Key: <project-token>"Returns a single document object. Returns 404 if the document does not exist or belongs to a different project.
Deleting a Document
Section titled “Deleting a Document”Deleting a document removes both the metadata record from PostgreSQL and all associated vector points from Qdrant. The document’s content is no longer retrievable after deletion.
curl -X DELETE https://<domain>/api/rag/documents/<document-id> \ -H "X-API-Key: <project-token>"Returns 204 No Content on success.
From a Jupyter Notebook
Section titled “From a Jupyter Notebook”The project token and base URL are pre-injected as environment variables, so you can manage documents directly from notebooks:
import osimport requests
base_url = os.environ["FTN_BASE_URL"]api_key = os.environ["FTN_API_KEY"]
headers = {"X-API-Key": api_key}
# Upload a text documentresp = requests.post( f"{base_url}/api/rag/documents", headers=headers, json={ "name": "Project Runbook", "text": open("runbook.txt").read(), })doc = resp.json()print(f"Indexed {doc['chunk_count']} chunks — status: {doc['status']}")
# Upload a PDFwith open("spec.pdf", "rb") as f: resp = requests.post( f"{base_url}/api/rag/documents", headers=headers, files={"file": ("spec.pdf", f, "application/pdf")}, )print(resp.json())
# List all documentsdocs = requests.get(f"{base_url}/api/rag/documents", headers=headers).json()for d in docs["documents"]: print(d["id"], d["name"], d["chunk_count"], d["status"])
# Delete a documentrequests.delete(f"{base_url}/api/rag/documents/{doc['id']}", headers=headers)RAG Statistics
Section titled “RAG Statistics”For an admin overview of document counts and chunk totals across all projects:
curl https://<domain>/api/rag/stats \ -H "Authorization: Bearer <admin-jwt>"{ "projects": [ { "project_id": "a1b2c3d4-...", "project_name": "data-pipelines", "doc_count": 8, "chunk_count": 143, "ready_count": 8, "failed_count": 0 } ]}