# Datanotes HTTP API

> OpenAPI 3.1.0 description of the Datanotes 3.0.0 HTTP API: https://datanotes.dev/openapi.json

Database of Markdown notes: tables of notes, rows, queries, history, a change feed and the vault's notes.

**Operations**: every operation is `POST /datanotes/operations/<name>/`, its body the operation's arguments (the same names as the MCP tool of the same name). An unknown argument is refused (400) with the closest real one.

**Authentication**: every route but `GET /`, this document and `/datanotes/docs` needs `Authorization: Bearer <token>`: the owner's token (`httpServerToken` in the settings: everything) or a named token (`/datanotes/tokens/create/`: reads everything; writes the tables in its `write` patterns, changes the schema with `ddl`; the engine's administration stays the owner's: 403 otherwise).

**Actor**: send `X-Datanotes-Actor: {"harness": …, "model": …, "session": …}` (JSON) to record on whose behalf a write is made; the journal adds the credential (`auth`: `owner`, or the named token's name), which the client cannot claim.

**Errors**: `{error}` with 400 (bad input), 401 (missing or wrong token), 403 (a named token beyond its permissions), 404 (unknown table, view, migration or note), 409 (ambiguous table name), 500. A refused write answers 422 (409 for undo) with the operation's result, `ok: false`: nothing was written.

## Servers

- `/`

## Authentication

- `bearer`: http bearer. The owner's token or a named token.

## Operations

### Schema (DDL)

Operations that define the database: tables, columns, constraints, bases and views.

- [Create a table: `name`, in `catalog` and `schema` (default: the database's defaults); the definition](https://datanotes.dev/api/operations/create_table/): `POST /datanotes/operations/create_table/`
- [Drop a table. Refused while it has rows unless `mode: "archive"`, which removes the](https://datanotes.dev/api/operations/drop_table/): `POST /datanotes/operations/drop_table/`
- [Add a typed column. Sparse by default (written only on rows that have a value — use for](https://datanotes.dev/api/operations/add_column/): `POST /datanotes/operations/add_column/`
- [Rename a column in metastore.yaml and on every row. Refused if `to_column` is already a column](https://datanotes.dev/api/operations/rename_column/): `POST /datanotes/operations/rename_column/`
- [Remove a column from the schema and every row. Previous values are journaled; revert with](https://datanotes.dev/api/operations/drop_column/): `POST /datanotes/operations/drop_column/`
- [Change a column's attributes (type, min/max/step, values, required, default, label,](https://datanotes.dev/api/operations/alter_column/): `POST /datanotes/operations/alter_column/`
- [Alter a table: move it (`catalog`, `schema`), rename it (`name`), change](https://datanotes.dev/api/operations/alter_table/): `POST /datanotes/operations/alter_table/`
- [Create or replace a base: a named set of columns (same spec as `create_table`) that tables](https://datanotes.dev/api/operations/define_base/): `POST /datanotes/operations/define_base/`
- [Set the bases a table inherits from (`bases`, in order; `[]` removes them). Inherited](https://datanotes.dev/api/operations/set_table_bases/): `POST /datanotes/operations/set_table_bases/`
- [COMMENT ON CATALOG / SCHEMA: set the `description` of a catalog (`catalog` alone) or of one of its](https://datanotes.dev/api/operations/comment_on/): `POST /datanotes/operations/comment_on/`
- [Rename one value of an enum column (e.g. `high` → `very high`) in metastore.yaml and every](https://datanotes.dev/api/operations/rename_value/): `POST /datanotes/operations/rename_value/`
- [ALTER TABLE ADD CONSTRAINT `name` CHECK: `check` is a where filter every row must match,](https://datanotes.dev/api/operations/add_constraint/): `POST /datanotes/operations/add_constraint/`
- [Remove the CHECK constraint `name` from `table`.](https://datanotes.dev/api/operations/drop_constraint/): `POST /datanotes/operations/drop_constraint/`
- [Create (or `replace`) a view: a named select stored in metastore.yaml. Name it with the prefix](https://datanotes.dev/api/operations/create_view/): `POST /datanotes/operations/create_view/`
- [Remove a view from `metastore.yaml` (undo with `undo_migration`).](https://datanotes.dev/api/operations/drop_view/): `POST /datanotes/operations/drop_view/`
- [CREATE TABLE `target` CLONE `source`: same definition plus independent copies of its rows](https://datanotes.dev/api/operations/clone_table/): `POST /datanotes/operations/clone_table/`

### Rows (DML)

Operations that write rows: insert, update, merge, delete, move, adopt and batch.

- [Insert a row (new note). Values are validated against the columns; the note path comes from](https://datanotes.dev/api/operations/insert_row/): `POST /datanotes/operations/insert_row/`
- [Update every row matching `where` (required: `{}` is every row). `where`: keys AND-ed; values are equality or operators](https://datanotes.dev/api/operations/update_rows/): `POST /datanotes/operations/update_rows/`
- [Update exactly ONE row, addressed by its `id` (the system UUID) or its `path`; refused when](https://datanotes.dev/api/operations/update_row/): `POST /datanotes/operations/update_row/`
- [Delete rows matching `where`. `mode`: "archive" (default, move to the table's archive](https://datanotes.dev/api/operations/delete_rows/): `POST /datanotes/operations/delete_rows/`
- [Move rows matching `where` (required: `{}` is every row) to another table (e.g. split a domain out of `log` into its own](https://datanotes.dev/api/operations/move_rows/): `POST /datanotes/operations/move_rows/`
- [Turn existing notes (not rows of any table yet) into rows of `table`, keeping their body and](https://datanotes.dev/api/operations/adopt_rows/): `POST /datanotes/operations/adopt_rows/`
- [Several writes that succeed or fail together. `operations`: `[{"op": ..., ...arguments}]` where](https://datanotes.dev/api/operations/batch/): `POST /datanotes/operations/batch/`
- [Add the reciprocal links missing on a table's `inverse` link columns (e.g. Alice lists Carol](https://datanotes.dev/api/operations/align_relations/): `POST /datanotes/operations/align_relations/`
- [MERGE (upsert) `rows` into `table` by the key columns `on` (e.g. `["occurred_at"]` or](https://datanotes.dev/api/operations/merge_rows/): `POST /datanotes/operations/merge_rows/`
- [Bring generated and identity columns up to date (all rows, or only `paths`), e.g. after](https://datanotes.dev/api/operations/recompute_columns/): `POST /datanotes/operations/recompute_columns/`

### Queries

Operations that read rows and notes: select_rows, link targets, text and semantic search, the link graph.

- [Read a note or another file of the vault: `{path, content, front_matter, tags, modified_at,](https://datanotes.dev/api/operations/read_note/): `POST /datanotes/operations/read_note/`
- [The direct children of a vault folder: `{folder, folders, files}`, each a vault path, sorted;](https://datanotes.dev/api/operations/list_folder/): `POST /datanotes/operations/list_folder/`
- [Plain-text search: every word of `query` must occur, case- and accent-insensitive, in the](https://datanotes.dev/api/operations/search_notes/): `POST /datanotes/operations/search_notes/`
- [Query rows: `{table, total, rows: [{path, ...}]}`. `table` may be a view,](https://datanotes.dev/api/operations/select_rows/): `POST /datanotes/operations/select_rows/`
- [The rows a link column may point to, for one row: what an agent or a picker offers before writing](https://datanotes.dev/api/operations/list_link_targets/): `POST /datanotes/operations/list_link_targets/`
- [Find notes by meaning, not exact words: `{query, results: [{path, score, heading, snippet,](https://datanotes.dev/api/operations/semantic_search/): `POST /datanotes/operations/semantic_search/`
- [Direct neighbours of notes: what they link to (`out`) and what links to them (`in`,](https://datanotes.dev/api/operations/graph_neighbors/): `POST /datanotes/operations/graph_neighbors/`
- [Breadth-first walk over links from `from_nodes` (`{"paths": [...]}` and/or `{"table", "where"}`).](https://datanotes.dev/api/operations/graph_traverse/): `POST /datanotes/operations/graph_traverse/`
- [Shortest chain of links between two sets of notes (each `{"paths": [...]}` and/or](https://datanotes.dev/api/operations/graph_path/): `POST /datanotes/operations/graph_path/`

### History

Operations on the journal, a table's versions, the change feed and webhooks: undo, time travel, restore and vacuum.

- [Revert a journaled table operation by its `migration_id`. Refused (nothing written) when any](https://datanotes.dev/api/operations/undo_migration/): `POST /datanotes/operations/undo_migration/`
- [The operation journal, newest first: `total` (the matching operations) and `migrations`, each with](https://datanotes.dev/api/operations/list_migrations/): `POST /datanotes/operations/list_migrations/`
- [DESCRIBE HISTORY: the table's versions, newest first — `version`, `timestamp`, `operation`](https://datanotes.dev/api/operations/describe_history/): `POST /datanotes/operations/describe_history/`
- [Change data feed between two versions (both included, end defaults to latest): one entry](https://datanotes.dev/api/operations/table_changes/): `POST /datanotes/operations/table_changes/`
- [Change feed across all tables: numbered row changes (`seq`), oldest first, from engine writes](https://datanotes.dev/api/operations/list_changes/): `POST /datanotes/operations/list_changes/`
- [Webhooks of the change feed: id, url, filter (tables, ops, columns, exclude_actor), `cursor` (last change delivered), `status` (ok, failing, disabled), `last_delivery`, `last_error`. Secrets are not shown.](https://datanotes.dev/api/operations/list_webhooks/): `POST /datanotes/operations/list_webhooks/`
- [Push row changes to a URL (a service that would rather be called than poll `list_changes`): batches of up to 100 events POSTed as `{webhook, events, last}`, signed with `X-Datanotes-Signature: sha256=<HMAC of the body>`, retried with backoff until a 2xx. Events are those of `list_changes` (hand edits with their `violations`; the hand edits the service has seen are recorded before it stops). Starts from now unless `since`. Returns the webhook with its `secret` — shown only here: give it to the receiver. Only on the user's explicit request: a webhook sends the database's changes (and, with `since`, its past ones) to that address.](https://datanotes.dev/api/operations/create_webhook/): `POST /datanotes/operations/create_webhook/`
- [Stop and remove a webhook (by `id` from list_webhooks).](https://datanotes.dev/api/operations/delete_webhook/): `POST /datanotes/operations/delete_webhook/`
- [RESTORE TABLE to a `version` or `timestamp`: rows and definition return to that state —](https://datanotes.dev/api/operations/restore_table/): `POST /datanotes/operations/restore_table/`
- [RESTORE DATABASE to a `timestamp` (each table as of its last version committed at or before it) or](https://datanotes.dev/api/operations/restore_database/): `POST /datanotes/operations/restore_database/`
- [Delete transaction-log history older than `retain_from_version` (default: keep only the](https://datanotes.dev/api/operations/vacuum_table/): `POST /datanotes/operations/vacuum_table/`

### Auxiliary

Operations that describe and check the database, configure it and maintain its indexes.

- [List every table: `name`, `catalog`, `schema`, `qualified` (`personal.metrics.mood` — use it](https://datanotes.dev/api/operations/list_tables/): `POST /datanotes/operations/list_tables/`
- [Describe a table (`catalog.schema.table` or a shorter unambiguous name): `qualified`, `name`,](https://datanotes.dev/api/operations/describe_table/): `POST /datanotes/operations/describe_table/`
- [Describe the database: its folder (`root`), `metastore` (`metastore.yaml`: read it freely,](https://datanotes.dev/api/operations/describe_database/): `POST /datanotes/operations/describe_database/`
- [Configure the database (persisted in metastore.yaml `config:`): `root` (rename the database](https://datanotes.dev/api/operations/configure_database/): `POST /datanotes/operations/configure_database/`
- [Validate every row against the table (required, types, ranges, enums, links, unknown](https://datanotes.dev/api/operations/check_table/): `POST /datanotes/operations/check_table/`
- [Read-only schema inference from notes' front matter.](https://datanotes.dev/api/operations/infer_table/): `POST /datanotes/operations/infer_table/`
- [The semantic search index: `enabled`, `provider`, `model`, `files`, `passages`, `pending`,](https://datanotes.dev/api/operations/describe_semantic_index/): `POST /datanotes/operations/describe_semantic_index/`
- [Bring the semantic search index up to date: embed the notes changed since the last run (the](https://datanotes.dev/api/operations/sync_semantic_index/): `POST /datanotes/operations/sync_semantic_index/`

### change feed

The change feed as Server-Sent Events, and the tool calls MCP servers report.

- [Record an agent's tool call in the change feed (`tool_call` events): what an MCP server reports after each call, its own tools included (`served_by`). Any token may call it.](https://datanotes.dev/api/operations/record_tool_call/): `POST /datanotes/tool-calls/record/`
- [The change feed as Server-Sent Events (id = sequence number). Resumes from Last-Event-ID or `since`.](https://datanotes.dev/api/operations/changes_stream/): `GET /datanotes/changes/stream`

### tokens

Named tokens and their permissions; what MCP servers report about their agents' sessions.

- [Tell the engine the model and harness behind an agent session (what an MCP server reports): later requests carrying that session in `X-Datanotes-Actor` are journaled with it. Any token may call it.](https://datanotes.dev/api/operations/identify_session/): `POST /datanotes/sessions/identify/`
- [List the named tokens (owner only).](https://datanotes.dev/api/operations/tokens_list/): `POST /datanotes/tokens/`
- [The calling token's own entry (a named token; 400 for the owner).](https://datanotes.dev/api/operations/tokens_me/): `POST /datanotes/tokens/me/`
- [Create a named token (owner only). Returns the token once.](https://datanotes.dev/api/operations/tokens_create/): `POST /datanotes/tokens/create/`
- [Change a named token. A token may change its own description and autolink default; permissions are the owner's.](https://datanotes.dev/api/operations/tokens_update/): `POST /datanotes/tokens/update/`
- [Revoke a named token (owner only): it stops working at once.](https://datanotes.dev/api/operations/tokens_delete/): `POST /datanotes/tokens/delete/`
- [Give a named token a new value (owner only): the old one stops working.](https://datanotes.dev/api/operations/tokens_rotate/): `POST /datanotes/tokens/rotate/`

### service

Status, stop, this document, the interactive console, the docs.

- [The sections of the docs of this version closest in meaning to a query: what `search_docs` fuses with its ranking by words (hybrid search).](https://datanotes.dev/api/operations/semantic_search_docs/): `POST /datanotes/manual/search/`
- [The engine's health at a glance (owner only; `datanotes status` prints it): engine, database, journal, git sync, change feed, webhooks, named tokens, hand edits breaking the rules in the last 24 h, logs.](https://datanotes.dev/api/operations/engine_status/): `POST /datanotes/status/`
- [Stop the engine gracefully (owner only; `datanotes stop` calls it): it answers at once, then finishes the requests under way, makes the last git sync and store backup commit and push, releases its lock and exits.](https://datanotes.dev/api/operations/stop_engine/): `POST /datanotes/stop/`
- [This document (as `GET /datanotes/openapi.json`, with a token).](https://datanotes.dev/api/operations/openapi_post/): `POST /datanotes/openapi/`
- [Status and version (no token needed; `authenticated` tells whether the token sent is accepted, `auth` whose it is).](https://datanotes.dev/api/operations/status/): `GET /`
- [This document (public: it describes the API, it holds no data).](https://datanotes.dev/api/operations/openapi/): `GET /datanotes/openapi.json`
- [This document in an interactive console (Swagger UI, served by the engine; public). Authorize with a token to try the routes.](https://datanotes.dev/api/operations/docs/): `GET /datanotes/docs`
- [The index of the docs of this version (every page of datanotes.dev as Markdown, shipped with the package): each page with its description and route.](https://datanotes.dev/api/operations/manual_index/): `GET /datanotes/manual/`
- [A page of the docs of this version as Markdown, or one of its sections with `section`, the anchor of its heading. The same text as the page with `.md` on datanotes.dev, offline.](https://datanotes.dev/api/operations/manual_page/): `GET /datanotes/manual/{page}.md`
