Orcha
Reference

REST API reference

Token-authenticated endpoints for files, folders, bundles, retrieval, ingestion, and structured databases.

Orcha provides a REST API for managing context files, folders, and structured databases within workspaces. All API endpoints require authentication via Bearer token.

Base URL: https://app.tryorcha.com

Authentication

Include your API token in the Authorization header:

Authorization: Bearer orca_your_token_here

Create tokens on the Tokens page with appropriate permissions (read, write, delete). The full token value is shown exactly once, when the token is created; store it securely. Afterwards Orcha keeps only a hashed verifier, so the secret cannot be viewed again. If it is lost, create a new token.

Rate limits

Every /api/v1 request and every MCP tool call counts against per-token minute, hour, and day windows. The limits are abuse protection rather than a billing lever, and they are sized for multi-agent workloads: 600 requests per minute on Free, 1,200 on Starter, 2,400 on Studio and Team, and 6,000 on Pro and Business. Hourly and daily ceilings apply on the lower tiers and are unlimited on the higher ones.

Every response carries the current state:

X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
X-RateLimit-Limit-Hour, X-RateLimit-Remaining-Hour, X-RateLimit-Reset-Hour
X-RateLimit-Limit-Day, X-RateLimit-Remaining-Day, X-RateLimit-Reset-Day

Exceeding a window returns 429 Too Many Requests with a Retry-After header in seconds. Over MCP, only real work counts: tools/call, resources/read, and prompts/get. Protocol overhead such as initialize and tools/list is free.

Workspaces

All files, folders, and structured databases belong to a workspace. When you create an API token, it is automatically scoped to the workspace you are currently viewing. Resources created via the API are placed in the token's associated workspace.

Your current workspace ID can be found in the workspace switcher in the sidebar.

Token scoping

Tokens can be scoped to restrict access:

Scope typeBehavior
allToken can access all folders and files in the workspace
foldersToken is restricted to specified folders and their subfolders

When a token is folder-scoped:

  • Only files within the allowed folders are accessible
  • Subfolders of allowed folders are automatically included
  • Root-level files (no folder) are excluded
  • Root-level databases are excluded; a database assigned to an allowed folder remains accessible
  • Attempting to access out-of-scope resources returns 403 Forbidden

Files

List files

GET /api/v1/files

Returns all files accessible to the token within the associated workspace.

Query parameters:

  • folderId (optional): Filter files by folder ID

Response:

[
  {
    "id": "abc123",
    "name": "context.md",
    "fileType": "markdown",
    "folderId": null,
    "workspaceId": "90fd0364-57fd-4179-9892-bc8afa92c6b7",
    "createdAt": "2026-01-06T10:00:00Z",
    "updatedAt": "2026-01-06T10:00:00Z"
  }
]

Permission required: read

Get file

GET /api/v1/files/:id

Returns a specific file including its content.

Response:

{
  "id": "abc123",
  "name": "context.md",
  "content": "# My Context\n\nThis is the file content...",
  "fileType": "markdown",
  "folderId": null,
  "workspaceId": "90fd0364-57fd-4179-9892-bc8afa92c6b7",
  "createdAt": "2026-01-06T10:00:00Z",
  "updatedAt": "2026-01-06T10:00:00Z"
}

Permission required: read

Get raw file content

GET /api/v1/files/:id/raw

Downloads the raw file content with appropriate Content-Type header.

Content-Type by file type:

  • markdown: text/markdown
  • json: application/json
  • yaml: text/yaml
  • text: text/plain

Permission required: read

Create file

POST /api/v1/files

Creates a new file in the token's associated workspace.

Request body:

{
  "name": "new-file.md",
  "content": "# Hello World\n\nFile content here.",
  "fileType": "markdown",
  "folderId": null
}

Fields:

  • name (required): File name
  • content (required): File content
  • fileType (required): One of markdown, json, yaml, text
  • folderId (optional): Parent folder ID, null for root

