Docs › Reference
Everything it does
Intents, variants, the board, Markdown export and screenshots, in full.
What you can say, and what the agent gets#
Intent. Each note can say what it wants. Fix: something is broken. Change: it works but should be different. Question: you want an answer, not an edit. The agent looks, replies in the thread and changes no code. Approve: this is right as it is; the agent leaves it alone and says so. Variants: show me a few versions to compare in the page, and I will pick one (see Variants). A note with no intent is read for what it says. The agent acts on the intent (the skill spells it out), and the board filters by it.
Where it is in the code. Besides the selector and the test id, an annotation carries the component path (the chain of components the element sits in, innermost first, framework plumbing left out: <PayButton> <CheckoutForm> <App>), and with the Vite plugin the exact file, line and column.
How it looks. At the detailed level the agent also gets the element's computed styles (colours as hex, type, size, spacing, border, layout; defaults left out), the chain of ancestors it sits in, and the animations that were running (name, duration, easing, how far through). That is what a note like "too cramped" or "wrong blue" is about.
Pause. The toolbar's pause button (Alt+Shift+P) freezes CSS animations and transitions, Web Animations and video and audio where they are, including ones that start while it is paused, so you can annotate a frame that only exists for 200ms. The annotation records the animations on the element and how far through each was (css slide-in 300ms ease-out (paused at 40%)). Resume puts back only what it paused. Animations driven from JavaScript frame by frame (requestAnimationFrame, canvas, WebGL) cannot be paused.
Iframes, shadow DOM and portals. Annotating works inside same-origin iframes (nested ones too, and ones added later or navigated), inside open shadow roots, and in portals (they render into the page, so they simply work). The element is recorded with how to reach it (iframe#preview → shadow root of my-widget), pins land on it in the right place, and the screenshot includes the frame's contents. A selector can reach in with >>>: iframe#preview >>> button.pay. A cross-origin iframe is a wall the browser does not let any script through, so it is picked as one element and its contents are left out of the screenshot (a placeholder is drawn).
Variants: compare versions in the page#
Ask for a few versions of something, flip between them live in your running app, and pick the one you like. Press Annotate, pick the thing, choose Variants, and say what to explore ("three layouts for this header"). The agent puts each version into the code, side by side, and the page shows a switcher over them: Original · Stacked · Compact, with Use this on the one you want. Switching is instant and changes nothing in your code. When you pick, the agent keeps only that version, deletes the others and every trace of the switching, and commits the result as one change. Write back instead ("make Stacked bolder and add one with the button on the right") and the agent changes the versions and offers a new set. Take a pick back before the agent has applied it, or pick again.
How it works, so it works in anything: a version is just an element with two attributes, and Notato needs no framework to see it.
<div data-notato-variant="header" data-notato-variant-name="Original" style="display: contents"> … </div>
<div data-notato-variant="header" data-notato-variant-name="Stacked" style="display: contents"> … </div>
data-notato-variant is the same on every version of the same thing, and data-notato-variant-name is that version's name. Elements that share a name are one version. The page shows the chosen version and hides the others with a style rule, so it also covers elements the app creates later (a re-render or a hot reload), and the switcher appears and disappears with the markers. Nothing else changes: a reload keeps what you were looking at, and with no server it is a preview with no Use this. With a hot-reload dev server the versions appear as the agent writes them; without one, refresh. Alt+Shift+←/→ steps through versions. Variants only works on elements in the page and in same-origin iframes, not inside shadow roots.
A driver (an agent with a browser tool) can check its work: window.__notato.variants.list() shows each group and which version is showing, and .select(group, name) shows one, so the agent can look at every version before it says they are ready. The Variants choice is only offered where there is a server and an agent on the other end: not in test mode.
Behind it: a variants record on the annotation (the group and the names, each with a one-line summary, the pick and when), the status variant_chosen while the pick waits for the agent, the MCP tool notato_variants_ready, and POST /annotations/:id/variants/choose. A pick arrives through notato_watch marked VARIANT CHOSEN; the board lists the versions with a Pick on each, and webhooks send annotation.variants_ready and annotation.variant_chosen.
Everything you write reaches the agent. notato_watch delivers every reply in an annotation's thread, marked FOLLOW-UP: an answer to a question the agent asked, a note on one you reopened, a request about versions, more to do on something it already resolved. A reply does not reopen a finished note: the agent reopens it if there is work in it, and leaves it alone for a "thanks".
Keeping something from the agent. Two switches, both for people, and both kept by the server, so nothing depends on how a message is worded:
- People only, on a note: the note and its whole thread are between people, and never reach the agent. Turn it on as you write the note, or on an existing one from its card on the board or in the toolbar; anyone on the thread can turn it off again, and each change is recorded in the thread. Turning it off hands the note back to the agent, with what was said meanwhile (it arrives marked SHARED WITH YOU). Webhooks still get People only notes: they are for people.
- Aside, on a reply: one remark for the people on the thread ("Sam, ignore the agent for a sec"). The agent is not sent it and is not shown it, so it never takes an aside as the thing to answer.
The agent can still read a People only note it asks for by id (notato_get), labelled as one, and is told not to act on it; its tools refuse to change one.
The board: every project in one place#
The server serves a board at its own address (http://localhost:4747 in dev mode). One server can hold many projects (each app's project setting is the project its notes file under), and the board is built around that.
Projects. On notato dev a project appears with its first note: nothing to set up. You can also create one first with New project on the board, which shows how to connect each kind of app to it. On a shared server an admin creates every project first: that is what hands out its token, and a note for a project the server does not have is refused, so a typo or a stray app cannot start one. Renaming a project changes only the name people see; deleting one removes its notes, their screenshots and its tokens.
- The sidebar lists every project with what is still to do, and a dot when something happened since you last looked. All projects shows a card for each, most recently active first.
- Inbox, per project: the notes on the left, the one you open on the right with its screenshot, where it is in the code, the conversation with the agent, and the buttons to reply, resolve, reopen, dismiss, ask for a revert or pick a version. To do, Done, Dismissed and All split it by status; search looks through the notes, the replies, the pages, the selectors and the components; Filter narrows it by page, person, severity, intent, author or bundle; it sorts by recent activity, age or severity and groups by page, status, person or severity. Notes with something new since your last visit are marked.
- Activity: everything said in the project, newest first, a day at a time.
- Overview: what is to do and done, new notes a day for the last 30 days, the typical time to a first reply, the pages with the most to do and who is giving feedback. Every row opens the inbox on that slice.
- Settings (for whoever can administer the server: anyone on this machine in dev mode, the admin in serve mode): whether agents can connect, with the MCP address and how to connect each agent (see MCP over HTTP), screenshots on or off, the webhooks (see Webhooks), your name on replies and how much a Markdown copy says. Server settings go in
notato.config.json, the filenotato configedits, and apply at once. Only the board itself can change them: a request from an app's page is refused, so a page cannot point a webhook somewhere.
It updates as things happen. Keys in the inbox: j/k (or the arrows) move, / searches, r replies, Cmd/Ctrl+Enter sends, Esc closes. Every note has its own link (#/p/<project>/a/<id>). Replying as, at the foot of the sidebar (or in Settings), sets the name your replies carry (kept in this browser). On a phone the projects are a drawer and a note opens over the list.
Copy as Markdown, and export#
Any annotation can be written as Markdown at four levels, to paste into another agent, an issue or a pull request:
| Level | What it holds |
|---|---|
compact | A line each: pin, intent, severity, route, comment, element, source. For scanning many |
standard | What it takes to find and fix it: target, source, component path, viewport, a console and network summary, steps, thread |
detailed | Adds computed styles, ancestors, classes, animations and the environment |
forensic | Everything captured: the full console and network, other plugins' context, the identity as recorded |
The toolbar's copy button copies this page's annotations at the level you pick (remembered). The board copies everything its inbox shows (the ⋯ menu, in the order shown and after the search), and each note from the copy button in its header. From a terminal:
npx notato export --detail compact --status open # to the terminal
npx notato export --project checkout-web --intent question -o questions.md
notato_get and notato_watch take the same detail, and the server has GET /annotations/:id/markdown?detail= and GET /projects/:id/markdown?detail=&status=&intent=. Nothing here needs screenshots.
Screenshots: on or off#
Screenshots are what the agent sees, and they can contain whatever is on screen. They are on by default and can be turned off for a project:
npx notato config # what is set, and where each value comes from
npx notato config set screenshots off
npx notato config set screenshots on
That writes notato.config.json in the folder you start Notato from, so it can be committed and shared ({ "screenshots": "off" }). NOTATO_SCREENSHOTS=off overrides the file. The server enforces it: it drops any screenshot a page sends, including from imported bundles, so none is ever stored, and a change applies to the next annotation without a restart. Pages ask the server before each capture, so no screenshot is rendered either, and the popover says so ("No screenshot will be taken: they are turned off"). The annotation is still made, still numbered, and still carries the selector, component and source, which is what the agent works from; the board shows it without an image.
A broken settings file fails safe: screenshots stay off until it is fixed, doctor warns, and the server logs why. A page can also opt out for itself with <Notato screenshots={false} />, which is the only control in test mode with no server; a server that has them off wins over a page that has them on.