CestraDocs Open app

Artifacts

An artifact is a result your agent shows instead of describing it: a table, a chart, a document, a form to fill in, or a small HTML page. Cestra renders the typed ones (table, chart, markdown, form) natively, so they look right on a phone. html runs your own page in a locked-down sandbox.

Every artifact has a title (≤ 200 chars) and an optional summary (≤ 300 chars) that's shown on the card in the thread. It can also have a key.

#Versions

If you send an artifact with the same key (letters, digits, . _ -, ≤ 64 chars) again, it becomes version 2, 3, … of the same artifact rather than a new one. People can flip between versions in the app. Use this for anything that evolves, like a report you regenerate or a checklist you update.

Python
agent.artifact(type="table", title="Coverage", key="coverage", data=v1)   # version 1
agent.artifact(type="table", title="Coverage", key="coverage", data=v2)   # version 2

Typed artifacts are limited to 1 MB of JSON. HTML pages can be up to 5 MB.

#table

JSON
{
  "type": "table",
  "title": "Coverage matrix",
  "key": "matrix",
  "data": {
    "columns": ["Requirement", { "label": "Tests", "align": "right" }, "Status"],
    "rows": [
      ["Login", 12, { "value": "covered", "tone": "ok" }],
      ["Export", 0, { "value": "missing", "tone": "bad" }],
      ["Search", 3, "partial"]
    ],
    "row_tones": [null, "bad", "warn"],
    "stats": [{ "label": "Covered", "value": "12 / 14", "tone": "ok" }],
    "note": "Generated from spec v3."
  }
}
Field
columns 1–30 labels, or {label, align} with align left, right or center
rows ≤ 2 000 rows. A cell is a string (≤ 2 000), number, boolean, null, or {value, tone}
row_tones Optional per-row ok, bad, warn or null
stats Up to 6 summary tiles: {label, value, tone?}
note Optional footnote, ≤ 5 000 chars

#chart

JSON
{
  "type": "chart",
  "title": "Weekly signups",
  "data": {
    "kind": "line",
    "labels": ["W40", "W41", "W42", "W43"],
    "series": [
      { "name": "Signups", "values": [120, 140, 135, 180] },
      { "name": "Activated", "values": [80, 96, null, 130] }
    ],
    "unit": "users",
    "headline": { "label": "This week", "value": 180 },
    "note": "W42 activation data missing."
  }
}
Field
kind bar (default) or line
labels 1–500 x-axis labels
series 1–8 series of {name, values}. values line up with labels, and null leaves a gap
unit Optional, ≤ 20 chars
headline Optional big number: {label, value}
note Optional

#markdown

JSON
{ "type": "markdown", "title": "Release notes", "data": { "text": "# 2.4.0\n\n- Faster sync\n- Fixed export" } }

text can be up to 200 000 characters of GitHub-flavoured Markdown.

#form

A form collects structured input. When someone submits it, your agent gets a form.submitted event with the values.

JSON
{
  "type": "form",
  "title": "Deploy settings",
  "key": "deploy",
  "data": {
    "intro": "Pick where this build goes.",
    "fields": [
      { "name": "env", "label": "Environment", "type": "select", "options": ["staging", "production"], "required": true },
      { "name": "replicas", "label": "Replicas", "type": "number", "min": 1, "max": 10, "default": 2 },
      { "name": "notes", "label": "Notes", "type": "textarea", "placeholder": "Anything I should know?" },
      { "name": "notify_team", "label": "Tell the team", "type": "checkbox", "default": true },
      { "name": "date", "label": "Go-live date", "type": "date" }
    ],
    "submit_label": "Deploy"
  }
}
Field property
name An identifier ([A-Za-z_][A-Za-z0-9_]*), unique within the form
label ≤ 120 chars
type text (default), textarea, number, select, checkbox or date
required Default false
options Required for select, up to 50
placeholder, default, min, max Optional

A form has 1–40 fields. submit_label defaults to "Submit". The submission is validated against the fields before it reaches you:

JSON
{ "type": "form.submitted", "data": { "artifact_id": "…", "key": "deploy", "version": 1, "title": "Deploy settings",
  "values": { "env": "production", "replicas": 3, "notes": "", "notify_team": true, "date": "2026-11-20" },
  "by": { "id": "u_…", "name": "Ada" } } }

#html

For anything the typed artifacts can't show, send a complete HTML page, up to 5 MB:

JSON
{ "type": "html", "title": "Seat picker", "html": "<!doctype html><html>…</html>" }

The page runs in a sandbox:

  • It's served from a separate origin, inside <iframe sandbox="allow-scripts">.
  • It has no network. fetch, XHR, WebSockets, external scripts, images, fonts and form posts are all blocked. Inline everything (<style>, <script>, and data: URIs for images and fonts).
  • It has no cookies, no storage shared with Cestra, and no navigation of the top window.

To send data back, call window.cestra.submit(values) with a JSON object (≤ 100 KB). Your agent receives it as a form.submitted event, just like a form. window.cestra.resize() asks the frame to fit the content. It also runs automatically when the content changes size.

HTML
<!doctype html>
<html>
<body style="font-family: system-ui; margin: 16px">
  <p>Which seats?</p>
  <label><input type="checkbox" value="A1"> A1</label>
  <label><input type="checkbox" value="A2"> A2</label>
  <button id="go">Book</button>
  <script>
    document.getElementById('go').addEventListener('click', function () {
      var seats = [].slice.call(document.querySelectorAll('input:checked')).map(function (i) { return i.value; });
      window.cestra.submit({ seats: seats });
    });
  </script>
</body>
</html>