Response: Returns the created file object with workspaceId.

Permission required: write

Update file

PATCH /api/v1/files/:id

Updates an existing file.

Request body:

{
  "name": "renamed-file.md",
  "content": "# Updated Content",
  "folderId": "folder-id"
}

All fields are optional. Only provided fields will be updated.

Permission required: write

Delete file

DELETE /api/v1/files/:id

Moves a file to trash (soft delete). Files can be restored from the Trash page.

Permission required: delete

Folders

List folders

GET /api/v1/folders

Returns all folders accessible to the token within the associated workspace.

Query parameters:

  • parentId (optional): Filter by parent folder ID

Response:

[
  {
    "id": "folder123",
    "name": "Projects",
    "parentId": null,
    "workspaceId": "90fd0364-57fd-4179-9892-bc8afa92c6b7",
    "createdAt": "2026-01-06T10:00:00Z",
    "updatedAt": "2026-01-06T10:00:00Z"
  }
]

Permission required: read

Get folder

GET /api/v1/folders/:id

Returns a specific folder.

Permission required: read

Get files in folder

GET /api/v1/folders/:id/files

Returns all files in a folder, including files in subfolders by default.

Query parameters:

  • recursive (optional): Set to false to only return direct files (not from subfolders). Default is true.

Permission required: read

Get subfolders

GET /api/v1/folders/:id/subfolders

Returns direct subfolders of a folder.

Permission required: read

Create folder

POST /api/v1/folders

Creates a new folder in the token's associated workspace.

Request body:

{
  "name": "New Folder",
  "parentId": null
}

Fields:

  • name (required): Folder name
  • parentId (optional): Parent folder ID, null for root

Permission required: write

Update folder

PATCH /api/v1/folders/:id

Updates an existing folder.

Request body:

{
  "name": "Renamed Folder",
  "parentId": "new-parent-id"
}

Permission required: write

Delete folder

DELETE /api/v1/folders/:id

Moves a folder to trash (soft delete). The folder and its contents can be restored from the Trash page.

Permission required: delete

Bundles

A bundle is a curated set of files that agents load whole instead of searching. Each bundle has a name, a workspace-unique slug, an optional description, and an optional useWhen string that tells an agent when the bundle applies.

EndpointPermissionPurpose
GET /api/v1/bundlesreadList bundles in the token workspace
POST /api/v1/bundleswriteCreate a bundle, optionally with an initial fileIds array
GET /api/v1/bundles/:idOrSlugreadGet a bundle with its current files
PATCH /api/v1/bundles/:idwriteUpdate name, slug, description, useWhen, or isPublic
DELETE /api/v1/bundles/:iddeleteDelete a bundle. Its files are untouched
POST /api/v1/bundles/:id/fileswriteAdd files by fileIds (non-empty array)
DELETE /api/v1/bundles/:id/files/:fileIddeleteRemove one file from the bundle
GET /api/v1/bundles/:slug/exportreadExport bundle metadata plus the full content of every file

Bundle reads return the files a folder-scoped token is allowed to see; out-of-scope files are filtered out rather than erroring.

GET /api/v1/bundles/:slug/export
{
  "bundle": {
    "id": "uuid",
    "name": "Deployment runbook",
    "slug": "deployment-runbook",
    "description": "Everything needed to ship a release",
    "useWhen": "Use when preparing or debugging a production deploy"
  },
  "files": [
    {
      "id": "uuid",
      "name": "release-checklist.md",
      "fileType": "markdown",
      "content": "# Release checklist\n...",
      "updatedAt": "2026-07-15T12:00:00.000Z"
    }
  ]
}

Export is the one endpoint that accepts an unauthenticated request, and only for a bundle explicitly marked isPublic. Every other bundle request needs a token.

Retrieval

Search context

POST /api/v1/search_context

Returns ranked, workspace-scoped chunks with source citations.

