# REST API

The REST API is at `/api/v1` (`https://app.siftware.dev/api/v1`). It returns JSON. Use it where MCP does not
fit: scripts, CI, bots, and deletes.

## Authentication

Send `Authorization: Bearer <token>`. A token belongs to one user and carries abilities such as `tasks:read`,
`tasks:write`, `knowledge:read`, `knowledge:write`, `time:write` and `canvas:write`. It can never do more than
its user can in the browser. Abilities only narrow it.

- Personal token: issued in Settings > You > Connections > API tokens. The Agent preset holds every ability.
- Bot token: issued by an admin on the bot's page. Bots are the way to run an unattended agent.
- Every token expires. Expiry is 1 to 365 days, set at issue.
- `GET /api/v1/me` shows who a token is and what it can do.
- An OAuth token from the MCP server is refused here. Use a token issued in Settings.

## Pagination

List endpoints take `page` and `per_page` (default 25, at most 100). The response has `data`, `links` and
`meta`. `meta.total` is the full count and `meta.last_page` the last page. Follow `links.next` until it is
null. A `per_page` over 100 is refused on most lists and lowered to 100 on the rest.

## Idempotency

A retried write must not repeat.

- `Idempotency-Key` header: 8 to 128 characters. Used on task create and complete, task and doc comments,
  and canvas ops (required there). The same key and body returns the first response and
  `Idempotent-Replayed: true`. The same key with a different body is a 422. Remembered for 24 hours.
- `client_ref` body field: used on task create and time log create. Remembered for good.

## Errors

Every error is JSON with `message` and a stable `code`. Match on `code`.

| Status | `code` | Meaning |
|---|---|---|
| 401 | `unauthenticated` | No token, or a revoked or expired one |
| 403 | `missing_ability` | The token lacks an ability; `required_abilities` names it |
| 403 | `forbidden` | The role or project access refuses it |
| 404 | `not_found` | No such record, or one you cannot see |
| 409 | varies | A conflict with current state, such as a stale doc `version` |
| 422 | `validation_failed` | Bad input; `errors` lists each field |
| 429 | `rate_limited` | Too many requests |

Rate limits: 120 a minute for a person's token, 600 a minute for an agent or bot token.

## Docs over REST

`GET /api/v1/articles/{key}` returns a `version`. With `?markdown=1` it also returns `body_markdown` and
`blocks`. `POST /articles/{key}/edits` edits by section, `POST /articles/{key}/revisions/{id}/undo` undoes an
edit, and `PATCH` takes a `version`; a stale version is a 409. `POST /articles` takes `for` to send a doc to a
person's Inbox.

## OpenAPI

The full reference is an OpenAPI file: `https://app.siftware.dev/api-docs/openapi.json`. A browsable version
is at `/api-docs` in the app.

## Related

- [MCP tools reference](/docs/ai/mcp-tools.md)
- [Machine-readable docs](/docs/ai/machine-readable-docs.md)
- [Rules for agents](/docs/ai/rules-for-agents.md)
