Orcha
Reference

REST API reference

Token-authenticated endpoints for files, folders, memories, 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

OpenAPI specification

The complete /api/v1 surface is described by a machine-readable OpenAPI 3.1 specification:

Both spec downloads work with standard tooling, including client generators such as openapi-generator and openapi-typescript.

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, and each message in a JSON-RPC batch counts individually. Repeated requests with invalid tokens are limited separately per source address.

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

Memories

A memory is a curated set of context that agents load whole instead of searching. Each memory has a name, a workspace-unique slug, an optional description, an optional useWhen string that tells an agent when the memory applies, and a version counter that increments on every metadata or membership change.

Members come in four kinds, each referencing a target in the same workspace:

KindReference fieldResolves to
filefileIdThe file's current content
folderfolderIdEvery live file in the folder's subtree (capped at 50 files per folder member)
source_documentsourceDocumentIdThe synced document's retained body
databasedatabaseId (optional databaseQuery)The database's fields and records; databaseQuery accepts the same search, filter, sortFieldId, sortDirection, and limit shape as query_database
EndpointPermissionPurpose
GET /api/v1/memoriesreadList memories in the token workspace
POST /api/v1/memorieswriteCreate a memory, optionally with an initial members array
GET /api/v1/memories/:idOrSlugreadGet a memory with its members resolved to contents
PATCH /api/v1/memories/:idwriteUpdate name, slug, description, useWhen, or isPublic
DELETE /api/v1/memories/:iddeleteDelete a memory. Its members' targets are untouched
POST /api/v1/memories/:id/memberswriteAdd members (non-empty members array of kind + reference)
DELETE /api/v1/memories/:id/members/:memberIddeleteRemove one member from the memory
GET /api/v1/memories/:slug/exportreadExport memory metadata plus resolved member contents

Resolution is bounded and honest about gaps: soft-deleted targets, catalog-only synced documents, truncated folder expansions, and scope exclusions each produce a coverage note instead of a silent omission. Folder-scoped tokens see only in-scope files, never connected-source members, and only databases whose folder is in scope.

GET /api/v1/memories/:slug/export
{
  "memory": {
    "id": "uuid",
    "name": "Deployment runbook",
    "slug": "deployment-runbook",
    "description": "Everything needed to ship a release",
    "useWhen": "Use when preparing or debugging a production deploy",
    "version": 4,
    "updatedAt": "2026-08-01T12:00:00.000Z"
  },
  "items": [
    {
      "memberId": "uuid",
      "kind": "file",
      "id": "uuid",
      "name": "release-checklist.md",
      "fileType": "markdown",
      "content": "# Release checklist\n...",
      "updatedAt": "2026-07-15T12:00:00.000Z"
    }
  ],
  "coverage": [
    {
      "memberId": "uuid",
      "kind": "database",
      "name": "Release tasks",
      "note": "Returned 50 of 112 records; use query_database for the rest."
    }
  ]
}

Export is the one endpoint that accepts an unauthenticated request, and only for a memory explicitly marked isPublic. An unauthenticated export serves file and folder members only; connected-source and database members are excluded with a coverage note. Every other memory 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
/memoriesCurated memories; each directory holds file members and a README with useWhen guidance plus pointers to other member kinds
/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

Directory sync

The write path behind the CLI's orcha sync, available to any client. A sync posts a manifest of relative paths and text content; the server derives the folder tree, hashes every document, and reconciles against what earlier syncs with the same name created: unchanged files are skipped, changed files are updated, and files absent from the manifest can be pruned to trash. Requires a workspace-wide token; folder-scoped tokens are rejected.

Preview a sync

POST /api/v1/sync/preview
{
  "name": "team-notes",
  "conflictPolicy": "update",
  "prune": true,
  "documents": [
    { "path": "notes/alpha.md", "content": "# Alpha", "tags": ["planning"] }
  ]
}

Creates a run in ready state without writing any content and returns it with a wouldPrune list: previously synced files and folders absent from this manifest that a prune pass would move to trash. Limits: 5,000 documents, 5 MB per document, 24 MB of content per run. Paths must be relative; absolute paths, .. segments, and control characters are rejected. conflictPolicy is update (default), skip, or duplicate; update applies local changes but never overwrites a file edited in Orcha since the last sync, flagging it as a conflict instead.

Permission required: write

Start a sync run

POST /api/v1/sync/:runId/start

Re-checks plan limits, then queues the run for execution. Returns 202 Accepted, 403 with error: "LIMIT_EXCEEDED" when the run exceeds plan limits, or 409 when the run is not in a startable state.

Permission required: write

Get sync run status

GET /api/v1/sync/:runId

Returns the run status (ready, queued, running, completed, completed_with_warnings, failed, cancelled, rolled_back), progress counters (created, updated, skipped, conflicts, pruned), warnings, and execution results.

Permission required: read

Delete a sync run

DELETE /api/v1/sync/:runId

Removes the run and its stored manifest snapshot; dry-run previews use this to clean up after themselves. Synced content is untouched, but deleting a completed run forfeits its rollback. Busy runs return 409.

Permission required: write

Sync runs also appear in the web app's import history, where completed runs can be rolled back within 7 days; rollback restores pruned files from trash and reverts updates, always leaving files edited in Orcha alone.

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.

Imports

Import 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. These endpoints answered on /api/migrations/* before August 2026; that path still works as a deprecated alias.

EndpointPurpose
POST /api/import/previewValidate an obsidian_zip or notion_zip, check quota, and create a dry-run report
GET /api/import?workspaceId=:idList import runs and progress
GET /api/import/:runIdGet one run
POST /api/import/:runId/startQueue or resume a ready, failed, or cancelled run
POST /api/import/:runId/cancelRequest cooperative cancellation
POST /api/import/:runId/rollbackRoll back untouched imported objects within seven days
GET /api/import/: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).

Feedback

Submit agent feedback

POST /api/v1/feedback

Sends feedback about the workspace's context to its maintainers: missing or stale content, wrong retrieval results, tool problems, or anything else worth fixing. Delivery reuses the product feedback pipeline (an email to the feedback inbox plus an internal notification); nothing is stored in the workspace. This endpoint backs the orcha feedback CLI command; MCP agents use the submit_feedback tool instead.

{
  "message": "search_context returns the 2025 release runbook for 'deploy checklist'; the current one is not indexed.",
  "category": "stale_content",
  "context": { "query": "deploy checklist" }
}
FieldTypeRequiredDescription
messagestringYesThe feedback itself, up to 5000 characters
categorystringNoOne of missing_context, stale_content, wrong_result, tool_problem, other (default other)
contextobjectNoStructured details, e.g. the query used or the document path; must serialize to 10 KB or less
channelstringNorest or cli (default rest); the CLI sets this so triage can tell the surfaces apart

The response is { "message": "Feedback sent" }. A 502 Bad Gateway means an email provider is configured but delivery failed. Permission required: read

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:

{
  "message": "Validation error message"
}

Validation failures add an errors array with the individual issues.

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