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

# Generative UI

> How Puntego renders useful cards, tables, record links, and follow-up controls without accepting arbitrary HTML, URLs, or model execution.

Generative UI turns a guide answer into a small, typed interface inside the
same assistant turn. It can summarize records, compare options, show progress,
or point to a declared product control.

It is not an HTML renderer and it is not an execution surface. Model output is
validated against a fixed schema, rendered with Puntego-owned components, and
then intersected with capabilities the workspace has already declared.

## Supported presentation blocks

A generated answer can use bounded callouts, cards, choices, comparisons,
feature matrices, key/value lists, metric grids (KPI tiles), progress, steps,
status, tables, records, short forms, action groups, and charts. Text is always
inserted as text; scripts, markup, styles, event handlers, arbitrary React
elements, and URLs are not accepted.

A generated status reports only success, warning, or error. Receipts are not
generated UI: the model cannot author a receipt block, and the Worker drops any
such block before the visual reaches the browser. An action reports its own outcome: a
guide action through its recorded result, a Verified Action through its
receipt.

Cards, records and table rows can include one interaction:

* **Show a product control** uses an exact Target ID from the current route.
* **Continue the conversation** submits bounded text back to the guide.
* **Ask for a Verified Action** your workspace enabled, through a single-use
  button (see below).
* **Open a record's page**, one your workspace declared (see below).

## Decision blocks

Some answers are a decision, not a paragraph:

* **A question with known answers.** When the guide needs the visitor to pick
  between alternatives it knows (plans, workspaces, pages), it asks the
  question once and offers the alternatives as a choice under it, never a
  sentence asking the visitor to type which one. The choice is a radio group:
  arrow keys move between options, Enter or Space picks one, and the keys 1–9
  pick directly. Picking sends the option in the visitor's own words, marks it
  as chosen and retires the other options. The visitor can still type an
  answer of their own.
* **Plans side by side.** A feature matrix compares two to four plans feature
  by feature (included, not included, or a short value such as "10 seats").
  Each value is copied from your plans page or docs; a value or a figure they
  do not state leaves the matrix out of the visual.
  When the visitor's [account facts](/account-facts) name their plan, that
  column says **Your plan**. Puntego marks it from the facts, never from the
  model, and the mark only describes: it authorizes nothing.
