Board
Read and edit the whiteboards of DFL Board: stickies, shapes, text, frames, groups, arrows, images, PDFs and markdown cards, plus the viewport, the presence of people and agents, and the board chat.
- Endpoint
https://board.mcp.devfellowship.com/mcp
- Tools
- 52 in 10 groups
- Repo
devfellowship/dfl-board- Auth
-
Your
dfl-auth logintoken asAuthorization: Bearer <token>. Every call runs as you, under RLS. See Auth & security.
Connect
Add to your project's .mcp.json (or the global ~/.claude/mcp.json).
{ "mcpServers": { "dfl-board": { "type": "http", "url": "https://board.mcp.devfellowship.com/mcp", "headers": { "Authorization": "Bearer YOUR_ACCESS_TOKEN" } } }}
Then restart Claude Code (or run /mcp to reconnect). You should see the dfl-board tools
appear.
Cursor needs mcp-remote as a proxy so the Authorization header is passed correctly. Add
to ~/.cursor/mcp.json:
{ "mcpServers": { "dfl-board": { "command": "npx", "args": [ "mcp-remote", "https://board.mcp.devfellowship.com/mcp", "--transport", "http-only", "--header", "Authorization:${DFL_TOKEN}" ], "env": { "DFL_TOKEN": "Bearer YOUR_ACCESS_TOKEN" } } }}
VS Code (1.102+) has native MCP support. Add to your workspace .vscode/mcp.json (or run
MCP: Add Server from the command palette):
{ "servers": { "dfl-board": { "type": "http", "url": "https://board.mcp.devfellowship.com/mcp", "headers": { "Authorization": "Bearer YOUR_ACCESS_TOKEN" } } }}
For older MCP extensions (e.g. Continue) that only speak SSE, use the mcp-remote proxy pattern shown
in the Cursor tab.
Add to ~/.codex/config.toml:
[[mcp_servers]]name = "dfl-board"url = "https://board.mcp.devfellowship.com/mcp"[mcp_servers.headers]Authorization = "Bearer YOUR_ACCESS_TOKEN"Other clients (Cursor, VS Code, codex, the Anthropic SDK): see Getting started, step 3.
| Endpoint | https://board.mcp.devfellowship.com/mcp. Bearer token (dfl-auth login) or OAuth (browser sign-in and consent, scopes board.read and board.write), the same as the other hosts. The old URL https://boards.devfellowship.com/mcp is retired. |
| Served by | packages/dfl-mcp-board (the OAuth resource) in front of the board server, devfellowship/dfl-board, which runs every tool. Each call reaches the board server with your own token. |
| Source of this page | A committed snapshot of the host’s live tools/list (docs/external-hosts/board.tools-list.json). Refresh it with scripts/refresh-external-host.mjs board. |
| Not in the one DFL MCP | The gateway router does not route to this host. Connect it as its own server. |
Boards & reads
Section titled “Boards & reads”Find a board and read what is on it: the outline page by page, a search, one item with its neighbours, the items of a frame, and the recent operations.
archive_board
Archive a board you own. It deletes nothing: the board and its items stay, get_board still reads it, and list_boards hides it unless include_archived is true.write
archive_boardarchive board
Archive a board you own. It deletes nothing: the board and its items stay, get_board still reads it, and list_boards hides it unless include_archived is true. Returns {id, slug, archived_at}. A second call keeps the first archived_at. Only the owner may archive; another caller gets forbidden.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
create_board
Create an empty board. You become its owner.write
create_boardcreate board
Create an empty board. You become its owner. Returns {id, slug, name, visibility, url}. Without slug the server makes one from the name, with a short suffix when it is taken. With slug, a taken slug fails with slug_conflict and writes nothing, so send your own slug when you may retry after a timeout.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | — |
slug | string | no | — |
settings | object | no | — |
list_boards
Read visible boards with live item counts.read
list_boardslist boards
Read visible boards with live item counts. Query matches literal name or slug substrings before the limit; case folding is ASCII-only. Archived boards are hidden unless include_archived is true; an archived board carries archived_at.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | number | no | Default: 50. |
query | string | no | — |
include_archived | boolean | no | Default: false. |
get_board
Read current board metadata, live item bounds/count, and deduplicated active authenticated subscribers or unexpired agent activity leases.read
get_boardget board
Read current board metadata, live item bounds/count, and deduplicated active authenticated subscribers or unexpired agent activity leases. Empty bounds are null.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
get_board_outline
Read one page of outline rows in deterministic location order: {items, next_offset}.read
get_board_outlineget board outline
Read one page of outline rows in deterministic location order: {items, next_offset}. Each row carries text, the visible text of the item (props.text, the bound label of a shape or sticky, or a frame title, with inline marks removed; absent when the item has none), cut after 200 characters with a trailing "…" and text_truncated: true when longer (the same rule as neighbours_summary) (text_truncated is absent otherwise); get_item returns the full text. Each page is at most 30,000 bytes of JSON and can hold fewer than limit (default 200) rows: long texts make a page shorter, never larger. Call again with offset=next_offset until next_offset is null. Region uses the board spatial index. has_description: false lists only the items with no description (null or blank), true only the items with one.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
region | object | no | — |
kinds | enum[] | no | One of: text, sticky, shape, frame, image, embed, report, pdf_page, diagram, document, spec_run, sheet, plan, agent_card, repo, sandpack. |
has_description | boolean | no | — |
limit | number | no | Default: 200. |
offset | number | no | Default: 0. |
search_items
Read ranked items, including parent_id, z, description and text (the visible text, cut after 200 characters with text_truncated: true, as in get_board_outline).read
search_itemssearch items
Read ranked items, including parent_id, z, description and text (the visible text, cut after 200 characters with text_truncated: true, as in get_board_outline). limit: default 20, max 100. Ranking: each field is scored on its own, never averaged and never divided by the total text length. A field that equals the query (case and accents ignored) scores 1, so an exact text match is always first; a partial match scores below 1, from how close the field is to the query or from how often the query occurs in it (saturating, so a long text that mentions the query many times is not pushed down). The best of text, name, frame title and image caption counts; a description match adds to that score and never lowers it, and a word found only in a description still returns the item. Without a query, items come in id order with score 0; has_description: false lists the items that still need a description.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
query | string | no | — |
region | object | no | — |
kinds | enum[] | no | One of: text, sticky, shape, frame, image, embed, report, pdf_page, diagram, document, spec_run, sheet, plan, agent_card, repo, sandpack. |
has_description | boolean | no | — |
limit | number | no | Default: 20. |
get_item
Read the complete visible item, raw elements, geometry, and neighbours_summary.read
get_itemget item
Read the complete visible item, raw elements, geometry, and neighbours_summary. Neighbours (L18): the nearest item, measured edge to edge (the gap between the two boxes, 0 when they touch or overlap; never an arrow or a frame that holds the item), plus every item linked to it by an arrow or connector, with direction from (the arrow points from that item to this one), to (from this one to that item) or both, the arrow_id and the arrow label. No item is listed twice. Sorted by distance, then id. Each neighbour carries id, kind, name, side, distance and its visible text (props.text without marks, cut at 200 characters with an ellipsis and text_truncated: true). props carry backgroundColor and strokeColor when the item has them. props.text keeps its inline marks as written (bold, *italic*, __underline__, ~~strike~~; \* \_ \~ for a literal marker). A pen stroke is kind shape with props.geo freedraw, strokeWidth and point_count. An image item carries caption (the alt text that an agent or a user wrote, else the generated caption; null until the caption job is done), caption_source (user or generated), caption_status (queued, done or failed) and caption_text_in_image (the text read inside the image, when there is any); search_items matches the caption and the text in the image. Set an alt text with update_item {id, caption}.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
id | string | yes | — |
get_items
Read 1 to 100 items in one call: {items}, one entry per id in the order given (a repeated id is listed once).read
get_itemsget items
Read 1 to 100 items in one call: {items}, one entry per id in the order given (a repeated id is listed once). An unknown or deleted id comes back as {id, error:"not_found"} and the other entries are still returned. compact (default false): false returns each item as get_item does (raw elements, neighbours_summary and, for an image, the caption fields); true returns {id, kind, name, text, text_truncated?, description, caption?, bbox, parent_id, version} with no elements, which is the cheap way to read a whole board. text is the visible text (marks removed, cut at 200 characters). neighbours (default: true for the full form, false for compact) adds neighbours_summary with the get_item rule.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
ids | string[] | yes | — |
compact | boolean | no | Default: false. |
neighbours | boolean | no | — |
list_items_near
Read the items near an anchor, or actual ambiguous anchor candidates.read
list_items_nearlist items near
Read the items near an anchor, or actual ambiguous anchor candidates. Never guess an ambiguous query. The rows start with the neighbours of get_item (the same rule: the nearest item edge to edge, plus every arrow-linked item with direction from, to or both, the arrow_id and label), so the radius never drops the nearest item; then every other item whose edge-to-edge distance is at most radius (default 800), by distance. Each row carries the visible text (cut at 200 characters, text_truncated: true). dx and dy are center to center.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
anchor | object | object | object | yes | — |
radius | number | no | Default: 800. |
limit | number | no | Default: 20. |
list_frame_items
Read all direct frame children, not descendants.read
list_frame_itemslist frame items
Read all direct frame children, not descendants. Oversized results fail explicitly; use paged outline instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
frame_id | string | yes | — |
list_ops
Read actual ordered board operations after a durable sequence (seq greater than since_seq).read
list_opslist ops
Read actual ordered board operations after a durable sequence (seq greater than since_seq). This read never appends an operation. Every write response carries the seq of its operation, so since_seq = the seq of your last write reads what changed since then. summary: true returns, for the same ops, one row per changed item: {last_seq (the last op read; the next since_seq), actors: {id: {slug, name, kind}}, items: [{id, last_seq, count, ops (distinct op names), actors (actors keys)}]}, ordered by last_seq. Read the changed items with get_items {ids, compact: true}.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
since_seq | number | yes | — |
limit | number | no | Default: 100. |
summary | boolean | no | Default: false. |
Put stickies, shapes and text on a board, then move, resize, edit, nest or delete them. Every write takes an operation_id, so a retry does not add a second item.
create_item
Create one item on a board.write
create_itemcreate item
Create one item on a board. props may set backgroundColor (fill), strokeColor (the line; on a sticky or shape also the label text) and textColor (the text only, it wins over strokeColor for the text): transparent, #RRGGBB or #RRGGBBAA. props may set fontSize (8 to 256) and textAlign (left, center or right) on text, shape labels and stickies. Text props (text on a text item, a shape label or a sticky) may hold inline marks: bold, *italic*, __underline__, ~~strike~~ (one line per pair, a non-space right after the opener and right before the closer); write \* \_ \~ for a literal marker. A text with no marker is stored as given. A website preview is kind embed with props {url (https), title?, device? (desktop, tablet, mobile or {w, h} in CSS px, 200 to 4096), shared_location? {url, scroll_y?}}: the server checks whether the site allows framing and returns frameable (yes, no or unknown); a URL that resolves to a private or metadata address is refused with invalid_input. A mark over a website preview (kind shape, sticky or text) passes parent_id = the preview id: it moves, scales and deletes with the card. props.anchor {space:"frame", url, epoch?, frame:{x,y,w,h}, device:{w,h}} is optional (frame and device in device-viewport CSS px); without it the server computes the anchor from the item box at the preview home URL. An anchor without a preview parent is refused with invalid_input. The result includes the saved item and a reveal hint. A text that does not fit its w and h fails with text_does_not_fit and details {suggested_h (the box height that fits at your w), min_w (the smallest width for the longest word), suggested_font_size (when a smaller font fits the box)}: retry with those values. fit: "grow" makes h grow to the text instead of failing (w and h stay the minimum). Retry: send your own operation_id (a UUID). After a timeout or a lost response, call again with the same operation_id and the same input: the server returns the stored result and creates nothing new. Other input with that operation_id fails with operation_conflict. For 3 or more items, use one upsert_items call (up to 100 items, one commit, a lean response), not parallel create_item calls. return: "changed" (the default) gives a lean response {id, version, kind, bbox, overlaps} (a website preview adds frameable), with no raw elements: use it when you only need the id. overlaps (create_item and create_item_near) lists the ids of the items whose box the new item intersects (arrows, lines and the containing frames excluded), so you need no image read to check the placement. return: "compact" gives the get_items compact item with neighbours_summary. return: "full" gives the whole item with elements and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
kind | enum | yes | One of: text, sticky, shape, frame, image, embed, report, pdf_page, diagram, document, spec_run, sheet, plan, agent_card, repo, sandpack. |
name | string | no | — |
description | string | no | — |
x | number | no | — |
y | number | no | — |
w | number | no | — |
h | number | no | — |
parent_id | string | no | — |
props | object | no | Default: {}. |
fit | literal("grow") | no | — |
media_url | string | no | — |
icon | object | no | — |
return | enum | no | One of: changed, compact, full. |
create_item_near
Create an item beside an anchor.write
create_item_nearcreate item near
Create an item beside an anchor. An ambiguous query returns candidates and does not write. A website preview (kind embed, props {url}) is probed like create_item and returns frameable. return: "changed" (the default) gives a lean response {id, version, kind, bbox, overlaps} (a website preview adds frameable), with no raw elements: use it when you only need the id. overlaps (create_item and create_item_near) lists the ids of the items whose box the new item intersects (arrows, lines and the containing frames excluded), so you need no image read to check the placement. return: "compact" gives the get_items compact item with neighbours_summary. return: "full" gives the whole item with elements and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
kind | enum | yes | One of: text, sticky, shape, frame, image, embed, report, pdf_page, diagram, document, spec_run, sheet, plan, agent_card, repo, sandpack. |
name | string | no | — |
description | string | no | — |
x | number | no | — |
y | number | no | — |
w | number | no | — |
h | number | no | — |
parent_id | string | no | — |
props | object | no | Default: {}. |
fit | literal("grow") | no | — |
anchor | object | object | yes | — |
side | enum | yes | One of: right, left, above, below. |
gap | number | no | Default: 24. |
align | enum | no | One of: start, center, end. Default: "center". |
avoid_overlap | boolean | no | Default: true. |
return | enum | no | One of: changed, compact, full. |
update_item
Update one item with the supplied changed fields.write
update_itemupdate item
Update one item with the supplied changed fields. props may set backgroundColor (fill), strokeColor (the line; on a sticky or shape also the label text) and textColor (the text only, it wins over strokeColor for the text): transparent, #RRGGBB or #RRGGBBAA. props may set fontSize (8 to 256) and textAlign (left, center or right) on text, shape labels and stickies. Text props (text on a text item, a shape label or a sticky) may hold inline marks: bold, *italic*, __underline__, ~~strike~~ (one line per pair, a non-space right after the opener and right before the closer); write \* \_ \~ for a literal marker. A text with no marker is stored as given. On an arrow, props may set startArrowhead and endArrowhead (arrow or none) and arrowheadSize (s, m or l); on an arrow or a line, strokeWidth (1 to 40). On an image, caption sets the alt text (1 to 500 characters): it replaces the generated caption, get_item reads it with caption_source: user, and the caption worker never overwrites it. Use base_version to reject stale edits. By default the response is lean: {id, version, changed: {field: stored value}} for the fields you sent, with no raw elements. return: "compact" gives the get_items compact item (id, kind, name, text, description, bbox, parent_id, version) with neighbours_summary; return: "full" gives the whole item as get_item does, with neighbours_summary and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
id | string | yes | — |
name | string | no | — |
description | string | no | — |
caption | string | no | — |
locked | boolean | no | — |
props | object | no | — |
return | enum | no | One of: changed, compact, full. |
upsert_items
Create or update 1 to 100 items in one board-service operation.write
upsert_itemsupsert items
Create or update 1 to 100 items in one board-service operation. Batch descriptions: send items as up to 100 {id, description} entries, for example {board, items:[{id:"a1", description:"Card A"}, {id:"b2", description:"Card B"}]}. For an existing item only the fields you send change; the rest of the item (text, label, style, position, parent, arrow bindings) is kept. An update entry may also set x, y (move, as set_item_position) and w, h (resize, as set_item_size); send each pair whole. So {board, items:[{id:"a1", x:0, y:0}, {id:"b2", x:240, y:0}]} moves many items in one call and one commit; one stale base_version or one locked item refuses the whole batch. More than 100 entries are refused with invalid_input; split them into several calls. Item props may set backgroundColor and strokeColor: transparent, #RRGGBB or #RRGGBBAA. Item props may also set fontSize (8 to 256) and textAlign (left, center or right). Text props (text on a text item, a shape label or a sticky) may hold inline marks: bold, *italic*, __underline__, ~~strike~~ (one line per pair, a non-space right after the opener and right before the closer); write \* \_ \~ for a literal marker. A text with no marker is stored as given. A new website preview (kind embed) is probed like create_item: its props carry frameable. A new mark may set parent_id to a website preview and props.anchor, as in create_item. Build a diagram in ONE call: a create entry may set ref (a local name, [A-Za-z0-9_-], 1 to 64), and a later entry may use it. place {anchor: {id} or {ref}, side (right, left, above, below), gap (40), align (center)} puts a new entry beside an item or an earlier entry, instead of x, y. layout {direction (row, column or grid), x, y, gap (40), columns (grid, default the square root)} lays out, in input order, every create entry that has no x, y and no place. A connector entry {kind:"connector", from: {id} or {ref}, to: {id} or {ref}, from_anchor?, to_anchor?, routing?, label?, stroke_color?, start_arrowhead?, end_arrowhead?, stroke_width?} draws an arrow as create_connector does; its ends must be earlier entries or existing items. A ref that is not defined by an earlier entry is refused with invalid_input. Retry: send your own operation_id (a UUID). After a timeout or a lost response, call again with the same operation_id and the same input: the server returns the stored result and creates nothing new. Other input with that operation_id fails with operation_conflict. By default the response is lean: {items:[{id, version, changed}]} with the stored value of each field sent per item (a new item: {id, version, created:true, kind, bbox, ref when you sent one}), with no raw elements. return: "compact" gives the get_items compact item per entry, with neighbours_summary; return: "full" gives {items, reveal} with the whole items, each with neighbours_summary.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
items | object | object | object[] | yes | — |
layout | object | no | — |
return | enum | no | One of: changed, compact, full. |
create_mind_map
Create a whole mind map in ONE call from a tree: {board, root: {text, children?: [{text, children?}]}, x?, y?, direction?write
create_mind_mapcreate mind map
Create a whole mind map in ONE call from a tree: {board, root: {text, children?: [{text, children?}]}, x?, y?, direction? (right, default; or left), routing? (curved default, straight or orthogonal), operation_id}. x, y (default 0, 0) are the scene point of the top-left corner of the root; call get_board_outline first and pick a free area. The server lays the tree out (tidy tree: each depth is one column, each subtree its own band, a parent sits at the middle of its children) so no two nodes overlap and every child is to the right of its parent (to the left with direction left). A node is a sticky of 240 px width whose height follows its text (text 1 to 300 characters, marks allowed); an edge is one connector bound at both ends. Limits: 200 nodes, depth 8. The server writes at most 50 nodes per commit, so a larger tree is several commits (batches in the result). Turn a long text into the tree yourself: short node texts, 3 to 7 children per node. Returns {root_id, node_count, connector_count, batches, nodes:[{id, text, depth, parent_id, bbox}], connector_ids, reveal}. The parent of a node is the connector that ends on it; to extend the map later, create stickies and connectors with upsert_items, using the ids returned here. Retry: send your own operation_id (a UUID). After a timeout or a lost response, call again with the same operation_id and the same input: the server returns the stored result and creates nothing new. Other input with that operation_id fails with operation_conflict.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
root | object | yes | — |
x | number | no | Default: 0. |
y | number | no | Default: 0. |
direction | enum | no | One of: right, left. Default: "right". |
routing | enum | no | One of: straight, orthogonal, curved. Default: "curved". |
operation_id | string | no | — |
delete_item
Delete one item by id. This operation can remove the item and its owned elements.write
delete_itemdelete item
Delete one item by id. This operation can remove the item and its owned elements.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
base_version | number | no | — |
id | string | yes | — |
operation_id | string | no | — |
set_item_position
Move one item. Exactly one alternative: x plus y, or anchor plus side with optional gap (24), align (center), avoid_overlap (true).write
set_item_positionset item position
Move one item. Exactly one alternative: x plus y, or anchor plus side with optional gap (24), align (center), avoid_overlap (true). Do not mix alternatives. An ambiguous anchor returns candidates without a write. By default the response is lean: {id, version, changed: {field: stored value}} for the fields you sent, with no raw elements. It also carries overlaps: the ids of the items whose box the moved item intersects (arrows, lines and the containing frames excluded). return: "compact" gives the get_items compact item (id, kind, name, text, description, bbox, parent_id, version) with neighbours_summary; return: "full" gives the whole item as get_item does, with neighbours_summary and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
id | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
x | number | no | — |
y | number | no | — |
anchor | object | object | no | — |
side | enum | no | One of: right, left, above, below. |
gap | number | no | — |
align | enum | no | One of: start, center, end. |
avoid_overlap | boolean | no | — |
return | enum | no | One of: changed, compact, full. |
set_item_size
Set one item width and height.write
set_item_sizeset item size
Set one item width and height. The board service preserves its geometry rules. By default the response is lean: {id, version, changed: {field: stored value}} for the fields you sent, with no raw elements. return: "compact" gives the get_items compact item (id, kind, name, text, description, bbox, parent_id, version) with neighbours_summary; return: "full" gives the whole item as get_item does, with neighbours_summary and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
id | string | yes | — |
w | number | yes | — |
h | number | yes | — |
keep_aspect | boolean | no | Default: false. |
return | enum | no | One of: changed, compact, full. |
set_item_description
Set the description of one item.write
set_item_descriptionset item description
Set the description of one item. For more than one item, use upsert_items with up to 100 {id, description} entries in one call. By default the response is lean: {id, version, changed: {field: stored value}} for the fields you sent, with no raw elements. return: "compact" gives the get_items compact item (id, kind, name, text, description, bbox, parent_id, version) with neighbours_summary; return: "full" gives the whole item as get_item does, with neighbours_summary and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
id | string | yes | — |
description | string | yes | — |
return | enum | no | One of: changed, compact, full. |
set_item_parent
Place items in a frame or a website preview, or remove them from their current parent (frame_id null).write
set_item_parentset item parent
Place items in a frame or a website preview, or remove them from their current parent (frame_id null). frame_id names a frame or a preview (an embed item). A preview takes marks only (shapes, lines, arrows, pen strokes, text, stickies): they move, scale and delete with the card, and each gets props.anchor in frame space at the preview home URL. An item that leaves a preview loses its anchor.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
ids | string[] | yes | — |
frame_id | string | yes | — |
Frames & groups
Section titled “Frames & groups”Box related items in a titled frame, or group items so they move together.
create_frame
Create a frame. Exactly one alternative: x, y, w, h bounds, or fit_ids with optional padding (48).write
create_framecreate frame
Create a frame. Exactly one alternative: x, y, w, h bounds, or fit_ids with optional padding (48). Do not mix alternatives. Both require board and title. return: "changed" (the default) gives a lean response {id, version, kind, bbox, overlaps} (a website preview adds frameable), with no raw elements: use it when you only need the id. overlaps (create_item and create_item_near) lists the ids of the items whose box the new item intersects (arrows, lines and the containing frames excluded), so you need no image read to check the placement. return: "compact" gives the get_items compact item with neighbours_summary. return: "full" gives the whole item with elements and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
title | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
x | number | no | — |
y | number | no | — |
w | number | no | — |
h | number | no | — |
fit_ids | string[] | no | — |
padding | number | no | — |
return | enum | no | One of: changed, compact, full. |
create_group
Group the selected items and return the new group id.write
create_groupcreate group
Group the selected items and return the new group id.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
ids | string[] | yes | — |
remove_group
Remove one group from all items that contain it.write
remove_groupremove group
Remove one group from all items that contain it.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
group_id | string | yes | — |
Images, PDFs & documents
Section titled “Images, PDFs & documents”Add an image, a Lucide icon or emoji, every page of a PDF, or a markdown document card to a board.
add_asset
Upload one PNG, JPEG or WebP image (base64, max 6 MiB) to the board.write
add_assetadd asset
Upload one PNG, JPEG or WebP image (base64, max 6 MiB) to the board. Returns a stable media URL. Pass that URL to create_item with kind image.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
file_content | string | yes | — |
file_name | string | yes | — |
mime_type | enum | yes | One of: image/png, image/jpeg, image/webp. |
operation_id | string | no | — |
add_image
Upload and place one image in one call.write
add_imageadd image
Upload and place one image in one call. Exactly one source: file_content (base64 PNG, JPEG or WebP, max 6 MiB; file_name and mime_type are then required), media_url (a https://media.devfellowship.com/<id> link, read without a session; a server without MEDIA_ANONYMOUS_KEY_PREFIXES answers not_available) or url (any public https:// image URL: the server downloads it, max 6 MiB and 10 s, accepts PNG, JPEG or WebP by their bytes only, never SVG, HTML or animated images, refuses private, internal and DFL fleet addresses also after a redirect, and stores a re-encoded copy without EXIF; the item keeps props.source_url, and the result returns source_url; a refusal answers url_refused with the rule that refused it). Optional x, y (scene point of the top-left corner) and w (display width; the height keeps the ratio). A retry with the same operation_id returns the first result. By default the response is lean: {id, version, kind, bbox (the stored position and size), media_url, file_id}, with no raw elements. return: "compact" gives the get_items compact item (id, kind, name, text, description, bbox, parent_id, version) with neighbours_summary; return: "full" gives the whole item as get_item does, with neighbours_summary and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
file_content | string | no | — |
media_url | string | no | — |
url | string | no | — |
file_name | string | no | — |
mime_type | enum | no | One of: image/png, image/jpeg, image/webp. |
x | number | no | — |
y | number | no | — |
w | number | no | — |
operation_id | string | no | — |
return | enum | no | One of: changed, compact, full. |
search_images
Search Wikimedia Commons for photos and diagrams (free licences, no key).read
search_imagessearch images
Search Wikimedia Commons for photos and diagrams (free licences, no key). Returns {provider, query, results:[{url, width, height, title, author, licence, licence_url, source_page, original_width, original_height}]}. url is an 800 px JPEG, PNG or WebP: pass it as-is to add_image {url}. Put author, licence and source_page in the item description of the image you add. Not for company logos or product screenshots: Commons holds mostly photos, maps and diagrams. Limit: 30 uncached searches per actor per minute (rate_limited). resolver_upstream means Commons failed: retry once.
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | yes | — |
limit | number | no | Default: 5. |
add_stroke
Draw 1 to 50 freehand strokes in one call (a line, a path, a highlight, a sketch).write
add_strokeadd stroke
Draw 1 to 50 freehand strokes in one call (a line, a path, a highlight, a sketch). Each stroke is {points: [[x, y], ...] (scene coordinates, 2 to 10,000), color? (#RRGGBB, default #E07A4A), width? (1 to 32), opacity? (1 to 100), brush? (pen, marker or highlighter), pressures? (one value 0 to 1 per point)}. brush pen (or no brush) uses pressures; marker keeps one width; highlighter is 3 times the default width with opacity 40. A brush sets only width and opacity; your explicit width or opacity wins. The server simplifies each stroke like the web pen and splits it above 5,000 points, so one stroke can give more than one id. Each stroke is a shape item with props.geo freedraw (get_item shows strokeWidth and point_count); get_board_image draws it. Optional parent_id puts the strokes in a frame. By default the response is lean: {ids, bbox (the union box), version}; return: "full" gives {items, reveal}. All strokes are one operation. Retry: send your own operation_id (a UUID). After a timeout or a lost response, call again with the same operation_id and the same input: the server returns the stored result and creates nothing new. Other input with that operation_id fails with operation_conflict.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
strokes | object[] | yes | — |
parent_id | string | no | — |
return | enum | no | One of: changed, full. |
add_icon
Place one icon or emoji on a board, as the toolbar picker does.write
add_iconadd icon
Place one icon or emoji on a board, as the toolbar picker does. Exactly one of query (the picker name and tag search, best hit wins: "users" finds the users icon) or name (exact Lucide name such as users, or an emoji character or name). set (lucide or emoji) narrows the search; without it Lucide icons come first. A Lucide icon becomes an image item (256 px PNG, stroke color, default #f6f1e7) with customData.dfl.icon = {set, name, color}. The same icon and color on a board reuses the stored file (reused: true, no upload). An emoji becomes a text item. Optional x, y (scene point of the top-left corner), size (display size in scene units, default 96) and color (#RGB or #RRGGBB, Lucide only). Returns the saved item with its set and name. A retry with the same operation_id returns the first result.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
query | string | no | — |
name | string | no | — |
set | enum | no | One of: lucide, emoji. |
x | number | no | — |
y | number | no | — |
size | number | no | — |
color | string | no | — |
operation_id | string | no | — |
create_items_from_pdf
Import a PDF (max 20 MB, 50 pages) as one frame named after the file, with one image item per page in a grid (columns, default 4).write
create_items_from_pdfcreate items from pdf
Import a PDF (max 20 MB, 50 pages) as one frame named after the file, with one image item per page in a grid (columns, default 4). Exactly one source: file_content (base64) or media_url (a https://media.devfellowship.com/<id> link). Returns frame_id, page_ids and page_count. A retry with the same operation_id returns the first result.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
file_content | string | no | — |
media_url | string | no | — |
file_name | string | no | — |
x | number | no | — |
y | number | no | — |
columns | number | no | — |
operation_id | string | no | — |
add_markdown
Add one markdown card. The markdown (max 20,000 characters) is saved as a DFL document with YOUR DFL JWT, so documents access rules decide the write; the board stores only a reference and renders the markdown inline.write
add_markdownadd markdown
Add one markdown card. The markdown (max 20,000 characters) is saved as a DFL document with YOUR DFL JWT, so documents access rules decide the write; the board stores only a reference and renders the markdown inline. Optional title (else the first heading), x, y (top-left scene point), w, h. Read back with get_item; its entity_id is the document id. Edit later with update_item {id, props:{markdown}}. A retry with the same operation_id returns the first result.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
markdown | string | yes | — |
title | string | no | — |
x | number | no | — |
y | number | no | — |
w | number | no | — |
h | number | no | — |
operation_id | string | no | — |
Arrows
Section titled “Arrows”Connect two items with an arrow that follows them when they move, and pick its route (straight, elbow or curved).
create_connector
Create an arrow bound to two items (from_id to to_id), listed in both items boundElements.write
create_connectorcreate connector
Create an arrow bound to two items (from_id to to_id), listed in both items boundElements. Optional from_anchor and to_anchor (top, right, bottom, left; default: the sides that face each other), from_anchor_mode and to_anchor_mode (auto or manual; default auto: the end re-picks the facing side when an item moves; manual pins the end to its side, as a browser drop on an anchor point does, and without an anchor it pins the facing side), routing (straight, orthogonal, curved; default straight), label, stroke_color, start_arrowhead and end_arrowhead (arrow or none; default none and arrow), arrowhead_size (s, m or l; default m), stroke_width (1 to 40; default 2). The arrow follows both items when either one moves or resizes. Returns the arrow item with from, to, from_anchor, to_anchor and routing in props, plus from_anchor_mode or to_anchor_mode for a manual end. return: "changed" (the default) gives a lean response {id, version, kind, bbox, overlaps} (a website preview adds frameable), with no raw elements: use it when you only need the id. overlaps (create_item and create_item_near) lists the ids of the items whose box the new item intersects (arrows, lines and the containing frames excluded), so you need no image read to check the placement. return: "compact" gives the get_items compact item with neighbours_summary. return: "full" gives the whole item with elements and the reveal hint.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
from_id | string | yes | — |
to_id | string | yes | — |
from_anchor | enum | no | One of: top, right, bottom, left. |
to_anchor | enum | no | One of: top, right, bottom, left. |
from_anchor_mode | enum | no | One of: auto, manual. |
to_anchor_mode | enum | no | One of: auto, manual. |
routing | enum | no | One of: straight, orthogonal, curved. |
label | string | no | — |
stroke_color | string | no | — |
start_arrowhead | enum | no | One of: none, arrow. |
end_arrowhead | enum | no | One of: none, arrow. |
arrowhead_size | enum | no | One of: s, m, l. |
stroke_width | number | no | — |
return | enum | no | One of: changed, compact, full. |
update_connector
Change one arrow in place; its id stays.write
update_connectorupdate connector
Change one arrow in place; its id stays. Pass at least one change. from_id / to_id re-pin that end to another item (the arrow leaves the old item boundElements and joins the new one). from_anchor / to_anchor (top, right, bottom, left) and from_anchor_mode / to_anchor_mode (auto or manual, as in create_connector) change an end; a free end takes them only with an item id. An anchor you leave out stays when its item stays (for an auto end, when the other item stays too); else it is the side that faces the other end. Also routing (straight, orthogonal, curved), stroke_color, start_arrowhead and end_arrowhead (arrow or none), arrowhead_size (s, m or l), stroke_width (1 to 40). Use base_version to reject stale edits. Refused with invalid_input: a non-arrow id, the same item on both ends, an arrow or a bound label as an end. Returns the saved arrow item.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
id | string | yes | — |
from_id | string | no | — |
to_id | string | no | — |
from_anchor | enum | no | One of: top, right, bottom, left. |
to_anchor | enum | no | One of: top, right, bottom, left. |
from_anchor_mode | enum | no | One of: auto, manual. |
to_anchor_mode | enum | no | One of: auto, manual. |
routing | enum | no | One of: straight, orthogonal, curved. |
stroke_color | string | no | — |
start_arrowhead | enum | no | One of: none, arrow. |
end_arrowhead | enum | no | One of: none, arrow. |
arrowhead_size | enum | no | One of: s, m, l. |
stroke_width | number | no | — |
set_connector_routing
Set the routing of one arrow: straight (2 points), orthogonal (elbow points) or curved (4 points, one cubic Bezier).write
set_connector_routingset connector routing
Set the routing of one arrow: straight (2 points), orthogonal (elbow points) or curved (4 points, one cubic Bezier). The points are recomputed from the bound items.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
id | string | yes | — |
routing | enum | yes | One of: straight, orthogonal, curved. |
Website previews
Section titled “Website previews”Point a live website preview at a page, and read the marks that people drew over it.
set_preview_location
Present a page of one website preview (an embed item) to everyone: it writes props.shared_location = {url, scroll_y?, set_by, set_at}; the server fills set_by (your actor id) and set_at.write
set_preview_locationset preview location
Present a page of one website preview (an embed item) to everyone: it writes props.shared_location = {url, scroll_y?, set_by, set_at}; the server fills set_by (your actor id) and set_at. Browsers that follow you navigate their preview to url. url must be an https address under the embed URL policy. Each viewer still navigates the preview on their own (navigation is per viewer). Returns the saved preview item.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
operation_id | string | no | — |
base_version | number | no | — |
item_id | string | yes | — |
url | string | yes | — |
scroll_y | number | no | — |
list_preview_annotations
List the marks drawn over one website preview (an embed item): {preview_id, url, device, shared_location, annotations}.read
list_preview_annotationslist preview annotations
List the marks drawn over one website preview (an embed item): {preview_id, url, device, shared_location, annotations}. Each annotation is an item in the get_item shape; its parent_id is the preview and props.anchor is {space:"frame", url, epoch, frame:{x,y,w,h}, device:{w,h}}: the page URL the board knew when the mark was drawn, the navigation epoch, and the mark box in device-viewport CSS px. Pass url to keep only the marks of that page. It writes nothing. To add a mark, call create_item with parent_id = the preview id (and an optional props.anchor).
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
item_id | string | yes | — |
url | string | no | — |
Export & links
Section titled “Export & links”Render a board or a selection to a PNG, and make or read a link that opens a board on a given item.
get_board_image
Render the board, a viewport {x,y,w,h} in scene units, or a selection (item_ids, up to 200; the image box is their union plus 24 px, and only those items, their bound labels, the children of a selected frame and arrows…write
get_board_imageget board image
Render the board, a viewport {x,y,w,h} in scene units, or a selection (item_ids, up to 200; the image box is their union plus 24 px, and only those items, their bound labels, the children of a selected frame and arrows with both ends selected draw), with optional scale, to a PNG and return a stable media.devfellowship.com link. Any signed-in DFL user holding the link can read this snapshot, even if they cannot open the board; later board access changes do not revoke it. Format svg is not available.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
format | enum | no | One of: png, svg. Default: "png". |
scale | number | no | — |
item_ids | string[] | no | — |
viewport | object | no | — |
make_board_link
Make a link that opens the board with up to 50 items selected and in view: {url}.read
make_board_linkmake board link
Make a link that opens the board with up to 50 items selected and in view: {url}. Use it to point a person at items you just read or made. Every id must be a live item of the board. The link has no credential: the reader still needs access to the board.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
item_ids | string[] | yes | — |
resolve_board_link
Use this when a user pasted a board link (a boards.devfellowship.com/b/<slug>?focus=<ids> URL) to say where they are or what they mean.read
resolve_board_linkresolve board link
Use this when a user pasted a board link (a boards.devfellowship.com/b/<slug>?focus=<ids> URL) to say where they are or what they mean. One call returns {board_id, slug, missing_ids, items}: the selected items in the get_item shape (kind, text, description, bbox, elements). missing_ids lists the ids that are deleted or unknown. A board the caller cannot read answers like the read tools.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | yes | — |
Viewport & follow
Section titled “Viewport & follow”Move what you (or your agent) look at on a board, select items for the others to see, and follow another actor.
set_viewport
Set an explicitly logical 1280x720 CSS-unit camera, not a physical browser frame.write
set_viewportset viewport
Set an explicitly logical 1280x720 CSS-unit camera, not a physical browser frame. Exactly one alternative: fit with optional padding (default 64 scene units), or center plus zoom. Do not mix alternatives. An impossible fit fails; ambiguous item queries return candidates without mutation.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
fit | object | object | object | no | — |
padding | number | no | — |
center | object | no | — |
zoom | number | no | — |
get_viewport
Read the calling actor's current viewport on an authorized board as {center, zoom}, in the logical 1280x720 camera frame.read
get_viewportget viewport
Read the calling actor's current viewport on an authorized board as {center, zoom}, in the logical 1280x720 camera frame. Returns null when no viewport is stored. Read only.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
move_viewport
Move the calling actor's stored viewport.write
move_viewportmove viewport
Move the calling actor's stored viewport. direction is up, down, left or right. Each of steps (1..10, default 1) moves the camera by step_fraction (0.1..1, default 0.5) of the logical 1280x720 frame divided by zoom. Zoom does not change. Fails with not_found when no viewport is stored: call set_viewport first. Then the move follows the set_viewport path and returns the same LogicalCamera.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
direction | enum | yes | One of: up, down, left, right. |
steps | number | no | — |
step_fraction | number | no | — |
set_selection
Select actual live elements on an authorized board.write
set_selectionset selection
Select actual live elements on an authorized board. An empty list clears selection. Preserve the actor camera and pointer.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
ids | string[] | yes | — |
set_follow_target
Request one actor follow target in the current board.write
set_follow_targetset follow target
Request one actor follow target in the current board. Self-follow and cycles fail. Ambiguity returns candidates without mutation. No browser follow control is sent yet.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
target | string | yes | — |
remove_follow_target
Remove this actor follow request in the current board with a new command revision.write
remove_follow_targetremove follow target
Remove this actor follow request in the current board with a new command revision. No other actor or tab is controlled.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
Board actors
Section titled “Board actors”The people and agents on a board: who is online, their board profile and where they look. Not the DFL identity actors of the ops host.
list_actors
Read actual public actor profiles.read
list_actorslist actors
Read actual public actor profiles. Online means current authorized subscribers or unexpired agent activity leases. Reads do not renew leases. Viewport is returned only for an explicit authorized board.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | no | — |
online_only | boolean | no | Default: false. |
search_actors
Search actual public profiles, including reciprocal linked-agent aliases.read
search_actorssearch actors
Search actual public profiles, including reciprocal linked-agent aliases. Return at most five deterministic candidates. No lease is created or renewed.
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | yes | — |
get_actor
Read one actual public actor profile.read
get_actorget actor
Read one actual public actor profile. Exactly one selector: id, slug, or me:true. Mixed or absent selectors fail. This read does not create online activity.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | no | — |
slug | string | no | — |
me | literal(true) | no | — |
update_actor
Update only name or a canonical media avatar URL for your own actor or a currently reciprocal linked actor.write
update_actorupdate actor
Update only name or a canonical media avatar URL for your own actor or a currently reciprocal linked actor. A paired board agent renames only itself: 1-40 characters, not another board actor name, not a reserved word. A paired board agent may also set the avatar, never the name, of its owner (the reciprocally linked human on the same board). No new board activity lease is created.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | — |
name | string | no | — |
avatar_media_url | string | no | — |
Read and post the board chat, and react to a message.
post_chat_message
Post one chat message on a board, seen live by every human and agent on it.write
post_chat_messagepost chat message
Post one chat message on a board, seen live by every human and agent on it. Text is one line, 1-500 characters after trim; control and bidi characters are refused. Optional reply_to: the id of a message on the same board; the new message replies to it (an unknown id or a message of another board is invalid_input). Needs write access to the board. Limit: 10 messages per 30 s per actor (rate_limited). Fails with chat_disabled when the board owner has not turned chat on (settings.chat_enabled). Returns {id, created_at}.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
text | string | yes | — |
reply_to | string | no | — |
react_chat_message
Toggle the caller's emoji reaction on one chat message of a board: adds it, or removes it when the caller already reacted with that emoji.write
react_chat_messagereact to chat message
Toggle the caller's emoji reaction on one chat message of a board: adds it, or removes it when the caller already reacted with that emoji. emoji is one of 👍 ❤️ 😂 🎉 🤔 👀 🔥 ✅ (anything else is invalid_input). Needs write access to the board. Limit: 30 toggles per 30 s per actor (rate_limited). An unknown message_id is not_found. Fails with chat_disabled when chat is off on the board. Returns {message_id, reactions:[{emoji, count, actor_ids}]}.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
message_id | string | yes | — |
emoji | string | yes | — |
list_chat_messages
List chat messages of a board, oldest first.read
list_chat_messageslist chat messages
List chat messages of a board, oldest first. Without since: the newest limit messages. With since (an ISO time or a message id): the first limit messages after it. limit 1-100, default 50. Returns [{id, author:{id,name,kind}, text, created_at, reply_to:{id, author_name, snippet}|null, reactions:[{emoji, count, actor_ids}]}]; snippet is at most 80 characters. A message the board owner deleted has deleted:true and text:null (a reply to it has reply_to.deleted:true and an empty snippet). Fails with chat_disabled when chat is off on the board.
| Parameter | Type | Required | Description |
|---|---|---|---|
board | string | yes | — |
since | string | no | — |
limit | number | no | Default: 50. |