Skip to content

Interactive Artifacts — Frontend API Contract

Feature: FEAT-EZ10MV · Interactive HTML Handler (extended from Infographic) Status: Merged to dev (PR #962) Audience: Frontend engineers building interactive artifact support in the navigator frontend.

This document is the authoritative contract for requesting and rendering Interactive Artifacts — self-contained HTML pages (dashboards, wizards, data grids, diagrams, reports) generated by the LLM using a curated catalog of vetted JavaScript libraries. It covers the conversation flow, tool payload schemas, artifact retrieval endpoints, content negotiation, download support, and the security model.


1. Interactive Artifacts vs Infographics

Dimension Infographic Interactive Artifact
Output model Block-based JSON (blocks[]) + rendered HTML Free-form self-contained HTML page
LLM task Produce structured data blocks Fill <!-- SLOT:* --> markers in an HTML skeleton
JavaScript None (pure HTML/CSS) Curated vetted libraries (ECharts, Mermaid, Grid.js, Stepper)
Dedicated generation endpoint POST /api/v1/agents/infographic/{agent_id} None — uses standard agent chat
Artifact type infographic interactive
Frontend rendering Component-per-block OR iframe Always iframe / <div> container OR signed public URL
Download support ✓ (?download=1)

Interactive Artifacts are the right choice when the user needs an explorable, clickable page: filterable grids, multi-step wizards, Mermaid diagrams, ECharts dashboards. Infographics are better for static, print-quality summaries.


2. How Generation Works — Conversation Flow

Interactive Artifacts are generated via the standard agent chat endpoint. The frontend sends a natural-language request; the agent calls the interactive_render tool internally and returns an InteractiveRenderResult as its final output.

Frontend                  ai-parrot Agent
  │                            │
  │── POST /api/v1/agents/chat/{agent_id}  ──▶ │
  │   { "message": "Build a Q4 sales dashboard" }│
  │                            │
  │                            │── [calls tool: interactive_list_templates]
  │                            │── [calls tool: interactive_render(template="dashboard", ...)]
  │                            │── [LLM fills scaffold slots with ECharts code]
  │                            │── [stores artifact, signs URL]
  │                            │
  │◀── 200 { "type": "interactive", ──────────── │
  │         "artifact_id": "...",
  │         "html_url": "/api/v1/artifacts/public/...",
  │         "html_inline": "<!DOCTYPE html>...",
  │         "template_name": "dashboard",
  │         "theme": "dark",
  │         "libraries_used": ["echarts"],
  │         "enhanced": true }
  │── GET {html_url}?download=1  ──▶  (optional: save as file)

The agent detects from context which scaffold template fits the request and invokes the tool autonomously. The frontend does not need to call template/library endpoints directly — they exist only for UI pickers if needed.


3. Base URL & Auth

  • Chat endpoint: /api/v1/agents/chat/{agent_id} (standard agent talk; see AgentTalk handler)
  • Artifact retrieval (authenticated): /api/v1/threads/{session_id}/artifacts/{artifact_id}
  • Artifact retrieval (public signed URL): /api/v1/artifacts/public/{signature}/{artifact_id}.html
  • Auth: Chat and authenticated artifact endpoints require Authorization: Bearer <token> + agent:chat PBAC permission. The public signed URL endpoint is unauthenticated (HMAC-verified).
  • Content-Type: application/json on all request bodies.

4. Chat Endpoint — Triggering Generation

Use the standard agent chat endpoint with a message that describes the desired artifact. The agent's system prompt includes the interactive catalog index; it will call interactive_render automatically.

Request

POST /api/v1/agents/chat/{agent_id}
Authorization: Bearer <token>
Content-Type: application/json
{
  "message": "Build a Q4 2025 sales dashboard showing revenue, unit sales, and monthly trend",
  "session_id": "sess_abc123",        // optional but recommended for continuity
  "user_id": "user_42",               // optional
  "use_conversation_history": true,   // optional, default false
  "use_vector_context": false         // optional, default true

  // Any additional kwargs are forwarded to the bot; see AgentTalk for the full schema.
}

To hint the agent toward a specific template, theme, or library, include that preference in the message:

{
  "message": "Create a multi-step onboarding wizard in dark theme for new customers"
}
{
  "message": "Show the org chart as a Mermaid diagram"
}

Response — Interactive Artifact Result

When the agent calls interactive_render, its response is an InteractiveRenderResult serialised as the agent's final output.

200 OK · Content-Type: application/json

{
  "type": "interactive",                        // discriminates from other agent responses
  "artifact_id": "interactive-a1b2c3d4e5f6",
  "html_url": "/api/v1/artifacts/public/1750000000.AbCdEfGhIj/interactive-a1b2c3d4e5f6.html",
  "html_inline": "<!DOCTYPE html>...",          // full HTML if payload < 50 KB, else null
  "template_name": "dashboard",
  "theme": "dark",
  "libraries_used": ["echarts"],
  "enhanced": true                              // false = LLM enhance failed, deterministic skeleton used
}

Field reference:

Field Type Always present Description
type "interactive" Response discriminator
artifact_id string Stable artifact ID; use to construct fetch/download URLs
html_url string HMAC-signed public URL valid for ~1 hour (see section 7)
html_inline string \| null Full HTML document when artifact < 50 KB; otherwise null
template_name string Scaffold template used (dashboard, wizard, diagram, grid, report)
theme string \| null Theme applied (light or dark)
libraries_used string[] JS libraries included in the page
enhanced boolean true = LLM authored content; false = empty skeleton (fallback)

enhanced: false is not an error — it means the LLM pass failed validation and the artifact contains a valid but empty skeleton. Show the artifact normally; the user can ask again with more context.


5. Artifact Retrieval

5.1 Authenticated Detail Fetch

GET /api/v1/threads/{session_id}/artifacts/{artifact_id}
Authorization: Bearer <token>

Query params:

Param Type Default Description
format html | json Overrides Accept header
download 1 | true | yes Adds Content-Disposition: attachment

Content-Type negotiation (same as infographic handler):

  1. Query param ?format=html / ?format=json
  2. Accept header (text/html → raw HTML; application/json → JSON envelope)
  3. Default: application/json

JSON response (200 OK · application/json):

{
  "artifact_id": "interactive-a1b2c3d4e5f6",
  "artifact_type": "interactive",
  "session_id": "sess_abc123",
  "created_at": "2025-10-01T12:00:00Z",
  "definition": {
    "html": "<!DOCTYPE html>...",
    "js_bundles": [
      {
        "name": "echarts",
        "scope": "cdn",
        "url": "https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js",
        "sri_hash": "sha384-<verified-hash>",
        "css_url": null,
        "css_sri_hash": null
      }
    ],
    "template": "dashboard",
    "libraries": ["echarts"],
    "theme": "dark"
  },
  "html_url": "/api/v1/artifacts/public/1750000000.AbCdEfGhIj/interactive-a1b2c3d4e5f6.html"
}

HTML response (200 OK · text/html; charset=utf-8):

Raw self-contained HTML with Content-Security-Policy header enforcing SRI allow-list. Safe to render in a sandboxed <iframe>.

Download (?download=1):

Same HTML response with added header:

Content-Disposition: attachment; filename="interactive-a1b2c3d4e5f6.html"

5.2 Public Signed URL

The html_url in every InteractiveRenderResult is a pre-signed URL valid for approximately one hour. No authentication required.

URL structure:

GET /api/v1/artifacts/public/{expiry}.{hmac_base64url}/{artifact_id}.html

  • expiry — Unix timestamp (seconds) after which the URL is rejected
  • hmac_base64urlHMAC-SHA256(secret, "{artifact_id}|{expiry}") encoded as URL-safe Base64
  • artifact_id_html{artifact_id}.html

Response: 200 OK · text/html; charset=utf-8 with full CSP + SRI headers.

Error cases:

Status When
400 Malformed signature (cannot split on .)
403 Signature expired or HMAC mismatch
404 Artifact not found

Recommended usage: embed as <iframe src="{html_url}" sandbox="allow-scripts"> for zero-auth public sharing; the URL can be passed to third-party consumers safely (it is time-limited and content-addressed).


6. Discovery Endpoints (Optional — for UI Pickers)

These endpoints allow the frontend to build template/library pickers. They are not required for basic generation flow.

6.1 GET /api/v1/agents/interactive/templates — List Templates

Note: Endpoint path is indicative; confirm with the server router if a dedicated InteractiveTalk handler is registered. Alternatively, prompt the agent with "list available interactive templates" and it will call interactive_list_templates internally.

Response:

{
  "templates": [
    {
      "name": "dashboard",
      "description": "KPI cards + responsive grid of ECharts visualizations",
      "slots": ["title", "subtitle", "kpis", "charts", "footer"],
      "allowed_libraries": ["echarts"],
      "default_theme": "light"
    },
    {
      "name": "wizard",
      "description": "Multi-step onboarding with stepper controls",
      "slots": ["title", "subtitle", "steps"],
      "allowed_libraries": ["stepper"],
      "default_theme": "light"
    },
    {
      "name": "diagram",
      "description": "Single diagram + notes section",
      "slots": ["title", "subtitle", "diagram", "notes"],
      "allowed_libraries": ["mermaid"],
      "default_theme": "light"
    },
    {
      "name": "grid",
      "description": "Sortable and searchable data grid",
      "slots": ["title", "grid"],
      "allowed_libraries": ["gridjs"],
      "default_theme": "light"
    },
    {
      "name": "report",
      "description": "Prose + embedded ECharts visualizations",
      "slots": ["title", "subtitle", "content", "footer"],
      "allowed_libraries": ["echarts"],
      "default_theme": "light"
    }
  ]
}

6.2 GET /api/v1/agents/interactive/libraries — List Libraries

{
  "libraries": [
    { "name": "echarts",  "description": "Apache ECharts 5 — bar/line/pie/scatter/radar/gauge charts", "category": "chart",   "scope": "cdn" },
    { "name": "mermaid",  "description": "Mermaid.js — flowcharts, sequence, class, ER diagrams",      "category": "diagram", "scope": "cdn" },
    { "name": "gridjs",   "description": "Grid.js — sortable, searchable, paginated HTML table",        "category": "grid",    "scope": "cdn" },
    { "name": "stepper",  "description": "Lightweight inline multi-step wizard (no external dep)",      "category": "wizard",  "scope": "inline" }
  ]
}

Library categories:

Category Use case
chart Quantitative data visualizations (bar, line, pie, scatter…)
diagram Structural / relational diagrams (flowchart, sequence, ER…)
grid Tabular data with sort / search / pagination
wizard Multi-step forms / onboarding flows

7. Tool Payloads (for custom agent integrations)

If you are building a custom agent that calls the toolkit directly (not through natural language chat), use these tool call schemas.

7.1 interactive_list_templates

Input: none

Output: array of template descriptors (see section 6.1 shape).

7.2 interactive_list_libraries

Input: none

Output: array of library descriptors (see section 6.2 shape).

7.3 interactive_get_scaffold

Input:

{
  "template_name": "dashboard"
}

Output: Full scaffold detail — HTML skeleton with <!-- SLOT:* --> markers visible, allowed libraries with usage snippets and TypeScript type references.

Errors:

Code Condition
TEMPLATE_UNKNOWN template_name not in catalog

7.4 interactive_render — Primary Tool

Input:

{
  "template_name": "dashboard",     // required — one of: dashboard, wizard, diagram, grid, report
  "brief": "Q4 sales metrics with monthly revenue trend, top-5 products by volume, and YoY comparison",
                                    // required in enhance mode; describes what each slot should contain
  "libraries": ["echarts"],         // optional; defaults to template's allowed libraries
  "mode": "enhance",                // "enhance" (LLM authors) | "deterministic" (empty skeleton)
  "theme": "dark",                  // "light" | "dark" — defaults to template's default_theme
  "title": "Q4 2025 Dashboard",     // optional; sets <title> and document heading
  "data_context": {                 // optional; source-of-truth data injected verbatim into LLM prompt
    "q4_revenue": 2500000,
    "products": [
      { "name": "Product A", "units": 12000 },
      { "name": "Product B", "units": 9500 }
    ],
    "monthly": [780000, 920000, 800000]
  }
}

Field rules:

Field Required Constraints
template_name Must exist in catalog
brief ✓ in enhance mode Non-empty; describes content per slot
libraries Each name must exist AND be in the template's allowed_libraries
mode "enhance" (default) or "deterministic"
theme "light" or "dark"
title Becomes <title> tag and top heading
data_context Any JSON object; injected verbatim into the LLM enhance prompt

Output: InteractiveRenderResult (see section 4 — Response field reference).

Validation errors:

Code Condition
TEMPLATE_UNKNOWN template_name not in catalog
LIBRARY_UNKNOWN A library in libraries[] is not in the catalog
LIBRARY_NOT_ALLOWED A library in libraries[] is not in the template's allowed_libraries
ENHANCE_BRIEF_MISSING mode="enhance" but brief is empty or missing
ENHANCE_OUTPUT_INVALID LLM-produced HTML references a non-whitelisted external script/stylesheet (security fallback to skeleton)

8. Built-in Scaffold Catalog

8.1 Templates

Name Slots Allowed Libraries Default Theme Best for
dashboard title, subtitle, kpis, charts, footer echarts light KPI overviews, multi-chart pages
wizard title, subtitle, steps stepper light Multi-step forms, onboarding
diagram title, subtitle, diagram, notes mermaid light Flowcharts, sequence, ER diagrams
grid title, grid gridjs light Sortable data tables
report title, subtitle, content, footer echarts light Long-form prose with charts

8.2 Libraries

Name CDN / Inline Version Category Notes
echarts CDN (jsdelivr) 5.4.3 chart SRI-verified hash; bar/line/pie/scatter/radar/gauge
mermaid CDN (jsdelivr) 10.x diagram SRI placeholder — verify before production
gridjs CDN + stylesheet 6.x grid Requires companion CSS; SRI placeholder
stepper Inline wizard Zero external dep; bundled directly in HTML

8.3 Themes

Name --ip-bg --ip-text --ip-primary Use case
light #ffffff #0f172a #6366f1 (indigo) Default; light backgrounds
dark #0f172a #f1f5f9 #818cf8 (light indigo) Dark dashboards

Both themes are implemented as CSS custom properties injected into <head>. The frontend can further override them by setting variables on the iframe container if embedding via srcdoc.


9. Security Model

All interactive artifacts are hardened against XSS and supply-chain attacks at two layers.

9.1 Subresource Integrity (SRI)

Every CDN library in the catalog carries a sri_hash field. The artifact HTML emits:

<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"
        integrity="sha384-<hash>"
        crossorigin="anonymous"></script>

The browser rejects the script if the hash does not match. A library with a placeholder hash (sha384-REGENERATEME) is flagged at startup with a WARNING log and must be updated before production use.

9.2 Content Security Policy

The public artifact endpoint (/api/v1/artifacts/public/…) sets a strict Content-Security-Policy header derived from the artifact's js_bundles list:

Content-Security-Policy:
  default-src 'none';
  script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/echarts@5.4.3/ ;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  font-src 'self';
  connect-src 'none';
  frame-ancestors 'self'

connect-src 'none' prevents the LLM-generated JavaScript from making outbound network calls. frame-ancestors 'self' allows embedding only from the same origin.

9.3 HTML Validation Gate

Before an LLM-enhanced artifact is persisted, a validator parses its <script src> and <link href> tags and rejects any URL not present in the SRI allow-list. If validation fails:

  1. The toolkit logs a WARNING security event.
  2. The empty deterministic skeleton is stored instead.
  3. The response carries "enhanced": false.

Inline <script> and <style> blocks are always permitted (no hash required for inline).

9.4 Signed URLs

Public URLs use HMAC-SHA256(secret, "{artifact_id}|{expiry}"). The server validates the signature and rejects expired or tampered URLs with 403 Forbidden. URLs are valid for approximately one hour; use the authenticated endpoint for long-lived links.


10. Frontend Integration Flow

Minimal — Iframe Embed

The simplest integration: send a chat message and embed the signed URL.

// 1. Generate
const res = await fetch(`/api/v1/agents/chat/${agentId}`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ message: userQuery, session_id: sessionId }),
});
const data = await res.json();

