@limeade-labs/sparkui 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,428 @@
1
+ # API Reference
2
+
3
+ All API endpoints require authentication via a Bearer token in the `Authorization` header (except `GET /` health check and `GET /s/:id` page serving).
4
+
5
+ ```
6
+ Authorization: Bearer YOUR_PUSH_TOKEN
7
+ ```
8
+
9
+ Base URL: `http://localhost:3457` (or your configured `SPARKUI_BASE_URL`)
10
+
11
+ ---
12
+
13
+ ## POST /api/push
14
+
15
+ Create a new ephemeral page from a template, raw HTML, or both.
16
+
17
+ ### Request Body
18
+
19
+ ```json
20
+ {
21
+ "template": "macro-tracker",
22
+ "data": { ... },
23
+ "html": "<html>...</html>",
24
+ "ttl": 3600,
25
+ "meta": { "title": "My Page" },
26
+ "og": {
27
+ "title": "Custom OG Title",
28
+ "description": "Custom description",
29
+ "image": "https://example.com/image.png"
30
+ },
31
+ "callbackUrl": "https://your-server.com/webhook",
32
+ "callbackToken": "your-webhook-secret",
33
+ "openclaw": {
34
+ "enabled": true,
35
+ "channel": "slack",
36
+ "to": "C0AKMF5E0KD",
37
+ "eventTypes": ["completion"]
38
+ }
39
+ }
40
+ ```
41
+
42
+ | Field | Type | Required | Description |
43
+ |-------|------|----------|-------------|
44
+ | `template` | string | One of `template` or `html` | Template name (see [Templates](./templates.md)) |
45
+ | `data` | object | With `template` | Data to pass to the template renderer |
46
+ | `html` | string | One of `template` or `html` | Raw HTML content |
47
+ | `ttl` | number | No | Time-to-live in seconds (default: 3600) |
48
+ | `meta` | object | No | Arbitrary metadata stored with the page |
49
+ | `og` | object | No | Open Graph overrides: `title`, `description`, `image` |
50
+ | `callbackUrl` | string | No | URL to POST browser events to |
51
+ | `callbackToken` | string | No | Bearer token sent with callback requests |
52
+ | `openclaw` | object | No | OpenClaw webhook config (see below) |
53
+
54
+ ### OpenClaw Config
55
+
56
+ | Field | Type | Required | Description |
57
+ |-------|------|----------|-------------|
58
+ | `enabled` | boolean | Yes | Enable OpenClaw event forwarding |
59
+ | `channel` | string | No | Delivery channel (default: `"slack"`) |
60
+ | `to` | string | No | Target channel/user ID |
61
+ | `eventTypes` | string[] | No | Events to forward: `["completion"]`, `["event"]`, or both. Default: `["completion"]` |
62
+ | `sessionKey` | string | No | Session key override for routing |
63
+
64
+ ### Response — `201 Created`
65
+
66
+ ```json
67
+ {
68
+ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
69
+ "url": "/s/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
70
+ "fullUrl": "http://localhost:3457/s/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
71
+ }
72
+ ```
73
+
74
+ ### Examples
75
+
76
+ **Push from template:**
77
+
78
+ ```bash
79
+ curl -X POST http://localhost:3457/api/push \
80
+ -H "Authorization: Bearer $PUSH_TOKEN" \
81
+ -H "Content-Type: application/json" \
82
+ -d '{
83
+ "template": "macro-tracker",
84
+ "data": {
85
+ "date": "2026-03-14",
86
+ "calories": {"current": 1250, "target": 1900},
87
+ "protein": {"current": 62, "target": 86},
88
+ "fat": {"current": 45, "target": 95},
89
+ "carbs": {"current": 15, "target": 25},
90
+ "meals": [
91
+ {"name": "Eggs & bacon", "calories": 450, "time": "6:30 AM"},
92
+ {"name": "Grilled chicken salad", "calories": 480, "time": "12:00 PM"}
93
+ ]
94
+ },
95
+ "ttl": 7200
96
+ }'
97
+ ```
98
+
99
+ **Push raw HTML:**
100
+
101
+ ```bash
102
+ curl -X POST http://localhost:3457/api/push \
103
+ -H "Authorization: Bearer $PUSH_TOKEN" \
104
+ -H "Content-Type: application/json" \
105
+ -d '{
106
+ "html": "<!DOCTYPE html><html><body style=\"background:#111;color:#eee;font-family:sans-serif;padding:40px\"><h1>Hello World</h1></body></html>",
107
+ "ttl": 1800
108
+ }'
109
+ ```
110
+
111
+ ---
112
+
113
+ ## POST /api/compose
114
+
115
+ Create a page by composing individual [components](./components.md). This is the recommended approach for custom layouts.
116
+
117
+ ### Request Body
118
+
119
+ ```json
120
+ {
121
+ "title": "Page Title",
122
+ "sections": [
123
+ { "type": "header", "config": { "title": "Hello", "subtitle": "World", "icon": "⚡" } },
124
+ { "type": "stats", "config": { "items": [{"label": "Views", "value": "42", "icon": "👁️"}] } },
125
+ { "type": "button", "config": { "label": "Done", "action": "complete", "style": "primary" } }
126
+ ],
127
+ "ttl": 3600,
128
+ "openclaw": { "enabled": true, "channel": "slack", "to": "C0AKMF5E0KD" }
129
+ }
130
+ ```
131
+
132
+ | Field | Type | Required | Description |
133
+ |-------|------|----------|-------------|
134
+ | `title` | string | No | Page title (default: `"Composed"`) |
135
+ | `sections` | array | Yes | Array of `{ type, config }` component objects |
136
+ | `ttl` | number | No | Time-to-live in seconds |
137
+ | `openclaw` | object | No | OpenClaw webhook config |
138
+
139
+ ### Response — `201 Created`
140
+
141
+ ```json
142
+ {
143
+ "id": "def-456-...",
144
+ "url": "/s/def-456-...",
145
+ "fullUrl": "http://localhost:3457/s/def-456-..."
146
+ }
147
+ ```
148
+
149
+ > **Tip:** The compose API is the fastest path to a custom page. Mix and match components — no HTML required.
150
+
151
+ ---
152
+
153
+ ## GET /api/pages
154
+
155
+ List pages with optional filters.
156
+
157
+ ### Query Parameters
158
+
159
+ | Parameter | Type | Default | Description |
160
+ |-----------|------|---------|-------------|
161
+ | `status` | string | `"active"` | Filter by status: `"active"` or `"all"` |
162
+ | `template` | string | — | Filter by template name |
163
+
164
+ ### Response — `200 OK`
165
+
166
+ ```json
167
+ {
168
+ "pages": [
169
+ {
170
+ "id": "a1b2c3d4-...",
171
+ "createdAt": "2026-03-14T18:00:00.000Z",
172
+ "expiresAt": "2026-03-14T19:00:00.000Z",
173
+ "views": 3,
174
+ "meta": { "title": "My Dashboard", "template": "macro-tracker" }
175
+ }
176
+ ],
177
+ "total": 1
178
+ }
179
+ ```
180
+
181
+ ### Example
182
+
183
+ ```bash
184
+ # List all active pages
185
+ curl http://localhost:3457/api/pages \
186
+ -H "Authorization: Bearer $PUSH_TOKEN"
187
+
188
+ # Filter by template
189
+ curl "http://localhost:3457/api/pages?template=macro-tracker" \
190
+ -H "Authorization: Bearer $PUSH_TOKEN"
191
+ ```
192
+
193
+ ---
194
+
195
+ ## GET /api/pages/:id
196
+
197
+ Get details for a specific page.
198
+
199
+ ### Response — `200 OK`
200
+
201
+ ```json
202
+ {
203
+ "id": "a1b2c3d4-...",
204
+ "createdAt": "2026-03-14T18:00:00.000Z",
205
+ "expiresAt": "2026-03-14T19:00:00.000Z",
206
+ "views": 5,
207
+ "meta": {
208
+ "title": "Macro Tracker",
209
+ "template": "macro-tracker",
210
+ "data": { ... }
211
+ }
212
+ }
213
+ ```
214
+
215
+ ### Errors
216
+
217
+ - `404` — Page not found
218
+
219
+ ---
220
+
221
+ ## PATCH /api/pages/:id
222
+
223
+ Update an existing page's content, data, or TTL.
224
+
225
+ ### Request Body
226
+
227
+ ```json
228
+ {
229
+ "template": "macro-tracker",
230
+ "data": { ... },
231
+ "html": "<html>...</html>",
232
+ "ttl": 7200
233
+ }
234
+ ```
235
+
236
+ | Field | Type | Description |
237
+ |-------|------|-------------|
238
+ | `template` | string | Re-render with a different or same template |
239
+ | `data` | object | New data for template rendering. If no `template` given, re-renders the page's existing template |
240
+ | `html` | string | Replace HTML directly |
241
+ | `ttl` | number | Extend the page's time-to-live |
242
+
243
+ > **Tip:** To update a template page with new data (e.g., updated macro totals), just send `data` — it will re-render using the page's original template.
244
+
245
+ ### Response — `200 OK`
246
+
247
+ Returns updated page details (same format as `GET /api/pages/:id`).
248
+
249
+ ### Errors
250
+
251
+ - `404` — Page not found
252
+ - `410` — Page expired
253
+
254
+ ### Example
255
+
256
+ ```bash
257
+ # Update macro data
258
+ curl -X PATCH http://localhost:3457/api/pages/a1b2c3d4-... \
259
+ -H "Authorization: Bearer $PUSH_TOKEN" \
260
+ -H "Content-Type: application/json" \
261
+ -d '{
262
+ "data": {
263
+ "date": "2026-03-14",
264
+ "calories": {"current": 1700, "target": 1900},
265
+ "protein": {"current": 80, "target": 86},
266
+ "fat": {"current": 85, "target": 95},
267
+ "carbs": {"current": 20, "target": 25}
268
+ }
269
+ }'
270
+ ```
271
+
272
+ Connected WebSocket clients receive an `update` message and refresh automatically.
273
+
274
+ ---
275
+
276
+ ## DELETE /api/pages/:id
277
+
278
+ Delete a page immediately.
279
+
280
+ ### Response — `200 OK`
281
+
282
+ ```json
283
+ {
284
+ "id": "a1b2c3d4-...",
285
+ "deleted": true
286
+ }
287
+ ```
288
+
289
+ Connected WebSocket clients receive a `destroy` message.
290
+
291
+ ### Errors
292
+
293
+ - `404` — Page not found
294
+
295
+ ---
296
+
297
+ ## GET /s/:id
298
+
299
+ Serve a page's HTML to the browser. **No authentication required.**
300
+
301
+ - `200` — Returns the rendered HTML page
302
+ - `410` — Page has expired or been removed (shows a styled "Gone" page)
303
+
304
+ ---
305
+
306
+ ## GET /
307
+
308
+ Health check endpoint. **No authentication required.**
309
+
310
+ ```json
311
+ {
312
+ "status": "ok",
313
+ "service": "sparkui",
314
+ "version": "1.1.0",
315
+ "pages": 3,
316
+ "wsClients": 1,
317
+ "templates": ["macro-tracker", "ws-test", "feedback-form", "checkout", "workout-timer"],
318
+ "uptime": 3600
319
+ }
320
+ ```
321
+
322
+ ---
323
+
324
+ ## GET /og/:id.svg
325
+
326
+ Dynamic Open Graph image for social previews. Returns an SVG with the page title and template name. Cached for 1 hour.
327
+
328
+ ---
329
+
330
+ ## WebSocket Protocol
331
+
332
+ Connect to `ws://localhost:3457/ws?page=PAGE_ID` to receive real-time updates.
333
+
334
+ ### Client → Server Messages
335
+
336
+ ```json
337
+ {"type": "heartbeat"}
338
+ ```
339
+
340
+ Keeps the connection alive. Server responds with `{"type": "pong"}`.
341
+
342
+ ```json
343
+ {"type": "event", "pageId": "abc-123", "data": {"action": "button_click"}}
344
+ ```
345
+
346
+ General UI events (button clicks, checklist toggles, etc.).
347
+
348
+ ```json
349
+ {"type": "completion", "pageId": "abc-123", "data": {"formData": {"name": "John", "rating": 5}}}
350
+ ```
351
+
352
+ Completion events (form submissions, checklist completion, timer done).
353
+
354
+ ### Server → Client Messages
355
+
356
+ ```json
357
+ {"type": "update", "pageId": "abc-123"}
358
+ ```
359
+
360
+ Page content was updated via `PATCH`. Client should reload.
361
+
362
+ ```json
363
+ {"type": "destroy", "pageId": "abc-123"}
364
+ ```
365
+
366
+ Page was deleted. Client should show an expiration message.
367
+
368
+ ```json
369
+ {"type": "pong"}
370
+ ```
371
+
372
+ Response to heartbeat.
373
+
374
+ ### Event Forwarding
375
+
376
+ Events received via WebSocket are forwarded to:
377
+
378
+ 1. **Callback URL** — If `callbackUrl` was set during push, events are POSTed there
379
+ 2. **OpenClaw** — If `openclaw.enabled` is true, matching events (per `eventTypes`) are forwarded to the OpenClaw hooks endpoint
380
+
381
+ Callback payload:
382
+
383
+ ```json
384
+ {
385
+ "type": "completion",
386
+ "pageId": "abc-123",
387
+ "data": { "formData": { ... } },
388
+ "timestamp": 1710450000000
389
+ }
390
+ ```
391
+
392
+ ### Connection Health
393
+
394
+ - Server pings all clients every 30 seconds
395
+ - Stale connections (no pong response) are terminated after 60 seconds
396
+ - Browser client should implement reconnection logic (the built-in templates do this automatically)
397
+
398
+ ---
399
+
400
+ ## Error Responses
401
+
402
+ All error responses follow this format:
403
+
404
+ ```json
405
+ {
406
+ "error": "Description of the error"
407
+ }
408
+ ```
409
+
410
+ | Status | Meaning |
411
+ |--------|---------|
412
+ | `400` | Bad request — missing or invalid fields |
413
+ | `401` | Unauthorized — missing or invalid Bearer token |
414
+ | `404` | Not found — page doesn't exist |
415
+ | `410` | Gone — page has expired |
416
+ | `500` | Server error |
417
+
418
+ ---
419
+
420
+ ## Authentication
421
+
422
+ All `/api/*` endpoints (except the health check) require a Bearer token:
423
+
424
+ ```
425
+ Authorization: Bearer spk_your_token_here
426
+ ```
427
+
428
+ The token is configured via the `PUSH_TOKEN` environment variable or `.env` file. If no token is set, SparkUI generates one on first start and appends it to `.env`.
@@ -0,0 +1,206 @@
1
+ # ChatGPT Setup
2
+
3
+ Use SparkUI with ChatGPT Custom GPTs via the Actions feature. This lets a GPT create ephemeral web pages during conversations and share links with users.
4
+
5
+ ## Overview
6
+
7
+ ChatGPT Custom GPTs support "Actions" — HTTP API calls the GPT can make during a conversation. By configuring SparkUI's push API as an action, your GPT can generate interactive pages (dashboards, forms, timers) and share the URL.
8
+
9
+ ## Prerequisites
10
+
11
+ 1. SparkUI server running and **publicly accessible** (not just localhost)
12
+ 2. A ChatGPT Plus account (Actions require a paid plan)
13
+ 3. Your push token
14
+
15
+ > **Important:** Your SparkUI server must be reachable from the internet. Use a reverse proxy, cloud deployment, or tunnel (e.g., Cloudflare Tunnel, ngrok) and set `SPARKUI_BASE_URL` accordingly.
16
+
17
+ ## OpenAPI Spec
18
+
19
+ Create this OpenAPI specification for the GPT builder. Replace `https://your-sparkui-server.com` with your actual public URL.
20
+
21
+ ```yaml
22
+ openapi: 3.1.0
23
+ info:
24
+ title: SparkUI
25
+ description: Create ephemeral interactive web pages on demand
26
+ version: 1.1.0
27
+ servers:
28
+ - url: https://your-sparkui-server.com
29
+ paths:
30
+ /api/push:
31
+ post:
32
+ operationId: pushPage
33
+ summary: Create a new ephemeral page from a template or raw HTML
34
+ requestBody:
35
+ required: true
36
+ content:
37
+ application/json:
38
+ schema:
39
+ type: object
40
+ properties:
41
+ template:
42
+ type: string
43
+ description: "Template name: macro-tracker, checkout, workout-timer, feedback-form, ws-test"
44
+ data:
45
+ type: object
46
+ description: Template data (varies by template)
47
+ html:
48
+ type: string
49
+ description: Raw HTML content (alternative to template)
50
+ ttl:
51
+ type: integer
52
+ description: Time-to-live in seconds (default 3600)
53
+ default: 3600
54
+ anyOf:
55
+ - required: [template, data]
56
+ - required: [html]
57
+ responses:
58
+ "201":
59
+ description: Page created successfully
60
+ content:
61
+ application/json:
62
+ schema:
63
+ type: object
64
+ properties:
65
+ id:
66
+ type: string
67
+ url:
68
+ type: string
69
+ fullUrl:
70
+ type: string
71
+ /api/compose:
72
+ post:
73
+ operationId: composePage
74
+ summary: Compose a page from individual components
75
+ requestBody:
76
+ required: true
77
+ content:
78
+ application/json:
79
+ schema:
80
+ type: object
81
+ required: [sections]
82
+ properties:
83
+ title:
84
+ type: string
85
+ default: "SparkUI"
86
+ sections:
87
+ type: array
88
+ items:
89
+ type: object
90
+ required: [type]
91
+ properties:
92
+ type:
93
+ type: string
94
+ description: "Component: header, button, timer, checklist, progress, stats, form, tabs"
95
+ config:
96
+ type: object
97
+ ttl:
98
+ type: integer
99
+ default: 3600
100
+ responses:
101
+ "201":
102
+ description: Page created
103
+ content:
104
+ application/json:
105
+ schema:
106
+ type: object
107
+ properties:
108
+ id:
109
+ type: string
110
+ url:
111
+ type: string
112
+ fullUrl:
113
+ type: string
114
+ ```
115
+
116
+ ## Setting Up the Action in GPT Builder
117
+
118
+ 1. Go to [ChatGPT GPT Builder](https://chatgpt.com/gpts/editor)
119
+ 2. Create a new GPT or edit an existing one
120
+ 3. Click **Configure** → scroll to **Actions** → **Create new action**
121
+ 4. Paste the OpenAPI spec above into the schema editor
122
+ 5. Under **Authentication**:
123
+ - Select **API Key**
124
+ - Auth Type: **Bearer**
125
+ - Paste your `PUSH_TOKEN` as the API key
126
+ 6. Click **Save**
127
+
128
+ ### GPT Instructions
129
+
130
+ Add these instructions to your GPT's system prompt:
131
+
132
+ ```
133
+ You have access to SparkUI, which creates ephemeral interactive web pages.
134
+
135
+ When the user needs something visual (dashboards, trackers, forms, timers):
136
+ 1. Use the pushPage action with an appropriate template and data
137
+ 2. Share the fullUrl with the user
138
+
139
+ Available templates:
140
+ - macro-tracker: Nutrition tracking (data: date, calories, protein, fat, carbs, meals)
141
+ - checkout: Product checkout demo (data: product with name/description/price)
142
+ - workout-timer: Exercise routine (data: title, exercises, rounds, restSeconds)
143
+ - feedback-form: Feedback collection (data: title, subtitle, questions)
144
+
145
+ For custom layouts, use composePage with sections array. Components:
146
+ header, button, timer, checklist, progress, stats, form, tabs
147
+
148
+ Pages expire after TTL seconds. Always share the fullUrl link.
149
+ ```
150
+
151
+ ## Authentication
152
+
153
+ SparkUI uses Bearer token authentication. In the GPT builder:
154
+
155
+ - **Auth type:** API Key
156
+ - **Scheme:** Bearer
157
+ - **Key:** Your `PUSH_TOKEN` value (e.g., `spk_abc123...`)
158
+
159
+ The GPT sends this automatically with every action call as:
160
+
161
+ ```
162
+ Authorization: Bearer spk_abc123...
163
+ ```
164
+
165
+ ## Example Prompts and Expected Behavior
166
+
167
+ ### Nutrition Dashboard
168
+
169
+ **User:** "Show me a nutrition dashboard for today. I've had 1,200 calories, 55g protein, 40g fat, 20g carbs out of targets 1,900/86/95/25."
170
+
171
+ **GPT calls:** `pushPage` with `macro-tracker` template
172
+
173
+ **GPT responds:** "Here's your nutrition dashboard for today: [link]. You're at 63% of your calorie target with solid macro distribution."
174
+
175
+ ### Feedback Form
176
+
177
+ **User:** "Create a feedback form for our new feature launch"
178
+
179
+ **GPT calls:** `pushPage` with `feedback-form` template
180
+
181
+ **GPT responds:** "Here's your feedback form: [link]. Share this with your team to collect ratings and comments."
182
+
183
+ ### Custom Dashboard
184
+
185
+ **User:** "Make a project status dashboard showing 42 tasks done, 8 in progress, 3 blocked"
186
+
187
+ **GPT calls:** `composePage` with header + stats + progress components
188
+
189
+ **GPT responds:** "Here's your project dashboard: [link]. It shows your current task breakdown with trend indicators."
190
+
191
+ ### Workout Timer
192
+
193
+ **User:** "Create a workout: 3 rounds of push-ups (15), squats (20), and planks (30 sec) with 60-second rest"
194
+
195
+ **GPT calls:** `pushPage` with `workout-timer` template
196
+
197
+ **GPT responds:** "Your workout is ready: [link]. Open it on your phone and hit Start when you're ready."
198
+
199
+ ## Limitations
200
+
201
+ - **Public access required** — SparkUI must be internet-accessible (not localhost)
202
+ - **No WebSocket** — ChatGPT can create pages but can't receive real-time events back. Use the `callbackUrl` parameter if you need webhook notifications.
203
+ - **Rate limits** — ChatGPT may throttle action calls. Keep page creation to a reasonable frequency.
204
+ - **TTL** — Pages are ephemeral. Set longer TTL values if users need extended access.
205
+
206
+ > **Tip:** For bidirectional communication (agent receives user actions), consider using [OpenClaw](./openclaw-setup.md) or [MCP](./mcp-setup.md) integrations instead.