* **The details an action still needs.** For a Verified Action the visitor
  wants but has not described yet, the answer can show a short form. Its
  fields come from the action's own input schema, not from the model: the
  model only picks the action and names the fields in the visitor's language.
  The form checks what the action's policy will check (required fields,
  lengths, number bounds, the allowed values of a list, dates and web
  addresses) when the visitor submits, shows each problem next to its field,
  opens with any detail the visitor already wrote in the conversation (only
  their own words, never a guess, and editable before sending),
  and sends a list's own value, never its display label. Submitting sends the
  request through the same single-use button path described below: planner,
  policy, confirmation, receipt and audit. A form never asks for a password, a
  card number or a one-time code, and never for the visitor's email or phone.
  An action that requires the visitor's email is offered as a form only when
  your server binds that email ([Verified Actions](/verified-actions#visitor-email)):
  Puntego fills it after the visitor confirms. Any other action that requires
  one of these is not offered as a form.

Unknown targets and actions are removed before the document reaches the
browser. A declared target that is no longer visible renders unavailable
instead of falling back to a selector or URL.

<h2 id="charts">
  Charts and KPI tiles
</h2>

When a shape says more than a list, the answer draws a chart: "Show it as a bar
chart" after a table of plans, "Graph my API calls by month", "How are my
seats?". A visitor can ask for one in any language, or the guide uses one when a
measure over time or across a few categories is the answer.

| Chart | When | Points |
| - | - | - |
| **Bar** | compares categories (plans, teams, meters) | 2 to 12 |
| **Line** / **area** | one measure over time, oldest first | 2 to 12, or up to 24 when every value comes from your data |
| **Donut** | the parts of one whole (seats in use and left) | 2 to 5 parts |
| **Sparkline** | a bare trend beside a figure | 2 to 12, or up to 24 from your data |

A chart has one series, or two where the second is the comparison it is read
against (this year and last year). A single number is a KPI tile, not a chart;
several attributes per item stay a table.

**Every figure is Puntego's, never the model's text.** The model picks the form
and names the values; the Worker writes each point: a value from the visitor's
[account facts](/account-facts) or a Verified Action read is bound exactly as
described in [Values from your data](#bound-values), and a number the model
read in your docs or on the page is rendered by the Worker in the chart's format
(an amount in its currency, a percentage, a count with its unit) and the
answer's language. While the visitor's facts are available, a typed point that
your docs, the page or the visitor's message does not state, or that only the
facts hold, leaves the whole chart out and the answer stays. Two values that
read differently (a count and a percentage) never share one chart.

**KPI tiles.** A metric grid tile can carry what a KPI needs: the limit the
value is measured against ("18 of 25", with a meter), the change against an
earlier figure as a chip with ▲ or ▼ and its sign (success or danger tone
when the change is good or bad news), what that change is against ("vs
August"), and a small trend line. Each part is bound or rendered like a chart
point.

**Accessible by design.** A chart in the card:

* is a group named by its title, with a one-line summary for assistive tech
  ("Bar chart of 3 values. Highest: $299.00 (Scale). Lowest: $29.00
  (Starter).");
* is one tab stop: each bar, point or part is named with its category and value
  ("Growth: \$99.00"); the arrow keys, Home and End walk them (Up and Down switch
  to the comparison series), Escape hides the tooltip, and focus shows the same
  tooltip as hover or a tap;
* shows the same figures as a table behind **View as table**, and keeps that
  table if its drawing code cannot load;
* never tells series or parts apart by color alone: two series have a legend,
  a donut lists every part with its value and share, and a change carries its
  glyph and sign;
* draws its marks in your accent made to stand 3:1 off the card's surface in
  light and dark themes, with neutral steps (`--gp-chart-2` … `--gp-chart-5` in
  [design tokens](/design-tokens)) for a second series or further parts;
* grows its bars and draws its lines once, and is static when the visitor
  prefers reduced motion.

The chart's drawing code loads only with the first chart. A widget states which
block kinds it can draw (`ui_capabilities`, see the
[API reference](/api-reference)); only one that lists `chart` receives a chart
or a tile's limit, change and trend. An older widget receives the rest of the
answer's visual.

## Where generated UI appears

* **In the answer card.** The latest answer shows its visual in the card, below
  the answer text, on desktop and on phones.
* **In the conversation history.** An earlier answer's visual collapses to a
  one-line summary (for example "Seats by team · Table, 4 rows") that opens to
  the visual. Only the content comes back: its buttons and record links belong
  to the page and the answer that offered them.
* **Across pages.** A visual stays when the visitor moves to another route of
  a single-page app or to another page in the same tab, as part of the short,
  masked copy of the conversation the widget keeps for that tab. It keeps
  what it shows; its buttons and record links stay with the page that
  offered them. A visual containing detected personal data is not copied to
  the next page.
* **In follow-ups.** A visitor can refer to an earlier visual ("sort it by
  seats"). The widget sends back at most the two latest visuals, and the guide
  uses only copies Puntego itself issued to that visitor in that conversation.
  An earlier visual grounds the answer; it never authorizes an action.

A visual is checked block by block: a block that fails validation is left out
and the rest of the answer, its sources and the other blocks still show. The
widget tells Puntego which block kinds it can present, and a visual only ever
contains those kinds.

<h2 id="bound-values">
  Values from your data are written by Puntego
</h2>

When a visual shows a value from the visitor's own account ([account
facts](/account-facts)), such as seats in use, seats left, a usage meter, a
renewal date or whether a feature is included, the AI model never types it. The
model only names the fact to show; Puntego writes the value from the facts your
app passed with that message:

* **Exact.** The visitor reads the source value itself: every digit, the unit
  you sent, the currency with at least its usual decimals, the same calendar
  day. Only the notation follows the answer's language: `6,420 of 10,000` in
  English, `6420 de 10.000` in Spanish, `6.420 de 10.000` in Portuguese;
  `Nov 1, 2026`, `1 nov 2026`, `1 de nov. de 2026`.
* **Worked out by Puntego.** Seats left, what remains of a usage meter and the
  share used (to one decimal place) are calculated from your facts, never by
  the model.
* **Typed, with its source.** Each such value carries its raw value, its type
  and where it came from: which fact, and whether your server signed it or the
  page reported it. A page-reported value still only grounds the answer:
  showing it in a visual never makes it confirmed and never authorizes
  anything.
* **More room.** A table whose value cells all come from your data can show up
  to 20 rows (8 otherwise), and a list whose values all come from your data up
  to 24 items (12 otherwise).

A usage meter whose `unit` is a currency code such as `USD`, `EUR` or `BRL` is
shown as an amount. Numbers keep up to four decimal places, the precision
account facts reach the guide with. A text value or unit that could not be
shown as it is (a link, markup, personal data or something shaped like a
credential) is not offered to the model at all.

While the visitor's facts are available, a figure the model types into a
value of a visual instead of naming a fact is shown only when the visitor's
message, your docs, the page (with the context your app sends besides the
facts) or your guide's instructions state it: a price or limit from your docs
still shows next to the visitor's own values. A copy of a value only the facts
hold, a figure the model worked out from them, or an invented one is never
shown; that block is left out and the written answer stays.

## Answers from a Verified Action read

When the guide answers from a read your workspace enabled (for example the
account entitlement lookup behind "Does Growth include SSO?"), the answer card
has the same shape as any other answer:

* **The verdict first.** The first sentence answers the question on its own:
  "Yes. Your Growth plan includes SSO."
* **The fact that proves it.** At most two presentation blocks, usually one
  highlighted value such as **SSO · Included**, or a chart of the figures the
  read returned (calls by month). Every figure a block shows
  must be one of the values the read returned (or one the visitor wrote);
  a block with any other
  figure is left out and the answer stays. The block never carries a button or
  a record link: a read result shows, it does not act.
* **Up to two follow-up chips.** Each chip is a real next step on the page the
  visitor is on: a question or step about a control or an action your
  workspace declared (and about nothing else), a page an owner-declared link
  reaches, a control on this page, or "What can I do on this page?".
  Puntego builds that list itself; the guide only picks from it,
  so a chip is never invented text.

If the read cannot return the facts, the answer says so and offers a way
forward instead of a verdict.

## Record links are product links, not web links

An interactive table row looks like a familiar record link, but its capability
is a stable Target ID such as `account_details`. Puntego resolves that ID
against the customer's declared runtime target and points at the real control.
The model cannot provide `href`, `javascript:`, a CSS selector, or an external
destination.

This keeps the interaction useful when routes or layouts evolve while
preserving the customer's target allowlist.

## Record links open owner-declared pages

A record (one invoice, one project) can link to its page in your product. You
declare, per kind of record, the page that shows one, in **Guides → Guide Action
Contract → Record Pages**: an ID (`invoice`), what one record is (`Invoice`) and a path on
your own site with `{id}` once (`/billing/invoices/{id}`, or
`/#/invoices/{id}` on a hash-routed app).

The model only names the kind of record and the record's own ID; Puntego fills
your template and never accepts a URL, another origin, or personal data (an
email, a card number) as an ID. The ID must also appear in what the answer is
grounded on (the visitor's words, the page, your docs, a read or host tool
result, or the visitor's account facts); otherwise the record is shown without
a link. Every figure a record shows (an amount, a date) must appear in the
visitor's words, the page, your docs, or a read or host tool result, or the
record is left out of the visual. When the visitor follows the link, Puntego
first dispatches a cancelable `puntego:navigate` event (and the SDK `navigate`
event with `reason: 'record'`; both carry the same `path`) so a single-page app can route it with its own
router; without a handler that calls `preventDefault()`, Puntego opens the
path. See [Routing record links](/sdk-reference#routing-record-links-in-a-single-page-app).

## Verified Action buttons are single-use and governed

A generated card, table row, or action group can offer one button that asks
for a Verified Action your workspace enabled, such as **Open a support ticket**
next to an answer about a billing problem. The button has no authority of its
own:

* **Only what the guide could already do.** The Worker offers a button only for
  an action the guide could plan for this visitor in this answer: enabled by an
  owner or admin, meant for visitors, and included in the workspace's current
  plan. It never offers an action in a forbidden category (refunds,
  cancellations, access, roles, passwords, API keys, billing), and never one
  whose inputs include a password, a one-time code, or card data.
* **One press.** The button carries a short-lived proof bound to this visitor,
  this conversation, the next turn, and this install. Pressing it records the
  proof as used before anything else happens, so a second press, a replayed
  request, or two presses at the same moment are refused; an expired proof or
  one minted for another workspace or install is refused the same way. The
  visitor sees "That button was already used or has expired. Ask again to get a
  new one." If Puntego cannot record the use, the press is refused rather than
  run.
* **The same path as asking.** Pressing the button is the same as asking in the
  conversation: the action planner, identity rules, policy, connector checks,
  confirmation, receipt, and audit described in
  [Verified Actions](/verified-actions) all still apply, and the plan is checked
  again when the button is pressed.
* **Reviewed where the answer is.** A standard write is reviewed inside the
  answer card: what will change, what will be sent, the action's own name on
  the primary button, and **Not now**. A write whose name says it pays, signs,
  or deletes something asks in a dialog where the visitor types a confirmation
  word. The card ends on the action's receipt.

A button for the action the answer is already running is not offered again in
the same answer.

When the answer points at a control on the page, the visual keeps what it shows
and loses its controls, except a button for that same action: "Where do I open
a support ticket?" rings the page's ticket control and keeps the governed
**Open a support ticket** button beside it. It is the same action when you
declared that control as one of the action's page targets, or when the
control's name on the page (or your own description of it) names what the
action files, such as a ticket or a meeting; the guide's own wording never
decides it. The answer's words never promise a
table or a button the card does not show, and never deny the action a button
beside them offers: Puntego holds such a sentence until it knows what the card
shows. An action filed in the visitor's own name (a ticket, a meeting) is only
offered to a visitor whose email your server binds; see
[Verified Actions](/verified-actions#visitor-email).

## Production checklist

1. Register stable Target IDs for controls that generated cards or rows may
   reference.
2. Exercise an available target, a missing target, and a target that leaves
   the page after the answer renders.
3. Request each enabled Verified Action in the conversation and from a
   generated button, then confirm its receipt and audit record contain no
   connector secrets or unredacted sensitive fields, and that pressing the same
   button twice is refused.
4. For each record page you declare, open a generated record link and check
   that your router takes it over (`puntego:navigate`) or that the page loads.

## Related guides

<CardGroup cols={2}>
  <Card title="Verified Actions" icon="shield-check" href="/verified-actions">
    Configure exact reads and governed writes, confirmation, reconciliation,
    receipts, and audit behavior.
  </Card>

  <Card title="Guide tools SDK" icon="mouse-pointer-click" href="/guide-tools-sdk">
    Declare stable targets and product controls for live-page guidance.
  </Card>

  <Card title="Privacy and PII" icon="lock" href="/privacy-and-pii">
    Review redaction and sensitive-field boundaries before production traffic.
  </Card>

  <Card title="Security" icon="shield" href="/security">
    Review the broader runtime, tenant, and connector trust boundaries.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.