Uploading & managing files
Upload documents into Context212 so they become searchable in seconds.
Before you can search, your documents need to be in Context212. Uploading a file triggers an ingestion pipeline that parses the content, splits it into chunks, generates embeddings, and indexes everything. The whole process typically takes a few seconds for a standard PDF.
Ingestion is asynchronous: the upload returns immediately with a pending status, and you poll GET /api/v1/files/{id} for completion.
This tutorial covers POST /api/v1/files and GET /api/v1/files. The full schema for every endpoint and parameter lives in the API reference.
Upload a file
Send the file as multipart/form-data with the destination workspace_id.
Accepted formats: csv, doc, docx, htm, html, jpeg, jpg, md, odp, odt, pdf, png, ppt, pptx, txt, xhtml, xls, xlsx.
import { client, filesCreate } from "@context212/sdk";
client.setConfig({
headers: {
Authorization: `Bearer ${process.env.C212_API_KEY}`,
},
});
const { data, error } = await filesCreate({
body: {
workspace_id: 42,
file, // File or Blob
},
});
if (error) throw error;
console.log(data.id, data.status, data.upload_session_uuid);
// → 12345 pending 550e8400-e29b-41d4-a716-446655440000The response is a 201 with the new file record, including an upload_session_uuid you can use later to find every file uploaded in the same batch.
Optional fields: title (defaults to filename without extension), filename (override the uploaded name), and parser (omit to use the platform default).
When external_metadata.external_id is set and a manually uploaded document with the same ID already exists in that workspace, the API returns 200 with the existing file instead of creating a duplicate.
Wait for indexing to complete
Poll GET /api/v1/files/{id} until status reaches embedded:
import { filesRetrieve } from "@context212/sdk";
const { data: body, error } = await filesRetrieve({
path: { id: data.id },
});
if (error) throw error;
console.log(body.status, body.status_detail);
// → embedded nullThe status field moves through these stages:
| Status | What's happening |
|---|---|
pending | Queued for processing |
parsing | Extracting text from the document |
parsing_failed | Parsing failed, see status_detail |
embedding | Generating vector embeddings |
embedding_failed | Embedding failed, see status_detail |
embedded | Indexed and ready to search |
updating | Re-indexing in progress |
fail | Generic failure, see status_detail |
status_vision tracks the same lifecycle for vision/image embeddings: pending, processing, embedded, fail, or - (not available for this file).
Organising documents with tags and titles
Add a human-readable title and assign tag IDs at upload time. Tags can be sent as a JSON-encoded array string or as repeated form fields with the same name.
import { filesCreate } from "@context212/sdk";
await filesCreate({
body: {
workspace_id: 42,
title: "Q4 Financial Report",
tags: "[1, 2]", // JSON-encoded list of tag IDs
file, // File or Blob
},
});If a tag ID is invalid, the file is still created but the response is a 207 (multi-status) with a message explaining which tags were rejected.
To replace tags after upload, PATCH /api/v1/files/{id} with a new tags array. It replaces all existing tags, manual and auto-assigned. Send [0] (sentinel) to remove every tag when using multipart format. To add tags without touching existing ones, POST /api/v1/files/{id}/tags.
Tracking documents from external systems
If you're ingesting documents from a third-party system (ServiceNow, Confluence, SharePoint, etc.), store the source identifier in external_metadata. This lets you find the Context212 file later given only the external ID, and surface the original URL in your UI.
import { filesCreate } from "@context212/sdk";
await filesCreate({
body: {
workspace_id: 42,
file, // File or Blob
external_metadata: JSON.stringify({
external_id: "SRV-456789",
doc_type: "incident",
additional_metadata: {
external_url: "https://servicenow.example.com/incident/SRV-456789",
},
}),
},
});external_id is required when creating; doc_type and additional_metadata are optional. When sent via multipart/form-data, the whole external_metadata value must be a JSON string.
Retrieve it later by external ID:
GET /api/v1/files?external_metadata__external_id=SRV-456789Listing and filtering your documents
GET /api/v1/files supports rich filtering. A few common patterns:
import { filesList } from "@context212/sdk";
// All files in a workspace
await filesList({ query: { workspace_id: "42" } });
// Semantic search across filenames and titles, with the top chunk inline
await filesList({
query: { search: "security policy", search_details: true },
});
// PDFs tagged 'legal', most recent first
await filesList({
query: { tag_id: "3", extension: "pdf", ordering: "-created_at" },
});
// Files in a 10–50 page window
await filesList({
query: { total_pages_min: 10, total_pages_max: 50 },
});Set include_details=true to receive the signature (TLSH hash for duplicate detection) and parser fields on each result.
For graph-aware filtering (concepts and relations), see the Ontology tutorials.
Deleting files
Single delete:
import { filesDestroy } from "@context212/sdk";
await filesDestroy({ path: { id: fileId } });Bulk delete:
import { filesBulkDeleteCreate } from "@context212/sdk";
await filesBulkDeleteCreate({
body: { ids: [123, 124, 125] },
});Both return 204 No Content on success. Files in synced (datasource-managed) workspaces cannot be deleted manually. The API returns 400.
Common errors
| Status | Cause |
|---|---|
400 | Validation error, unsupported file type, or synced-workspace constraint |
401 | Missing or invalid API key |
403 | Permission denied (no upload/delete rights) |
404 | File does not exist or is not accessible |
429 | Too many concurrent uploads for this session |