Orcha
Reference

REST API reference

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

Orcha has a REST API for the context files, folders, and structured databases in a workspace. All API endpoints need a Bearer token.

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

OpenAPI specification

An OpenAPI 3.1 specification describes the full /api/v1 surface:

Standard tools, such as openapi-generator and openapi-typescript, accept both formats.

Authentication

Put your API token in the Authorization header:

Authorization: Bearer orca_your_token_here

Create tokens on the API Tokens page with the permissions that you need (read, write, delete). Orcha shows the full token value one time, when it creates the token. Store the value in a safe place. After that, Orcha keeps only a hash of the token. You cannot see the secret again. If you lose the secret, create a new token.

Rate limits

Each /api/v1 request and each MCP tool call counts against the minute, hour, and day windows of the token. The limits protect the service against abuse. Orcha does not use them for billing. They are large enough for multi-agent workloads.

The per-minute limit is 600 requests on Free, 1,200 on Starter, 2,400 on Studio and Team, and 6,000 on Pro and Business. The lower tiers also have hourly and daily limits. The higher tiers have no hourly or daily limit.

Each response has headers that show 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

If you exceed a window, the server returns 429 Too Many Requests with a Retry-After header in seconds. Over MCP, only tools/call, resources/read, and prompts/get count. Protocol messages such as initialize and tools/list are free. Each message in a JSON-RPC batch counts as one request. Repeated requests with invalid tokens have a separate limit for each source address.

Workspaces

All files, folders, and structured databases belong to a workspace. When you create an API token, Orcha binds it to the workspace that is open at that time. Orcha puts the resources that you create through the API in the workspace of the token.

The workspace switcher in the sidebar shows your current workspace ID.

Token scoping

A token can have a scope that limits access:

Scope typeBehavior
allThe token can access all folders and files in the workspace
foldersThe token can access only the specified folders and their subfolders

For a folder-scoped token:

  • Only files in the permitted folders are available
  • Subfolders of the permitted folders are included
  • Root-level files (files with no folder) are excluded
  • Root-level databases are excluded. A database in a permitted folder is available
  • A request for a resource that is out of scope returns 403 Forbidden

Files

List files

GET /api/v1/files

Returns all files in the workspace that the token can access.

Query parameters:

  • folderId (optional): Return only the files in this folder

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 one file with 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

Returns the raw file content with the applicable Content-Type header.

Content-Type for each 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 workspace of the token.

Request body:

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

Fields:

  • name (required): The file name
  • content (required): The file content
  • fileType (required): One of markdown, json, yaml, text
  • folderId (optional): The parent folder ID. Use null for the root

Response: The created file object with workspaceId.

Permission required: write

Update file

PATCH /api/v1/files/:id

Updates a file.

Request body:

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

All fields are optional. The server updates only the fields that you send.

Permission required: write

Delete file

DELETE /api/v1/files/:id

Moves a file to trash (soft delete). You can restore the file from the Trash page.

Permission required: delete

Folders

List folders

GET /api/v1/folders

Returns all folders in the workspace that the token can access.

Query parameters:

  • parentId (optional): Return only the folders in this parent folder

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 one folder.

Permission required: read

Get files in folder

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

Returns all files in a folder. By default, the response includes the files in subfolders.

Query parameters:

  • recursive (optional): Set to false to return only the direct files. The default is true.

Permission required: read

Get subfolders

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

Returns the direct subfolders of a folder.

Permission required: read

Create folder

POST /api/v1/folders

Creates a new folder in the workspace of the token.

Request body:

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

Fields:

  • name (required): The folder name
  • parentId (optional): The parent folder ID. Use null for the root

Permission required: write

Update folder

PATCH /api/v1/folders/:id

Updates a 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). You can restore the folder and its contents from the Trash page.

Permission required: delete

Memories

A memory is a curated set of context. An agent loads the full set instead of a search. Each memory has a name, a slug that is unique in the workspace, an optional description, and an optional useWhen string. The useWhen string tells an agent when the memory applies. A version counter increases on each change to the metadata or the members.

There are four member kinds. Each member refers to a target in the same workspace:

