Skip to main content
The Agent Dashboard (dashboard → Agent Dashboard, under Activity) is a single json-render page you design once and that renders in your owner console and in each buyer’s own dashboard sidebar — never on the public chat page. Out of the box its spec is static; this page shows how to bind every number, table and chart on it to live data.
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:
  1. Layout (json-render spec) — the visual template. It reads values from its state, e.g. a stat whose value is { "$state": "/kpis/open_tickets" }. Seed state with empty defaults so the page renders before the first fetch.
  2. 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).
On save, the platform mints an opaque, content-addressed 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:
Layout — bind the first row’s title:

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.
To use one of your own tools, set "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: define handle(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), or auto (default — follows content-type, then sniffs JSON, then falls back to text).
  • headers may carry an API key. They are stored server-side only and are stripped from everything a browser receives. On a cross-origin redirect, Authorization/Cookie are dropped (standard fetch behaviour).
  • Caps: 1 MB response body, 10 s timeout (operators can tune live_sources in config/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
parses to { "report": { "@generated": "2026-07-12", "system": [ { "@name": "api", "@status": "ok" }, … ] } }.
The result of a source is visible to everyone who can see the dashboard (and to anonymous visitors if you use the same source on a front-page panel). The API key itself never leaves the server — but don’t point a source at an endpoint whose response contains secrets.

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’s state so 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; .xml files are parsed like the url XML format.

Simple — one KPI the agent keeps current

Then tell the agent (Instructions): “After every support conversation, update the dashboard: 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 via ctx.tools) patches individual pointers as it works, so unrelated values never clobber each other.
Example agent calls over a day:

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.
Create a dedicated read-only database role for dashboard sources anyway (GRANT SELECT only). The platform enforces a read-only transaction, but least privilege at the database beats trusting any single layer.

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 state seeds the path; result_target starts with /; result_path matches the real result shape (open the browser dev tools — the dashboard POSTs to /agent/<name>/widget-action and the JSON response shows exactly what came back); you saved after editing (sources only run once minted).
  • file panel shows nothing — the file doesn’t exist yet (reads as null until the first write_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).
  • db refuses 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.