// 2. If it's an interactive artifact, embed it
if (data.type === 'interactive') {
  const iframe = document.createElement('iframe');
  iframe.src = data.html_url;
  iframe.sandbox = 'allow-scripts';  // needed for ECharts/Mermaid; do NOT add allow-same-origin
  iframe.style.width = '100%';
  iframe.style.height = '600px';
  iframe.style.border = 'none';
  container.appendChild(iframe);
}

Using html_inline (when available)

For artifacts under 50 KB, skip the second HTTP request:

if (data.type === 'interactive' && data.html_inline) {
  const iframe = document.createElement('iframe');
  iframe.srcdoc = data.html_inline;
  iframe.sandbox = 'allow-scripts';
  container.appendChild(iframe);
} else if (data.type === 'interactive') {
  iframe.src = data.html_url;  // fallback: fetch from signed URL
}

Download Button

const downloadUrl = `/api/v1/threads/${sessionId}/artifacts/${data.artifact_id}?format=html&download=1`;
downloadLink.href = downloadUrl;
downloadLink.download = `${data.template_name}-${data.artifact_id}.html`;

Or use the signed public URL (no auth required):

const downloadUrl = `${data.html_url}?download=1`;
1. Boot-time (optional):
   GET /api/v1/agents/interactive/templates  →  populate template picker
   GET /api/v1/agents/interactive/libraries  →  populate library picker

