Skip to main content

anytype-api

Call Anytype's local HTTP API v2 directly — search, read, create and edit objects, queries, collections and chats over REST. Use when writing scripts, SDK code or curl against the API. For interactive note/task work prefer the `anytype` CLI and its skill (cmd/anytype/SKILL.md); this guide is the raw-HTTP layer beneath it.

Quellinformationen

Repository
anyproto/anytype-api
Letzte Quellaktivität
22. September 2026 um 21:35
Erkannte Sprache von SKILL.md
Englisch
Sterne
113
Forks
21

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
anytype-api
description
Call Anytype's local HTTP API v2 directly — search, read, create and edit objects, queries, collections and chats over REST. Use when writing scripts, SDK code or curl against the API. For interactive note/task work prefer the `anytype` CLI and its skill (cmd/anytype/SKILL.md); this guide is the raw-HTTP layer beneath it.
# Anytype API v2 — HTTP guide for agents Local REST API at `http://127.0.0.1:31009` (the Anytype app must be running). Every call sends `Authorization: Bearer <key>`; keys are created in the app (Settings → API keys) — **the API mints none**. Bodies are compact JSON, and every name this API owns is `snake_case` — params, fields and op names alike. (Inside an object's `blocks` and `properties` you are reading the AnyBlock format's content; property and type keys there are served as slugs by default, as display names under `?keys=name` — see the properties bullet below.) Every list takes `?offset=&limit=` (default 25, max 1000) and returns `{data, total, offset, limit, has_more}`. **First call: `GET /v2/auth/whoami`.** A key may be scoped to particular spaces and to read-only. `grant.scoped: false` means the whole account; otherwise `grant.spaces` lists what you may touch and `grant.permission` whether you may write. Ask this instead of discovering limits through 403s (`space_not_granted` / `write_not_granted` — the message names the grant). ## The data model in six ideas - **Spaces** contain everything; nearly every route is `/v2/spaces/{space_id}/…`. `GET /v2/spaces` lists them. - An **object** = `properties` (typed key-values) + `blocks` (document content). `type` is served as a type **key** (`page`, `task`) — never an id; on input a type NAME (`"Meeting note"`) resolves too. - **Properties** are addressed by key — a snake_case slug minted once at creation and frozen, so it survives renames (`due_date`, `icon_emoji`, `manual_property`). `GET …/properties` spells them that way, and served documents spell them that way **by default**; `?keys=name` on a read serves the display-name vocabulary instead (`"Due date"`) — pick slugs for anything long-lived, names for showing a user their own words. Input is forgiving everywhere and accepts BOTH: `dueDate`, `DueDate`, `due-date`, the display name `Due date` and even `Дата выполнения` all resolve to the one property they name; an input matching two properties is a 400 listing both, and an unknown one never silently creates anything. (One exception: the compact `filter` STRING validates before folding — spell a multi-word name with underscores there, `Due_date`.) Select/multi_select values are option **names** (`"In progress"`, case-sensitive) — never option ids. A name the property does not already hold is **refused** — check it against `GET …/properties/{key}/options`, or resend with **`?create_missing_options=true`** to create it (a PATCH caps that at 64). Unknown property keys are rejected with a did-you-mean. - **Blocks** are a FLAT array in pre-order with an integer `indent` (absent = 0) — no `children` key. Inline formatting is markdown inside `text`. Use block ids exactly as a read served them. - Title and description are **not blocks** — they live in `properties` (`name`, `description`). A fresh object has zero blocks. - A **query** is a live query over a type (its type key is still `set`, the name it carries internally); a **collection** is a hand-curated list (edited via `add_items`/`remove_items`). **Chats** store messages outside blocks, paged by order-id cursors. ## Which operation | Intent | Call | |---|---| | find objects | `POST …/{space_id}/search` (or `POST /v2/search` across spaces — rows then carry `space_id`). Search with filters; don't enumerate `GET …/objects` | | read one object | `GET …/objects/{id}` — start with `?outline=true` | | change property values | PATCH op `set_properties` — `add`/`remove` for list values, `set` for scalars | | complete a task object | `set_properties` (`"set":{"done":true}` or the status option) — a property, not a block edit | | change a word/phrase | op `replace_text` `{find, replace}` — `id` optional; never retype the block | | toggle a checkbox block | op `update_block` `{"match":"Draft timeline","set":{"checked":true}}` — merge; text untouched. `match` or `id`, never both | | add content | op `insert_blocks` with a `markdown` payload — write markdown, the server parses it | | restructure | ops `move_block` / `replace_subtree` / `delete_block` (`delete_block` takes `match` too) | | one table cell | op `set_cell` — never rewrite the table | | show/hide a view column, edit a view | op `update_view` — works on queries, collections and a type's default view (PATCH the type OBJECT id from `GET …/types/{key}`) | | add / reorder / remove a view | ops `insert_view` (`copy_from` duplicates one) · `move_view` (`position:"first"` = default tab) · `delete_view` | | create an object | `POST …/objects` — shortcut `{type, name, properties, markdown}` covers most cases | | delete an object you created | `DELETE …/objects/{id}` — archives (Bin, reversible in the app). Only works on objects THIS key created after provenance shipped; anything else → 403 `not_created_by_this_key`, permanently — don't retry, archive in the app instead. Ownership is matched on the app name EXACTLY (byte-for-byte — re-pair under the identical name to keep delete rights). User content only: system objects 403. Probe first with `?dry_run=true` | | curate a collection | PATCH ops `add_items` / `remove_items` on the collection object | | read a query / collection | `GET …/queries/{id}/objects` · `…/collections/{id}/objects` (`?view=`, `?fields=`) | | new type / property | `POST …/types` · `POST …/properties`; select options ride the property, or `?create_missing_options=true` mints them from values | | upload a file | `POST …/files` (multipart or `{"url":…}`) → the id file blocks and chat attachments need | | download a file or icon | `GET …/files/{file_id}/content`; use a file id or a space/member's `icon_image`. Optional `?width=` selects an image size. Supports ranges, conditional reads, and `HEAD`. | | chat | `GET/POST …/chats/{id}/messages`, `POST …/read` — see Chats | | put an object in the sidebar | `POST …/widgets` `{target, scope}` — `scope:"personal"` is this account's own sidebar (any writer); `scope:"space"` is the shared one (owner/admin; not offered in one-to-one spaces). `GET …/widgets` lists both; `PATCH`/`DELETE …/widgets/{id-or-target}` change or remove one. Not `is_favorite` | | comment on an object | `POST …/objects/{id}/discussion` → `{id}`, then the chat routes with that id — see Chats | ## Read cheaply - `GET …/objects/{id}?outline=true` → every block's `{indent, id, type}` plus its text truncated to 80 runes — structure + addressable ids at a fraction of the tokens. Follow up with `?block={id}` for one subtree, or PATCH directly: **editing needs no prior full read once you know the ids** (but copy exact text from a full read — outline text may be cut). - When the request already quotes the text to change, skip the read entirely: `replace_text {find, replace}` locates the block itself, and `update_block`/`delete_block` take `match` for the same job (one match, or a refusal listing the candidates). - `?include=properties` or `?include=blocks` reads half the object. `?format=md` is a read-only markdown rendering. - Echo block ids back exactly as a read served them; if one is rejected as unknown, re-read and use the fresh ids. `?ids=full` is the backup/export shape — the read to archive or clone from, not needed for editing. - List/search rows are minimal `{id, name, type}`; add columns with `fields=` (property keys) instead of GETting each object. - Every object read returns an `etag` (envelope + `ETag` header). ## Edit: PATCH ops `PATCH …/objects/{id}` body `{"ops":[…]}` — one atomic batch (≤512 ops, ≤256 blocks per op): any invalid op rejects the whole PATCH with `ops[i]`-addressed issues. Fourteen ops: ```json { "ops": [ { "op": "set_properties", "set": {"status": ["Done"]}, "unset": ["oldKey"], "add": {"tags": ["urgent"]}, "remove": {"assignee": ["bafy…"]} }, { "op": "update_block", "match": "Draft timeline", "set": {"checked": true} }, { "op": "replace_text", "find": "Q3 report", "replace": "Q4 report" }, { "op": "insert_blocks", "after": "b3", "markdown": "## Notes\n- first\n- second" }, { "op": "move_block", "id": "b9", "inside": "b2", "position": "last" }, { "op": "delete_block", "id": "b4", "recursive": true }, { "op": "set_cell", "table_id": "t1", "row": "r2", "col": "c1", "value": "done" }, { "op": "update_view", "columns": {"status": {"hidden": false}} }, { "op": "insert_view", "name": "Board", "copy_from": "viewAll1", "set": {"type": "kanban", "groupBy": "status"} } ] } ``` - **`set_properties`**: a key appears in at most one of `set`/`unset`/`add`/`remove`. `add`/`remove` are per-entry list edits (select/multiSelect/objects/files) — appending one tag never rewrites the array. `remove` never creates the option it names. `set: {"k": []}` = present-but-empty; `unset` removes presence. - **`update_block`** is THE block-field op (merge; explicit `null` clears a field) — checkbox, color, language, retype, or full text rewrite. - **`match` addresses the block by its TEXT** on `update_block` and `delete_block` — the `id` alternative: give one or the other, **never both** (and never neither). The text must appear in exactly ONE block or the op refuses: zero → read the outline, several → the error lists candidate ids to retry with. Repeats inside the one matched block are fine — `match` names a block, not an occurrence. It reads the document as the ops before it in the batch left it. - **`replace_text`**: `id` is optional — omitted, `find` locates the block and must appear in exactly ONE block (zero or several matching blocks refuse; the ambiguity error lists candidate ids to retry with). Within the matched block `find` must match exactly once ("found 2 matches — provide more context"); `replace_all: true` is the escape, within that one block only. Preferred over `update_block` for word-level edits. `replace_subtree {id, blocks}` swaps a block plus descendants. - **`insert_blocks`**: `blocks` (flat array) or `markdown` — mutually exclusive, same targeting. Target with one of `after`/`before`/`inside` (+`position: first|last` inside that container); omit all three and `position` picks an end of the DOCUMENT — `last` (or absent) appends, `first` inserts at the start, both on an empty object too. Payload `indent: 0` = the anchor's level (`after`/`before`) or the container's child level (`inside`). `move_block` targets the same way, so `{"op":"move_block","id":"b9","position":"first"}` moves a block to the top of the document. - **Author new content without ids** — an `id` names an EXISTING block, so `insert_blocks` takes none anywhere in its payload (rows and columns included); the server mints them and returns them in `created_blocks`, keyed by the payload path that produced each — `ops[0].blocks[0]`, `ops[0].blocks[0].rows[1]`, `ops[0].blocks[0].columns[0]`. The same holds wherever you leave an id out of an existing-content payload (a new row in `update_block set.rows`, a block inside a `set_cell` array), so you never have to re-read to learn an id you just created. - **`update_view`** edits ONE dataview view — never resend the views array. `block`/`view` are optional when the object has one dataview and it one view (types, queries, collections usually do). `set` merges view fields (`name`, `type`, `groupBy`, `sorts`, `filters` — arrays replace whole; `filter` takes the compact string; null clears a field); `columns` merges per property key: `{"hidden": false}` shows a column, `null` removes it, a new key appends one. Works on Blocks-restricted objects — view config is not a block edit. - **`insert_view`/`move_view`/`delete_view`** complete the family (same addressing, same channels; insert_view's name is its own required field — not in `set`). insert_view needs only `name` — bare default: every listed property visible, newest first; `copy_from` duplicates a view (then `set`/`columns` override); the minted id returns in `created_views`, keyed `ops[i]`. move_view REQUIRES one of `after`/`before`/`position` (`"first"` = default tab). delete_view refuses the last view — insert the replacement first (one atomic batch swaps a bad default view). - Response: new `etag`, `created_blocks` (payload position → real id; nested row/column/cell slots included), `created_views` (same, for minted view ids), `created` (options minted under `?create_missing_options=true`), `diff_stats {blocks_added, blocks_removed, blocks_changed, blocks_moved, properties_changed}`, `warnings` (advisory, e.g. an unguarded date filter). - **There is no whole-document replace** — never read a document, regenerate it and write it back. Replace a section with `replace_subtree`; start over by batching `delete_block`s with the new `insert_blocks`. ## Query `POST …/search` body: `{query?, type?, filter?|filters?, sorts?, fields?}`. Pagination is the query params — a body `limit` is rejected. Search is a read: no `Idempotency-Key`, `dry_run` ignored. ```json { "query": "report", "type": "task", "filter": "done = false AND (due_date < currentWeek() OR due_date IS EMPTY)", "sorts": [ { "property": "due_date", "direction": "asc" } ],
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen