POST /v1/search call you already use for the web — scoped to your organization, invisible to everyone else.
The flow is always the same four steps:
- Presign —
POST /v1/files/presignreturns a short-lived upload URL bound to your file’s exact size. - Upload —
PUTthe raw bytes to that URL. The bytes go straight to storage; they never pass through the API. - Index —
POST /v1/files/indexstarts an indexing run; pollGET /v1/files/index/{sync_id}until it completes. - Search — query with
scope.indexes: ["workspace"]and your organization id asworkspace_id.
Searching your files
Workspace search runs through the ordinary search endpoint with a scope:workspace_idis your organization id; the API verifies it matches the key’s organization, so no other tenant’s files are ever reachable.- Use
"indexes": ["web", "workspace"]to blend web and file results in one response; workspace hits are tagged"index": "workspace"and ranked separately from web results. - Workspace results carry the same provenance handles as web results —
doc_id, passages, and asource_uripointing at the stored object — and can be opened withPOST /v1/documentbydoc_id.
Managing files
Filenames are the identity: the API deals in the sanitized
name echoed by presign, never in storage paths. Uploading the same filename again replaces the file (and re-indexing updates the document in place).
Indexing runs
POST /v1/files/index accepts { "mode": "incremental" } (default — new and changed files only) or { "mode": "full" } (reprocess everything). It answers 202 with a sync_id; poll GET /v1/files/index/{sync_id}:
completed and failed. The SDK/CLI upload helpers trigger one incremental run per upload call by default; when uploading many files, batch them (index=False / --no-index) and start one run at the end.
Supported types and limits
Unsupported types are rejected at presign time with
415 unsupported_file_type; oversized files with 413 file_too_large.
How presigned uploads work
The presign response’surl is a signed storage URL:
- It authorizes exactly one
PUTof exactly the declaredsize, for 15 minutes. A body of any other length is rejected by storage with403. - Send no
Authorizationheader on thePUT— the signature in the URL is the authorization, and your API key must never be sent to storage. - Treat the URL like a short-lived token: anyone holding it can upload that one object until it expires.