accueil / serveurs / Recherche & Web / ai.zerowidth/napkin
ai.zerowidth/napkin
serveur MCP (Model Context Protocol) · zerowidth-mcp 1.0.0
Référencé dans l'annuaire. Seul le pentest comportemental fait foi, et il n'a pas encore été exécuté sur ce serveur.
Est-ce que ai.zerowidth/napkin est sûr ?
Au dernier audit MCP Hub, le serveur MCP « ai.zerowidth/napkin » est classé « Non encore testé ». Référencé dans l'annuaire. Seul le pentest comportemental fait foi, et il n'a pas encore été exécuté sur ce serveur. Le verdict est réévalué en continu ; consultez la fiche pour l'état à jour.
Méthode : MCP Hub analyse la description de chaque outil (couche 1) et, pour le verdict fort, exécute le serveur dans une micro-VM isolée avec leurres et réseau bloqué (couche 2). On publie des noms et des compteurs, jamais le contenu brut renvoyé par un outil.
Les 68 outils
- comments_create≈ 409 tokens
Posts a comment on a workspace entity — a new thread, or a reply when rootId is given. Use it to leave findings where the discussion already lives (an eval result on the flow being debated, a summary on a long thread). Mention people via mentionedUserIds (from workspace member ids) to ring their notification bell; never mention someone who didn't ask to be pulled in.
Paramètres, retour & exemple (7)
Paramètresbody· string · requisrootId· string · optionnel
Reply into this thread; omit to start a new one.entityId· string · requisworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnelentityKind· string · requis · workbench_flow | compass_page | compass_opportunity | caliper_review | caliper_dataset | caliper_rubric | caliper_eval | caliper_spec
What the thread hangs on.mentionedUserIds· array · optionnel
Exemple d'appel{"name": "comments_create", "arguments": {"body": "texte", "entityId": "texte", "entityKind": "workbench_flow"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - comments_list≈ 306 tokens
Lists the comment threads on one workspace entity (open first, then resolved) with authors and timestamps. Read this before weighing in on contested work — the threads are where disagreement lives before it becomes a decision.
Paramètres, retour & exemple (3)
ParamètresentityId· string · requisworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.entityKind· string · requis · workbench_flow | compass_page | compass_opportunity | caliper_review | caliper_dataset | caliper_rubric | caliper_eval | caliper_spec
What the thread hangs on.
Exemple d'appel{"name": "comments_list", "arguments": {"entityId": "texte", "entityKind": "workbench_flow"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - comments_resolve≈ 231 tokens
Sets a comment thread's resolved state (rootId = the thread's root comment id). Resolve ONLY when the human asked or the thread's question is demonstrably settled — and say what settled it in a reply first. Reopening is for new evidence.
Paramètres, retour & exemple (4)
ParamètresrootId· string · requisresolved· boolean · requisworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnel
Exemple d'appel{"name": "comments_resolve", "arguments": {"rootId": "texte", "resolved": true}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - entity_tags_browse≈ 258 tokens
Without a tag: every tag in use across the workspace with how many entities carry it, most-used first — the vocabulary the team already organizes by. With a tag: everything filed under it across every tool, each with its kind, id, title, and path. Use it to reuse existing labels instead of inventing near-duplicates, and to answer 'show me everything about X' when X is a label.
Paramètres, retour & exemple (2)
Paramètrestag· string · optionnel
A tag to expand into its items. Omit to list tags.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "entity_tags_browse", "arguments": {"tag": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - entity_tags_get≈ 352 tokens
Returns the tags on a batch of entities of one kind — the labels galleries organize by. Ids come from the kind's list/get tool or from search_workspace. Use it before entity_tags_set so you replace the full set knowingly, and to answer 'what is this filed under'. Entities the user can't see are omitted.
Paramètres, retour & exemple (3)
Paramètresids· array · requisworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.entityKind· string · requis · workbench_flow | workbench_task | workbench_kb | compass_page | compass_opportunity | caliper_dataset | caliper_rubric | caliper_eval
Which kind the ids belong to.
Exemple d'appel{"name": "entity_tags_get", "arguments": {"ids": [], "entityKind": "workbench_flow"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - entity_tags_set≈ 413 tokens
Replaces the FULL tag set on one entity (an empty list clears it). Read the current tags with entity_tags_get first and pass the merged list — this is not additive. Tags are lowercase letters, numbers, spaces, and hyphens; prefer labels already in use (entity_tags_browse) so the workspace's vocabulary stays small. The id comes from the kind's list/get tool or search_workspace.
Paramètres, retour & exemple (5)
Paramètrestags· array · requisentityId· string · requisworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.entityKind· string · requis · workbench_flow | workbench_task | workbench_kb | compass_page | compass_opportunity | caliper_dataset | caliper_rubric | caliper_eval
Exemple d'appel{"name": "entity_tags_set", "arguments": {"tags": [], "entityId": "texte", "entityKind": "workbench_flow"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - get_doc≈ 161 tokens
Fetch the full Markdown body of a specific docs page by its slug. Use this after `search_docs` when the user needs the complete content of a page. No authentication required.
Paramètres, retour & exemple (1)
Paramètresslug· string · requis
Page slug. Accepts 'compass/api', '/compass/api', or 'docs/compass/api'.
Exemple d'appel{"name": "get_doc", "arguments": {"slug": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - list_docs≈ 156 tokens
Enumerate all available docs pages, optionally filtered by product (e.g. 'compass', 'legal', 'overview'). Use this to discover what slugs exist before calling `get_doc`. No authentication required.
Paramètres, retour & exemple (1)
Paramètresproduct· string · optionnel
Optional product slug filter (e.g. 'compass', 'legal', 'overview').
Exemple d'appel{"name": "list_docs", "arguments": {"product": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_boards_create≈ 282 tokens
Creates a blank Napkin board — a sketch (free canvas) or a deck (slides). NOT for written documents: 'draft/write a doc, note, memo' is napkin_docs_create. Use when the user asks for one, or proactively when a sketch/deck would carry the conversation better than words; hand it over with `[[board:ID]]` alone on its own line. For a deck with content, prefer `napkin_deck_write` (one call, whole deck).
Paramètres, retour & exemple (3)
Paramètreskind· string · optionnel · sketch | deck
Defaults to "sketch".name· string · requis
Board name.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_boards_create", "arguments": {"name": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_boards_get≈ 254 tokens
Returns a board's shapes as data — each with its id, kind, position, size, and text — so you can TARGET one to change or remove with napkin_draw (`updates` / `deletes` take these ids and absolute board coordinates). Pen strokes come back as a point count, not the points. For what the board LOOKS like, use napkin_boards_view instead. Board ids come from napkin_boards_list.
Paramètres, retour & exemple (2)
ParamètresboardId· string · requis
Napkin board id (from napkin_boards_list).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_boards_get", "arguments": {"boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_boards_list≈ 348 tokens
Lists sketch boards in the active workspace — name, id, kind (sketch / deck), shape count, last activity. Boards are SPATIAL canvases (drawing, diagrams); the markdown DOCUMENTS are docs — napkin_docs_list. Archived boards are hidden unless `includeArchived`; `q` narrows by name. When the user mentions a sketch, drawing, whiteboard, or napkin, find it here, then LOOK at it with `napkin_boards_view` before discussing its contents. When referring the user to a sketch in your reply, put `[[board:ID]]` alone on its own line — it renders as a clickable card with a live thumbnail.
Paramètres, retour & exemple (4)
Paramètresq· string · optionnel
Name contains (case-insensitive).kind· string · optionnel · sketch | deck
Only sketches or only decks.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.includeArchived· boolean · optionnel
Also list archived boards (default false).
Exemple d'appel{"name": "napkin_boards_list", "arguments": {"q": "texte", "kind": "sketch", "workspace": "texte", "includeArchived": true}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_boards_update≈ 355 tokens
Edits a board's details: name, description, visibility, or `archived` (true takes it out of the gallery; false brings it back — the recovery move for a board you created by mistake or the user no longer wants). Board ids come from napkin_boards_list. Boards are working material: no approval card, every change attributed.
Paramètres, retour & exemple (6)
Paramètresname· string · optionnelboardId· string · requis
Napkin board id (from napkin_boards_list).archived· boolean · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.visibility· string · optionnel · PRIVATE | WORKSPACE | SHARED
Who can see it: PRIVATE (only the user), WORKSPACE (every member, the default), or SHARED (specific people, granted afterwards). Say 'make it private' → PRIVATE.description· string · optionnel
Exemple d'appel{"name": "napkin_boards_update", "arguments": {"boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_boards_view≈ 246 tokens
Renders the sketch board to an image and returns it so you can SEE what's drawn — layout, arrows, handwriting-style strokes, sticky notes — not just data about it. Use this before answering any question about a sketch's contents, and cite the board when you do — `[[board:ID]]` alone on its own line embeds the sketch card in your reply.
Paramètres, retour & exemple (2)
ParamètresboardId· string · requis
Napkin board id to render.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_boards_view", "arguments": {"boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_add_files≈ 650 tokens
Adds pictures to a brand kit section — logo versions, product marks, example screenshots, imagery. Each file comes from a public https `url` (fetched by the server) or, for a small file that isn't online, base64 `data` with a `filename`. PNG, JPEG, WebP, GIF and SVG, up to 20 MB each. Every file becomes a workspace file and an asset on the section, appended after the ones already there. Give each a `name` and a `note` saying what it's for; on logo sections set `backdrop` to the hex of the ground it's made for (#ffffff for a dark logo, #000000 for a reversed one), which is how pages pick the right version. Same rule as napkin_brand_draft: you can change any kit except the workspace's brand. Files that fail are reported and the rest still land.
Paramètres, retour & exemple (5)
Paramètreskind· string · optionnel · overview | voice | colors | typography | logo | imagery | motion | layout
Only when no section has that title yet: the kind of section to make.files· array · requiskitId· string · requis
The kit to add to (napkin_brand_list).section· string · requis
Title of the section to add them to, like Logo, Imagery or Examples.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_add_files", "arguments": {"files": [], "kitId": "texte", "section": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_apply≈ 384 tokens
Restyles a piece in the brand — the workspace's brand unless you name a kit. `deck` (a board id): every slide gets the brand's background, typefaces, and text colors; sizes are kept. `interface` (an interface id): rewrites its brand.css, so anything styled with var(--brand-*) updates. `accent` makes one of the kit's colors the piece's accent — use it when the piece is about one product or campaign that has its own color in the brand (read the kit's Colors section to see which). Docs need nothing — they read the brand when exported. Check a deck afterwards with napkin_slide_view.
Paramètres, retour & exemple (5)
Paramètresdeck· string · optionnel
Board id of a deck.kitId· string · optionnel
A brand kit id from napkin_brand_list. Omit for the workspace's brand.accent· string · optionnel
Name of one of the kit's colors to use as this piece's accent.interface· string · optionnel
Interface id.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_apply", "arguments": {"deck": "texte", "kitId": "texte", "accent": "texte", "interface": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_check≈ 280 tokens
Finds words the brand says to avoid in a piece of writing, with what to say instead and why. Pass `text`, or a deck (`boardId`) or doc (`docId`) to check its words. Run it on your own drafts before handing them back.
Paramètres, retour & exemple (5)
Paramètrestext· string · optionnel
Text to check.docId· string · optionnel
A doc to check.kitId· string · optionnel
A brand kit id from napkin_brand_list. Omit for the workspace's brand.boardId· string · optionnel
A deck or sketch to check.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_check", "arguments": {"text": "texte", "docId": "texte", "kitId": "texte", "boardId": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_draft≈ 1688 tokens
Creates a new brand kit, or edits one that is NOT the workspace's brand. Use it when someone asks you to put their brand together (from their site, a deck, or what they tell you) or to try a variation. You can't change the workspace's brand itself — make a draft and tell the user they can choose it in Napkin's Brand section. A kit is a document of sections, each with `title`, `kind` (overview, voice, colors, typography, logo, imagery, motion, layout, examples, custom), markdown `body`, and `rules` ({rule, why}); colors sections hold `swatches` ({name, value: 6-digit hex, note}), typography sections hold `faces` ({name: what it's for, family: a font name or sans / serif / mono / rounded / marker, note}), voice sections hold `use` and `avoid` ({term, instead, why}), and any section can hold `assets` ({fileId: an image already in the workspace's files, name, note, backdrop: hex}) — logo versions, example images — and `links` ({url, title, note}). An examples section holds work that gets the brand right: screenshots as assets and pages as links, each with a note on what makes it good. Exact numbers (timings, spacing) go in the section's prose. New kits start with empty standard sections plus the workspace's colors. `content.sections` upserts by title (or id): fields you pass replace, lists inside replace, sections you don't mention are kept; `removeSections` drops by title. `content.uses` says how Napkin uses the brand — color roles (ink, muted, background, surface, accent, accent2) and fonts (heading, body, mono) — by swatch or face NAME. Give every rule a `why`: the reason is what lets people decide cases the rule doesn't cover.
Paramètres, retour & exemple (6)
Paramètresname· string · optionnelkitId· string · optionnel
Kit to edit. Omit to create a new kit.content· object · optionnelfromKitId· string · optionnel
New kits only: start from this kit's values.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.description· string · optionnel
Exemple d'appel{"name": "napkin_brand_draft", "arguments": {"name": "texte", "kitId": "texte", "content": {}, "fromKitId": "texte", "workspace": "texte", "description": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_examples≈ 248 tokens
Shows you the pictures in a kit's Examples sections — screenshots of work that gets the brand right — with each one's note on what makes it good, plus the example links. Look before you design a deck, page or interface in the brand, and hold your work to the same bar: the layouts, density, type sizes and use of color you see there.
Paramètres, retour & exemple (2)
ParamètreskitId· string · optionnel
Kit to look at. Omit for the workspace's brand.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_examples", "arguments": {"kitId": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_fonts≈ 292 tokens
Gets the font files for a kit's typefaces from Google Fonts and keeps them in the kit, so decks, slide pictures and the canvas draw the brand's real typefaces instead of a stand-in. Only typefaces that name a family (like Poppins) and don't have files yet are fetched. Works on any kit, including the workspace's brand: it adds the files for the typefaces the kit already names and changes nothing else. A typeface Google doesn't have is reported back; the user can upload its files in the kit's typography section.
Paramètres, retour & exemple (2)
ParamètreskitId· string · optionnel
Kit to fetch fonts for. Omit for the workspace's brand.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_fonts", "arguments": {"kitId": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_get≈ 330 tokens
Reads a brand kit — the workspace's brand unless you name another. READ THIS BEFORE writing or designing anything for the workspace: a deck, a doc, an interface, copy. Returns `brief` (the whole kit as markdown: every section — overview, voice, colors, typography, imagery, motion, and whatever else the team wrote — with the reason behind each rule; follow the reasons when a case isn't covered), `css` (the `--brand-*` variables; interfaces already link it as brand.css, so style with var(--brand-accent) etc. rather than hex values), and `tokens` (the resolved values Napkin uses). Before any kit exists it returns values from the workspace's colors with `kit: null`.
Paramètres, retour & exemple (2)
ParamètreskitId· string · optionnel
A brand kit id from napkin_brand_list. Omit for the workspace's brand.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_get", "arguments": {"kitId": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_list≈ 168 tokens
Lists the workspace's brand kits: id, name, description, and which one is the workspace's brand (`isDefault`). Most workspaces have one. Use napkin_brand_get to read a kit.
Paramètres, retour & exemple (1)
Paramètresworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_list", "arguments": {"workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_brand_view≈ 379 tokens
Look at the pictures in a brand kit — logos, product marks, footage stills, layout references, examples — before you use them. Without `section` or `names` it lists every picture by section with its note, so you know what exists. With `section` (a section title, like Imagery) or `names` (pictures' names as the brief lists them) it shows you those pictures, up to 8 at a time. Look before you build a design around a picture: whether a photo has room for text, which part of it carries the color, and whether it suits texture inside type are things you can only tell by seeing it.
Paramètres, retour & exemple (4)
ParamètreskitId· string · optionnel
A kit from napkin_brand_list. Omit for the workspace's brand.names· array · optionnel
Show these pictures, by name.section· string · optionnel
Show the pictures in this section.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_brand_view", "arguments": {"kitId": "texte", "names": [], "section": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_deck_compose≈ 2312 tokens
Builds a designed deck: each slide is a layout with its slots filled, or HTML and CSS you write; a real browser lays it out, and it becomes an ordinary Napkin deck people can edit. Use this whenever a deck should look designed — it's how to get layout, big numbers, color blocks, and pictures right. LAYOUTS FIRST - For ads, posts, carousels and title slides, start from a layout (napkin_layouts_list): give `layout` and `slots` instead of `html`. Layouts adapt to every size, fit their text, keep to each size's safe zones, and use the brand's own logos. A slide made from a layout remembers it, so when someone resizes it, it's laid out again instead of scaled. - A set of ads is one slide with `sizes`: ["1:1","4:5","9:16","1.91:1"] — each size gets its own arrangement. - Write HTML only for something no layout does. HOW TO WRITE A SLIDE - `html` is the inside of one slide: a `.slide` box 960×540 px (the deck's own size if it has one). Margins are reset; lay out with flexbox or grid, padding and gap. Put each slide's CSS in a <style> block inside its html, or shared CSS in `css`. - Read the brand first (napkin_brand_get). Style ONLY with its variables: var(--brand-ink), var(--brand-muted), var(--brand-background), var(--brand-surface), var(--brand-accent), var(--brand-accent-2), var(--brand-color-<color name>), var(--brand-font-heading), var(--brand-font-body), var(--brand-radius). Headings already use the heading face. - Pictures from the brand kit: <img src="kit:<file name exactly as the brief lists it>">, sized with CSS; object-fit: cover crops to fill, contain fits whole. Any workspace image: src="file:<file id>". - What carries over: boxes with a solid background, border, and rounded corners; text, including bold, italic, color, and size changes inside a line; pictures. What doesn't: gradients, shadows, background images, inline SVG — use solid color boxes instead. - Design like a designer: one idea per slide, a clear hierarchy, big numbers set large in the accent color, generous space, text 18px or larger (never under 14px), and something visual on most slides — a color panel, a photo, the logo. Keep every slide on the brand's own palette and voice. - Use the brand's own pictures. Put the logo on the title and closing slides (the reversed one on dark color), and use the kit's photos and icons where they carry the point — the brief lists every file under "Files", by name. - Say only what's true. Facts, figures, prices, eligibility, and how things work come from the brand kit and from what the user told you — never invent them. When a slide needs a fact you don't have, leave it out or ask. HOW TO USE IT - New deck: pass `name` and all `slides`. Rebuild a deck: pass `boardId` and all `slides` (replaces everything on it). - Ads, posts and other non-slide pieces: pass `size` — a key like `1:1` (square post, 1080×1080), `4:5` (portrait post), `9:16` (story or reel), `1.91:1` (LinkedIn or Facebook landscape), `og` (link preview), `300x250` or `728x90` (display ads). The `.slide` box is then that shape with its long side 960 px, so type sizes mean what they do on a slide, and the deck exports at the size's real pixels. A single ad is a one-slide deck; a carousel is a deck at `1:1` or `4:5`. - A set of ads — the same ad at several sizes, or several headlines — is one deck: give each slide its own `size`. Lay each size out for its own shape rather than squeezing one layout into all of them (a 9:16 story stacks what a 1.91:1 landscape puts side by side), and keep text out of the outer 7% of every edge and the top and bottom 14% of a 9:16. The deck's PNG export names each file by its pixels. Fix one slide: `boardId`, `slide` (1-based), and a single slide. Add slides: `append: true`. - The result lists each slide's `warnings` — text that overflows its box or the slide, overlapping text, text too small, low contrast, pictures that didn't load — and shows you a picture of every slide it made. Fix every warning and anything that reads badly in the pictures by recomposing just that slide. The contrast check is the standard one (4.5:1 for small text, 3:1 for large or bold text), so a contrast warning is real: darken the color, lighten the background, or set the text larger; never explain it away. You don't need napkin_slide_view afterwards: the pictures are the slides. - Cite the deck with [[board:ID]] alone on its own line.
Paramètres, retour & exemple (9)
Paramètrescss· string · optionnel
CSS shared by every slide.name· string · optionnel
Name for a new deck.size· string · optionnel · 16:9 | 16:10 | 4:3 | 1:1 | 4:5 | 9:16 | 1.91:1 | x-post
The size of every slide, for a new deck or a whole rebuild: 16:9 = Widescreen slide 1920×1080; 16:10 = 16:10 slide 1920×1200; 4:3 = Standard slide 1024×768; 1:1 = Square 1080×1080; 4:5 = Portrait post 1080×1350; 9:16 = Story and reel 1080×1920; 1.91:1 = Landscape post 1200×628; x-post = X post 1600×900; og = Link preview 1200×630; pin = Pin 1000×1500; youtube-thumb = YouTube thumbnail 1280×720; linkedin-page = LinkedIn page cover 1128×191; linkedin-profile = LinkedIn profile banner 1584×396; x-header = X header 1500×500; youtube-banner = YouTube channel banner 2560×1440; facebook-cover = Facebook cover 851×315; 300x250 = Medium rectangle 300×250; 336x280 = Large rectangle 336×280; 728x90 = Leaderboard 728×90; 970x250 = Billboard 970×250; 300x600 = Half page 300×600; 160x600 = Wide skyscraper 160×600; 320x50 = Mobile banner 320×50; 320x100 = Large mobile banner 320×100; email-header = Email header 600×200; letter = Letter page 2550×3300; a4 = A4 page 2480×3508. Omit for a 16:9 deck or to keep a deck's size.slide· integer · optionnel
Replace just this slide (1-based) with the one slide given.accent· string · optionnel
Name of one of the brand's colors to use as var(--brand-accent) for this deck — for a deck about one product or campaign that has its own color in the brand.append· boolean · optionnel
Add these slides after the existing ones.slides· array · requisboardId· string · optionnel
Deck to rebuild or change. Omit to make a new deck.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_deck_compose", "arguments": {"slides": []}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_deck_export≈ 350 tokens
Exports a deck's slides as PNG files at each slide's real pixels — a square post at 1080×1080, a story at 1080×1920, a leaderboard at 728×90 — ready to upload to an ad platform or a social post. One slide comes back as a PNG; several as a .zip named by deck, slide and size. Hidden slides are left out. The file is saved to the workspace's files; give the user `downloadUrl` (it opens for anyone signed in to the workspace) and say it's also in their files. Slide numbers and the workspace mark aren't drawn. For a PDF or PowerPoint, the user exports from the deck's File menu.
Paramètres, retour & exemple (3)
Paramètresslides· array · optionnel
Just these slides (1-based, as the deck shows them). Omit for every slide.boardId· string · requis
The deck to export.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_deck_export", "arguments": {"boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_deck_outline≈ 268 tokens
Returns a Napkin deck as a markdown outline — one section per slide in presentation order, with slide titles, text content (bold + bullets preserved), empty layout stubs still awaiting content, and a summary of drawn marks. This is the cheap way to know what a deck SAYS; use `napkin_slide_view` when you need to see how a specific slide LOOKS. Works on any board that has slides. Cite the deck with `[[board:ID]]` alone on its own line.
Paramètres, retour & exemple (2)
ParamètresboardId· string · requis
Napkin board id (a deck).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_deck_outline", "arguments": {"boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_deck_set_theme≈ 463 tokens
Paints every slide of a deck with a theme — slide background, heading and body typefaces, and text colors — in one call, the same as picking it in the deck's theme menu. Font sizes are kept. Prefer "brand" (the workspace's own brand kit) unless the user asks for something else. Built-in themes: plain (Helvetica on white. Gets out of the way.); editorial (Garamond headings on cream. Reads like a document.); stage (Helvetica, light on dark, for a projected room.); blueprint (Consolas headings on mist. For technical decks.); warm (Rounded on amber. Softer than it sounds.); notebook (Marker headings. Keeps the sketchbook feel.). For one-off colors or sizes on a single shape, use napkin_draw's `updates` instead. Check the result with napkin_slide_view.
Paramètres, retour & exemple (4)
Paramètrestheme· string · requis
`brand` for the workspace's brand kit, `brand:<kit id>` for a particular kit (napkin_brand_list), or a built-in theme key.accent· string · optionnel
Brand themes only: the name of one of the kit's colors to use as this deck's accent instead of the kit's own — for a deck about one product or campaign that has its own color in the brand.boardId· string · requis
Napkin board id (a deck).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_deck_set_theme", "arguments": {"theme": "texte", "boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_deck_write≈ 471 tokens
Creates a deck from a markdown outline — the SAME dialect `napkin_deck_outline` reads back, so any deck you've read shows the format. Decks are SLIDES for presenting; a prose document ('write this up', 'draft a doc') is napkin_docs_create instead. Rules: `## Heading` starts a slide (heading becomes the slide title and its biggest text block); optional trailing `[layout: title | section | title-body | title-lead | two-column | three-column | comparison | statement | quote | closing | blank]`; body lines fill the layout's remaining text areas in order, split into areas by a line containing only `---`; `- ` bullets are kept as bullets. Slides default to title-body (or title, when bodyless). Layout text areas you don't fill stay as visible prompts for the user. Example: ## Q3 Review [layout: title] What happened, what's next ## Revenue [layout: title-body] - up 40% QoQ - churn flat ## Bets [layout: two-column] Double down on decks --- Sunset legacy plans After writing, cite the deck with `[[board:ID]]` alone on its own line — the user opens it from there.
Paramètres, retour & exemple (3)
Paramètresname· string · requis
Deck name.outline· string · requis
The markdown outline (dialect above).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_deck_write", "arguments": {"name": "texte", "outline": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_diagrams_create≈ 407 tokens
Create a Napkin diagram — an interactive FLOWCHART (process, decision tree, system map), not a doc (napkin_docs_create) or drawing canvas (napkin_boards_create). Pass the mermaid source. Use for process maps the user asked you to draw. After creating, cite it as `[[diagram:ID]]` alone on its own line — it renders as a clickable card. NEVER cite it as [[board:ID]].
Paramètres, retour & exemple (3)
Paramètrestitle· string · optionnelsource· string · optionnel
Mermaid flowchart source. Start with `flowchart TD` (top-down) or `flowchart LR` (left-right). Declare shaped nodes — `A["Rect"]`, `B("Rounded")`, `C{"Decision"}`, `D(["Stadium"])`, `E(("Circle"))`, `F{{"Hexagon"}}` — and link them with `A -->|label| B`, `A -.-> B` (dotted), or `A ==> B` (thick). Group steps with `subgraph Name["Title"] ... end`. Color a node with a trailing `style A fill:#fee2e2` line. Only the flowchart subset is supported. Each label is a short name on one line, written plainly: no `\n`, no backslashes before quotes. A step's label is its name everywhere in Compass.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_diagrams_create", "arguments": {"title": "texte", "source": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_diagrams_get≈ 194 tokens
Fetch one Napkin diagram's mermaid source by id (from napkin_diagrams_list). Read before editing — napkin_diagrams_update replaces the whole source.
Paramètres, retour & exemple (2)
ParamètresdiagramId· string · requis
Diagram id from napkin_diagrams_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_diagrams_get", "arguments": {"diagramId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_diagrams_list≈ 287 tokens
Lists the workspace's Napkin diagrams (interactive flowcharts — processes, decision trees, system maps) newest first. Archived diagrams are hidden unless `includeArchived`; `q` narrows by title. Fetch one's mermaid source with napkin_diagrams_get. When referring the user to a diagram in your reply, put `[[diagram:ID]]` alone on its own line — it renders as a clickable card. NEVER cite a diagram as [[board:ID]]; boards are sketches.
Paramètres, retour & exemple (3)
Paramètresq· string · optionnel
Title contains (case-insensitive).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.includeArchived· boolean · optionnel
Also list archived diagrams (default false).
Exemple d'appel{"name": "napkin_diagrams_list", "arguments": {"q": "texte", "workspace": "texte", "includeArchived": true}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_diagrams_update≈ 561 tokens
Edit a Napkin diagram: retitle, describe, REPLACE the whole mermaid source, set visibility, or `archived` (true takes it out of the gallery, false brings it back — the recovery move for a diagram created by mistake). Read the diagram first (napkin_diagrams_get) so your new source keeps the parts the user wants. Diagram ids come from napkin_diagrams_list.
Paramètres, retour & exemple (8)
Paramètrestitle· string · optionnelsource· string · optionnel
Mermaid flowchart source. Start with `flowchart TD` (top-down) or `flowchart LR` (left-right). Declare shaped nodes — `A["Rect"]`, `B("Rounded")`, `C{"Decision"}`, `D(["Stadium"])`, `E(("Circle"))`, `F{{"Hexagon"}}` — and link them with `A -->|label| B`, `A -.-> B` (dotted), or `A ==> B` (thick). Group steps with `subgraph Name["Title"] ... end`. Color a node with a trailing `style A fill:#fee2e2` line. Only the flowchart subset is supported. Each label is a short name on one line, written plainly: no `\n`, no backslashes before quotes. A step's label is its name everywhere in Compass.archived· boolean · optionneldiagramId· string · requis
Diagram id from napkin_diagrams_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.visibility· string · optionnel · PRIVATE | WORKSPACE | SHARED
Who can see it: PRIVATE (only the user), WORKSPACE (every member, the default), or SHARED (specific people, granted afterwards). Say 'make it private' → PRIVATE.description· string · optionnel
Short gallery summary.
Exemple d'appel{"name": "napkin_diagrams_update", "arguments": {"diagramId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_docs_create≈ 308 tokens
Create a Napkin doc — a markdown DOCUMENT (prose, structure, code blocks), not a drawing canvas (that's napkin_boards_create) and not slides (napkin_deck_write). Optionally pass a title and initial markdown body. Use for drafts the user asked you to start. After creating, cite it as `[[doc:ID]]` alone on its own line — it renders as a clickable card. NEVER cite it as [[board:ID]].
Paramètres, retour & exemple (3)
Paramètresbody· string · optionnel
Initial markdown body. Write each paragraph as ONE long line — the editor wraps text to the reader's column, and hard-wrapped source shows up as narrow ragged lines when someone opens the paragraph to edit it.title· string · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_docs_create", "arguments": {"body": "texte", "title": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_docs_get≈ 190 tokens
Fetch one Napkin doc's full markdown body by id (from napkin_docs_list). Read before editing — napkin_docs_update replaces or appends against the current body.
Paramètres, retour & exemple (2)
ParamètresdocId· string · requis
Doc id from napkin_docs_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_docs_get", "arguments": {"docId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_docs_list≈ 291 tokens
Lists the workspace's Napkin docs (quick markdown documents — drafts, notes, working text; the LINEAR surface, distinct from boards which are drawing canvases) newest first, with excerpts. Archived docs are hidden unless `includeArchived`; `q` narrows by title. Fetch one with napkin_docs_get. When referring the user to a doc in your reply, put `[[doc:ID]]` alone on its own line — it renders as a clickable card. NEVER cite a doc as [[board:ID]]; boards are sketches.
Paramètres, retour & exemple (3)
Paramètresq· string · optionnel
Title contains (case-insensitive).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.includeArchived· boolean · optionnel
Also list archived docs (default false).
Exemple d'appel{"name": "napkin_docs_list", "arguments": {"q": "texte", "workspace": "texte", "includeArchived": true}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_docs_update≈ 676 tokens
Edit a Napkin doc. For a change to existing prose use `edits` — a list of exact find/replace pairs, which is the SAFEST option and the one to reach for by default: it leaves everything you did not target untouched, and it fails loudly rather than clobbering a doc somebody else is typing in. `append` adds a section at the end. `body` replaces the WHOLE document and should be a last resort. Also retitle, describe, set visibility, or `archived` (true takes it out of the gallery, false brings it back). Read the doc with napkin_docs_get first — `edits` match the current text exactly. Doc ids come from napkin_docs_list.
Paramètres, retour & exemple (10)
Paramètresbody· string · optionnel
Full replacement markdown body. Prefer `edits` — this overwrites everything. Write each paragraph as ONE long line; the editor wraps it for the reader.docId· string · requis
Doc id from napkin_docs_list.edits· array · optionnel
Targeted find/replace pairs, applied in order. All must match or none are applied.title· string · optionnelappend· string · optionnel
Markdown appended to the end (own paragraph).archived· boolean · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.visibility· string · optionnel · PRIVATE | WORKSPACE | SHARED
Who can see it: PRIVATE (only the user), WORKSPACE (every member, the default), or SHARED (specific people, granted afterwards). Say 'make it private' → PRIVATE.description· string · optionnel
Short gallery summary.
Exemple d'appel{"name": "napkin_docs_update", "arguments": {"docId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_draw≈ 2236 tokens
Draws shapes, strokes, and labels on a board — diagram on a sketch, annotate a deck slide — and, via `updates` / `deletes`, changes or removes shapes that are already there (ids from napkin_boards_get; the fix for something you drew wrong). For NEW elements never compute absolute board coordinates: with `slide`, coordinates are slide-local — (0, 0) top-left to (960, 540) bottom-right of that slide. Without `slide` (sketches), (0, 0) is the top-left of the existing content — the same area `napkin_boards_view` renders, so place by what you saw there; on an empty board just start at (0, 0). Example — circle a slide's title and margin-note it: {"boardId": "…", "slide": 2, "elements": [ {"type": "ellipse", "x": 60, "y": 40, "w": 400, "h": 90, "color": "#dc2626"}, {"type": "arrow", "x": 560, "y": 140, "x2": 470, "y2": 90, "color": "#dc2626"}, {"type": "text", "x": 575, "y": 130, "w": 260, "text": "tighten this claim", "color": "#dc2626"} ]} After drawing, cite the board with `[[board:ID]]` alone on its own line.
Paramètres, retour & exemple (6)
Paramètresslide· integer · optionnel
1-based slide number (decks only) — makes coordinates slide-local.boardId· string · requis
Napkin board id.deletes· array · optionnel
Ids of existing shapes to remove (from napkin_boards_get).updates· array · optionnel
Existing shapes to change — move, resize, retext, restyle. Read napkin_boards_get first for ids and current geometry.elements· array · optionnel
New elements to draw, painted in order.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_draw", "arguments": {"boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_create≈ 3498 tokens
Create an interface — a small screen of its own the user can open and send to people. Use when someone asks for a custom chat UI, a little tool, or a prototype of how something would feel in their product. It starts with a working index.html you then edit. It can reach NOTHING in the workspace until a flow is granted with napkin_interfaces_grant. Cite it in your reply as `[[interface:ID]]` alone on its own line — it renders as a card the user can click. Do NOT paste an image URL or try to embed the picture yourself; that renders as a broken image. An interface is plain HTML, CSS and JavaScript in one or more files, served from its own origin. Put styles in a <style> block. LIBRARIES: there is no build step and no CDN, and a <script src> to any other site is blocked. A fixed shelf of libraries is served from the interface's own origin instead; load them with exactly these tags (the versions are pinned, and any other /lib/ path is not found): PREACT + HTM, for a page with several screens or a lot of state — components and hooks with no build: <script src="/lib/preact@10.29.8/preact.min.umd.js"></script> <script src="/lib/preact@10.29.8/hooks.umd.js"></script> <script src="/lib/htm@3.1.1/htm.umd.js"></script> then var html = htm.bind(preact.h); var useState = preactHooks.useState; function App(){ var s = useState(0); return html`<button onClick=${function(){ s[1](s[0] + 1) }}>${s[0]}</button>` } preact.render(html`<${App}/>`, document.getElementById("app")). Templates are tagged strings (html`…`), not JSX. CHART.JS, for a chart: <script src="/lib/chart.js@4.5.1/chart.umd.min.js"></script> then new Chart(canvasElement, { type: "bar" | "line" | "doughnut" | …, data: { labels: [...], datasets: [{ label, data: [...] }] }, options: {} }). Color the datasets from the brand variables, read with getComputedStyle(document.documentElement).getPropertyValue("--brand-accent"). LUCIDE, for icons: <script src="/lib/lucide@1.49.0/lucide.min.js"></script>. In plain markup, write <i data-lucide="calendar"></i> and call lucide.createIcons() once it's on the page (again after adding more). In a Preact page DON'T — createIcons swaps elements Preact owns; draw the icon from its shapes instead: lucide.icons.Calendar is a list of [tag, attributes], so render <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> with preact.h(tag, attributes) for each (the app starter has an Icon component that does this). Names are lucide.dev's: kebab-case in markup, PascalCase in lucide.icons. A one-screen page needs none of these: plain HTML, CSS and JavaScript is still the default. React, Tailwind and every other library are not available. index.html is the entry point and must exist. THE BRAND: every interface carries the workspace brand as files — link it with <link rel="stylesheet" href="brand.css"> and style with its variables: var(--brand-ink), var(--brand-muted), var(--brand-background), var(--brand-surface), var(--brand-accent), var(--brand-accent-2), var(--brand-color-<colour name>) (napkin_interfaces_view lists the exact names this brand defines), var(--brand-font-heading), var(--brand-font-body), var(--brand-radius). Never hard-code the brand's colours or font names. brand.css already loads the brand's own font files; web fonts (Google Fonts or any other) are BLOCKED, so linking one leaves the page in a system font. The brand's logos are files under brand/ (brand.css lists them at the top): <img src="brand/<name>.svg">. Use the logo rather than typing the name, and real icons (inline SVG) rather than emoji. Read the brand with napkin_brand_get first, and say only what the brand kit or the user tells you. PICTURES: a picture from anywhere on the web is BLOCKED and draws as a broken image, so never use an external image URL. Show a picture from the workspace's files with <img src="file:<fileId>" alt="…"> (in CSS, url(file:<fileId>)) — PNG, JPEG, GIF or WebP, up to 5 MB. Write the reference literally; a file id assembled in JavaScript is not found. When the user gives you a picture in chat, save it with workspace_files_save_from_chat and use the file id it returns. A picture that's only on a website has to be attached in chat first, or uploaded in the interface's Files panel. LOOK before you hand it over: napkin_interfaces_view draws the page as it is now. Fix anything that doesn't look like the brand and look again. Load the client with <script src="/zerowidth.js"></script>. Then zw.ready() resolves with the context { viewer, grants, flows, tables, metrics, tasks }; ctx.flows lists the flow grants, each with its `id`, `name` and `variables`. zw.flows.run(flow.id, input) runs one. Call it inside zw.ready().then(...), and handle the rejection — it rejects only when the page isn't open in ZeroWidth, so show "Open this from ZeroWidth" there instead of a dead button. A run can take up to two minutes; show it working (stream it, below), and show the error's message when one fails — err.message is a sentence written for the person using the page. Input is {kind:"chat", messages:[{role:"user", content:"…"}]} or {kind:"form", values:{…}} — the same shapes the public API takes. zw.replyText(result) pulls the assistant text out of a chat result. SOURCES: when the flow searches a knowledge base, zw.sources(result) lists the documents the run actually read, as [{ name, documentId }], once each. Show those under the answer for "where did this come from" — never ask the model to write a sources line and parse it out of the text. The flow needs the knowledge base attached BEFORE it is published (workbench_flows_attach_knowledge_base, then workbench_flows_publish, then re-grant the new version here); the interface runs the published version it was granted. FEEDBACK: to let someone rate an answer or flag a wrong one, call zw.feedback.send(result, { rating: "up" | "down", correction, comment }) with the result zw.flows.run returned. It goes to Caliper, into the flow's feedback dataset, with the question and the answer taken from the run itself; `correction` is what the answer should have been. Needs no extra grant and no table — never make a table to log flagged answers. It returns a promise: say it was sent only once it resolves, and show the error's message if it rejects. Tell the user flagged answers are in Caliper, in the dataset named after the flow, under Datasets. VARIABLES: a flow grant lists `variables` — the values the flow reads besides the conversation, usually the {{key}} tokens in its prompt (who the user is, their plan, the page they are on). Send them on the same chat run: {kind:"chat", messages:[…], values:{<name>: value}}, using each variable's exact `name` and its `type` (string, number, boolean, array, object). A variable left out reaches the prompt empty. Read them from napkin_interfaces_get while you write the page, or at run time from ctx.flows[i].variables. A SHIM is not a flow and takes a different call: zw.shims.run(shimId, text), which decides on the viewer's own device with no network and no cost. It resolves with the same envelope a flow does, so read the decision with zw.decision(result) — NOT result.decision, which is undefined and makes an interface show one answer for every input. The decision is { answer, confidence, familiarity, action, probs, gates }; branch on `action`, the shim's own call about whether it was sure enough. Calling zw.flows.run with a shim id is refused. ctx.grants tells you which you have: each entry carries a `kind` of "flow" or "shim" alongside its id and name. DATA: an interface can be granted a sheet TABLE or a Ledger METRIC with napkin_interfaces_grant_data. ctx.tables lists each table with its `fields` (name, type, options) and `access` ("read", "add" or "edit"); ctx.metrics lists each metric with its unit and `access` ("read" or "record"). Records travel keyed by field NAME, choices by option NAME. zw.tables.query(table, { where: [{ field, op, value }], sort: [{ field, dir }], limit }) resolves { records: [{ id, fields }], total }; zw.tables.add(table, { Field: value }) or a list adds records; zw.tables.update(table, id, fields) and zw.tables.remove(table, ids) need "edit". zw.metrics.read(metric, { from, to }) resolves { metric, readings: [{ value, at, note }] }; zw.metrics.record(metric, value, { note }) needs "record". Name a table or metric by its id or its name. Through a share link a guest can read and add, never change or delete — so an intake form works when shared, a triage board needs a signed-in viewer. A granted Workbench TASK (ctx.tasks; each says whether it `takesInput`) starts with zw.tasks.start(task, input), which resolves { runId, status } at once — the run carries on after the page closes — and zw.tasks.get(task, runId) reads { status, output } for runs this page started. Running a shim on every keystroke is fine — it costs nothing and there is no rate limit. Debounce ~150ms and COALESCE: remember the latest text and run it when the current call finishes, so the answer matches what is on screen. Clear any in-flight guard on failure as well as success, or one call that doesn't come back wedges the interface. `d.action` is "act" | "suggest" | "refuse" — those three strings, nothing else. Branch on it rather than on a confidence threshold you invent; "refuse" means the shim doesn't recognise the input well enough to answer, so say so rather than showing a low-confidence guess. If you run a requestAnimationFrame loop, remove CSS transitions from any property it writes — the two fight and the property looks frozen. A flow answers in markdown: render it with el.replaceChildren(zw.markdown(text)). It builds DOM nodes, so model output is never treated as markup. STREAM chat answers: zw.flows.run(id, input, { onEvent: fn }) delivers the text token by token, and a flow that takes several seconds reads as broken without it. Use zw.textDelta(event) for each chunk — it returns null for anything that isn't text, so pass it every event — and accumulate. The promise still settles at the end with the whole result; take the final text from there. onEvent also sees node_start / node_complete / node_error if you want to name the step. Don't name a top-level variable `history`, `name`, `status`, `length`, `origin` or `top`: those are already window properties, so `var history = []` leaves you with the browser's History object and `history.push` fails. Prefix it, or keep it inside a function. THE KIT: every new interface also has kit.css, components built only from the brand's variables — link it after brand.css (<link rel="stylesheet" href="kit.css">) and build from its classes rather than styling from scratch: layout (.page, .page.wide, .header, .stack, .row, .grid), type (h1, h2, h3, .muted, .small, .label, .num, .stat), buttons (.btn, .btn.secondary, .btn.quiet, .btn.sm, .btn.icon), fields (.field with a .label, then .input / .select / .textarea, then a .hint or .error; .check for a checkbox), .chips and .chip, .badge (.up, .down, .accent), .card, .list, .meter, states (.empty, .skeleton, .spinner, .notice, .notice.error), chat (.thread, .bubble.me, .bubble.them, .typing, .composer), and .icon for a Lucide icon. Its tokens are var(--ink), var(--muted), var(--bg), var(--surface), var(--accent), var(--line), var(--tint), var(--radius) and spacing var(--s1)…var(--s7). The starters show the kit used well; read one before writing a page of your own. Design it the way a good product is designed: a clear hierarchy, real spacing, states for loading, empty and failure, and a layout that works on a phone first. Use the brand kit (napkin_brand_get) for what the brand looks like and how it sounds.
Paramètres, retour & exemple (3)
Paramètresname· string · optionnelstarter· string · optionnel · blank | chat | form | decision | app | dashboard | intake | board
Which worked example to start from — a whole working page, written well, that you then shape to what was asked. `blank`: a title and the client library. Start here unless one of the others is exactly the shape you want. `chat`: a conversation against one flow, streamed, keeping the history client-side so the flow sees the whole thread. `form`: labelled fields sent as a form run, with the answer underneath. `decision`: text in, a shim's answer out, with its confidence and the action it recommends. `app`: an app with several screens (Preact, htm and Lucide from the shelf): tabs along the bottom on a phone and the top on a wide screen, the screen kept in the address, a home screen and a streamed chat. For a member app or a tool with a few views. `dashboard`: cards over the granted data (Chart.js from the shelf): each Ledger metric's latest reading and a line of its history, each table's count, a bar of how it splits on its first choice column, and its latest records. `intake`: a form built from a granted table's columns that adds a record to it, with a done state and 'add another'. Works through a share link. `board`: a granted table's records in lanes by its first choice column, moved a lane at a time by someone who may edit. Needs a signed-in viewer to move cards. Take `blank` (the default) when none of the others is the shape you want, rather than deleting one you didn't need. The data starters (dashboard, intake, board) read what's granted with napkin_interfaces_grant_data, so grant the table or metric too.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_interfaces_create", "arguments": {"name": "texte", "starter": "blank", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_get≈ 3052 tokens
Read one interface: its name, the flows it may run (with the published version each is pinned to), and the contents of its files. READ BEFORE EDITING — napkin_interfaces_write matches text exactly against what is there now. Pass a path to read one file when the interface has several. An interface is plain HTML, CSS and JavaScript in one or more files, served from its own origin. Put styles in a <style> block. LIBRARIES: there is no build step and no CDN, and a <script src> to any other site is blocked. A fixed shelf of libraries is served from the interface's own origin instead; load them with exactly these tags (the versions are pinned, and any other /lib/ path is not found): PREACT + HTM, for a page with several screens or a lot of state — components and hooks with no build: <script src="/lib/preact@10.29.8/preact.min.umd.js"></script> <script src="/lib/preact@10.29.8/hooks.umd.js"></script> <script src="/lib/htm@3.1.1/htm.umd.js"></script> then var html = htm.bind(preact.h); var useState = preactHooks.useState; function App(){ var s = useState(0); return html`<button onClick=${function(){ s[1](s[0] + 1) }}>${s[0]}</button>` } preact.render(html`<${App}/>`, document.getElementById("app")). Templates are tagged strings (html`…`), not JSX. CHART.JS, for a chart: <script src="/lib/chart.js@4.5.1/chart.umd.min.js"></script> then new Chart(canvasElement, { type: "bar" | "line" | "doughnut" | …, data: { labels: [...], datasets: [{ label, data: [...] }] }, options: {} }). Color the datasets from the brand variables, read with getComputedStyle(document.documentElement).getPropertyValue("--brand-accent"). LUCIDE, for icons: <script src="/lib/lucide@1.49.0/lucide.min.js"></script>. In plain markup, write <i data-lucide="calendar"></i> and call lucide.createIcons() once it's on the page (again after adding more). In a Preact page DON'T — createIcons swaps elements Preact owns; draw the icon from its shapes instead: lucide.icons.Calendar is a list of [tag, attributes], so render <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> with preact.h(tag, attributes) for each (the app starter has an Icon component that does this). Names are lucide.dev's: kebab-case in markup, PascalCase in lucide.icons. A one-screen page needs none of these: plain HTML, CSS and JavaScript is still the default. React, Tailwind and every other library are not available. index.html is the entry point and must exist. THE BRAND: every interface carries the workspace brand as files — link it with <link rel="stylesheet" href="brand.css"> and style with its variables: var(--brand-ink), var(--brand-muted), var(--brand-background), var(--brand-surface), var(--brand-accent), var(--brand-accent-2), var(--brand-color-<colour name>) (napkin_interfaces_view lists the exact names this brand defines), var(--brand-font-heading), var(--brand-font-body), var(--brand-radius). Never hard-code the brand's colours or font names. brand.css already loads the brand's own font files; web fonts (Google Fonts or any other) are BLOCKED, so linking one leaves the page in a system font. The brand's logos are files under brand/ (brand.css lists them at the top): <img src="brand/<name>.svg">. Use the logo rather than typing the name, and real icons (inline SVG) rather than emoji. Read the brand with napkin_brand_get first, and say only what the brand kit or the user tells you. PICTURES: a picture from anywhere on the web is BLOCKED and draws as a broken image, so never use an external image URL. Show a picture from the workspace's files with <img src="file:<fileId>" alt="…"> (in CSS, url(file:<fileId>)) — PNG, JPEG, GIF or WebP, up to 5 MB. Write the reference literally; a file id assembled in JavaScript is not found. When the user gives you a picture in chat, save it with workspace_files_save_from_chat and use the file id it returns. A picture that's only on a website has to be attached in chat first, or uploaded in the interface's Files panel. LOOK before you hand it over: napkin_interfaces_view draws the page as it is now. Fix anything that doesn't look like the brand and look again. Load the client with <script src="/zerowidth.js"></script>. Then zw.ready() resolves with the context { viewer, grants, flows, tables, metrics, tasks }; ctx.flows lists the flow grants, each with its `id`, `name` and `variables`. zw.flows.run(flow.id, input) runs one. Call it inside zw.ready().then(...), and handle the rejection — it rejects only when the page isn't open in ZeroWidth, so show "Open this from ZeroWidth" there instead of a dead button. A run can take up to two minutes; show it working (stream it, below), and show the error's message when one fails — err.message is a sentence written for the person using the page. Input is {kind:"chat", messages:[{role:"user", content:"…"}]} or {kind:"form", values:{…}} — the same shapes the public API takes. zw.replyText(result) pulls the assistant text out of a chat result. SOURCES: when the flow searches a knowledge base, zw.sources(result) lists the documents the run actually read, as [{ name, documentId }], once each. Show those under the answer for "where did this come from" — never ask the model to write a sources line and parse it out of the text. The flow needs the knowledge base attached BEFORE it is published (workbench_flows_attach_knowledge_base, then workbench_flows_publish, then re-grant the new version here); the interface runs the published version it was granted. FEEDBACK: to let someone rate an answer or flag a wrong one, call zw.feedback.send(result, { rating: "up" | "down", correction, comment }) with the result zw.flows.run returned. It goes to Caliper, into the flow's feedback dataset, with the question and the answer taken from the run itself; `correction` is what the answer should have been. Needs no extra grant and no table — never make a table to log flagged answers. It returns a promise: say it was sent only once it resolves, and show the error's message if it rejects. Tell the user flagged answers are in Caliper, in the dataset named after the flow, under Datasets. VARIABLES: a flow grant lists `variables` — the values the flow reads besides the conversation, usually the {{key}} tokens in its prompt (who the user is, their plan, the page they are on). Send them on the same chat run: {kind:"chat", messages:[…], values:{<name>: value}}, using each variable's exact `name` and its `type` (string, number, boolean, array, object). A variable left out reaches the prompt empty. Read them from napkin_interfaces_get while you write the page, or at run time from ctx.flows[i].variables. A SHIM is not a flow and takes a different call: zw.shims.run(shimId, text), which decides on the viewer's own device with no network and no cost. It resolves with the same envelope a flow does, so read the decision with zw.decision(result) — NOT result.decision, which is undefined and makes an interface show one answer for every input. The decision is { answer, confidence, familiarity, action, probs, gates }; branch on `action`, the shim's own call about whether it was sure enough. Calling zw.flows.run with a shim id is refused. ctx.grants tells you which you have: each entry carries a `kind` of "flow" or "shim" alongside its id and name. DATA: an interface can be granted a sheet TABLE or a Ledger METRIC with napkin_interfaces_grant_data. ctx.tables lists each table with its `fields` (name, type, options) and `access` ("read", "add" or "edit"); ctx.metrics lists each metric with its unit and `access` ("read" or "record"). Records travel keyed by field NAME, choices by option NAME. zw.tables.query(table, { where: [{ field, op, value }], sort: [{ field, dir }], limit }) resolves { records: [{ id, fields }], total }; zw.tables.add(table, { Field: value }) or a list adds records; zw.tables.update(table, id, fields) and zw.tables.remove(table, ids) need "edit". zw.metrics.read(metric, { from, to }) resolves { metric, readings: [{ value, at, note }] }; zw.metrics.record(metric, value, { note }) needs "record". Name a table or metric by its id or its name. Through a share link a guest can read and add, never change or delete — so an intake form works when shared, a triage board needs a signed-in viewer. A granted Workbench TASK (ctx.tasks; each says whether it `takesInput`) starts with zw.tasks.start(task, input), which resolves { runId, status } at once — the run carries on after the page closes — and zw.tasks.get(task, runId) reads { status, output } for runs this page started. Running a shim on every keystroke is fine — it costs nothing and there is no rate limit. Debounce ~150ms and COALESCE: remember the latest text and run it when the current call finishes, so the answer matches what is on screen. Clear any in-flight guard on failure as well as success, or one call that doesn't come back wedges the interface. `d.action` is "act" | "suggest" | "refuse" — those three strings, nothing else. Branch on it rather than on a confidence threshold you invent; "refuse" means the shim doesn't recognise the input well enough to answer, so say so rather than showing a low-confidence guess. If you run a requestAnimationFrame loop, remove CSS transitions from any property it writes — the two fight and the property looks frozen. A flow answers in markdown: render it with el.replaceChildren(zw.markdown(text)). It builds DOM nodes, so model output is never treated as markup. STREAM chat answers: zw.flows.run(id, input, { onEvent: fn }) delivers the text token by token, and a flow that takes several seconds reads as broken without it. Use zw.textDelta(event) for each chunk — it returns null for anything that isn't text, so pass it every event — and accumulate. The promise still settles at the end with the whole result; take the final text from there. onEvent also sees node_start / node_complete / node_error if you want to name the step. Don't name a top-level variable `history`, `name`, `status`, `length`, `origin` or `top`: those are already window properties, so `var history = []` leaves you with the browser's History object and `history.push` fails. Prefix it, or keep it inside a function. THE KIT: every new interface also has kit.css, components built only from the brand's variables — link it after brand.css (<link rel="stylesheet" href="kit.css">) and build from its classes rather than styling from scratch: layout (.page, .page.wide, .header, .stack, .row, .grid), type (h1, h2, h3, .muted, .small, .label, .num, .stat), buttons (.btn, .btn.secondary, .btn.quiet, .btn.sm, .btn.icon), fields (.field with a .label, then .input / .select / .textarea, then a .hint or .error; .check for a checkbox), .chips and .chip, .badge (.up, .down, .accent), .card, .list, .meter, states (.empty, .skeleton, .spinner, .notice, .notice.error), chat (.thread, .bubble.me, .bubble.them, .typing, .composer), and .icon for a Lucide icon. Its tokens are var(--ink), var(--muted), var(--bg), var(--surface), var(--accent), var(--line), var(--tint), var(--radius) and spacing var(--s1)…var(--s7). The starters show the kit used well; read one before writing a page of your own. Design it the way a good product is designed: a clear hierarchy, real spacing, states for loading, empty and failure, and a layout that works on a phone first. Use the brand kit (napkin_brand_get) for what the brand looks like and how it sounds.
Paramètres, retour & exemple (3)
Paramètrespath· string · optionnel
One file to read, e.g. index.html. Omit for every file.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.interfaceId· string · requis
Interface id from napkin_interfaces_list.
Exemple d'appel{"name": "napkin_interfaces_get", "arguments": {"interfaceId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_grant≈ 408 tokens
Set what an interface may run — flows and shims, each pinned to a published version. This is the interface's ONLY reach into the workspace, and it replaces the whole list, so include everything it should keep. Ids and published versions come from workbench_flows_list / workbench_shim_list and their revisions. Ask the user before widening this.
Paramètres, retour & exemple (4)
Paramètresgrants· array · requis
The complete list. Passing an empty list removes all access.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.interfaceId· string · requis
Exemple d'appel{"name": "napkin_interfaces_grant", "arguments": {"grants": [], "interfaceId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_grant_data≈ 570 tokens
Set the sheet tables, Ledger metrics and Workbench Tasks an interface may use (ADR 0085, 0086), replacing the whole list. Tables: access read, add (read and add records) or edit (also change and delete); optionally limit to some columns by name. Metrics: read or record. Tasks: the page may start them. The page then calls zw.tables.query / add / update / remove, zw.metrics.read / record and zw.tasks.start / get. Anyone who can open the interface, including people it's shared with, gets this access (a shared link can only read and add), so ask the user before widening it.
Paramètres, retour & exemple (6)
Paramètrestasks· array · optionnel
Workbench Tasks the page may start with zw.tasks.start, and check with zw.tasks.get.tables· array · optionnelmetrics· array · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.interfaceId· string · requis
Exemple d'appel{"name": "napkin_interfaces_grant_data", "arguments": {"interfaceId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_list≈ 199 tokens
List the interfaces in a workspace — small self-contained screens (a custom chat, a control panel, a prototype) that run on their own origin and can call the flows they've been granted. Returns ids, names and how many flows each may run.
Paramètres, retour & exemple (2)
Paramètresworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.includeArchived· boolean · optionnel
Exemple d'appel{"name": "napkin_interfaces_list", "arguments": {"workspace": "texte", "includeArchived": true}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_publish≈ 234 tokens
Save where an interface is now as a numbered version. Share links point at a published version, so what someone was sent doesn't change while work continues. Do this when the user says it's ready to show people, not after every edit.
Paramètres, retour & exemple (3)
Paramètresnote· string · optionnel
One line on what changed.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.interfaceId· string · requis
Exemple d'appel{"name": "napkin_interfaces_publish", "arguments": {"interfaceId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_view≈ 733 tokens
See what an interface LOOKS like and DOES right now: the page RUNS from its current files under the same rules as the live page (no web fonts, no remote pictures), and you get a picture plus anything that went wrong (script errors, failed promises, blocked requests, console errors). Use it after every write and before saying an interface is finished: reading the code tells you what you wrote, not what it renders. `size: "phone"` looks at it 390px wide — use it for anything meant for a phone, like a mobile app mockup. `steps` drive the page before and between pictures, so you can check every screen and that buttons work: `{click: "Plan"}` (by visible text or label, or a CSS selector like "#nav-plan"), `{type: {into: "textarea", text: "hi"}}`, `{press: "Enter"}`, `{wait: 500}`, and `{shot: "Plan screen"}` for a picture. With no steps you get one picture of the first screen. Check each tab of an app in ONE call: shot, click a tab, shot, click the next, shot. Flows don't run here: a flow call gets a placeholder reply, so you can see the chat send and answer without spending anything; tables and metrics read empty. Look for text that's too small or runs together, stray system fonts, emoji standing in for icons, and anything that doesn't look like the brand.
Paramètres, retour & exemple (4)
Paramètressize· string · optionnel · desktop | phone
The window to look through. Default desktop (1280 wide); phone is 390 wide.steps· array · optionnel
Done in order, with a picture at each `shot` (at most 6). Default: one shot of the first screen.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.interfaceId· string · requis
Exemple d'appel{"name": "napkin_interfaces_view", "arguments": {"interfaceId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_interfaces_write≈ 3603 tokens
Write one file of an interface. Use `edits` — exact find/replace pairs — which is the SAFEST option and the one to reach for by default: it leaves everything you did not target untouched, and it fails loudly rather than clobbering a file somebody is editing. `body` replaces the WHOLE file and is for a new file or a deliberate rewrite. ONE CALL HOLDS ABOUT 15,000 CHARACTERS of file: a longer `body` is cut off mid-write and fails. Write a big file in parts instead — the first part with `body`, then each further part with `body` plus `append: true`, in order — and split an app across files (index.html, style.css, app.js) so each stays readable. `deleteFile` removes one (index.html can't be removed). Read with napkin_interfaces_get first — `edits` match the current text exactly. Also rename, describe, set visibility, or archive. An interface is plain HTML, CSS and JavaScript in one or more files, served from its own origin. Put styles in a <style> block. LIBRARIES: there is no build step and no CDN, and a <script src> to any other site is blocked. A fixed shelf of libraries is served from the interface's own origin instead; load them with exactly these tags (the versions are pinned, and any other /lib/ path is not found): PREACT + HTM, for a page with several screens or a lot of state — components and hooks with no build: <script src="/lib/preact@10.29.8/preact.min.umd.js"></script> <script src="/lib/preact@10.29.8/hooks.umd.js"></script> <script src="/lib/htm@3.1.1/htm.umd.js"></script> then var html = htm.bind(preact.h); var useState = preactHooks.useState; function App(){ var s = useState(0); return html`<button onClick=${function(){ s[1](s[0] + 1) }}>${s[0]}</button>` } preact.render(html`<${App}/>`, document.getElementById("app")). Templates are tagged strings (html`…`), not JSX. CHART.JS, for a chart: <script src="/lib/chart.js@4.5.1/chart.umd.min.js"></script> then new Chart(canvasElement, { type: "bar" | "line" | "doughnut" | …, data: { labels: [...], datasets: [{ label, data: [...] }] }, options: {} }). Color the datasets from the brand variables, read with getComputedStyle(document.documentElement).getPropertyValue("--brand-accent"). LUCIDE, for icons: <script src="/lib/lucide@1.49.0/lucide.min.js"></script>. In plain markup, write <i data-lucide="calendar"></i> and call lucide.createIcons() once it's on the page (again after adding more). In a Preact page DON'T — createIcons swaps elements Preact owns; draw the icon from its shapes instead: lucide.icons.Calendar is a list of [tag, attributes], so render <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> with preact.h(tag, attributes) for each (the app starter has an Icon component that does this). Names are lucide.dev's: kebab-case in markup, PascalCase in lucide.icons. A one-screen page needs none of these: plain HTML, CSS and JavaScript is still the default. React, Tailwind and every other library are not available. index.html is the entry point and must exist. THE BRAND: every interface carries the workspace brand as files — link it with <link rel="stylesheet" href="brand.css"> and style with its variables: var(--brand-ink), var(--brand-muted), var(--brand-background), var(--brand-surface), var(--brand-accent), var(--brand-accent-2), var(--brand-color-<colour name>) (napkin_interfaces_view lists the exact names this brand defines), var(--brand-font-heading), var(--brand-font-body), var(--brand-radius). Never hard-code the brand's colours or font names. brand.css already loads the brand's own font files; web fonts (Google Fonts or any other) are BLOCKED, so linking one leaves the page in a system font. The brand's logos are files under brand/ (brand.css lists them at the top): <img src="brand/<name>.svg">. Use the logo rather than typing the name, and real icons (inline SVG) rather than emoji. Read the brand with napkin_brand_get first, and say only what the brand kit or the user tells you. PICTURES: a picture from anywhere on the web is BLOCKED and draws as a broken image, so never use an external image URL. Show a picture from the workspace's files with <img src="file:<fileId>" alt="…"> (in CSS, url(file:<fileId>)) — PNG, JPEG, GIF or WebP, up to 5 MB. Write the reference literally; a file id assembled in JavaScript is not found. When the user gives you a picture in chat, save it with workspace_files_save_from_chat and use the file id it returns. A picture that's only on a website has to be attached in chat first, or uploaded in the interface's Files panel. LOOK before you hand it over: napkin_interfaces_view draws the page as it is now. Fix anything that doesn't look like the brand and look again. Load the client with <script src="/zerowidth.js"></script>. Then zw.ready() resolves with the context { viewer, grants, flows, tables, metrics, tasks }; ctx.flows lists the flow grants, each with its `id`, `name` and `variables`. zw.flows.run(flow.id, input) runs one. Call it inside zw.ready().then(...), and handle the rejection — it rejects only when the page isn't open in ZeroWidth, so show "Open this from ZeroWidth" there instead of a dead button. A run can take up to two minutes; show it working (stream it, below), and show the error's message when one fails — err.message is a sentence written for the person using the page. Input is {kind:"chat", messages:[{role:"user", content:"…"}]} or {kind:"form", values:{…}} — the same shapes the public API takes. zw.replyText(result) pulls the assistant text out of a chat result. SOURCES: when the flow searches a knowledge base, zw.sources(result) lists the documents the run actually read, as [{ name, documentId }], once each. Show those under the answer for "where did this come from" — never ask the model to write a sources line and parse it out of the text. The flow needs the knowledge base attached BEFORE it is published (workbench_flows_attach_knowledge_base, then workbench_flows_publish, then re-grant the new version here); the interface runs the published version it was granted. FEEDBACK: to let someone rate an answer or flag a wrong one, call zw.feedback.send(result, { rating: "up" | "down", correction, comment }) with the result zw.flows.run returned. It goes to Caliper, into the flow's feedback dataset, with the question and the answer taken from the run itself; `correction` is what the answer should have been. Needs no extra grant and no table — never make a table to log flagged answers. It returns a promise: say it was sent only once it resolves, and show the error's message if it rejects. Tell the user flagged answers are in Caliper, in the dataset named after the flow, under Datasets. VARIABLES: a flow grant lists `variables` — the values the flow reads besides the conversation, usually the {{key}} tokens in its prompt (who the user is, their plan, the page they are on). Send them on the same chat run: {kind:"chat", messages:[…], values:{<name>: value}}, using each variable's exact `name` and its `type` (string, number, boolean, array, object). A variable left out reaches the prompt empty. Read them from napkin_interfaces_get while you write the page, or at run time from ctx.flows[i].variables. A SHIM is not a flow and takes a different call: zw.shims.run(shimId, text), which decides on the viewer's own device with no network and no cost. It resolves with the same envelope a flow does, so read the decision with zw.decision(result) — NOT result.decision, which is undefined and makes an interface show one answer for every input. The decision is { answer, confidence, familiarity, action, probs, gates }; branch on `action`, the shim's own call about whether it was sure enough. Calling zw.flows.run with a shim id is refused. ctx.grants tells you which you have: each entry carries a `kind` of "flow" or "shim" alongside its id and name. DATA: an interface can be granted a sheet TABLE or a Ledger METRIC with napkin_interfaces_grant_data. ctx.tables lists each table with its `fields` (name, type, options) and `access` ("read", "add" or "edit"); ctx.metrics lists each metric with its unit and `access` ("read" or "record"). Records travel keyed by field NAME, choices by option NAME. zw.tables.query(table, { where: [{ field, op, value }], sort: [{ field, dir }], limit }) resolves { records: [{ id, fields }], total }; zw.tables.add(table, { Field: value }) or a list adds records; zw.tables.update(table, id, fields) and zw.tables.remove(table, ids) need "edit". zw.metrics.read(metric, { from, to }) resolves { metric, readings: [{ value, at, note }] }; zw.metrics.record(metric, value, { note }) needs "record". Name a table or metric by its id or its name. Through a share link a guest can read and add, never change or delete — so an intake form works when shared, a triage board needs a signed-in viewer. A granted Workbench TASK (ctx.tasks; each says whether it `takesInput`) starts with zw.tasks.start(task, input), which resolves { runId, status } at once — the run carries on after the page closes — and zw.tasks.get(task, runId) reads { status, output } for runs this page started. Running a shim on every keystroke is fine — it costs nothing and there is no rate limit. Debounce ~150ms and COALESCE: remember the latest text and run it when the current call finishes, so the answer matches what is on screen. Clear any in-flight guard on failure as well as success, or one call that doesn't come back wedges the interface. `d.action` is "act" | "suggest" | "refuse" — those three strings, nothing else. Branch on it rather than on a confidence threshold you invent; "refuse" means the shim doesn't recognise the input well enough to answer, so say so rather than showing a low-confidence guess. If you run a requestAnimationFrame loop, remove CSS transitions from any property it writes — the two fight and the property looks frozen. A flow answers in markdown: render it with el.replaceChildren(zw.markdown(text)). It builds DOM nodes, so model output is never treated as markup. STREAM chat answers: zw.flows.run(id, input, { onEvent: fn }) delivers the text token by token, and a flow that takes several seconds reads as broken without it. Use zw.textDelta(event) for each chunk — it returns null for anything that isn't text, so pass it every event — and accumulate. The promise still settles at the end with the whole result; take the final text from there. onEvent also sees node_start / node_complete / node_error if you want to name the step. Don't name a top-level variable `history`, `name`, `status`, `length`, `origin` or `top`: those are already window properties, so `var history = []` leaves you with the browser's History object and `history.push` fails. Prefix it, or keep it inside a function. THE KIT: every new interface also has kit.css, components built only from the brand's variables — link it after brand.css (<link rel="stylesheet" href="kit.css">) and build from its classes rather than styling from scratch: layout (.page, .page.wide, .header, .stack, .row, .grid), type (h1, h2, h3, .muted, .small, .label, .num, .stat), buttons (.btn, .btn.secondary, .btn.quiet, .btn.sm, .btn.icon), fields (.field with a .label, then .input / .select / .textarea, then a .hint or .error; .check for a checkbox), .chips and .chip, .badge (.up, .down, .accent), .card, .list, .meter, states (.empty, .skeleton, .spinner, .notice, .notice.error), chat (.thread, .bubble.me, .bubble.them, .typing, .composer), and .icon for a Lucide icon. Its tokens are var(--ink), var(--muted), var(--bg), var(--surface), var(--accent), var(--line), var(--tint), var(--radius) and spacing var(--s1)…var(--s7). The starters show the kit used well; read one before writing a page of your own. Design it the way a good product is designed: a clear hierarchy, real spacing, states for loading, empty and failure, and a layout that works on a phone first. Use the brand kit (napkin_brand_get) for what the brand looks like and how it sounds.
Paramètres, retour & exemple (11)
Paramètresbody· string · optionnel
Full replacement contents for `path`. Creates the file when it doesn't exist. Prefer `edits` on a file that already has something in it. With `append: true`, added to the end of the file instead.name· string · optionnelpath· string · optionnel
Which file to write. Defaults to index.html. Use lowercase names like app.js or style.css.edits· array · optionnel
Targeted find/replace pairs, applied in order. All must match or none are applied.append· boolean · optionnel
With `body`: add it to the end of `path` (creating the file if needed) instead of replacing it. How to write a file too long for one call: first part with `body`, the rest with `append: true`, in order.archived· boolean · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.deleteFile· boolean · optionnel
Remove `path` from the interface.visibility· string · optionnel · PRIVATE | WORKSPACE | SHARED
Who can see it: PRIVATE (only the user), WORKSPACE (every member, the default), or SHARED (specific people, granted afterwards). Say 'make it private' → PRIVATE.description· string · optionnelinterfaceId· string · requis
Interface id from napkin_interfaces_list.
Exemple d'appel{"name": "napkin_interfaces_write", "arguments": {"interfaceId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_layouts_list≈ 270 tokens
Lists the slide layouts napkin_deck_compose can build from — the brand kit's own first, then the built-in ones — with when to use each and the slots it takes (`needs` must be filled; `takes` are optional). Every layout adapts to every size, fits its text, and uses the brand's colors, faces and logos; the logo fills itself in. Pick by what the slide has to say: one line (statement), a number (stat), a quote, a list (points, steps), an event, a carousel (cover, inner, closing), a picture (image-top, image-full, split, corner, product-shot), or a display ad (banner).
Paramètres, retour & exemple (1)
Paramètresworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_layouts_list", "arguments": {"workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_add_chart≈ 366 tokens
Adds a LIVE chart to a sheet — its SQL re-runs against the tabs on every view, so it never goes stale. Write the query with napkin_sheets_query first to confirm the shape (first column = x axis, numeric columns = series, or set x/series explicitly). Prefer aggregated queries (GROUP BY) — a chart of raw rows is rarely the answer.
Paramètres, retour & exemple (8)
Paramètresx· string · optionnelsql· string · requis
The SELECT to chart.type· string · requis · line | bar | area | scatter | pietitle· string · requis
Chart title.series· array · optionnelsheetId· string · requis
Sheet id from napkin_sheets_list.stacked· boolean · optionnel
Stack the series (bar/area only).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_sheets_add_chart", "arguments": {"sql": "texte", "type": "line", "title": "texte", "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_create≈ 192 tokens
Creates a Napkin sheet (multi-tab spreadsheet), optionally titled. Then populate it with napkin_sheets_set_cells (headers in row 1). Tell the user where it landed — the Sheets tab in Napkin.
Paramètres, retour & exemple (2)
Paramètrestitle· string · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_sheets_create", "arguments": {"title": "texte", "workspace": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_list≈ 245 tokens
Lists the workspace's Napkin sheets (multi-tab spreadsheets — the small-data surface) newest first with tab/chart counts. Archived sheets are hidden unless `includeArchived`; `q` narrows by title. Inspect one with napkin_sheets_schema, then query it with napkin_sheets_query.
Paramètres, retour & exemple (3)
Paramètresq· string · optionnel
Title contains (case-insensitive).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.includeArchived· boolean · optionnel
Also list archived sheets (default false).
Exemple d'appel{"name": "napkin_sheets_list", "arguments": {"q": "texte", "workspace": "texte", "includeArchived": true}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_query≈ 265 tokens
Runs a SQLite SELECT over a sheet's tabs-as-tables (get the table/column names from napkin_sheets_schema first). Full SQLite dialect: WHERE, GROUP BY, ORDER BY, JOINs across tabs, aggregates. Results cap at 200 rows. This is THE way to answer questions about a sheet's data — never eyeball cells when a query can answer precisely.
Paramètres, retour & exemple (3)
Paramètressql· string · requis
A single SELECT.sheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_sheets_query", "arguments": {"sql": "texte", "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_schema≈ 222 tokens
Returns a sheet's tabs as SQL table definitions — CREATE TABLE scripts with row counts (each tab is a table; its header row names the columns; formula cells contribute computed values). Read this BEFORE writing a napkin_sheets_query so your SQL matches the real tables.
Paramètres, retour & exemple (2)
ParamètressheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_sheets_schema", "arguments": {"sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_set_cells≈ 369 tokens
Sets cells in one tab of a sheet, by A1 address — values or formulas ('=SUM(B2:B9)'). Additive and surgical: only the addressed cells change. Check napkin_sheets_schema first so you know the tab names and where data ends; put headers in row 1 when creating a new region. To build a whole small table, write header cells + data cells in one call.
Paramètres, retour & exemple (5)
Paramètrescells· object · requis
A1 → raw value/formula, e.g. {"A1":"Region","B2":"=SUM(B3:B9)"}.sheetId· string · requis
Sheet id from napkin_sheets_list.tabName· string · optionnel
Tab name (defaults to the first tab).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.
Exemple d'appel{"name": "napkin_sheets_set_cells", "arguments": {"cells": {}, "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_update≈ 356 tokens
Edits a sheet's details: title, description, visibility, or `archived` (true takes it out of the gallery; false brings it back — the recovery move for a sheet created by mistake or no longer wanted). Cells are edited with napkin_sheets_set_cells, not here. Sheet ids come from napkin_sheets_list.
Paramètres, retour & exemple (6)
Paramètrestitle· string · optionnelsheetId· string · requis
Sheet id from napkin_sheets_list.archived· boolean · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.visibility· string · optionnel · PRIVATE | WORKSPACE | SHARED
Who can see it: PRIVATE (only the user), WORKSPACE (every member, the default), or SHARED (specific people, granted afterwards). Say 'make it private' → PRIVATE.description· string · optionnel
Short gallery summary.
Exemple d'appel{"name": "napkin_sheets_update", "arguments": {"sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_sheets_view_chart≈ 288 tokens
Renders one of a sheet's pinned charts against the LIVE data and returns the image so you can SEE it — the exact chart the user sees. The text part carries `imageUrl`; to show the user the chart inline in your reply, put a markdown image on its own line: ``. Use after napkin_sheets_add_chart to confirm the chart reads well, or whenever the user asks about a chart.
Paramètres, retour & exemple (3)
ParamètreschartId· string · requis
Chart id (napkin_sheets_list shows chart counts; the sheet's charts carry ids).sheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_sheets_view_chart", "arguments": {"chartId": "texte", "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_slide_add≈ 346 tokens
Appends one slide to an existing deck. Pick a layout (title | section | title-body | title-lead | two-column | three-column | comparison | statement | quote | closing | blank), give the title, and optionally `content` — markdown for the layout's remaining text areas, split by `---` lines (two-column/comparison take one block per column). Check the deck first with `napkin_deck_outline`.
Paramètres, retour & exemple (5)
Paramètrestitle· string · optionnel
Slide title.layout· string · requis
Layout key: title | section | title-body | title-lead | two-column | three-column | comparison | statement | quote | closing | blank.boardId· string · requis
Napkin board id (a deck).content· string · optionnel
Markdown body — blocks split on `---` lines.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_slide_add", "arguments": {"layout": "texte", "boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_slide_fill≈ 318 tokens
Fills a slide's empty layout text areas by NAME — the names `napkin_deck_outline` surfaces as `[empty text stub: "…"]`. Pass `texts` mapping those exact names to content (markdown `- ` bullets welcome). Read the outline first to see which stubs a slide still has open.
Paramètres, retour & exemple (4)
Paramètresslide· integer · requis
1-based slide number in presentation order.texts· object · requis
Placeholder name → content, e.g. {"Add a title": "Q3 Review"}.boardId· string · requis
Napkin board id (a deck).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_slide_fill", "arguments": {"slide": 1, "texts": {}, "boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_slide_update≈ 400 tokens
Changes one slide as a whole: move it to another position in the deck (`moveTo`, 1-based — the other slides shift around it), hide it from the show without deleting it (`skipped`), show or hide its slide number (`pageNumber`), or replace its speaker notes (`notes`, markdown). Slide numbers come from napkin_deck_outline. To change what's ON the slide, use napkin_slide_fill or napkin_draw.
Paramètres, retour & exemple (7)
Paramètresnotes· string · optionnel
Speaker notes (markdown); replaces the current notes.slide· integer · requis
1-based slide number in presentation order.moveTo· integer · optionnel
New 1-based position for this slide.boardId· string · requis
Napkin board id (a deck).skipped· boolean · optionnel
true hides the slide from present mode and exports.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.pageNumber· boolean · optionnel
Show the slide number on this slide.
Exemple d'appel{"name": "napkin_slide_update", "arguments": {"slide": 1, "boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_slide_view≈ 295 tokens
Renders a single slide of a Napkin deck to an image — exactly what present mode shows, cropped to the slide. Navigate by 1-based `slide` number in presentation order (get the map from `napkin_deck_outline` first; the result echoes `slideIndex`/`slideCount` so you can step through a deck slide by slide). Use this to check visual layout, drawings, and images that the text outline can't carry.
Paramètres, retour & exemple (3)
Paramètresslide· integer · requis
1-based slide number in presentation order.boardId· string · requis
Napkin board id (a deck).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "napkin_slide_view", "arguments": {"slide": 1, "boardId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_add_columns≈ 403 tokens
Adds columns to the end of a table. Existing records leave them empty.
Paramètres, retour & exemple (5)
Paramètrestable· string · requis
The table tab's name or id.columns· array · requissheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.
Exemple d'appel{"name": "napkin_tables_add_columns", "arguments": {"table": "texte", "columns": [], "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_add_records≈ 300 tokens
Adds records, each a map of field name → value. Choices by option name (a new name becomes a new option); dates as YYYY-MM-DD; people by user id; checkboxes true/false. Every problem is reported at once and nothing is written until all records are valid.
Paramètres, retour & exemple (5)
Paramètrestable· string · requis
The table tab's name or id.records· array · requissheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.
Exemple d'appel{"name": "napkin_tables_add_records", "arguments": {"table": "texte", "records": [], "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_add_view≈ 528 tokens
Adds a saved view everyone sees: a grid (with where / sort), a board (lanes from a select or person column — groupBy), or a form people fill in to add records (formFields, formTitle, formIntro). A form's share link is made from the sheet's form view.
Paramètres, retour & exemple (12)
Paramètreskind· string · requis · grid | board | formname· string · requissort· array · optionneltable· string · requis
The table tab's name or id.where· array · optionnelgroupBy· string · optionnel
Board: the select or person column whose values are the lanes.sheetId· string · requis
Sheet id from napkin_sheets_list.formIntro· string · optionnelformTitle· string · optionnelworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.formFields· array · optionnel
Exemple d'appel{"name": "napkin_tables_add_view", "arguments": {"kind": "grid", "name": "texte", "table": "texte", "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_create≈ 510 tokens
Makes a table tab — typed columns, one record per row, with grid, board and form views — in an existing sheet, or in a new sheet when sheetId is omitted. Use a table (not napkin_sheets_set_cells) for lists people keep: requests, vendors, bugs, contacts. The first column names each record. Column types: text, long_text, number, date, checkbox, select, multi_select, person, url, email.
Paramètres, retour & exemple (6)
Paramètresname· string · requis
The table's name, e.g. Requests.columns· array · requissheetId· string · optionnel
Add to this sheet; omit to make a new sheet.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.sheetTitle· string · optionnel
Title for a new sheet.
Exemple d'appel{"name": "napkin_tables_create", "arguments": {"name": "texte", "columns": []}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_delete_records≈ 250 tokens
Deletes records by id. People can undo this in the sheet only while it's open, so confirm with the person first.
Paramètres, retour & exemple (5)
Paramètresids· array · requistable· string · requis
The table tab's name or id.sheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.
Exemple d'appel{"name": "napkin_tables_delete_records", "arguments": {"ids": [], "table": "texte", "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_get≈ 196 tokens
A table tab's columns (name, type, choices), its views, and how many records it holds. Read this before writing records so field names and choices match.
Paramètres, retour & exemple (3)
Paramètrestable· string · requis
The table tab's name or id.sheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.
Exemple d'appel{"name": "napkin_tables_get", "arguments": {"table": "texte", "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_query≈ 402 tokens
Records from a table tab, keyed by field name, with optional where / sort / search / limit (max 500). For counts, sums and joins, napkin_sheets_query runs SQL over tables too (each table is an SQL table with an id column).
Paramètres, retour & exemple (9)
Paramètressort· array · optionnellimit· integer · optionnelmatch· string · optionnel · all | anytable· string · requis
The table tab's name or id.where· array · optionneloffset· integer · optionnelsearch· string · optionnelsheetId· string · requis
Sheet id from napkin_sheets_list.workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.
Exemple d'appel{"name": "napkin_tables_query", "arguments": {"table": "texte", "sheetId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - napkin_tables_update_records≈ 289 tokens
Changes some fields of records by id (from napkin_tables_query). Fields left out keep their values.
Paramètres, retour & exemple (5)
Paramètrestable· string · requis
The table tab's name or id.sheetId· string · requis
Sheet id from napkin_sheets_list.updates· array · requisworkspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.
Exemple d'appel{"name": "napkin_tables_update_records", "arguments": {"table": "texte", "sheetId": "texte", "updates": []}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - search_docs≈ 226 tokens
Search ZeroWidth product documentation. Returns matching pages with title, slug, public URL, and a query-relevant snippet. Use this when the user asks about a ZeroWidth product (Compass, Workbench, Caliper, Prism, Ledger, Napkin, zv1), an API behavior, or a policy. No authentication required — the docs corpus is public.
Paramètres, retour & exemple (2)
Paramètreslimit· integer · optionnel
Max number of results. Defaults to 10.query· string · requis
Search query — keywords or natural-language phrase.
Exemple d'appel{"name": "search_docs", "arguments": {"query": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - search_workspace≈ 670 tokens
Finds anything in the workspace in one call — uploaded files (searching the text inside them), folders, Workbench flows, shims, knowledge bases and tasks, Compass pages and changes, Caliper datasets, evals, rubrics, reviews, specs and sources, Ledger entries, Napkin sketches, decks, docs, sheets, diagrams and interfaces. The same search the person uses in Home. Use it FIRST whenever the user mentions something without saying where it lives ('the SOW', 'the dispatch interviews', 'that invoice'). The query is words in any order: each word must appear in the thing's name, its tags, the names of the folders it's filed under, or the text inside it — so 'northwind sow' finds the SOW filed in the Northwind folder, and 'dispatch interview' finds 'Dispatcher interview 1.md'. Results are ranked best first. Each hit carries its id, kind, path (for a link), folderPath (where it's filed, top-down), tags, and a snippet when the words were found inside it rather than in its name — say where a thing lives using folderPath. A file's id feeds workspace_files_read; other ids feed the kind's *_get tool. Pass folderId to search only inside one folder and everything under it. If nothing comes back, try fewer or different words before telling the user it doesn't exist. Results only include what the user can see, and only kinds this token may read.
Paramètres, retour & exemple (5)
Paramètresq· string · requis
Words, any order, case-insensitive. Fewer words find more.kinds· array · optionnel
Restrict to these kinds (file, folder, flow, page, doc, dataset, …). Omit to search everything.limit· integer · optionnelfolderId· string · optionnel
Only search inside this folder and the folders under it (ids from workspace_folders_browse or a folder hit).workspace· string · optionnel
Workspace slug. Personal tokens with no default workspace MUST pass this; tokens with a default can override per call. Ignored for workspace API keys.
Exemple d'appel{"name": "search_workspace", "arguments": {"q": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]} - workspace_files_save_from_chat≈ 348 tokens
Save a picture the user attached in THIS conversation to the workspace's files, in the Home folder "From chat", and get back its file id. Use it when the user wants a picture they sent you used somewhere — an interface shows it as <img src="file:<fileId>"> (CSS: url(file:<fileId>)). Pick the picture by `name`: the name in its `[attached image: <name>]` marker. Omit `name` only when the latest message with pictures has exactly one. Saving the same picture again returns the file it was already saved as. Only pictures from this conversation can be saved; a picture on a website can't be fetched, so ask the user to attach it here.
Paramètres, retour & exemple (3)
Paramètresname· string · optionnel
The picture's name, as in `[attached image: <name>]`.workspace· string · optionnel
Workspace slug. Ignored when the workspace is already set for this chat.approvalId· string · optionnel
Approval id from a prior needs_confirmation response. Omit on the first call.
Exemple d'appel{"name": "workspace_files_save_from_chat", "arguments": {"name": "texte", "workspace": "texte", "approvalId": "texte"}}Exemple de retour{"content": [{"type": "text", "text": "…"}]}
C'est votre serveur ?
Revendiquez-le sur l'espace créateur (preuve GitHub, domaine ou npm) pour afficher le badge « propriétaire vérifié » et lancer un pentest comportemental à la demande afin d'obtenir le badge « Vérifié ».