API ReferenceKnowledge Base
Get an upload URL
POST /api/v1/knowledge-base/upload-url — get a presigned S3/MinIO URL to PUT a file directly.
POST /api/v1/knowledge-base/upload-urlGet a presigned PUT URL so you can upload a file directly to storage without proxying through zoxaAI. Two-step flow: get URL → PUT file → call process-document to start chunking + embedding.
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
filename | string | ✓ | The file's display name. Used to name the S3 key. |
mime_type | string | ✓ | Content type to enforce on upload (e.g. application/pdf). |
Response
{
"upload_url": "https://.../signed?...",
"document_uuid": "abc12345-...",
"s3_key": "knowledge_base/<org>/<uuid>/file.pdf"
}| Field | Notes |
|---|---|
upload_url | Presigned PUT URL valid for 30 minutes. Maximum 100 MB. |
document_uuid | UUID for the future document record. Pass it back to process-document. |
s3_key | Internal storage key. Pass it back to process-document. |
Examples
# 1. Get the URL
RESP=$(curl -s -X POST https://dashboard.zoxa.ai/api/v1/knowledge-base/upload-url \
-H "X-API-Key: zsk_..." \
-H "Content-Type: application/json" \
-d '{"filename":"product-faq.pdf","mime_type":"application/pdf"}')
UPLOAD_URL=$(echo "$RESP" | jq -r .upload_url)
DOC_UUID=$(echo "$RESP" | jq -r .document_uuid)
S3_KEY=$(echo "$RESP" | jq -r .s3_key)
# 2. PUT the file
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--upload-file ./product-faq.pdf
# 3. Trigger processing
curl -X POST https://dashboard.zoxa.ai/api/v1/knowledge-base/process-document \
-H "X-API-Key: zsk_..." \
-H "Content-Type: application/json" \
-d "{\"document_uuid\":\"$DOC_UUID\",\"s3_key\":\"$S3_KEY\",\"retrieval_mode\":\"chunked\"}"import fs from "node:fs";
// 1. URL
const { upload_url, document_uuid, s3_key } = await fetch(
"https://dashboard.zoxa.ai/api/v1/knowledge-base/upload-url",
{
method: "POST",
headers: { "X-API-Key": "zsk_...", "Content-Type": "application/json" },
body: JSON.stringify({ filename: "product-faq.pdf", mime_type: "application/pdf" }),
},
).then((r) => r.json());
// 2. PUT
await fetch(upload_url, {
method: "PUT",
headers: { "Content-Type": "application/pdf" },
body: fs.createReadStream("./product-faq.pdf"),
});
// 3. Process
await fetch("https://dashboard.zoxa.ai/api/v1/knowledge-base/process-document", {
method: "POST",
headers: { "X-API-Key": "zsk_...", "Content-Type": "application/json" },
body: JSON.stringify({ document_uuid, s3_key, retrieval_mode: "chunked" }),
});import httpx
# 1. URL
resp = httpx.post(
"https://dashboard.zoxa.ai/api/v1/knowledge-base/upload-url",
headers={"X-API-Key": "zsk_..."},
json={"filename": "product-faq.pdf", "mime_type": "application/pdf"},
).json()
# 2. PUT
with open("product-faq.pdf", "rb") as f:
httpx.put(resp["upload_url"], content=f.read(), headers={"Content-Type": "application/pdf"})
# 3. Process
httpx.post(
"https://dashboard.zoxa.ai/api/v1/knowledge-base/process-document",
headers={"X-API-Key": "zsk_..."},
json={
"document_uuid": resp["document_uuid"],
"s3_key": resp["s3_key"],
"retrieval_mode": "chunked",
},
)Single-call alternative
For server-side flows that already have the file in memory, POST /files handles upload + register in one multipart request — slightly simpler if you don't need direct browser upload.
Errors
| Status | detail | When |
|---|---|---|
500 | "Failed to generate presigned upload URL" | Storage backend isn't returning a URL — usually misconfiguration. |
Related
POST /knowledge-base/process-document— step 3POST /knowledge-base/create-from-text— skip the upload for plain textPOST /files— combined upload + create