KindReference fieldResolves to
filefileIdThe current content of the file
folderfolderIdEach live file in the folder tree, to a maximum of 50 files for each folder member
source_documentsourceDocumentIdThe retained body of the synced document
databasedatabaseId (optional databaseQuery)The fields and records of the database. databaseQuery accepts the same search, filter, sortFieldId, sortDirection, and limit values as query_database
EndpointPermissionPurpose
GET /api/v1/memoriesreadList the memories in the workspace of the token
POST /api/v1/memorieswriteCreate a memory, with an optional initial members array
GET /api/v1/memories/:idOrSlugreadGet a memory with its members resolved to contents
PATCH /api/v1/memories/:idwriteUpdate the name, slug, description, useWhen, or isPublic
DELETE /api/v1/memories/:iddeleteDelete a memory. The targets of its members do not change
POST /api/v1/memories/:id/memberswriteAdd members (a non-empty members array of kind and reference)
DELETE /api/v1/memories/:id/members/:memberIddeleteRemove one member from the memory
GET /api/v1/memories/:slug/exportreadExport the memory metadata and the resolved member contents

Resolution has limits, and the response reports each gap. A soft-deleted target, a catalog-only synced document, a truncated folder, or a member out of scope each produce a coverage note. The response never omits a member silently. A folder-scoped token sees only the files in scope and the databases in a folder in scope. It never sees connected-source members.

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."
    }
  ]
}

The export endpoint is the only endpoint that accepts a request without a token. This works only for a memory with isPublic set to true. An export without a token returns file and folder members only. It excludes connected-source and database members with a coverage note. All other memory requests need a token.

Retrieval

Search context

POST /api/v1/search_context

Returns chunks in rank order, limited to the workspace, 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 is keyword, semantic, or hybrid. The query supports the websearch syntax ("quoted phrases" and -excluded terms).

searches (optional, 1 to 8 entries) adds typed sub-queries. The server fuses them with the original query by weighted reciprocal-rank fusion. The original query has double weight. lex entries go to full-text search. vec and hyde entries go to vector search. A hyde entry is a short hypothetical answer (Hypothetical Document Embeddings). minScore removes weak results. explain adds a score trace to each result.

mode (optional: fast, balanced (default), or fresh) controls the freshness cost. fast searches retained content only. balanced also searches the catalog of connected sources. If the metadata of a catalog entry is a clear match, balanced fetches a small number of catalog-only bodies live from the provider. These results have syncMode: "live". fresh also queries live-mode providers. Each live call counts against the daily provider budget of the connection. sourceIds (optional) limits the results to the specified connected sources.

The response contains results, coverage, and mode. results is the cited evidence. coverage has notes about the route: live fetches, catalog matches that were not fetched, and budget or availability limits. Do not quote coverage notes as content. Each result includes the chunk, document, and file identifiers, the score, and the citation. It also includes the chunk metadata (with startLine and endLine anchors and the Markdown headingPath) and the document updatedAt.

Permission required: read

Query a single connected source

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

Queries one connected source only. The server searches the retained content first. Then it uses the live-search path of the provider, or its catalog with a limited live fetch of the best bodies. All live calls count against the daily provider budget of the connection. The body accepts query (required) and limit (optional, default 5, maximum 20). The response contains results with citations, coverage notes, and providerOperations. A folder-scoped token cannot use this endpoint.

Permission required: read

Fetch a source document

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

Returns the full body of one connected-source document. A retained document comes from the index (origin: "indexed"). A catalog-only document comes live from the provider, under the daily budget (origin: "live"). The server does not store a live document. The response contains title, content, citation, origin, and providerOperations. A folder-scoped token cannot use this endpoint.

Permission required: read

Filesystem access

Execute a VFS script

POST /api/v1/vfs/exec

Runs a read-only script with filesystem commands against a virtual tree of the workspace of the token. The orcha CLI and the browse_context MCP tool use this endpoint. Use it directly when an agent must explore the context with familiar commands instead of the list endpoints.

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

The virtual tree has four directories and a root README.md that describes them:

PathContents
/filesWorkspace files and folders
/memoriesCurated memories. Each directory holds the file members and a README with the useWhen guidance and pointers to the other member kinds
/databasesStructured databases. Each record is a markdown file (the fields, then the page body)
/sourcesConnected read-only sources. The documents have 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. Use | to pipe output (grep, rg, head, tail, and wc accept piped input). Use ; or && to chain commands. The script runs in the restricted parser of Orcha. The parser rejects redirection, substitution, and each construct that writes. Thus the surface is read-only by design.

grep and rg patterns use the RE2 syntax. RE2 matches in linear time, so no pattern can stall a request. RE2 does not support constructs that need backtracking: lookahead, lookbehind, and backreferences. If a pattern uses one of these, the server returns an error that names the construct. Use -F to match the pattern as literal text. find -name accepts a glob (*, ?). It does not accept a regular expression.

The response contains the combined output and a citation for each file that the output includes:

{
  "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 }
}

A scan budget and an output cap limit each execution. The scan budget counts files opened, bytes read, nodes visited, and wall-clock time. The output cap is maxOutputBytes (1 KB to 200 KB, default 60 KB). If a limit stops the script, the output says so. Then narrow the path, or use search_context for ranked retrieval. failed reports the bash-style status of the script. It is true when the last command failed, or when a parse error or a budget stop ended the script. The orcha CLI exits with a non-zero code in that case. stat <path> prints the entity ID, the deep link, the citation label, the freshness, and the useWhen guidance of a node.

A folder-scoped token sees a pruned /files tree only. Paths that are out of scope do not exist in the tree. Soft-deleted content is never mounted. Usage analytics record each execution and the returned content, the same as other retrieval.

Permission required: read

Ingestion

Ingest a document

POST /api/v1/ingest

Validates content from a source outside Orcha and queues it for the index. If you send the same idempotency key with identical content again, the server detects the repeat and does not index it twice. The check applies in the workspace of the token.

{
  "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 repeat returns 200 OK with deduplicated: true. The same key with different content returns 409 Conflict. The maximum content size is 5 MB. The maximum metadata size is 16 KB.

Permission required: write

Get ingestion status

GET /api/v1/ingest/:documentId

Returns pending, indexing, indexed, or failed, with the content hash, the index time, the citation, and the error if there is one. Only a token for the workspace of the document can see the status.

Permission required: read

Directory sync

The orcha sync CLI command uses these endpoints. Other clients can use them too. A sync sends a manifest of relative paths and text content. The server derives the folder tree, hashes each document, and compares the manifest with the files that earlier syncs with the same name created. The server skips unchanged files and updates changed files. Files that are not in the manifest can move to trash (prune). A sync needs a workspace-wide token. The server rejects a folder-scoped token.

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 the ready state and writes no content. The response includes a wouldPrune list: the files and folders from earlier syncs that are not in this manifest. A prune pass moves them to trash. Limits: 5,000 documents, 5 MB for each document, and 24 MB of content for each run. Paths must be relative. The server rejects absolute paths, .. segments, and control characters. conflictPolicy is update (default), skip, or duplicate. update applies local changes. If a file was edited in Orcha after the last sync, update does not overwrite it and reports a conflict.

Permission required: write

Start a sync run

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

Checks the plan limits again, then queues the run. Returns 202 Accepted. If the run exceeds the plan limits, returns 403 with error: "LIMIT_EXCEEDED". If the run is not in a state that permits a start, returns 409.

Permission required: write

Get sync run status

GET /api/v1/sync/:runId

Returns the run status, the progress counters, the warnings, and the execution results. The status is one of ready, queued, running, completed, completed_with_warnings, failed, cancelled, or rolled_back. The counters are created, updated, skipped, conflicts, and pruned.

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 endpoint to remove themselves. The synced content does not change. If you delete a completed run, you cannot roll it back later. A busy run returns 409.

Permission required: write

Sync runs also appear in the import history in the web app. You can roll back a completed run for 7 days. A rollback restores pruned files from trash and reverts updates. It never changes a file that was edited in Orcha.

Retrieval analytics

Workspace administrators can send a request to this session-authenticated endpoint:

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

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

Structured databases

A structured database has typed properties and records. Record values use the stable property UUID as the key. They do not use the property name. Thus a property rename does not break API clients.

The 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. If config.multiple is true, it is an array of UUIDs. The referenced resource must be active and in the same workspace. A record can also set contentFileId. Then a normal Orcha file is the editable page body of the record.

EndpointPermissionPurpose
GET /api/v1/databasesreadList the databases that the token can access
POST /api/v1/databaseswriteCreate a database with a primary Name property and a default table view
GET /api/v1/databases/trashreadList the databases in trash
GET /api/v1/databases/:databaseIdreadGet the schema, properties, views, and record count
PATCH /api/v1/databases/:databaseIdwriteUpdate the name, description, icon, or folder
DELETE /api/v1/databases/:databaseIddeleteMove a database to trash
POST /api/v1/databases/:databaseId/restoredeleteRestore a database from trash
DELETE /api/v1/databases/:databaseId/permanentdeleteDelete a database in trash permanently
GET /api/v1/databases/:databaseId/recordsreadSearch, sort, filter, select 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
POST /api/v1/databases/:databaseId/records/:recordId/restorewriteRestore a record from trash
DELETE /api/v1/databases/:databaseId/records/:recordId/permanentdeleteDelete a record in trash permanently
GET /api/v1/databases/:databaseId/records/:recordId/versionsreadList the saved versions of a record
GET /api/v1/files/:fileId/database-backlinksreadList the database records that refer to 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 property that is not the primary property
POST /api/v1/databases/:databaseId/viewswriteCreate a saved table view
PATCH /api/v1/databases/:databaseId/views/:viewIdwriteUpdate the filters and the sort order of a view

The record query parameters are recordId, search, sortFieldId, sortDirection, filterFieldId, filterOperator, filterValue, limit (maximum 200), and offset. The 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
  }
}