2. User submits request:
   POST /api/v1/agents/chat/{agent_id}
   { "message": userQuery, "session_id": sessionId }

3. Response: check `data.type`
   - "interactive" → render artifact (sections 4, 5)
   - other types   → handle as standard text/infographic response

4. Render:
   - If html_inline present and page < 50KB: use srcdoc (no RTT)
   - Otherwise: load html_url in <iframe>
   - Always use sandbox="allow-scripts"

5. Action bar (optional):
   - Download: GET {html_url}?download=1
   - Refresh: re-send same message
   - Share: copy html_url (valid ~1 hour; re-generate for long-lived share)

6. Error states:
   - enhanced=false: show info banner "Artifact rendered with default layout"
   - 403 on html_url: URL expired — re-call GET /artifacts/{id} to get fresh URL
   - 404: artifact no longer in store

<iframe
  src="{html_url}"
  sandbox="allow-scripts"
  referrerpolicy="no-referrer"
  loading="lazy"
  style="width:100%;height:600px;border:none;border-radius:8px;"
></iframe>

Do NOT add allow-same-origin — that would let the page's JavaScript escape the sandbox and access the parent origin's cookies and storage.

Do NOT add allow-forms or allow-modals unless the artifact is a wizard that explicitly needs form submission (discuss with backend before enabling).

