Works with Cursor, Claude Code, Codex, Windsurf, OpenClaw, and any agent supporting the Skills protocol.
npx skills add gui-now/skills --skill gui-now -gUse the first one that fits. They all hit the same endpoint.
Agents should prefer the CLI over hand-writing HTTP — embedding a full HTML document inside a JSON string by hand is where these calls usually go wrong. See llms.txt.
Zero dependencies, Node 18+. Pipe HTML in, get a URL back.
npm install -g gui-nowOr run it without installing:
npx gui-now push index.htmlgui push index.html # push a file
cat index.html | gui push # or pipe from stdin
gui push index.html --title "Dashboard" # name it
gui push --markdown README.md # render markdown
gui push index.html --open # open in a browser after
gui push index.html --json # full JSON response
gui push index.html --expires 7d # Pro: longer expiry
gui open abc123 # open an existing canvas| Flag | Short | Description |
|---|---|---|
| --title | -t | Canvas title |
| --expires | -e | Expiry: 30m, 24h, 7d (up to 365d) or never, capped by your plan |
| --markdown | -m | Treat input as markdown |
| --open | -o | Open the URL after creating |
| --json | -j | Output the full JSON response |
| --help | -h | Show help |
Set it in the environment to lift the create limit to 100 an hour and unlock longer expiry:
export GUI_NOW_API_KEY=YOUR_PRO_KEYPublished as gui-now on npm. Note the JS SDK is a different package, gui-now-sdk.
https://gui.now/apiCreate a new canvas. Returns a shareable URL. Accepts HTML, Markdown, or structured components.
| Field | Type | Required | Description |
|---|---|---|---|
| html | string | * | Raw HTML content |
| markdown | string | * | Markdown content (server-rendered with syntax highlighting) |
| title | string | No | Display name in toolbar, OG meta, social previews |
| theme | string | No | "dark" or "light", for markdown. Omit it and the page follows each viewer's setting |
| frames | array | No | Multi-tab views. Each: {html, label?} |
| expires | string | No | "30m", "24h", "7d" (up to "365d") or "never". Past your plan's max you get the max and "expires_clamped": true; any other value is a 400. |
| password | string | Pro | Password-protect the canvas |
* One of html or markdown is required. Use <gui-*> component tags directly in either format.
Pass as a header to unlock Pro limits:
x-api-key: YOUR_PRO_KEYHTML
curl -X POST https://gui.now/api/canvas \
-H 'Content-Type: application/json' \
-d '{
"title": "My Dashboard",
"html": "<h1 style=\"color:white\">Hello World</h1>"
}'Markdown
curl -X POST https://gui.now/api/canvas \
-H 'Content-Type: application/json' \
-d '{
"markdown": "# Sprint Report\n\nShipped **3 features** this week.",
"title": "Sprint Report"
}'HTML with components
curl -X POST https://gui.now/api/canvas \
-H 'Content-Type: application/json' \
--data-binary @- <<'JSON'
{
"html": "<gui-grid columns=\"2\"><gui-card title=\"Revenue\" value=\"$12,450\" change=\"+8.2%\"></gui-card><gui-chart type=\"bar\" data='[{\"label\":\"Q1\",\"value\":42}]'></gui-chart></gui-grid>",
"title": "Dashboard"
}
JSONMulti-frame (tabbed)
curl -X POST https://gui.now/api/canvas \
-H 'Content-Type: application/json' \
-d '{
"html": "<h1>Default view</h1>",
"frames": [
{"html": "<h1 style=\"color:red\">Option A</h1>", "label": "Red"},
{"html": "<h1 style=\"color:blue\">Option B</h1>", "label": "Blue"}
]
}'{
"id": "abc123xyz",
"url": "https://gui.now/abc123xyz",
"edit_token": "tok_...",
"expires_at": "2026-02-28T16:00:00Z",
"pro": false,
"format": "html"
}Create a Mermaid diagram. It's drawn as a figure with your title as the caption, zooms with Ctrl/⌘ + scroll, and has an Edit as Mermaid button that shows the source. Add %% note: … lines for numbered notes under the figure, and :::accent on a flowchart node to highlight it. Supports flowcharts, sequence, class, state, ER, gantt, pie, mindmap, timeline.
curl -X POST https://gui.now/api/flow \
-H 'Content-Type: application/json' \
-d '{
"mermaid": "graph TD\n %% note: Skipping is fine for drafts.\n A[Start] --> B{Decision}:::accent\n B -->|Yes| C[Do it]\n B -->|No| D[Skip]",
"title": "My Flow"
}'Update an existing canvas. Requires edit_token. All viewers see changes live via SSE. Free: 3 edits. Pro: unlimited. The response lists what changed, any ignored fields, and a content_hash that /embed/:id also sends as X-Content-Hash. A body with nothing to update is a 400.
| Header | Value |
|---|---|
| Authorization | Bearer <edit_token> |
| Field | Type | Description |
|---|---|---|
| html | string | New HTML content |
| markdown | string | New body as Markdown, rendered like create (use one of html, markdown or mermaid) |
| mermaid | string | New body as a Mermaid diagram |
| theme | string | "dark" or "light", for markdown and mermaid. Omit it and the page follows each viewer's setting |
| title | string | Updated title |
| frames | array | Updated tabs |
| password | string | null | Set or remove password (Pro). Omit to keep it; re-sending the current one keeps readers unlocked, a new one signs them out |
curl -X PUT https://gui.now/api/canvas/abc123xyz \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer tok_...' \
-d '{"html": "<h1>Updated!</h1>"}'Extend canvas expiry by 24 hours from now. Send the edit token with a body like {"expires": "7d"} to pick how long, up to your plan's max. Extending never shortens a page.
curl -X POST https://gui.now/api/canvas/abc123xyz/extendDelete a canvas and its saved input for good. Its URL returns 404 afterwards. Send the edit_token, or call it signed in as the page's owner (from the app, or with one of your gui_live_ keys in x-api-key). A wrong token gets the same 404 as a missing page. Expired pages can still be deleted.
curl -X DELETE https://gui.now/api/canvas/abc123xyz \
-H 'Authorization: Bearer tok_...'{ "id": "abc123xyz", "deleted": true }Server-Sent Events stream. Receive real-time updates when canvas content changes.
const source = new EventSource(
'https://gui.now/api/canvas/abc123xyz/events'
);
source.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.type === 'update') {
// data.html, data.title, data.frames
}
};Events: connected (on subscribe), update (content changed).
Auto-injected into every canvas. No imports needed — just use the HTML tags.
<gui-chart>— Charts (bar, line, pie)<gui-chart type="bar" data='[{"label":"Q1","value":42},{"label":"Q2","value":58}]'></gui-chart><gui-table>— Sortable tables<gui-table data='[{"name":"Alice","role":"Eng"},{"name":"Bob","role":"PM"}]'></gui-table><gui-card>— Stat cards<gui-card title="Users" value="12,847" change="+12.3%"></gui-card><gui-code>— Code with syntax highlighting<gui-code language="python">def hello(): print("world")</gui-code><gui-kanban>— Kanban boards<gui-kanban columns='[{"title":"Todo","items":["Task 1"]},{"title":"Done","items":["Task 2"]}]'></gui-kanban><gui-timeline>— Timelines<gui-timeline data='[{"date":"Mar 1","title":"Launch"}]'></gui-timeline><gui-form>— Forms (synced)<gui-form fields='[{"name":"email","type":"email","label":"Email"}]'></gui-form><gui-grid>— Grid layouts (1-4 cols)<gui-grid columns="3"><gui-card title="A" value="1"></gui-card><gui-card title="B" value="2"></gui-card><gui-card title="C" value="3"></gui-card></gui-grid><gui-progress>— Progress bars<gui-progress label="Launch readiness" value="14" max="22" note="On track for Nov 17"></gui-progress><gui-callout>— Callouts (info, success, warn, danger)<gui-callout tone="warn" title="3 tasks blocked">Legal review is due Thursday.</gui-callout><gui-poll>— Polls (one vote per browser, synced)<gui-poll name="dinner" label="Dinner plan, Friday?" options='["Tacos","Thai","Pizza"]'></gui-poll><gui-checklist>— Checklists (shared, or claimable: one person per item)<gui-checklist name="bbq" claimable items='["Burgers","Buns","Charcoal"]'></gui-checklist>Form inputs sync across all viewers automatically.
Every canvas automatically gets:
| Feature | Free | Pro |
|---|---|---|
| Auth | None, or an account key (gui_live_) to save pages to your library | Your Pro key in the x-api-key header |
| Rate limit | 5 creates / hour per IP, 20 signed in | 100 creates / hour |
| Max size | 2 MB | 4 MB |
| Expiry | 24 hours, up to 72 (signed in: 72 hours, up to 7 days) | Never by default, or any duration up to 365 days |
| Edits | 3 per canvas | Unlimited |
| Password | — | Yes |
| Components | 10 of 16 types | All 16 types |
| Page footer | “Made with GUI” under the page | None |
Free tier requires no account or API key. Without one, creates are limited per IP address; signed in (a session or a gui_live_ account key), they count against your account wherever you create from.
GUI runs an MCP server, so an agent can publish without installing anything. Its tools are create_canvas, create_markdown_canvas, create_multi_frame, create_diagram, update_canvas, extend_canvas and list_pages.
https://gui.now/mcpNo sign-in: add this URL as a custom connector in Claude or ChatGPT, or as a remote MCP server (Streamable HTTP) in any other client, and it publishes straight away. Each new page comes with a claim link to keep it in a GUI library. Creates are limited per ChatGPT user or MCP session, 10 an hour.
https://gui.now/mcp/accountWith your account (optional): add this URL instead and sign in to GUI when asked. Before you allow it, GUI shows the account, the app that is connecting and the address it sends you back to. Pages go to your library, count against your account's limits, and list_pages can read your library.
Headers instead of sign-in: clients that take headers can send Authorization: Bearer <api key> to either URL with a key from your account. Clients that support MCP authorization find the sign-in at /.well-known/oauth-protected-resource/mcp/account.
For building canvases from inside an application. To create one from a terminal, use the CLI.
JavaScript / TypeScript
npmjs.com →npm install gui-now-sdkimport { createGui } from 'gui-now-sdk'
const gui = createGui()
const canvas = await gui.create({ html: '<h1>Hello</h1>' })
console.log(canvas.url)
await gui.update(canvas.id, canvas.edit_token, { html: '<h1>v2</h1>' })Python
pypi.org →pip install gui-nowfrom guinow import GuiNowClient
client = GuiNowClient()
canvas = client.create(
html="<h1>Hello</h1>",
title="My Dashboard",
)
print(canvas.url)
client.update(canvas.id, canvas.edit_token, html="<h1>v2</h1>")Point your agent at these files to teach it GUI: