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.

来源信息

仓库
anyproto/anytype-api
最近来源活动
2026年9月22日 21:35
检测到的 SKILL.md 语言
英语
星标
113
分支
20

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
2 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
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" } ],
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看