API
Programmatic access for developers
Overview
Agentation exposes callbacks that let you integrate annotations into your own workflows — send to a backend, pipe to terminal, trigger automations, or build custom AI integrations.
- Sync annotations to a database or backend service
- Build analytics dashboards tracking feedback patterns
- Create custom AI integrations (MCP servers, agent tools)
Props
onAnnotationAdd(annotation: Annotation) => void- Called when an annotation is created
onAnnotationDelete(annotation: Annotation) => void- Called when an annotation is deleted
onAnnotationUpdate(annotation: Annotation) => void- Called when an annotation comment is edited
onAnnotationsClear(annotations: Annotation[]) => void- Called when all annotations are cleared
onCopy(output: string) => void- Receives the formatted output after a Copy attempt, even if clipboard access fails.
onSubmit(output: string, annotations: Annotation[]) => void- Receives structured markdown and annotations when Send is clicked. Async callbacks are awaited; failures preserve feedback.
copyToClipboardbooleanDefault: true- Set to false to prevent writing to clipboard (if handling via onCopy)
endpointstring- MCP server URL for syncing annotations
sessionIdstring- Pre-existing session ID to use
onSessionCreated(sessionId: string) => void- Called when a new session is created
webhookUrlstring- Webhook URL to receive annotation events
classNamestring- Apply custom positioning and z-index to the toolbar host.
useHashLocationbooleanDefault: false- Keep separate feedback, layout state and sessions for hash-based routes.
appNamestring- Identify the app in copied and submitted feedback.
enableKeyboardShortcutsbooleanDefault: true- Enable global shortcuts. Popup Enter/Escape and keyboard activation of buttons remain available.
identifyingAttributesreadonly string[]- Replace the default list of identifying attributes captured from selected elements.
copyFormat"markdown" | "source" | "classes" | { attribute: string }Default: "markdown"- Choose full feedback, source paths, classes or an attribute value. Changes Copy only.
onOpenSource(sourceFile: string) => void- Show Open in editor when a source location is available. Your callback chooses the editor integration.
portalContainerHTMLElement | ShadowRoot | nullDefault: document.body- Keep the toolbar and popup inside a host overlay’s focus boundary.
Basic usage
Receive annotation data directly in your code:
import { Agentation, Annotation } from "agentation";
function App() {
const handleAnnotation = (annotation: Annotation) => {
console.log(annotation.element, annotation.comment);
};
return (
<>
<YourApp />
<Agentation onAnnotationAdd={handleAnnotation} />
</>
);
}Identify your app and choose what to copy
Use appName to distinguish feedback from different apps. By default, Copy produces markdown with the selected elements and your notes.
<Agentation appName="Checkout preview" />For a smaller output, copy source paths, CSS classes, or one identifying attribute. Each format returns unique values on separate lines.
<Agentation copyFormat="source" />
<Agentation copyFormat="classes" />
<Agentation copyFormat={{ attribute: "data-qa" }} />Attribute mode captures the requested attribute automatically. When the selected element has a value, you can save it without writing a comment. Missing metadata leaves the clipboard and saved feedback intact.
copyFormat also changes the text passed to onCopy. It does not change Send: onSubmit still receives structured markdown and the annotation array. With copyToClipboard={false}, your onCopy callback handles delivery.
Stable element identifiers
Agentation captures data-testid, data-test, data-qa, data-cy and data-component by default. Supply identifyingAttributes to replace that list.
<Agentation identifyingAttributes={["data-testid", "data-qa"]} />Values are available in annotation.attributes. Up to two short data attributes also appear in the element selector. Capture is limited to 16 names and 500 characters per value. Choose identifiers intended to be shared with your agent.
Open the source in your editor
Provide onOpenSource to add an Open in editor action to the feedback popup. It receives the detected file reference, such as src/Checkout.tsx:24:5.
<Agentation onOpenSource={(sourceFile) => openInEditor(sourceFile)} />openInEditor is your app’s editor integration. The action stays hidden if a trustworthy source location is unavailable. React development metadata is best effort; a production build needs source metadata supplied at build time.
Annotate a modal or popover
Pass the active overlay’s content element as portalContainer. A callback ref lets Agentation move its existing portal as the overlay opens and closes, preserving feedback state.
import { useState } from "react";
import { Dialog } from "@base-ui/react/dialog";
import { Agentation } from "agentation";
export function App() {
const [container, setContainer] = useState<HTMLElement | null>(null);
return (
<>
<Dialog.Root>
<Dialog.Trigger>Open settings</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Popup ref={setContainer}>
<Dialog.Title>Settings</Dialog.Title>
{/* Your settings form */}
<Dialog.Close>Close</Dialog.Close>
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
<Agentation portalContainer={container} />
</>
);
}The container must belong to the same document. Browsers with the Popover API keep Agentation in the top layer while its DOM remains inside the overlay’s focus boundary. Older browsers use an ordinary portal, so host overflow and transforms can still affect placement. Other modal libraries may need additional focus integration.
Same-origin embedded pages
Select elements inside same-origin iframes, including nested frames and open shadow roots. Single-element markers follow child scrolling and axis-aligned frame scaling. They hide when their target leaves the frame viewport or the frame navigates, and return with the original number when visible again.
Cross-origin contents cannot be inspected. You can still select the iframe element itself. Rotated or skewed frames and live multi-element groups across frame boundaries are outside this support.
Routing and keyboard control
For a hash router, enable useHashLocation so /app#/inbox and /app#/settings retain separate notes, layout state and default MCP sessions.
<Agentation useHashLocation />Hash changes and browser Back/Forward are tracked. If your router changes history without a navigation event, render Agentation from a component subscribed to that router’s location. Unsaved popup text is cancelled on navigation; saved feedback stays with its route. Query strings do not create separate feedback buckets. Existing pathname-only data stays in its original bucket.
For an app with its own keyboard shortcuts, turn off Agentation’s global shortcuts. The toolbar buttons and Enter/Escape within feedback popups remain usable.
<Agentation enableKeyboardShortcuts={false} />Annotation type
The Annotation object passed to callbacks. See Agentation Format for the full schema.
type Annotation = {
// Required
id: string; // Unique identifier
comment: string; // User's annotation text
elementPath: string; // CSS selector path
timestamp: number; // Unix timestamp (ms)
x: number; // % of viewport width (0-100)
y: number; // px from document top
element: string; // Tag name ("button", "div")
// Recommended
url?: string; // Page URL
boundingBox?: { // Element dimensions
x: number;
y: number;
width: number;
height: number;
};
// Context (varies by output format)
reactComponents?: string; // Component tree
cssClasses?: string;
sourceFile?: string; // path:line:column when available
attributes?: Record<string, string>; // Captured identifying attributes
computedStyles?: string;
accessibility?: string;
nearbyText?: string;
selectedText?: string; // If text was selected
// Browser component fields
isFixed?: boolean; // Fixed-position element
isMultiSelect?: boolean; // Created via drag selection
// Annotation kind (defaults to "feedback")
kind?: "feedback" | "placement" | "rearrange";
// Layout mode data
placement?: {
componentType: string;
width: number;
height: number;
scrollY: number;
text?: string;
};
rearrange?: {
selector: string;
label: string;
tagName: string;
originalRect: { x: number; y: number; width: number; height: number };
currentRect: { x: number; y: number; width: number; height: number };
};
};HTTP API
The agentation-mcp server provides a REST API for programmatic access:
Sessions
| Method | Endpoint | Description |
|---|---|---|
| POST | /sessions | Create a new session |
| GET | /sessions | List all sessions |
| GET | /sessions/:id | Get session with annotations |
Annotations
| Method | Endpoint | Description |
|---|---|---|
| POST | /sessions/:id/annotations | Add annotation |
| GET | /annotations/:id | Get annotation |
| PATCH | /annotations/:id | Update annotation |
| DELETE | /annotations/:id | Delete annotation |
| POST | /annotations/:id/thread | Add thread message |
| GET | /sessions/:id/pending | Get pending annotations |
| GET | /pending | Get all pending annotations |
Events (SSE)
| Method | Endpoint | Description |
|---|---|---|
| GET | /sessions/:id/events | Session event stream |
| GET | /events | Global event stream (optionally filter with ?domain=...) |
Health
| Method | Endpoint | Description |
|---|---|---|
| GET | /health | Health check |
| GET | /status | Server status |
Real-Time Events
Subscribe to real-time events via Server-Sent Events:
# Session-level: events for a single page
curl -N http://localhost:4747/sessions/:id/events
# Global: events across ALL sessions
curl -N http://localhost:4747/events
# Filtered by domain: events for pages on a specific domain
curl -N "http://localhost:4747/events?domain=localhost:3001"
# Reconnect after disconnect (replay missed events)
curl -N -H "Last-Event-ID: 42" http://localhost:4747/sessions/:id/eventsEvent types
annotation.created: New annotation added (includeskindfield for design annotations)annotation.updated: Annotation modified (comment, status, design data, etc.)annotation.deleted: Annotation removedsession.created: New session startedsession.updated: Session updatedsession.closed: Session closedaction.requested: Agent action requestedthread.message: New message in annotation thread
Environment Variables
| Variable | Description | Default |
|---|---|---|
AGENTATION_STORE | Storage backend (memory or sqlite) | sqlite |
AGENTATION_EVENT_RETENTION_DAYS | Days to keep events | 7 |
Storage
By default, data is persisted to SQLite at ~/.agentation/store.db. To use in-memory storage:
AGENTATION_STORE=memory npx agentation-mcp serverProgrammatic Usage
import { startHttpServer, startMcpServer } from 'agentation-mcp';
// Start HTTP server on port 4747
startHttpServer(4747);
// Start MCP server (connects via stdio)
await startMcpServer('http://localhost:4747');See MCP Server for AI agent integration and available tools.
In-memory event history
When AGENTATION_STORE=memory, these settings bound event replay history. They do not delete current sessions or annotations and do not change SQLite retention.
AGENTATION_MAX_EVENTS=10000
AGENTATION_EVENT_TTL_MS=3600000
AGENTATION_CLEANUP_INTERVAL_MS=300000The defaults retain up to 10,000 events for one hour, with background cleanup every five minutes. Reads also remove expired events. Invalid values fall back to these defaults.