> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gumloop.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Live artifacts

> HTML pages that load live data, take actions, call AI models, and run your agents for whoever opens them

[Live Artifacts](https://player.vimeo.com/video/1198267098?h=d9aed6720f\&badge=0\&autopause=0\&player_id=0\&app_id=58479)

A **live artifact** is a web page an agent builds for you that stays connected to your tools. Every time someone opens it or clicks one of its buttons, it fetches fresh data, takes an action, asks an AI model, or runs one of your agents. It does this as the person viewing the page, using their access and their credits. The only exception is a connector the source agent pins to its own account.

You don't write any code. Describe the page you want in plain words, and the agent builds it and saves it as an [artifact](https://docs.gumloop.com/core-concepts/agent_artifacts.md) you can share. Live artifacts need **Computer** turned on for the agent and don't run in incognito tasks.

> **[Dashboards on live data](#what-can-you-build)**
>
> Pipeline, support queue, or calendar views that refresh from your connectors.

> **[Forms that take action](#what-can-you-build)**
>
> Send a Slack message, create a Linear issue, or update a sheet from a button.

> **[Pages that call AI models](#can-a-page-call-ai-models)**
>
> Summarize, tag, and score the data on the page every time it opens.

> **[Apps powered by your agents](#turn-your-agents-into-apps)**
>
> Give your team a form that runs an agent and shows the result, no chat needed.

## How does a live artifact work?

A live artifact has two parts: the page people see, and small scripts behind it that do the work. When the page needs something, such as this week's deals or a new agent run, it asks a script. The script runs in a secure sandbox as the viewer and sends the result back to the page. The page itself never sees anyone's passwords or tokens.

```mermaid theme={"dark"}
flowchart TD
  A[Viewer opens the page and approves consent] --> B[Page asks a script for data or an action]
  B --> C[Script runs in a sandbox as the viewer]
  C --> D[Connectors, AI models, or your agents]
  D --> E[Result appears on the page]
```

> **For developers: how pages call scripts**
>
> Pages call scripts by name through three routes on their own origin. The agent wires these up for you.
>
> | Route | Use it for | What the script returns |
> | - | - | - |
> | `/gumloop/data/<key>` | Loading data when the page opens or refreshes | JSON printed to stdout |
> | `/gumloop/action/<key>` | Writing back, such as sending a message from a form | JSON printed to stdout |
> | `/gumloop/download/<key>` | A button that builds a file, such as an Excel export | One file the browser downloads |
>
> A page can send a JSON body with any request. The script reads it from the `GUMLOOP_PAYLOAD` environment variable, so one script can serve every filter, date range, or form submission. Scripts use the same `Gumloop()` client as the [Python SDK](https://docs.gumloop.com/sdk/python.md), with a short-lived token that acts as the viewer.

## What can a live artifact use?

| What it uses | What that looks like on a page |
| - | - |
| **Your connectors** | Show open HubSpot deals, list Linear issues, send a Slack message, add a row to a sheet |
| **AI models** | Summarize tickets, tag each row, score leads, answer questions about the data |
| **Gumloop itself** | Run one of your agents, search Company Brain, list agents and their recent sessions, browse skills or evaluation results |

A page can use only the connectors the agent chose when it built the page. [Your credentials, your data](#your-credentials-your-data) explains whose account each connector call uses. AI models also need a role setting, described in [Can a page call AI models?](#can-a-page-call-ai-models).

> **For developers: which client methods scripts can call**
>
> | Area | Client methods |
> | - | - |
> | Connectors | `client.mcp.execute`, `client.mcp.execute_many`. Only the tools a script names directly. |
> | AI models | `client.chat.completions.create`, `client.decisions.create` |
> | Gumloop SDK | `client.agents`, `client.sessions`, `client.brain`, `client.skills`, `client.artifacts`, `client.teams`, `client.organizations`, `client.evaluations`, `client.browser_profiles`, `client.wiki` |

## What can you build?

Anything that combines a page with live data or actions. Tell the agent which tools and data you want. The more specific you are, the better the result.

| What you want | Example prompts |
| - | - |
| **Dashboards and monitors** | "Build me a dashboard that shows my open Linear issues, today's Google Calendar events, and my unread Gmail count." "Make a monitoring page that shows my recent GitHub pull requests and their CI status." |
| **Interactive tools** | "Create a form where I can compose a message, pick a Slack channel from a dropdown, and send it." "Make a form that creates a new Linear issue with title, description, and assignee fields." |
| **Reports with live data** | "Generate a weekly summary that pulls my Gmail activity, calendar meetings, and Slack messages from the past 7 days." "Create a CRM overview that shows my HubSpot deals pipeline with live data." |
| **Multi-connector pages** | "Build a meeting prep page that, given a calendar event, pulls the attendee list from Google Calendar and shows recent email threads with each attendee." |
| **Apps on your agents** | "Build a page where my team types a company name and my Account Researcher agent writes a brief for it." See [Turn your agents into apps](#turn-your-agents-into-apps). |

A shared page shows each viewer their own data when it uses viewer-owned connections. For example, a "My open tasks" dashboard shows each person their own tasks.

## Turn your agents into apps

A live artifact can run your agents and read your Gumloop workspace, not just your connectors. That means you can put a simple page in front of any agent: your team fills in a form, the agent does the work, and the result shows up on the page. Nobody has to open a chat or write a prompt.

### Example: an account brief desk for a sales team

A sales rep types a company name and clicks **Generate brief**. The page runs an **Account Researcher** agent as that rep and lays out the result: what the company sells, who it sells to, recent news, and talking points for a first call. **Recent briefs** lists earlier brief sessions on that agent that the rep can open.

![Account brief desk for a sales team showing a New account brief form with Company set to HubSpot and a Generate brief button, a Recent briefs list with HubSpot, Snowflake, and Datadog, and a HubSpot brief marked Ready with What they sell, Who they sell to, Recent news with dates, and three numbered talking points](https://mintcdn.com/agenthub/RtiPAwvnXr0qYx3B/images/artifacts/live-artifact-brief-desk.png?fit=max\&auto=format\&n=RtiPAwvnXr0qYx3B\&q=85\&s=015d0fbbcfc010e2a13def80637becc4)

A live artifact that runs the Account Researcher agent. Each brief is a real agent session started by the person using the page.

To build one, ask the agent that should own the page:

> "Build a live page called Account brief desk for my sales team. It has a **Company** field and a **What you sell** field. When someone clicks **Generate brief**, start a session with my Account Researcher agent as that person, wait for it to finish, and show the brief in sections. List their recent briefs on the side."

What happens when a rep clicks **Generate brief**:

1. The page starts a session with the Account Researcher agent as the rep, and tells the agent which sections to write so the page can lay them out.
2. While the agent researches, the page checks on the session every few seconds.
3. When the agent finishes, the page shows the brief. The rep can open the same session in the agent's chat to see its sources.

Every brief is a normal session in the agent's chat. Its first message shows **via** and the page's filename, which links back to the page.

![First message of the Account brief: HubSpot session, from Admin Test Acc via account\_brief\_desk.html, asking for an account brief for HubSpot tailored to usage-based billing software](https://mintcdn.com/agenthub/RtiPAwvnXr0qYx3B/images/artifacts/live-artifact-session-via-page.png?fit=max\&auto=format\&n=RtiPAwvnXr0qYx3B\&q=85\&s=cd916526531930f58da47476464c30e8)

A session started from a live artifact names the page that started it.

> **See the scripts behind the brief desk**
>
> The agent writes three short scripts. `start` begins the session and returns right away, because agent work can take longer than one script request. `status` checks on the session until it finishes. `recent` lists earlier briefs.
>
> ```python start.py theme={"dark"}
> import json
> import os
>
> from gumloop import Gumloop
>
> AGENT_ID = "your_agent_id"  # the Account Researcher agent
>
> FORMAT = """Format the brief in Markdown exactly like this. Do not use em dashes.
> First line: one sentence that says what the company is.
> ## What they sell
> Two short bullets.
> ## Who they sell to
> Two short bullets.
> ## Recent news
> Three bullets. Start each with a date and a colon, for example "Sep 2, 2026: ...".
> ## Talking points
> Three numbered points, each under 30 words."""
>
> payload = json.loads(os.getenv("GUMLOOP_PAYLOAD", "{}"))
> company = (payload.get("company") or "").strip()[:120]
> pitch = (payload.get("pitch") or "").strip()[:300]
> if not company:
>     raise ValueError("Enter a company name.")
>
> prompt = f"Write an account brief for {company}."
> if pitch:
>     prompt += f" Tailor the talking points to this offer: {pitch}."
> prompt += "\n\n" + FORMAT
>
> client = Gumloop()  # runs as the person viewing the page
> resp = client.sessions.create(AGENT_ID, input=prompt, name=f"Account brief: {company}")
> print(json.dumps({"session_id": resp.session.id}))
> ```
>
> ```python status.py theme={"dark"}
> import json
> import os
> import time
>
> from gumloop import Gumloop
>
> payload = json.loads(os.getenv("GUMLOOP_PAYLOAD", "{}"))
> session_id = payload.get("session_id")
> if not session_id:
>     raise ValueError("Missing session_id.")
>
> client = Gumloop()
> deadline = time.monotonic() + 60  # stay under the 90-second request limit
> while True:
>     session = client.sessions.retrieve(session_id).session
>     if session.state in ("completed", "failed") or time.monotonic() > deadline:
>         break
>     time.sleep(3)
>
>
> def text_of(message):
>     # The final answer is the last text part; earlier parts are progress notes.
>     texts = [p.get("text", "") for p in (message.parts or []) if p.get("type") == "text"]
>     texts = [t for t in texts if t.strip()]
>     return texts[-1] if texts else (message.content or "")
>
>
> replies = [text_of(m) for m in (session.messages or []) if m.role != "user"]
> replies = [r for r in replies if r.strip()]
> print(json.dumps({
>     "state": session.state,
>     "name": session.name,
>     "brief": replies[-1] if replies else "",
> }))
> ```
>
> ```python recent.py theme={"dark"}
> import json
>
> from gumloop import Gumloop
>
> AGENT_ID = "your_agent_id"  # the Account Researcher agent
>
> client = Gumloop()
> page = client.sessions.list(AGENT_ID, type="api", page_size=8)
> print(json.dumps([
>     {"id": s.id, "name": s.name, "state": s.state, "created_at": str(s.created_at or "")}
>     for s in page.sessions
>     if (s.name or "").startswith("Account brief:")
> ]))
> ```
>
> The page calls `start` with the form values, then calls `status` with the session ID until `state` is `completed` or `failed`.

### Example: qualify a whole lead list at once

A rep pastes a list of companies and clicks **Qualify all**. The page starts one Account Researcher session per company, all at the same time, and fills in a table as each one finishes: the company's segment, a fit score from 1 to 5, and a one-line reason. The table sorts itself by fit, and the most recent run is still there the next time the rep opens the page.

![Lead qualifier page with a list of five companies and a What you sell field set to usage-based billing software, and a Results table sorted by fit: Clay marked Strong fit, Airtable and PostHog marked Possible, and Calendly and Supabase marked Weak fit, each with a segment and a one-sentence reason](https://mintcdn.com/agenthub/RtiPAwvnXr0qYx3B/images/artifacts/live-artifact-lead-qualifier.png?fit=max\&auto=format\&n=RtiPAwvnXr0qYx3B\&q=85\&s=ad014f8b56faa793cb43c21e89ce4be6)

One click runs the agent on every company in parallel and turns the answers into a sorted, scored table.

> "Build a live page called Lead qualifier. I paste up to 8 companies and what we sell, then click **Qualify all**. Run my Account Researcher agent on every company at the same time, have it return a segment, a fit score from 1 to 5, and a one-line reason, and show the results in a table sorted by fit. Show my latest run when I open the page."

What makes this a page and not a chat:

* **Many runs at once.** Each company is its own agent session, so five companies take about as long as one.
* **The same structure every time.** The page asks the agent for the same fields on every run, so results land in columns you can scan and sort.
* **Built for the whole team.** Anyone you share it with gets the same tool, running as them, with no prompt to write.

> **See the scripts behind the lead qualifier**
>
> `start_batch` starts one session per company and returns their IDs right away. `status_batch` checks every session in one request and pulls the score out of each finished reply. With no IDs, it loads the viewer's most recent run.
>
> ```python start_batch.py theme={"dark"}
> import json
> import os
>
> from gumloop import Gumloop
>
> AGENT_ID = "your_agent_id"  # Account Researcher
>
> FORMAT = """Reply with only a JSON object, no other text:
> {"segment": "<industry and size, under 6 words>",
>  "fit": <1 to 5, how well they fit the offer>,
>  "reason": "<one plain sentence, under 18 words, no em dashes>"}"""
>
> payload = json.loads(os.getenv("GUMLOOP_PAYLOAD", "{}"))
> companies = [c.strip()[:80] for c in payload.get("companies", []) if c.strip()][:8]
> offer = (payload.get("offer") or "").strip()[:300]
> if not companies:
>     raise ValueError("Add at least one company.")
>
> client = Gumloop()  # runs as the person viewing the page
> sessions = []
> for company in companies:
>     prompt = f"Qualify {company} as a sales lead for this offer: {offer}\n\n{FORMAT}"
>     resp = client.sessions.create(AGENT_ID, input=prompt, name=f"Lead check: {company}")
>     sessions.append({"company": company, "session_id": resp.session.id})
> print(json.dumps(sessions))
> ```
>
> ```python status_batch.py theme={"dark"}
> import json
> import os
> import re
> from datetime import datetime, timedelta
>
> from gumloop import Gumloop
>
> AGENT_ID = "your_agent_id"  # Account Researcher
>
> payload = json.loads(os.getenv("GUMLOOP_PAYLOAD", "{}"))
> client = Gumloop()
>
> ids = payload.get("session_ids", [])[:8]
> if not ids:
>     # No run in progress: show the viewer's latest lead checks when the page opens.
>     page = client.sessions.list(AGENT_ID, type="api", page_size=25)
>     checks = [s for s in page.sessions if (s.name or "").startswith("Lead check: ")]
>     times = [datetime.fromisoformat(str(s.created_at).replace("Z", "+00:00")) for s in checks]
>     if checks:
>         newest = max(times)
>         # Keep only the most recent run: sessions started within 5 minutes of the newest one.
>         ids = [s.id for s, t in zip(checks, times) if newest - t < timedelta(minutes=5)][:8]
>
>
> def reply_text(session):
>     for message in reversed(session.messages or []):
>         if message.role == "user":
>             continue
>         texts = [p.get("text", "") for p in (message.parts or []) if p.get("type") == "text"]
>         texts = [t for t in texts if t.strip()] or [message.content or ""]
>         if texts[-1].strip():
>             return texts[-1]
>     return ""
>
>
> results = []
> for session_id in ids:
>     session = client.sessions.retrieve(session_id).session
>     row = {
>         "session_id": session_id,
>         "company": (session.name or "").removeprefix("Lead check: "),
>         "state": session.state,
>     }
>     if session.state == "completed":
>         match = re.search(r"\{.*\}", reply_text(session), re.S)
>         try:
>             row.update(json.loads(match.group(0)) if match else {})
>         except ValueError:
>             row["reason"] = "The agent didn't return a score."
>     results.append(row)
> print(json.dumps(results))
> ```
>
> The page calls `start_batch` with the list, then calls `status_batch` every few seconds with the sessions that haven't finished. Checking all sessions in one request keeps the page under the limit of three script requests at once.

### More ideas

| Idea | Example prompt |
| - | - |
| **A handbook search page** that returns [Company Brain](https://docs.gumloop.com/core-concepts/brain.md) results each viewer can access | "Build a page with one search box that searches our Company Brain and shows each result's title, source, and link." |
| **Follow-up questions** that continue the same agent session | "Under each brief, add an **Ask a follow-up** box that sends the question to the same session." |
| **A meeting prep button** that combines a connector and an agent | "List my meetings for today from Google Calendar. Next to each one, add a **Prep me** button that runs my Account Researcher agent on the company I'm meeting." |
| **A skill catalog with an install button** | "List our team's skills with a search box, and add an **Add to my agent** button that attaches the skill to an agent I pick." |
| **A support reply desk** | "Show this week's open Zendesk tickets. Next to each one, add a **Draft reply** button that runs my Support agent on that ticket and shows its draft." |

### Whose access does a page use when it runs agents?

Everything a page does in Gumloop runs as the person viewing it, as if they did it themselves: their permissions, the agents and files shared with them, their connected accounts, and their credits. If a viewer can't open an agent or file in Gumloop, the page can't use it for them either.

A page can run your custom agents and the viewer's own [Gumball](https://docs.gumloop.com/core-concepts/gumball.md). Sessions it starts belong to the viewer, and the viewer pays for them.

### Where can't a page run agents or read Gumloop data?

| Where you open the page, or how it's shared | Agents and Gumloop data |
| - | - |
| Inside Gumloop | Available |
| On a `gumloopartifacts.com` custom domain | Available |
| **Public link access** set to **Anyone with the link** | Not available |
| The page has a **Password protected link**, even alongside team or organization access | Not available |
| On an [organization-owned artifact domain](https://docs.gumloop.com/core-concepts/agent_artifacts.md#organization-domains) | Not available |
| A page version saved before this feature existed | Not available until the agent saves a new version |

Connectors and AI models follow their own rules in [Your credentials, your data](#your-credentials-your-data) and [Can a page call AI models?](#can-a-page-call-ai-models).

> [!WARNING]
>
> Running agents and reading Gumloop data don't have their own approval prompt, and the consent overlay lists only connectors and outside sites. Once you approve a page, it can read and change anything you can reach in Gumloop, such as session history, agent configuration, skills, Brain results, and wiki content. It can also send that data to the outside sites it declares. Only approve live pages from people you trust.

> **For developers: how Gumloop decides which resources a page can use**
>
> When the agent saves a version of the page, Gumloop reads its scripts and records which SDK resources they use, such as `client.sessions` or `client.brain`. When a script runs, it gets access only to resources that both the page's current version and that script use.
>
> Call each resource directly on the `Gumloop()` client, for example `client.sessions.create(...)`. A resource reached indirectly, for example through `getattr` or `exec`, isn't detected, so Gumloop refuses those calls.

> **For developers: running agent work that takes longer than 90 seconds**
>
> Each script request stops after 90 seconds, but an agent session keeps running after the request that started it ends. Split the work in two: one script calls `client.sessions.create(agent_id, input=...)` and returns the session ID, and the page then calls a second script that calls `client.sessions.retrieve(session_id)` until the session's `state` is `completed` or `failed`. An agent's reply text can arrive in `message.parts` instead of `message.content`: read the parts whose `type` is `text`.

## Integration consent

The first time you open a live artifact, a consent overlay lists every connector the file can use and the actions it can take. No script runs until you approve.

![Integration consent overlay showing the file is requesting permission to use Slack with 1 tool, with an I acknowledge, continue button](https://mintcdn.com/agenthub/7vdhEwTuo1jEXJp0/images/artifact_integration_consent.png?fit=max\&auto=format\&n=7vdhEwTuo1jEXJp0\&q=85\&s=25f3ef36e180f15851fbc0e52fd6c860)

* You always see which connectors a file uses before it runs.
* No script fetches data until you approve.
* When someone shares a file with you, you decide whether it can use your accounts.

If the page connects to outside servers, the overlay also lists them under **It can also send data to these sites**. Gumloop saves your acknowledgement for that artifact version, so a file you already approved can open without the overlay.

## Your credentials, your data

A live artifact's connector calls depend on where you open it and whether the source agent's connectors are viewer-owned or agent-owned. Sharing a file or giving it a password doesn't give visitors access to the creator's personal connections.

| Where you open the artifact | Which connections its scripts can use |
| - | - |
| Inside Gumloop or on `gumloopartifacts.com` | Your available connections, plus agent-owned connections configured on the source agent when the artifact isn't public or password-shared |
| On an organization-owned artifact domain | Agent-owned connections configured on the source agent only, not your personal connections |

For example, a dashboard using viewer-owned Slack connections shows each signed-in viewer data from their own Slack account. A connector configured as agent-owned uses the account pinned to the agent instead.

> [!WARNING]
>
> If the artifact has **Anyone with the link** access or a **Password protected link**, its scripts can't use agent-owned connections. This also applies when the file keeps a password link alongside team or organization access. On an organization-owned artifact domain, that leaves no connector access for the scripts.

Your connector credentials are never exposed to the HTML. If a required connector is unavailable, complete the setup prompt before loading that data. On a hosted link, a live page always asks visitors to sign in with a Gumloop account, because the scripts run as the viewer. See [Authentication and sign-in](https://docs.gumloop.com/core-concepts/agent_artifacts.md#authentication-and-sign-in).

## Can a page call AI models?

Yes. A page's data scripts can send the data they load to an AI model and show the answer on the page. The page can summarize, tag, score, or answer questions about live data every time someone opens it, without anyone starting a chat.

**Example: a support triage board.** Ask an agent:

> "Build a dashboard of this week's open Zendesk tickets. Tag each ticket's sentiment as positive, neutral, or negative, and its product area. Put a three-sentence summary of the main themes at the top, and add a box where I can ask questions about these tickets."

When a teammate opens the page:

1. A data script loads the open tickets with that teammate's Zendesk connection.
2. A model tags each ticket and writes the summary. Each tag and the summary appear next to the data.
3. Typing a question into the box, such as "Which customers mentioned billing twice?", sends the question and the loaded tickets to a model and shows the answer on the page.

| What you want | Example prompt |
| - | - |
| **Summaries** | "Add a two-line summary under each deal in this pipeline dashboard, using its latest emails and notes." |
| **Tags and verdicts** | "Flag every invoice in this sheet that looks like a duplicate or is missing a PO number." |
| **Scores** | "Score each inbound lead from 1 to 5 for fit with our ideal customer profile and sort by score." |
| **Questions about the data** | "Add an **Ask** box so viewers can ask questions about this month's numbers." |
| **Drafts** | "Next to each overdue task, draft a short follow-up message the viewer can copy." |

Pages can use two kinds of models. Chat models write free text, such as summaries, answers, and drafts. [Decision models](https://docs.gumloop.com/core-concepts/ai_models.md#what-are-decision-models) answer closed questions like yes or no, pick one option, or a score, and suit tagging many rows quickly.

**Who can use it:** the person who publishes the page needs a [custom role](https://docs.gumloop.com/enterprise-features/user_groups.md#features-tab) with **AI models from agent code** turned on. The feature is off by default. Every run also checks the viewer's own role and model access, and bills the model usage to the viewer. There is no per-run approval. If an older page can't call models, ask the agent to save it again.

## Download buttons

Ask the agent to add a download button, for example to export the dashboard's data as Excel, a filtered CSV, or a PDF. The script builds the file when you click the button and returns it as a browser download, rather than saving it as a new artifact.

| Example prompt | What you get |
| - | - |
| "Add an **Export to Excel** button to this dashboard." | A button that builds an `.xlsx` of the current numbers. |
| "Let me download the filtered rows as CSV." | A button that exports whatever your filters selected. |

> [!NOTE]
>
> A download runs a script, so it takes a few seconds and costs credits like any other data load. Each request returns one file of up to 10 MB. If the script produces no file, the page shows an error instead of starting a download.

## What does a live artifact cost, and who pays?

Creating the page uses the agent conversation's usual credits. After that, the **viewer** pays for everything the page runs: script runtime, AI model calls, and agent sessions the page starts. Organization members use their organization's credit balance and any applicable user limits. If a credit limit blocks a run, the script returns an error. See [Credits](https://docs.gumloop.com/core-concepts/credits.md#interactive-artifact-costs) for rates.

## Limits and performance

| Limit | Value |
| - | - |
| Run time per script request | 90 seconds |
| Script requests running at once, per viewer, per page | 3. A fourth returns HTTP 429. |
| Download file size | 10 MB, one file per request |
| Caching | None. Every request runs its script again. |

Every request starts a sandbox and runs its script while the viewer waits, so well-built pages:

* Load what the page needs with one script, not many small ones.
* Pass filters in the request body instead of adding a script per filter.
* Show the page layout before the data arrives, with a clear error and a **Retry** button if a script fails.

## Refreshing data

Opening an artifact again can rerun its data scripts. A refresh button or auto-refresh timer starts another run, and each run uses credits. Data updates only when the page loads or refreshes, not continuously in the background. Long-running queries can time out. If one does, ask the agent to reduce the data scope.

## Why does my live artifact show an error or a blank section?

If a data script fails, the page receives an error response. Well-built pages show a friendly message. If the agent didn't include error handling, the section may simply be blank.

| Why it failed | What to do |
| - | - |
| You haven't connected the required connector | Connect the connector from the setup prompt. |
| Your connector token has expired | Refresh the connector from **Connectors**. |
| The script timed out | Ask the agent to reduce the data scope. For agent runs, ask it to start the session in one step and check on it in another. |
| You've run out of credits | Top up credits. |
| The page can't run an agent or read Gumloop data | Check that you can open that agent or file in Gumloop, and that the page isn't public, password-protected, or on an organization-owned domain. |

## Live artifacts and Chat Widgets

A [Chat Widget](https://docs.gumloop.com/core-concepts/agent_artifacts.md#how-are-chat-widgets-different-from-artifacts) is a compact chart, comparison, or mock-up inside a live web reply. Live widgets follow the same integration consent, connector account settings, and tool permissions as live artifacts, and their runs bill the viewer. Use a live artifact when you want a page you can share, host, or embed.

## Common questions

> **What's the difference between a static artifact and a live artifact?**
>
> A **static** artifact shows saved content, such as a PDF, image, or CSV. A **live** artifact runs attached scripts that load data or take actions each time someone uses it.

> **Do live artifacts use my credentials or the creator's?**
>
> Inside Gumloop and on `gumloopartifacts.com`, scripts can use your available connections and eligible agent-owned connections. Organization-owned artifact domains use agent-owned connections only. Public or password-shared artifacts can't use agent-owned connections. Agent runs and Gumloop data always use your access. See [Your credentials, your data](#your-credentials-your-data).

> **Can a live artifact take actions, or only read data?**
>
> Both. Agents can build forms and buttons that send Slack messages, create Linear issues, or start agent sessions. Actions use the same connections and permissions as reads.

> **Can a page start a session with any agent?**
>
> It can start a session with any custom agent the viewer can run, and with the viewer's own Gumball. A viewer who can't run an agent in Gumloop can't run it through the page.

> **Why can't my older page run agents?**
>
> Pages saved before this feature existed can't run agents or read Gumloop data. Ask the agent to save a new version of the page.

> **Why does my page run agents in Gumloop but not on a public link?**
>
> Pages with **Anyone with the link** access or a **Password protected link**, and pages on an organization-owned artifact domain, can't run agents or read Gumloop data. Share the page with people, teams, or your organization instead.

> **Can the page's HTML read my Gumloop session or cookies?**
>
> No. HTML artifacts run on an isolated origin and can read their own files, not the Gumloop app's session, cookies, or local storage. Data reaches the page only through its scripts and the outside sites it declares. See [Security](https://docs.gumloop.com/core-concepts/agent_artifacts.md#security).

## Related documentation

> **[Agent artifacts (files)](https://docs.gumloop.com/core-concepts/agent_artifacts.md)**
>
> Preview, version, share, and host any file an agent produces

> **[Python SDK](https://docs.gumloop.com/sdk/python.md)**
>
> Every SDK resource a data script can call

> **[Credits](https://docs.gumloop.com/core-concepts/credits.md#interactive-artifact-costs)**
>
> What viewers pay when a page runs
