Documents API

Documents are files ingested into Stagent for use as agent context, task outputs, or project references. Each document tracks its original file, extracted text, processing status, and version history. Documents can be linked to tasks, projects, or both.

Quick Start

Upload a document from the local filesystem, wait for processing, then download the file — a typical integration flow:

// 1. Upload a document from a local file path
const res = await fetch('/api/documents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  file_path: '/Users/team/reports/q2-analysis.pdf',
  projectId: 'proj-8f3a-4b2c',
  direction: 'input',
  metadata: { department: 'finance', quarter: 'Q2' },
}),
});
const doc: { documentId: string; status: string; processingStatus: string } = await res.json();
// → { documentId: "doc-7b2e-...", status: "uploaded", processingStatus: "queued" }

// 2. Poll until processing completes (text extraction, thumbnails)
let document: { status: string; extractedText: string };
do {
await new Promise((r) => setTimeout(r, 1000));
document = await fetch(`/api/documents/${doc.documentId}`).then((r) => r.json());
} while (document.status === 'processing');

console.log(document.status);        // "ready"
console.log(document.extractedText); // "Q2 2026 Revenue Report..."

// 3. Download the original file
const fileUrl: string = `/api/documents/${doc.documentId}/file`;
window.open(fileUrl); // triggers browser download

// 4. Check version history (output documents only)
const versions: Array<{ id: string; version: number }> = await fetch(
`/api/documents/${doc.documentId}/versions`
).then((r) => r.json());
console.log(`${versions.length} version(s) available`);

Base URL

/api/documents

Endpoints

List Documents

GET /api/documents

Retrieve all documents with optional filtering by task, project, status, direction, or full-text search. Results include joined task, project, and workflow metadata.

Query Parameters

Param Type Req Description
taskId string Filter by task ID
projectId string Filter by project ID
status enum uploaded, processing, ready, or error
direction enum input or output
search string Full-text search across originalName and extractedText

Response Body (Array)

FieldTypeReqDescription
idstring (UUID)*Document identifier
taskIdstring (UUID)Linked task ID
projectIdstring (UUID)Linked project ID
filenamestring*Stored filename (UUID-based)
originalNamestring*Original filename at upload
mimeTypestring*MIME type (e.g. text/markdown, application/pdf)
sizenumber*File size in bytes
storagePathstring*Absolute path to stored file
versionnumber*Document version number
directionenum*input or output
categorystringDocument category or metadata JSON
statusenum*uploaded, processing, ready, or error
extractedTextstringFull text extracted during processing
processedPathstringPath to processed/thumbnail version
processingErrorstringError message if processing failed
createdAtISO 8601*Creation timestamp
updatedAtISO 8601*Last modification
taskTitlestringJoined task title
projectNamestringJoined project name
workflowIdstringJoined workflow ID (via task)
workflowNamestringJoined workflow name
workflowRunNumbernumberWorkflow run number from task

Fetch all ready documents for a project — useful for building a document library or attaching context to tasks:

// Fetch ready documents for a project and group by direction
const response: Response = await fetch(
'/api/documents?projectId=proj-8f3a-4b2c&status=ready'
);
const docs: Array<{ direction: string }> = await response.json();

const inputs = docs.filter((d) => d.direction === 'input');
const outputs = docs.filter((d) => d.direction === 'output');
console.log(`${inputs.length} input docs, ${outputs.length} output docs`);

Example response:

[
  {
    "id": "doc-7b2e-3f9a",
    "originalName": "q2-analysis.pdf",
    "mimeType": "application/pdf",
    "size": 245780,
    "version": 1,
    "direction": "input",
    "status": "ready",
    "extractedText": "Q2 2026 Revenue Report...",
    "projectId": "proj-8f3a-4b2c",
    "projectName": "Marketing Analysis",
    "createdAt": "2026-04-03T09:15:00.000Z",
    "updatedAt": "2026-04-03T09:15:12.000Z"
  }
]

Create Document (from file path)

POST /api/documents

Create a document by copying a file from the local filesystem into Stagent's managed storage. Triggers asynchronous text extraction and preprocessing. Paths are validated against security rules.

Request Body

FieldTypeReqDescription
file_pathstring*Absolute path to a local file
taskIdstring (UUID)Link to a task
projectIdstring (UUID)Link to a project
directionenuminput or output (default: output)
metadataRecord<string, string>Key-value metadata pairs

Response Body

FieldTypeReqDescription
documentIdstring (UUID)*New document ID
statusstring*Always "uploaded"
processingStatusstring*Always "queued"
originalNamestring*Original filename
mimeTypestring*Detected MIME type
sizenumber*File size in bytes

Response 201 Created

Errors: 400 — Validation failure or file not found, 403 — Path traversal or restricted directory

Path Security: Files must reside under the user’s home directory or /tmp. System directories (/etc, /var, /proc, /sys, /dev, /root) and sensitive home directories (.ssh, .gnupg, .aws, .config, .env) are blocked.

Supported MIME Types: .md, .txt, .json, .csv, .html, .pdf, .png, .jpg, .jpeg, .gif, .webp, .docx, .xlsx

Ingest a PDF report as input context for a project — the file is copied into managed storage and text extraction begins automatically:

// Ingest a local file — processing starts automatically
const response: Response = await fetch('/api/documents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  file_path: '/Users/team/reports/q2-analysis.pdf',
  projectId: 'proj-8f3a-4b2c',
  direction: 'input',
  metadata: { department: 'finance', quarter: 'Q2' },
}),
});
const doc: { documentId: string; processingStatus: string } = await response.json();

console.log(doc.documentId);       // "doc-7b2e-..."
console.log(doc.processingStatus); // "queued"

Example response:

{
  "documentId": "doc-7b2e-3f9a",
  "status": "uploaded",
  "processingStatus": "queued",
  "originalName": "q2-analysis.pdf",
  "mimeType": "application/pdf",
  "size": 245780
}

Errors: 400 — Zod validation failure or file not found

{
  "error": "File not found at specified path"
}

Get Document

GET /api/documents/{id}

Retrieve a single document by ID with full metadata including joined task, project, and workflow details.

Response 200 — Document object (same schema as list item)

Fetch a document to check its processing status and read extracted text:

// Check processing status and read extracted text
const doc: { status: string; extractedText: string; processingError: string } =
await fetch('/api/documents/doc-7b2e-3f9a').then((r) => r.json());

if (doc.status === 'ready') {
console.log(`Extracted ${doc.extractedText.length} characters`);
} else if (doc.status === 'error') {
console.error(`Processing failed: ${doc.processingError}`);
}

Example response:

{
  "id": "doc-7b2e-3f9a",
  "originalName": "q2-analysis.pdf",
  "mimeType": "application/pdf",
  "size": 245780,
  "status": "ready",
  "direction": "input",
  "version": 1,
  "extractedText": "Q2 2026 Revenue Report\n\nTotal revenue grew 18% quarter-over-quarter...",
  "projectId": "proj-8f3a-4b2c",
  "projectName": "Marketing Analysis",
  "createdAt": "2026-04-03T09:15:00.000Z",
  "updatedAt": "2026-04-03T09:15:12.000Z"
}

Errors: 404{ "error": "Document not found" }

Update Document

PATCH /api/documents/{id}

Update document associations, category, metadata, or trigger reprocessing. Metadata is merged into the existing category JSON.

Request Body (all fields optional)

FieldTypeReqDescription
taskIdstring (UUID) | nullReassign or unlink task
projectIdstring (UUID) | nullReassign or unlink project
categorystringCategory label (max 100 chars)
metadataRecord<string, string>Key-value pairs merged into category JSON
reprocessbooleanSet true to re-run text extraction

Response 200 — Updated document object

Reassign a document to a different project and trigger reprocessing after the source file changed:

// Tag the document and trigger reprocessing
await fetch('/api/documents/doc-7b2e-3f9a', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
  category: 'report',
  metadata: { reviewed: 'true' },
  reprocess: true,
}),
});

Errors: 400 — Validation failure, 404 — Not found

Delete Document

DELETE /api/documents/{id}

Permanently delete a document and its stored file. If the document is linked to a task, cascade confirmation is required.

Query Parameters

Param Type Req Description
cascadeDelete boolean Set to true to confirm deletion of task-linked documents

Response 204 No Content

Delete a document that is linked to a task — requires the cascade flag to confirm:

// Delete a task-linked document (requires cascade confirmation)
const res: Response = await fetch(
'/api/documents/doc-7b2e-3f9a?cascadeDelete=true',
{ method: 'DELETE' }
);

if (res.status === 409) {
console.log('Document is linked to a task — add ?cascadeDelete=true');
}

Errors: 404 — Not found, 409 — Document linked to a task (add ?cascadeDelete=true to confirm)

Download Document File

GET /api/documents/{id}/file

Download the raw file content with appropriate Content-Type and Content-Disposition headers. Supports inline display for images and PDFs, and thumbnail mode.

Query Parameters

Param Type Req Description
inline 0 | 1 Set to 1 for inline display (images and PDFs only)
thumb 0 | 1 Set to 1 to serve the processed/thumbnail version if available

Response 200 — Raw file bytes with Content-Type and Content-Disposition headers

Download or display a document inline — useful for embedding PDFs or images in a preview panel:

// Embed a document preview in the UI
const previewUrl: string = '/api/documents/doc-7b2e-3f9a/file?inline=1';
const img: HTMLImageElement = document.createElement('img');
img.src = previewUrl;

// Or trigger a download
const link: HTMLAnchorElement = document.createElement('a');
link.href = '/api/documents/doc-7b2e-3f9a/file';
link.download = '';
link.click();

Errors: 404 — Document or file not found

List Document Versions

GET /api/documents/{id}/versions

Retrieve version history for an output document. Finds all documents with the same originalName and projectId, ordered by version descending. Only output documents with a project binding have version history.

Response Body (Array)

FieldTypeReqDescription
idstring (UUID)*Version document ID
versionnumber*Version number
sizenumber*File size in bytes
statusenum*Processing status
createdAtISO 8601*When this version was created
workflowRunNumbernumberWorkflow run that produced this version

Track how an output document evolves across workflow runs — useful for auditing agent-generated reports:

// List all versions of an output document
const versions: Array<{ version: number; size: number; createdAt: string }> =
await fetch('/api/documents/doc-7b2e-3f9a/versions').then((r) => r.json());

versions.forEach((v) => {
console.log(`v${v.version} — ${(v.size / 1024).toFixed(1)} KB — ${v.createdAt}`);
});

Example response:

[
  {
    "id": "doc-7b2e-3f9a",
    "version": 3,
    "size": 312400,
    "status": "ready",
    "createdAt": "2026-04-03T14:20:00.000Z",
    "workflowRunNumber": 5
  },
  {
    "id": "doc-6a1d-2e8b",
    "version": 2,
    "size": 287600,
    "status": "ready",
    "createdAt": "2026-04-02T10:10:00.000Z",
    "workflowRunNumber": 4
  }
]

Document Statuses

StatusDescription
uploadedFile stored, processing not yet started.
processingText extraction and preprocessing in progress.
readyProcessing complete, extracted text available.
errorProcessing failed. See processingError for details.

Document Directions

DirectionDescription
inputReference material provided to agents.
outputArtifact produced by agent execution.