KPI
kpiA headline number with change vs. previous value and a trend sparkline.
{"value":48320}{"value":48320,"previous":45910}Data & config fields
Data
Config
Every board has a write token and every widget has a key. Send JSON to /api/v1/boards/<board>/widgets/<key> and every open screen updates within about a second. Creating a board returns both. If a person gave you an existing board's edit link (…/b/<board>/join?key=<edit key>), use its edit key anywhere a token is asked for — see Using an edit link.
curl -X POST https:// instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ cpu \ -H "Authorization: Bearer <write token>" \ -H "Content-Type: application/ json" \ -d '{"value": 73}'
The token can also be sent as ?token= (only for devices that can’t set headers — URLs end up in logs, so prefer the header). Responses are JSON: {"ok": true, "widget": {...}}.
On a shared or headless machine, install the Connector without a login service and in its own directory. Create the token first with POST /boards/<board>/connectors (edit key).
curl -fsSL https:// instant-gui.com/ connect.sh | INSTANTGUI_HOME=/ tmp/ ig sh -s -- --board <board> --token <igc_…> --no-service
--no-service skips the start-at-login service (LaunchAgent, systemd user service, scheduled task) — without it the installer registers one. INSTANTGUI_HOME=<dir> puts the install and connect.yml in <dir> instead of ~/.instantgui. Then run node <dir>/bin/instantgui-connect.mjs yourself.
Tip: end a silent command with && echo "backup done: 42 files" so the board shows a summary. A command that prints nothing still logs “(no output)”, and the button still updates its last-run time and count.
A push body is a JSON object, sent with POST. (PUT with a type creates-or-updates the widget; PATCH with layout moves it — see agents.)
Buttons, toggles, sliders, inputs, selects and checklists change when people use them. Read the current state from your script:
curl https:// instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ trading \ -H "Authorization: Bearer <write token>" # → {"ok": true, "widget": {"key": "trading", "data": {"on": true}, ...}}
GET https://instant-gui.com/api/v1/boards/<board> returns every widget at once.
For devices that can only open a URL. Every query parameter except token becomes a data field.
https:// instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ temp/ push? token=<write token>& value=22.5
A person can hand you an existing board as an edit link: https://instant-gui.com/b/<board>/join?key=<edit key>. No browser is needed — the <board> in the path is the board id and the key value is the edit key. Send the edit key as a Bearer token on any call: it can read, push, create widgets and change the board. Treat it as a secret.
curl -X POST https:// instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ cpu \ -H "Authorization: Bearer <edit key>" \ -d '{"value": 73}' # Want a narrower write token for scripts or devices? Exchange the edit key: curl -X POST https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ token -H "Authorization: Bearer <edit key>" # → {"writeToken": "igw_..."} (add {"rotate": true} to issue a new one)
Create a board without an account. Keep the editLink private — it grants edit access.
curl -X POST https:// instant-gui.com/ api/ v1/ boards -d '{"title": "My bot", "template": "bot"}' # → {"board": {"id": "...", "urls": {...}}, "editLink": "...", "writeToken": "igw_..."}
Templates: blankbotserverbusinessiot
curl -X POST https:// instant-gui.com/ api/ v1/ boards/ <board>/ widgets \ -H "Authorization: Bearer <write token>" \ -d '{"type": "gauge", "key": "cpu", "config": {"title": "CPU"}, "data": {"value": 12}}' curl -X DELETE https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ cpu -H "Authorization: Bearer <write token>"
Machine-readable schemas for every widget: /api/v1/widgets
For agents: /llms.txt (short guide), /llms-full.txt and the OpenAPI spec
Rename it, move a widget, pause a button, embed it: one call each on the board you already built. No need to recreate it or resend the spec.
# Rename the board curl -s -X PATCH https:// instant-gui.com/ api/ v1/ boards/ <board> -H 'Authorization: Bearer <credential>' -d '{"title":"Shop live (EU)"}' # Move or resize a widget (only the fields you send change) curl -s -X PATCH https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ revenue -H 'Authorization: Bearer <credential>' -d '{"layout":{"x":0,"y":0,"w":6,"h":3}}' # Pause a control: viewers can no longer press it (editors still can). Turn it back on with true curl -s -X POST https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ ads -H 'Authorization: Bearer <credential>' -d '{"config":{"viewersCanUse":false}}' # Embed a private board: give the site a view token (revocable, read-only) curl -s -X POST https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ view-tokens -H 'Authorization: Bearer <credential>' -d '{"label":"Website"}'
Pausing sets config.viewersCanUse to false; editors can still press the control. Embedding a private board needs the view token from the last call.
Test a button the way a person would: press it as the editor, then read the result back. Use the action name from GET …/actions.
# 1. Press it as the editor (edit key or write token). Name = <widget key> or <widget key>/<button id>; list the valid names with GET https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ actions curl -s -X POST https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ actions -H 'Authorization: Bearer <credential>' -d '{"action":"backup/ run"}' # → 202 {"ok":true,"event":{"id":"<event id>","action":"backup/ run","status":"pending"},"result":"…/ actions? events=<event id>"} # 2. Read the result (poll until status is "done" or "error"; "result" holds the command's output) curl -s https:/ / instant-gui.com/ api/ v1/ boards/ <board>/ actions? events=<event id> -H 'Authorization: Bearer <credential>' # → {"ok":true,"events":[{"id":"<event id>","action":"backup/ run","status":"done","result":"backup ok",…}],…}
Viewers can press a control only if its config.viewersCanUse is true; the edit key and write token always can.
Mark a moment on a widget (e.g. from CI after a deploy) — it shows as a marker on line charts:
curl -X POST https:// instant-gui.com/ api/ v1/ boards/ <board>/ widgets/ <key>/ annotations \ -H "Authorization: Bearer <write token>" \ -d '{"text": "Deployed v2.4"}'
Change a board variable (sources using {{name}} re-fetch immediately):
curl -X PUT https:// instant-gui.com/ api/ v1/ boards/ <board>/ variables/ region \ -H "Authorization: Bearer <write token>" \ -d '{"value": "eu"}'
GET https://instant-gui.com/api/v1/boards/<board>/widgets/<key>/export downloads a widget’s data as CSV.
Click a bar (anywhere in its column), a pie slice, a line, or a text cell in a table, and the whole page filters by that value — on the live board, in the editor and in embeds. Charts highlight the matching category, slice or series; tables and logs narrow to the rows and lines that contain it; widgets without that value are left alone. The active filters show in a bar at the bottom of the screen: click a value again, press its ✕, or press Esc to clear. Filters are yours alone — nothing is saved and other viewers don’t see them.
Tip: use the same labels everywhere (bar categories, slice names, table cells) so widgets filter each other. In the editor, a widget’s settings open from the settings button in its toolbar (or Enter on the focused card) — clicking the widget uses it.
InstantGUI is a remote MCP server at https://instant-gui.com/api/mcp (Streamable HTTP). Tools: apply_board_spec, create_board, get_board, add_widget, push_data, update_widget_layout, update_board, lint_board, get_write_token, create_view_token, delete_widget, list_widget_types, get_widget_type, get_design_guide. Guide: /docs/agents.
No token needed to connect — each board you create gets its own write token.
{
"mcpServers": {
"instantgui": {
"url": "https:/ / instant-gui.com/ api/ mcp"
}
}
}Claude Code: claude mcp add --transport http instantgui https://instant-gui.com/api/mcp
Calling it with plain HTTP? Send both Content-Type: application/json and Accept: application/json, text/event-stream — without that Accept header the server answers 406 and names the missing header. The reply is a server-sent event: a line 'event: message' followed by a line starting 'data: ' that holds the JSON-RPC response; read the JSON from that data line.
curl -X POST https:// instant-gui.com/ api/ mcp \ -H "Content-Type: application/ json" \ -H "Accept: application/ json, text/ event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/ list"}'
Any board can live inside another site: https://instant-gui.com/e/<board> shows it read-only, live and without app chrome. Private boards need a view token — create one under Share → Embed or with the API (edit key or write token):
curl -X POST https:// instant-gui.com/ api/ v1/ boards/ <board>/ view-tokens -H "Authorization: Bearer <edit key>" -d '{"label": "Website"}' # → {"embedUrl": "https:/ / instant-gui.com/ e/ <board>? token=<view token>", "iframe": "<iframe …>", "id": "…"}
<iframe src="https:// instant-gui.com/ e/ <board>? token=<view token>" style="width:100%;height:640px;border:0" loading="lazy" title="Dashboard"></ iframe> <!-- optional: grow the iframe to its content --> <script>addEventListener("message", e => { if (e.data? .type === "instantgui:resize") for (const f of document.querySelectorAll("iframe")) if (f.contentWindow === e.source) f.style.height = e.data.height + "px" })</ script>
Other options: page=<page title>. The embed posts {type: "instantgui:resize", height} to the parent whenever its height changes (its layout never depends on the frame's height, so auto-sizing can't loop). Revoke a token with DELETE https://instant-gui.com/api/v1/boards/<board>/view-tokens/<id> — the embed stops at once; a wrong or revoked token is always refused.
The easiest way is the helper script: it sizes the iframe to the board, sends your page's colors and font, and follows your light/dark switch.
<div data-instantgui-board="<board>" data-token="<view token>" data-inherit-theme></div> <script src="https:/ / instant-gui.com/ embed.js" async></ script> <!-- or, mapping your own design tokens (any CSS color or var(--…), resolved on your page): --> <div id="board"></ div> <script src="https:/ / instant-gui.com/ embed.js"></ script> <script> InstantGUI.mount("#board", { board: "<board>", token: "<view token>", transparent: true, density: "compact", radius: "var(--radius)", font: "inherit", colors: { background: "var(--surface)", foreground: "var(--text)", border: "var(--border)", primary: "var(--accent)", mutedForeground: "var(--text-muted)" }, }); </ script>
data-inherit-theme / inheritTheme: true reads the page's background (the first non-transparent one behind the mount element), text color, font and common variables (--primary/--accent, --border, --muted-foreground, --radius…) and makes the board transparent. mount() returns {iframe, update(options), refreshTheme(), destroy()}.
Without the script, the same look works as URL parameters (every value is validated — anything else is ignored):
auto (follows the viewer's light/dark), light, dark or a theme idtransparent — no page background or outer padding; the board sits on your card. Or a color.accent=2557d6)compact — tighter card padding, no outer paddingsystem, serif, mono or a font-family list your page already loadsbackground, foreground, card, cardForeground, border, input, primary, primaryForeground, muted, mutedForeground, success, warning, danger, info, chart2…chart6. Unset ones are derived from foreground/background.PATCH https://instant-gui.com/api/v1/boards/<board> with {"embedStyle": {...}})Retheme live from your page — any subset of the same fields (vars holds the colors; reset: true drops earlier messages first):
iframe.contentWindow.postMessage({
type: "instantgui:theme", reset: true,
theme: "dark", bg: "transparent", radius: 14, density: "compact", font: "system",
vars: { background: "#161a20", foreground: "#eef1f5", border: "#2a313b", primary: "#6d9bff" },
}, "https:/ / instant-gui.com");The embed posts {type: "instantgui:ready"} when it's listening (send your theme then), {type: "instantgui:resize", height} and {type: "instantgui:scheme", scheme: "light"|"dark"} — with a transparent embed, set the iframe's style.colorScheme to that value, or browsers paint an opaque backdrop behind it (embed.js does this).
The other direction — make your page look like InstantGUI: <link rel="stylesheet" href="https://instant-gui.com/theme.css"> gives you every theme's tokens as --ig-* variables (--ig-background, --ig-card, --ig-primary…), following light/dark or pinned with <html data-ig-theme="midnight">, plus .ig-page, .ig-card and .ig-button.
Embeds are view-only by default: buttons and toggles show as inactive with a link to the board. For an embed on a page only you can reach (an internal admin tool, your own console), create its token with canUseControls (edit key only) — or switch on Let this embed use controls in the Embed dialog (board owner):
curl -X POST https:// instant-gui.com/ api/ v1/ boards/ <board>/ view-tokens -H "Authorization: Bearer <edit key>" \ -d '{"label": "Ops console", "canUseControls": true}' # → {"viewToken": "…", "canUseControls": true, "embedUrl": "https:/ / instant-gui.com/ e/ <board>? token=…", …}
Presses through that embed act like an editor's on the board's controls — same rate limits, Connector routing and "tap again to confirm" — and are attributed "by embed: Ops console" on the result and in the activity log. Anyone who can load the page with that token can press them, so don't put it on a public page; revoking the token stops it at once.
Set a Webhook URL (https) on any control. When someone uses it we POST:
{
"event": "widget.interaction",
"board": "<board>",
"widget": { "key": "restart", "type": "button" },
"data": { "presses": 4, "lastPressedAt": 1767225600000 },
"at": "2026-10-09T12:00:00.000Z"
}422 — the body doesn’t fit the widget; details lists each field and what’s wrong.403 missing or wrong token · 404 unknown board, key or route (always JSON) · 429 slow down. Every error message says what to do next.kpiA headline number with change vs. previous value and a trend sparkline.
{"value":48320}{"value":48320,"previous":45910}Data
Config
statsSeveral small numbers side by side, each with an optional change.
{"items":[{"label":"Visitors","value":12840,"change":8.1},{"label":"Signups","value":342}]}Data
Config
gaugeA dial for a value within a range — CPU, temperature, utilisation.
{"value":63}Data
Config
progressProgress toward a goal, as a bar or ring.
{"value":27}Data
Config
lineappendOne or more series over time. Push points and the chart scrolls live.
{"append":{"api":912,"web":455}}{"append":{"api":[1767225600000,912]}}Data
Config
barCompare values across categories. Vertical or horizontal, grouped or stacked.
{"categories":["Mon","Tue","Wed"],"series":[{"name":"Orders","values":[12,19,7]}]}Data
Config
pieShare of a whole across a few categories.
{"slices":[{"name":"Yes","value":62},{"name":"No","value":38}]}Data
Config
scatterappendCorrelation between two measures, with an optional linear trend line.
{"append":{"Campaigns":[820,74]}}Data
Config
radarCompare a few profiles across the same set of dimensions.
{"indicators":[{"name":"Tests","max":100},{"name":"Docs","max":100},{"name":"Perf","max":100},{"name":"Security","max":100},{"name":"UX","max":100}],"series":[{"name":"v2.0","values":[95,72,85,88,80]}]}Data
Config
treeHierarchies: org charts, file trees, decision trees.
{"root":{"name":"Root","children":[{"name":"A"},{"name":"B"}]}}Data
Config
tableappendRows of data with sorting and search. Append rows from scripts.
{"append":{"Order":"#1046","Customer":"Ken T.","Total":77,"Status":"Paid"}}{"rows":[{"Order":"#1047","Customer":"Ada L.","Total":54,"Status":"Paid"}]}Data
Config
logappendA scrolling feed of events — perfect for bots, jobs and deploys.
{"append":"Deploy finished in 42s"}{"append":{"text":"Disk at 91%","level":"warning"}}Data
Config
checklistappendinteractiveA shared to-do list. Viewers can tick items when allowed.
{"append":"Rotate API keys"}Data
Config
kanbanappendinteractiveCards in columns — drag them along as work moves. Scripts can add cards.
{"append":"Fix login redirect"}{"append":{"title":"Ship v2.1","column":"doing","tag":"release"}}Data
Config
timelineappendMilestones and events in order — releases, incidents, project history.
{"append":"Deployed v2.1"}{"append":{"title":"Incident resolved","date":"2026-10-09","tone":"success"}}Data
Config
peopleappendWho's on the team and who's around — on-call, shifts, presence.
{"append":{"name":"Maya Cohen","status":"away"}}Data
Config
statusappendHealth of several services at a glance. Update one service at a time.
{"append":{"name":"API","status":"down","detail":"502 from upstream"}}Data
Config
indicatorOne big status light: is the bot running? Is the door open?
{"status":"up","label":"Running"}{"status":"down","label":"Exchange API unreachable"}Data
Config
buttoninteractiveTrigger something: restart a bot, run a job, send a message.
{"presses":0}Data
Config
actionsinteractiveSeveral buttons in one panel — restart, deploy, pause, clear cache.
{"counts":{},"lastAction":null,"lastAt":null}Data
Config
toggleinteractiveAn on/off switch your script reads — feature flags, pause trading, lights.
{"on":true}Data
Config
sliderinteractivePick a number in a range — thresholds, volume, risk level.
{"value":40}Data
Config
inputinteractiveSend a line of text or a number to your script.
{"value":"hello"}Data
Config
selectinteractiveChoose one option from a list — mode, environment, strategy.
{"value":"Aggressive"}Data
Config
markdownHeadings, notes and links, written in Markdown.
{"text":"# Deploy status\nAll green ✅"}Data
Config
accordionappendQuestions and answers that expand on click — runbooks, FAQs, notes.
{"append":{"q":"Who owns this board?","a":"The platform team."}}Data
Config
imageShow an image or a camera snapshot that refreshes on a timer.
{"url":"https://example.com/cam.jpg"}Data
Config
embedEmbed a web page, video or form (YouTube, Google Docs, Loom…).
{"url":"https://example.com"}Data
Config
clockCurrent time in any time zone.
Data
No fields.
Config
countdownDays, hours and minutes until a launch, deadline or event.
{"target":"2026-12-31T23:59:00Z"}Data
Config