Upload Flow
Client-side S3 presigned URL upload with progress tracking.
Document upload uses a three-step presigned URL flow. The file goes directly from the browser to S3, bypassing the Next.js and Django servers.
Upload Component
DocumentUpload provides a drag-and-drop zone with multi-file support:
interface UploadFileState {
file: File;
status: "pending" | "uploading" | "confirming" | "done" | "error";
progress: number;
documentId?: string;
error?: string;
}Upload Steps
Step 1: Request Presigned URL
const res = await fetch(
`/api/workspaces/${workspaceId}/documents/upload/`,
{
method: "POST",
body: JSON.stringify({
filename: file.name,
content_type: file.type,
file_size: file.size,
knowledge_base_ids: knowledgeBaseId ? [knowledgeBaseId] : [],
}),
}
);
const { document_id, upload_url, s3_key } = await res.json();The backend validates the file (type, size) and returns a presigned S3 PUT URL.
Step 2: Upload to S3
// Direct browser → S3 upload
const uploadRes = await fetch(upload_url, {
method: "PUT",
body: file,
headers: { "Content-Type": file.type },
});The file goes directly to S3 — no data passes through Next.js or Django.
Step 3: Confirm Upload
await fetch(
`/api/workspaces/${workspaceId}/documents/${document_id}/confirm-upload/`,
{ method: "POST" }
);This triggers the backend Celery task to parse, chunk, and embed the document.
Progress Tracking
Each file tracks its own status:
pending → uploading → confirming → done
└→ errorThe UI shows:
- Pending: File queued for upload
- Uploading: Progress bar during S3 PUT
- Confirming: Sending confirm request to backend
- Done: Upload complete, ingestion started
- Error: Failed at any step, with error message
Processing Status Polling
After upload confirmation, the document enters the ingestion pipeline. The UI polls the processing status:
const res = await fetch(
`/api/workspaces/${workspaceId}/documents/${docId}/processing-status/`
);
const { status } = await res.json();
// status: "PENDING" | "STARTED" | "SUCCESS" | "FAILURE"The DocumentStatusBadge component shows the current state:
| Status | Badge | Color |
|---|---|---|
| pending | Pending | Orange |
| processing | Processing | Orange (animated) |
| completed | Ready | Green |
| failed | Failed | Red |
URL Ingestion
Documents can also be created from URLs (web pages or YouTube videos):
const res = await fetch(
`/api/workspaces/${workspaceId}/documents/from-url/`,
{
method: "POST",
body: JSON.stringify({
url: "https://example.com/article",
name: "Example Article",
knowledge_base_ids: [kbId],
}),
}
);The backend auto-detects the source type (web_url or youtube) and dispatches the appropriate Celery task.
File Type Support
| Extension | MIME Type |
|---|---|
.pdf | application/pdf |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
.doc | application/msword |
.txt | text/plain |
.csv | text/csv |
.md | text/markdown |
.pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
Maximum file size: 50 MB (configurable on the backend).