The server indexes records automatically. File references and record bodies use stable file IDs and /file/:fileId deep links. search_context results include native record citations with links to /databases/:databaseId?record=:recordId. A folder-scoped token cannot write a file reference or a body that is out of scope. The server redacts references that the token cannot access from record responses and excludes them from scoped retrieval results.

Imports

The import endpoints are session-authenticated web-app endpoints. They apply the read, write, and delete RBAC of the selected workspace. Uploads use multipart form data with the ZIP in the archive field. The import guide describes the procedure. Before August 2026, these endpoints were at /api/migrations/*. That path still works as a deprecated alias.

EndpointPurpose
POST /api/import/previewValidate an obsidian_zip or notion_zip, check the quota, and create a dry-run report
GET /api/import?workspaceId=:idList the import runs and their progress
GET /api/import/:runIdGet one run
POST /api/import/:runId/startQueue or resume a ready, failed, or cancelled run
POST /api/import/:runId/cancelSend a cooperative cancel request
POST /api/import/:runId/rollbackRoll back imported objects that did not change, for seven days
GET /api/import/:runId/reportDownload the dry-run, fidelity, and execution report
GET /api/assets/:assetId/contentRead an authorized imported attachment

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

Feedback

Submit agent feedback

POST /api/v1/feedback

Sends feedback about the context of the workspace to its maintainers: absent or stale content, wrong retrieval results, tool problems, or other problems. The server sends the feedback through the product feedback pipeline (an email to the feedback inbox and an internal notification). Orcha does not store the feedback in the workspace. The orcha feedback CLI command uses this endpoint. MCP agents use the submit_feedback tool.

{
  "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 text, to a maximum of 5000 characters
categorystringNoOne of missing_context, stale_content, wrong_result, tool_problem, other (default other)
contextobjectNoStructured details, for example the query or the document path. The serialized size must be 10 KB or less
channelstringNorest or cli (default rest). The CLI sets this value so that triage can identify the source

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

Permission required: read

Error responses

StatusMeaning
400 Bad RequestThe request body or the parameters are invalid
401 UnauthorizedThe authentication token is absent or invalid
403 ForbiddenThe token does not have the required permission, or the resource is out of scope
404 Not FoundThe resource does not exist
413 Payload Too LargeAn asset or migration upload exceeds the per-object limit of the plan
429 Too Many RequestsA rate-limit window is exhausted. See Retry-After

Error bodies have this shape:

{
  "message": "Validation error message"
}

A validation failure adds an errors array with each problem.

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