{
  "query": "deployment requirements",
  "strategy": "hybrid",
  "limit": 10,
  "tokenBudget": 2000,
  "minScore": 0.1,
  "explain": false,
  "searches": [
    { "type": "lex", "text": "\"production requirements\" -staging" },
    { "type": "vec", "text": "what is needed to run the app in production" },
    { "type": "hyde", "text": "Production deployments require PostgreSQL 16, Redis, and the DATABASE_URL and SESSION_SECRET environment variables." }
  ]
}

strategy may be keyword, semantic, or hybrid. The query supports websearch syntax ("quoted phrases", -excluded terms).

searches (optional, 1 to 8 entries) supplies typed sub-queries fused with the original query by weighted reciprocal-rank fusion; the original query runs at double weight. lex entries route to full-text search, vec and hyde entries to vector search. hyde is a short hypothetical answer passage (Hypothetical Document Embeddings). minScore drops weak results, and explain attaches per-result score traces.

mode (optional: fast, balanced (default), or fresh) controls upstream freshness cost. fast searches retained content only. balanced also searches the connected-source catalog and, on a decisive metadata match, fetches up to a few catalog-only bodies live from the provider, cited with syncMode: "live". fresh additionally consults live-mode providers. Live calls are metered against the connection's daily provider budget. sourceIds (optional) restricts results to specific connected sources.

The response contains results (cited evidence), coverage (routing notes: live fetches, catalog matches not fetched, budget or availability limits; never quote these as content), and mode. Each result includes its chunk/document/file identifiers, score, chunk metadata (including startLine/endLine anchors and Markdown headingPath), document updatedAt, and citation.

Permission required: read

Query a single connected source

POST /api/v1/sources/:sourceId/query

Queries exactly one connected source: retained content first, then the provider's live-search path or its catalog with a bounded live fetch of the best-matching bodies, all under the connection's daily provider budget. The body takes query (required) and limit (optional, default 5, max 20). The response contains results with citations, coverage notes, and providerOperations. Folder-scoped tokens cannot use this endpoint.

Permission required: read

Fetch a source document

GET /api/v1/sources/:sourceId/documents/:externalId

Returns one connected-source document's full body. Retained documents come from the index (origin: "indexed"); catalog-only documents are fetched live from the provider under the daily budget (origin: "live") and are not persisted. The response contains title, content, citation, origin, and providerOperations. Folder-scoped tokens cannot use this endpoint.

Permission required: read

Filesystem access

Execute a VFS script

POST /api/v1/vfs/exec

Runs a read-only, filesystem-shaped script against a virtual tree of the token's workspace. This is the endpoint behind the orcha CLI and the browse_context MCP tool; use it directly when an agent should explore context with familiar commands instead of paging through list endpoints.

{
  "script": "rg -i 'rate limit' /files | head -20",
  "cwd": "/",
  "maxOutputBytes": 60000
}

The virtual tree mounts four sections plus a root README.md that orients agents:

PathContents
/filesWorkspace files and folders
/bundlesCurated bundles; each directory holds member files and a README with useWhen guidance
/databasesStructured databases; each record renders as a markdown file (fields, then page body)
/sourcesConnected read-only sources; documents carry provider citations

Supported commands: pwd, cd, ls [-l], tree [-L n], find [-name glob] [-type f|d] [-maxdepth n], cat [-n], grep/rg (-i, -n, -l, -r, -F), head/tail [-n N], wc [-l|-w|-c], stat, and help. Pipe with | (grep, rg, head, tail, and wc accept piped input) and chain with ; or &&. The script runs in Orcha's own restricted parser; redirection, substitution, and every write-shaped construct are rejected, so the surface is read-only by construction.

grep/rg patterns use RE2 syntax, which matches in time linear to the input so no pattern can stall a request. Constructs that require backtracking, notably lookahead, lookbehind, and backreferences, are not supported and return an error naming the unsupported construct. Pass -F to match the pattern literally. find -name takes a glob (*, ?), not a regular expression.

The response returns the combined output plus citations for every file whose content was returned:

{
  "output": "/files/notes/api.md:2:rate limits are abuse guards\n",
  "cwd": "/",
  "truncated": false,
  "failed": false,
  "citations": [
    {
      "path": "/files/notes/api.md",
      "entityType": "file",
      "entityId": "3f2a...",
      "title": "api.md",
      "deepLink": "/file/3f2a...",
      "updatedAt": "2026-07-30T12:00:00.000Z"
    }
  ],
  "budget": { "filesOpened": 2, "bytesRead": 5120, "nodesVisited": 14, "exhausted": null }
}

Every execution is bounded by a scan budget (files opened, bytes read, nodes visited, wall clock) and an output cap (maxOutputBytes, 1 KB to 200 KB, default 60 KB). When a limit trips, the output says so explicitly; narrow the path or switch to search_context for ranked retrieval. failed reports the script's bash-style status: true when the last executed command errored or the script aborted on a parse error or budget stop (the orcha CLI exits non-zero on it). stat <path> prints any node's entity ID, deep link, citation label, freshness, and useWhen guidance.

Folder-scoped tokens see a pruned /files tree only (out-of-scope paths simply do not exist), and soft-deleted content is never mounted. Executions and returned content are recorded in usage analytics like any other retrieval.

Permission required: read

Ingestion

Ingest a document

POST /api/v1/ingest

Validates and queues non-Orcha source content for indexing. Replaying the same idempotency key with identical content is recognized and deduplicated within the token's workspace.

{
  "idempotencyKey": "github:org/repo:docs/guide.md:abc123",
  "title": "Deployment Guide",
  "content": "# Deployment\n\nProduction requirements...",
  "mimeType": "text/markdown",
  "externalId": "docs/guide.md",
  "externalRevision": "abc123",
  "metadata": {
    "repository": "org/repo"
  },
  "citation": {
    "provider": "github",
    "objectId": "docs/guide.md",
    "deepLink": "https://github.com/org/repo/blob/abc123/docs/guide.md",
    "location": "docs/guide.md",
    "version": "abc123"
  }
}

New work returns 202 Accepted; an identical replay returns 200 OK with deduplicated: true. Reusing the key with different content returns 409 Conflict. Content is limited to 5 MB and metadata to 16 KB.

Permission required: write

Get ingestion status

GET /api/v1/ingest/:documentId

Returns pending, indexing, indexed, or failed, plus content hash, indexing time, citation, and any available error. Status is visible only to tokens scoped to the document's workspace.

Permission required: read

Retrieval analytics

Workspace administrators can request the session-authenticated endpoint:

GET /api/workspaces/:workspaceId/retrieval-analytics?days=30

The response includes query volume, top documents, no-result rate, p50/p95 latency, cache rate, first successful retrieval and MCP activation, daily UTC series, and separate billing-dimension totals. days accepts 1 to 90.

Structured databases

Structured databases contain typed properties and records. Record values are keyed by stable property UUID rather than property name, so renaming a property does not break API consumers.

Supported property types are text, number, boolean, date, select, multi_select, url, relation, file_reference, and asset_reference. A file or asset reference is one UUID or, when config.multiple is true, an array of UUIDs. Referenced resources must be active and in the same workspace. A record can also set contentFileId to use a normal Orcha file as its editable page body.

