# Requests and responses

Send named API actions, read their results and handle errors.

Last updated 8 November 2026

Cabane's HTTP API is served from `https://app.cabane.ai`. Workspace requests select an operation: a named API action such as `files.read`.

For example, send this JSON to `POST /api/workspaces/{workspaceId}/ops/query`:

~~~json
{
  "op": "files.read",
  "args": {
    "path": "/api-example/hello.md"
  }
}
~~~

Replace `{workspaceId}` with the workspace's UUID. Send `Authorization: Bearer TOKEN` and `Content-Type: application/json`. All arguments go in one flat `args` object; the operation declaration tells you their names.

## Endpoints

| Endpoint | Purpose | Body |
| --- | --- | --- |
| `POST /api/workspaces/{workspaceId}/ops/query` | Read content | One `{op, args}` or a batch of reads |
| `POST /api/workspaces/{workspaceId}/ops/mutate` | Change content | One `{op, args}` |
| `POST /api/workspaces/{workspaceId}/ops/describe` | Discover operations and arguments | `{}` or `{"op":"files.read"}` |

[First API request](https://cabane.ai/docs/first-api-request) shows complete curl requests, including how to obtain the workspace ID and token.

## Success and failure

A successful operation returns HTTP 200 with `ok: true` and its answer in `result`. For the file written in the first-request walkthrough:

~~~json
{
  "ok": true,
  "result": {
    "path": "/api-example/hello.md",
    "mime": "text/markdown",
    "size": 38,
    "sha256": "edb3a4c9af5d8b0e6e755719bae5ffeee15c6aaca2bb994ec08f678dc116f52e",
    "created": true
  }
}
~~~

An operation failure also returns HTTP 200, with `ok: false`. Writing to that existing path without `overwrite` returns:

~~~json
{
  "ok": false,
  "error": {
    "kind": "conflict",
    "code": "name_conflict",
    "message": "name_conflict: a file already exists at \"/api-example/hello.md\". Pass { overwrite: true } to replace its contents."
  }
}
~~~

Branch on `error.kind` for the recovery strategy and `error.code` for the particular cause. Use `message` to explain the failure. Errors may also include `declaration` to correct a call or `current` with the current hash or version. A current hash is a reason to reread the file before replacing it.

Errors that prevent a request from reaching an operation use an HTTP error status and a different body:

~~~http
HTTP/1.1 400 Bad Request
Content-Type: application/json

{"error":"invalid_json"}
~~~

Common HTTP errors are `400 invalid_json` for malformed JSON, `400 invalid_id` for an invalid ID, `401 unauthenticated` for an unusable credential, and `404 not_found` for a route the credential cannot access. A batch sent to `mutate` returns `400 batch_not_supported_for_writes`. These responses have no `ok` field.

Check the HTTP status first, then `ok`. A client that checks only HTTP 200 will miss operation failures.

## Operation error kinds

| `kind` | What to do |
| --- | --- |
| `bad_call` | Correct the named arguments using the returned declaration or `describe`. |
| `unknown_op` | Check the operation name; use `describe` to find the intended action. |
| `wrong_kind` | Send reads to `query` and writes to `mutate`. |
| `not_found` | Check the identifier and the credential's access. A hidden object also appears missing. |
| `ambiguous` | Replace a name or text match with an exact identifier. |
| `conflict` | Reread the object and reconcile the name, content or version conflict. |
| `forbidden` | Use an identity with the required permission, or have the appropriate person perform the action. |
| `too_large` | Reduce the input size or item count named in the error. |
| `refused` | Inspect `code` and `message` for a state or policy requirement, such as an archived target. |
| `output_too_large` | Narrow the read or split the batch. See [Pagination and batching](https://cabane.ai/docs/pagination-and-batching). |
| `timeout` | Narrow an expensive read. For a write, establish what happened before retrying. |
| `transient` | Retry the same call; this kind explicitly permits that recovery. |
| `internal_error` | Preserve the error for investigation. Check the resulting state before replaying a write. |

There is no general guarantee that replaying a failed or disconnected write is safe. [Safe writes and retries](https://cabane.ai/docs/safe-writes-and-retries) explains which operations provide that guarantee.

## Discover an operation

Send `{}` to `describe` for the operation index. To inspect one operation, send:

~~~json
{
  "op": "files.read"
}
~~~

The response provides its call declaration and supporting types. You can also use a search phrase, such as `{"op":"read a file"}`, to find relevant operations. The [Operation reference](https://cabane.ai/docs/api) publishes these declarations together.

The call-line notation is a compact description of JSON arguments:

| Notation | Meaning |
| --- | --- |
| `path:string` | A required string argument named `path` |
| `maxBytes?:number` | An optional number argument |
| `overwrite?:boolean=false` | Optional; defaults to `false` |
| `id\|reference:string` | Supply one of those named arguments |
| `→ FileRead` | The result has the declared `FileRead` shape |

The call line is documentation; the request still uses `{"op":"…","args":{…}}`.

## Identifiers and file content

Use UUIDs in `id` and names ending in `Id`. Use the named alternative when you have a different identifier: `slug` or `roomSlug` for a room slug, `reference` or `taskReference` for a task reference, and `boardPrefix` for a board's reference prefix. Do not put a slug in an `id` field.

`files.read` returns text in `content`. For binary files, it returns `content: null` with MIME type, size, hash and an explanatory `note`. `files.write` accepts text, and `files.import` can fetch a public HTTPS source.
