This page assumes the json-render basics —
{ root, elements, state }, { "$state": "/path" } bindings, repeat, { "$item": "field" } — documented in Generative UI. Everything here also applies unchanged to the data_sources of custom dashboard pages and of live front-page panels, which share the same pipeline.How live data works
The Agent Dashboard editor has two boxes:- Layout (json-render spec) — the visual template. It reads values from its
state, e.g. astatwhosevalueis{ "$state": "/kpis/open_tickets" }. Seedstatewith empty defaults so the page renders before the first fetch. - Data sources (live values) — a JSON array. Each entry fetches something at view time and writes the result into the spec’s state at
result_target(a JSON Pointer).
action_id for every source and stores the executable definition (tool name, params, code, URL, headers, connection string, query…) server-side only. A viewer’s browser receives nothing but the id, POSTs it to the agent’s widget-action endpoint on page load (and on the optional refresh interval), and gets back the result. Secrets and queries never ship to any browser.
Caching & cost control. Results are cached server-side per action for refresh_interval_sec seconds (default 120 when no interval is set), so a hundred open dashboards cause one upstream run per window, not a hundred. Rate limits (owner 120/min, signed-in buyer 30/min, anonymous 10/min per IP), a wallet circuit-breaker and the platform kill switches sit in front of every run.
Fields every source shares
A page may declare up to 12 data sources; a 13th is rejected at save.
Choosing a source type
Each data source sets exactly one of the following:
A sixth kind — the agent-push panel (
writable: true + key) — exists on custom dashboard pages and front-page panels, where the agent pushes a single value with update_widget_data. It is not available on the Agent Dashboard section itself; use a file source there instead (it is strictly more capable — a whole document the agent can patch field-by-field).
1. Built-in (or custom) tool
Runs one of the curated read-only built-ins with parameters you fix at design time:web_fetch, web_search, read_rss_feed, read_recent_tweets, x_read_mentions, x_search_tweets, calculate — or any of this agent’s own type: "code" custom tools by name. Viewers can never change the params. Billing is the tool’s own per-call fee (e.g. an RSS poll), once per cache window.
Simple — latest headline as a stat
Data sources:Full example — auto-refreshing feed list
read_rss_feed returns { ok, count, rows: [{ title, url, published_at, … }] }, so result_path: "rows" extracts the array; the spec repeats a link per row. Refreshes every 15 minutes; between refreshes every viewer is served the cached result.
"tool": "my_tool_name" (it must be an enabled type: "code" tool on this agent) — its handle(input, ctx) receives your fixed params as input. This is the escape hatch when a source needs logic the other kinds can’t express.
2. Inline sandboxed code
A Python or JavaScript body executed in the code sandbox per (cached) request — no tool definition needed. The body follows the same convention as custom code tools: definehandle(input) and return a JSON-serializable value. There is no ctx.tools bridge in widget code (unlike custom tools run by the agent), and each run bills sandbox execution seconds — prefer url for a plain HTTP fetch, which is cheaper and faster.
Simple — computed value
Full example — fetch + reshape for a chart
The sandbox has network access, so code can call an API and reshape the payload into the[{ label, value }] array a chart expects. Refreshes hourly.
3. HTTPS endpoint (url)
The most direct way to read a REST API or any HTTPS document: the server fetches your fixed URL, parses the body, and serves the parsed value. No sandbox, no provider — one flat platform fee per upstream fetch (cached). Use this instead of web_fetch whenever the endpoint returns data rather than a web page.
- SSRF-guarded: only
http(s), every redirect hop re-validated, private/loopback/metadata addresses refused. - Parsing (
format):json(default when the content-type says JSON),xml(also RSS/Atom content-types — converted to JSON, attributes prefixed@, mixed text under#text),text(raw string), orauto(default — follows content-type, then sniffs JSON, then falls back to text). headersmay carry an API key. They are stored server-side only and are stripped from everything a browser receives. On a cross-origin redirect,Authorization/Cookieare dropped (standard fetch behaviour).- Caps: 1 MB response body, 10 s timeout (operators can tune
live_sourcesinconfig/widget-security.json).
Simple — public JSON API
Full example — authenticated API + an XML source side by side
Two sources: a JSON status API behind a bearer token, and a legacy XML export. The XML document{ "report": { "@generated": "2026-07-12", "system": [ { "@name": "api", "@status": "ok" }, … ] } }.
4. Per-agent data file (file)
Every agent has its own data directory on the server; a file source reads a .json or .xml file from its dashboard_data/ folder on every (cached) view. This is the recommended way to show values your agent maintains itself: the agent updates the file with the built-in write_dashboard_file tool, and the dashboard re-reads it live. Reading and writing are local disk operations — free.
The write side, available to the agent in owner-context sessions (owner chat/preview, schedules, the owner API — buyers and anonymous visitors can never trigger it):
write_dashboard_file({ file, value })— replace the whole document.write_dashboard_file({ file, value, path: "/kpis/open_tickets" })— patch one sub-value (RFC 6901 JSON Pointer); the rest of the file is preserved."/items/-"appends to an array.- Custom code tools can call it too:
await ctx.tools.write_dashboard_file({ file, value, path }). - Only files that a saved data source declares are writable (save the dashboard first, then let the agent write), only
.json(never.xml), values are sanitized to inert display data and capped at 1 MB. - A file that hasn’t been written yet reads as
null— seed your spec’sstateso the panel renders sensibly empty. - Self-hosting? Any external process (cron, ETL, another app) may write
AGENT_DATA_DIR/{user}/{agent_id}/dashboard_data/directly;.xmlfiles are parsed like theurlXML format.
Simple — one KPI the agent keeps current
write_dashboard_file file metrics.json, path /open_tickets, value = the current count.” — or have a scheduled run push the whole document:
Full example — a status board the agent patches field-by-field
One file backs several panels; the agent (or a code tool viactx.tools) patches individual pointers as it works, so unrelated values never clobber each other.
5. Read-only database (db)
Runs an owner-fixed, read-only SQL query against your own Postgres database (standard connection string) and writes the rows (an array of objects) into state. The query executes inside a READ ONLY transaction with a statement timeout, so it cannot write, and a runaway query is cut off.
Guarantees & limits: single statement only; writes rejected; results truncated to 500 rows and capped at 1 MB JSON; connection string and SQL live server-side only. On the hosted platform, connection strings resolving to private/loopback hosts are refused — self-hosted operators can allow them with live_sources.db_allow_private_hosts in config/widget-security.json (e.g. to reach a Postgres on localhost). Alias your columns to match what the spec needs — e.g. AS label / AS value feeds a chart directly.
Simple — one number
Full example — KPIs + chart + latest rows
Three sources against one database: a single-row KPI query, a chart-shaped aggregate, and a recent-items list. Bind values with$1-style placeholders via params.
6. Agent-push panels (writable) — custom pages & front page
On custom dashboard pages and live front-page panels, a source may instead be { "writable": true, "key": "sales", "result_target": "/sales" } — no pull at all; the panel shows whatever the agent last pushed with update_widget_data({ key: "sales", value: … }). The Agent Dashboard section deliberately doesn’t support this kind; a file source covers the same need with per-field patching on top.
What each kind costs & where it runs
All kinds share the endpoint’s rate limits and the wallet circuit-breaker (non-owner viewers are refused once your balance reaches the floor, so traffic can’t drag you deep into deficit).
Troubleshooting
- Panel stays empty — check, in order: the spec’s
stateseeds the path;result_targetstarts with/;result_pathmatches the real result shape (open the browser dev tools — the dashboard POSTs to/agent/<name>/widget-actionand the JSON response shows exactly what came back); you saved after editing (sources only run once minted). filepanel shows nothing — the file doesn’t exist yet (reads asnulluntil the firstwrite_dashboard_file), or the agent session wasn’t owner-context, or the dashboard wasn’t saved before the agent tried to write (the declared files are the allowlist).dbrefuses to connect — private/loopback host on the hosted platform (see above), wrong credentials, or the query isn’t a single read statement.- Stale values — that’s the cache: one upstream run per
refresh_interval_sec(or 120 s). Lower the interval (min 60 s) if you need it fresher.
Field reference: every
data_sources property, with types, is listed in the generated Configuration Schema Reference → agent_dashboard.