The built-in API cards
Ingestion API — push documents in
The Ingestion API lets your own application push documents into the knowledge base over HTTP, instead of a connector pulling them. It's a source like any other — what it ingests is indexed, access-controlled, analysed and retained exactly the same way. This article is the complete reference.
Setup
- Documents → Ingestion API/MCP: name it, and choose which groups may read what it ingests (that becomes each document's default access).
- Open the source and copy its endpoint and key from “API endpoint & key”. Rotate issues a new key and invalidates the old at once.
Authentication
Send the key in an Authorization: Bearer header on every request. The key belongs to this one source — it can ingest, never message. A missing or wrong key returns 401.
Endpoints
- POST /api/v1/ingest/documents — create or replace a document (external_id in the body)
- PUT /api/v1/ingest/documents/{external_id} — create or replace by id
- POST /api/v1/ingest/documents/batch — up to 100 at once ({"documents": [...]})
- GET /api/v1/ingest/documents — list with indexing status (query: limit, offset, prefix)
- GET /api/v1/ingest/documents/{external_id} — one document and its status
- DELETE /api/v1/ingest/documents/{external_id} — remove it (?purge=true erases immediately and permanently)
- POST /api/v1/ingest/sync — re-index now
- GET /api/v1/ingest/status — store and index counts
Document fields
| external_id | Your stable id for the document (1–512 chars: letters, digits, . _ : / -, starting alphanumeric). Send the same id again to update the document in place. |
|---|---|
| title | Optional display title. |
| text | Inline content — markdown, HTML or plain text. Provide exactly one of text or content_base64. |
| content_base64 | Base64-encoded bytes for binary formats (PDF, DOCX…). 50 MB per document, 100 MB per request. |
| filename / content_type | Optional hints for format detection; derived from external_id and content when omitted. |
| acl | Optional list of groups (e.g. ["grp:support"]). Named groups must already exist — an unrecognised name is rejected rather than silently dropped. Omit it to use the source's own groups. |
| metadata | Optional free-form JSON stored with the document. |
Example
A single request that creates or replaces one document, in curl:
curl -X POST <base>/api/v1/ingest/documents \
-H "Authorization: Bearer <key>" \
-H "Content-Type: application/json" \
-d '{"external_id":"kb/refunds",
"title":"Refund policy",
"text":"# Refunds\nWe refund within 30 days.",
"acl":["grp:support"]}'
The same call in Python, checking the response and reading the error body when it fails:
import requests
resp = requests.post(
"<base>/api/v1/ingest/documents",
headers={"Authorization": "Bearer <key>"},
json={
"external_id": "kb/refunds",
"title": "Refund policy",
"text": "# Refunds\nWe refund within 30 days.",
"acl": ["grp:support"],
},
)
if resp.status_code == 202:
body = resp.json()
print("queued, job", body["job"], "->", body["uri"])
else:
# error bodies are {"detail": "..."}
print(resp.status_code, resp.json().get("detail"))
And in JavaScript, using fetch on Node 18 or later:
const resp = await fetch("<base>/api/v1/ingest/documents", {
method: "POST",
headers: {
"Authorization": "Bearer <key>",
"Content-Type": "application/json",
},
body: JSON.stringify({
external_id: "kb/refunds",
title: "Refund policy",
text: "# Refunds\nWe refund within 30 days.",
acl: ["grp:support"],
}),
});
const body = await resp.json();
if (resp.status === 202) {
console.log("queued, job", body.job, "->", body.uri);
} else {
// error bodies are { detail: "..." }
console.error(resp.status, body.detail);
}
Behaviour worth knowing
- Writes return 202 with a queued indexing job — pushed documents become searchable within moments, and GET shows each document's indexing status.
- DELETE tombstones the document; ?purge=true is the GDPR path — content and index entries are erased at once.
- Rate limit: 300 requests per minute per key (429 when exceeded). Content over 50 MB, or a whole request over 100 MB, returns 413.
- Every push and delete is recorded in the audit log.
Machine-readable spec for Postman/codegen: /api/v1/openapi.json.
Last updated 20 Sep 2026