EndpointPermissionPurpose
GET /api/v1/databasesreadList accessible databases
POST /api/v1/databaseswriteCreate a database with a primary Name property and default table view
GET /api/v1/databases/:databaseIdreadGet schema, properties, views, and record count
PATCH /api/v1/databases/:databaseIdwriteUpdate name, description, icon, or folder
DELETE /api/v1/databases/:databaseIddeleteMove a database to trash
GET /api/v1/databases/:databaseId/recordsreadSearch, sort, filter, target a citation recordId, and paginate records
POST /api/v1/databases/:databaseId/recordswriteCreate a typed record
PATCH /api/v1/databases/:databaseId/records/:recordIdwriteMerge values and save a record version
DELETE /api/v1/databases/:databaseId/records/:recordIddeleteMove a record to trash
GET /api/v1/files/:fileId/database-backlinksreadList database records that reference a file
POST /api/v1/databases/:databaseId/fieldswriteAdd a typed property
PATCH /api/v1/databases/:databaseId/fields/:fieldIdwriteUpdate a property
DELETE /api/v1/databases/:databaseId/fields/:fieldIdwriteDelete a non-primary property
POST /api/v1/databases/:databaseId/viewswriteCreate a saved table view
PATCH /api/v1/databases/:databaseId/views/:viewIdwriteUpdate view filters and sorting

Record query parameters include recordId, search, sortFieldId, sortDirection, filterFieldId, filterOperator, filterValue, limit (maximum 200), and offset. Filter operators are contains, equals, not_equals, is_empty, and is_not_empty.

Example record creation:

{
  "contentFileId": "55555555-5555-4555-8555-555555555555",
  "values": {
    "22222222-2222-4222-8222-222222222222": "Launch",
    "33333333-3333-4333-8333-333333333333": 42,
    "44444444-4444-4444-8444-444444444444": true
  }
}

Records are indexed automatically. File references and record bodies use stable file IDs and /file/:fileId deep links, while search_context results include native record citations linking to /databases/:databaseId?record=:recordId. Folder-scoped tokens cannot write out-of-scope file references or bodies; inaccessible references are redacted from record responses and excluded from scoped retrieval results.

Migrations

Migration endpoints are session-authenticated web-app endpoints and enforce the selected workspace's read/write/delete RBAC. Uploads use multipart form data with the ZIP in the archive field. The import guide covers the workflow.

EndpointPurpose
POST /api/migrations/previewValidate an obsidian_zip or notion_zip, check quota, and create a dry-run report
GET /api/migrations?workspaceId=:idList migration runs and progress
GET /api/migrations/:runIdGet one run
POST /api/migrations/:runId/startQueue or resume a ready, failed, or cancelled run
POST /api/migrations/:runId/cancelRequest cooperative cancellation
POST /api/migrations/:runId/rollbackRoll back untouched imported objects within seven days
GET /api/migrations/:runId/reportDownload the dry-run/fidelity/execution report
GET /api/assets/:assetId/contentRead an authorized imported attachment

Preview fields are workspaceId, provider, and conflictPolicy (update, skip, or duplicate).

Error responses

StatusMeaning
400 Bad RequestInvalid request body or parameters
401 UnauthorizedMissing or invalid authentication token
403 ForbiddenToken lacks required permission or resource is out of scope
404 Not FoundRequested resource does not exist
413 Payload Too LargeAn asset or migration upload exceeds the plan's per-object limit
429 Too Many RequestsA rate-limit window is exhausted; see Retry-After

Error bodies have the shape:

{
  "error": "Validation error message"
}

Examples

List all files in workspace

curl -X GET \
  -H "Authorization: Bearer orca_your_token" \
  https://app.tryorcha.com/api/v1/files

Create a file

curl -X POST \
  -H "Authorization: Bearer orca_your_token" \
  -H "Content-Type: application/json" \
  -d '{"name": "example.md", "content": "# Example", "fileType": "markdown"}' \
  https://app.tryorcha.com/api/v1/files

Update a file

curl -X PATCH \
  -H "Authorization: Bearer orca_your_token" \
  -H "Content-Type: application/json" \
  -d '{"content": "# Updated content"}' \
  https://app.tryorcha.com/api/v1/files/file-id

Get files in a folder recursively

curl -X GET \
  -H "Authorization: Bearer orca_your_token" \
  https://app.tryorcha.com/api/v1/folders/folder-id/files