The sandbox="allow-scripts" policy is sufficient for ECharts, Mermaid, and Grid.js. The stepper library is pure DOM manipulation and needs no special permissions.


12. Known Limitations (v1)

  1. No dedicated generation endpoint. Unlike Infographic, there is no POST /api/v1/agents/interactive/{agent_id} shortcut. Generation always goes through the chat flow.
  2. SRI placeholders. mermaid and gridjs catalog entries carry sha384-REGENERATEME placeholder hashes. These must be verified and replaced before production deployment, or the browser will block those libraries.
  3. Fixed catalog. Libraries and scaffold templates cannot be registered via API (no equivalent of POST /infographic/templates). New entries require a code change in the catalog directory.
  4. No streaming. Artifact generation is synchronous; the chat endpoint returns only after the full HTML is generated and stored.
  5. No PDF/PNG export. The artifact is HTML-only. Screenshot-to-image conversion is out of scope for v1.
  6. ~1-hour signed URL expiry. The html_url in the response expires. For long-lived embeds, use the authenticated GET /api/v1/threads/{session_id}/artifacts/{artifact_id}?format=html endpoint instead.
  7. Single theme per artifact. Theme is chosen at generation time; the HTML is baked. To switch themes, regenerate the artifact.

13. Source References

Concern File
Data models (LibraryEntry, ScaffoldTemplate, InteractiveRenderResult) packages/ai-parrot/src/parrot/models/interactive.py
Toolkit + enhance pipeline packages/ai-parrot/src/parrot/tools/interactive_toolkit.py
Catalog registry + build_head() packages/ai-parrot/src/parrot/tools/interactive/catalog_registry.py
Scaffold templates (HTML skeletons + YAML metadata) packages/ai-parrot/src/parrot/tools/interactive/catalog/templates/
Library definitions (YAML frontmatter + source) packages/ai-parrot/src/parrot/tools/interactive/catalog/libraries/
Shared HTML SRI validator packages/ai-parrot/src/parrot/tools/_enhance_html_check.py
Bot enhance method (enhance_interactive) packages/ai-parrot/src/parrot/bots/abstract.py
System prompt injection constants packages/ai-parrot/src/parrot/bots/prompts/__init__.py
ArtifactType.INTERACTIVE enum packages/ai-parrot/src/parrot/storage/models.py
Artifact HTTP handler (retrieval + download) packages/ai-parrot-server/src/parrot/handlers/artifacts.py
Route registration app.py (lines 255-269)
Catalog tests packages/ai-parrot/tests/test_interactive_catalog.py
Toolkit tests packages/ai-parrot/tests/test_interactive_toolkit.py
E2E tests packages/ai-parrot/tests/integration/test_interactive_e2e.py
Infographic API (for comparison) docs/infographic_handler_api.md