# Apps

> Turn a PromptRails workflow into a hosted app, presentation, form, portal, or review queue without building a separate frontend.

Source: https://0.0.0.0:8080/docs/apps

Apps let you turn agents, prompts, data sources, and records into hosted web surfaces without building a separate frontend.

Use Apps for internal tools, demos, review queues, customer-facing forms, and lightweight portals where the UI needs to run a PromptRails resource and show the result.

## Overview

An app is built on a **visual canvas**. You place widgets, bind them to queries, and wire up interactions without writing frontend code. Widgets cover inputs, buttons, charts, tables, forms, markdown, images, embedded agent chat, prompt runs, and data source runs. Queries fetch or write data, while page parameters and app records keep state.

Builders get an editor with undo/redo, multi-select, guided data binding, version history, and an assistant that can place widgets. End users get a focused app instead of direct access to the full PromptRails workspace. Apps can render as scrolling web pages or full-screen presentation decks, with optional export controls for PDF and PowerPoint deliverables.

<TechnicalDetails title="App data model">

## App Structure

```
App  (slug, access control, version history)
├── Page 1 ("Customer Lookup")
│   ├── Widgets        — text inputs, table, agent chat, ... on the canvas
│   ├── Queries        — record_list, agent_run, ... data fetchers
│   └── Parameters     — shared page inputs ({{params.name}})
├── Page 2 ("Content Generator")
│   └── ...
└── Records            — app-scoped JSONB documents, grouped by collection
```

- **Pages** define the navigation structure; each owns its own canvas, queries, and parameters.
- **Widgets** are placed freely on the canvas and can be nested inside Container / Card widgets.
- **Queries** are page-scoped data fetchers referenced by widgets via `{{queryName.data}}`.
- **Records** are app-scoped documents (the persistence target for Form / Table widgets).
- **Versions** are immutable snapshots of the whole app, restorable from the editor.

</TechnicalDetails>

## The Canvas Editor

The editor lives at `/apps/:id/edit` and is organized around a left icon rail and contextual panels:

- **Components** — the widget toolbox; drag a widget onto the canvas.
- **Pages** — list / add / rename / delete pages, plus per-page **Parameters**.
- **Queries** — create and run data fetchers (bottom strip).
- **History** — save and restore version snapshots.
- **Settings** — app name, slug, display mode, access control, theme, and export controls.
- **Inspector** (right) — edits the selected widget's configuration.

Widgets use free placement: drop them where they should appear and adjust the layout visually. The editor also supports zoom, undo/redo, duplicate, copy/paste, delete, multi-select, and a context menu.

## Widget Library

Widgets are organized into five categories. Each is configured from the Inspector and can bind its properties to query output or page parameters.

| Category              | Widgets                                                                                                               |
| --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Common / Presentation | `text`, `markdown`, `button`, `link`, `alert`, `tag`, `code_block`, `divider`, `image`, `statistic`                   |
| Inputs                | `text_input`, `number_input`, `select`, `checkbox`, `switch`, `radio_group`, `date_input`, `date_range_input`, `form` |
| Layout                | `container`, `card`, `tabs`, `modal`, `spacer`                                                                        |
| Data                  | `table`, `list`, `chart`, `rest_endpoint`                                                                             |
| Agent                 | `agent_chat`, `prompt_run`, `data_source`                                                                             |

Highlights:

- **Chart** — bar, line, area, pie, donut, scatter, and number visualizations with guided query binding, column pickers, and configurable series.
- **Markdown** — renders Markdown + GFM, ideal for agent / prompt output.
- **List** — repeats a row template for any bound array, with dotted-path field access (`row.user.email`).
- **Table** — renders arrays of objects with a sticky header; bind to a page query, connect a data source directly, or use static/custom data.
- **Form** — collects inputs and runs a query on submit (typically `record_create`).
- **Container / Card** — group child widgets; nesting survives reload via `parent_id`.
- **Tabs** — exposes `activeIndex` / `activeLabel` so other widgets can react to the current pane.
- **Agent chat** — embeds a conversational agent surface directly on the page.
- **Prompt run / Data source** — run a prompt or connected data source from the page and expose its output to other widgets.

## Guided Data Binding

Tables and charts can be connected without writing raw `{{ ... }}` bindings:

- Pick an existing **Page query** from the widget Inspector.
- Use **Connect data source** from the same picker to create a page-load `data_source` query and bind the widget to `{{queryName.data}}` automatically.
- Switch to **Advanced binding** only when you need a custom expression or transformed rows.

For charts, PromptRails reads the latest query result and offers field pickers for the X/category field and value series. If the query has not run yet, you can still type field names and run the query once to load real columns.

<TechnicalDetails title="Canvas data and query details">

## Widget Bindings

Widgets read dynamic data through `{{ ... }}` bindings, resolved against page state at runtime:

```
{{ orders.data }}          # output of the "orders" query
{{ params.customer_email }}# value of the page parameter "customer_email"
{{ row.user.email }}       # dotted-path field inside a List/Table row
```

Reactive widgets (those with `{{...}}` bindings) are flagged in the editor so you can see at a glance what's data-driven.

## Queries

Queries are page-scoped data fetchers. Each has a name, a type, type-specific config, and a trigger.

| Type            | Purpose                                            |
| --------------- | -------------------------------------------------- |
| `record_list`   | Read documents from the app record store           |
| `record_create` | Write a document to the app record store           |
| `agent_run`     | Execute an agent                                   |
| `prompt_run`    | Render and execute a prompt template               |
| `data_source`   | Run a connected data source                        |
| `transformer`   | Derive a value from other query outputs            |
| `js`            | Sandboxed JavaScript evaluation against page state |

**Triggers** control when a query runs:

| Trigger     | Behavior                               |
| ----------- | -------------------------------------- |
| `manual`    | Runs only when explicitly triggered    |
| `page_load` | Runs automatically when the page loads |
| `on_change` | Re-runs when an upstream input changes |

Create queries from the dialog; source pickers list your agents, prompts, and data sources. Run a query from the detail panel and the result renders as a table, scalar card, or JSON depending on its shape. Query runs are persisted so page state can be replayed on refresh and public traffic can be audited.

## App Records

Records give an app its own document store without a separate database. Each record belongs to a **collection** (think of it as a table within the app) and holds an arbitrary JSON `data` payload.

- `record_create` queries write records (e.g. a Form submission).
- `record_list` queries read them back (e.g. into a Table), with GIN-indexed filtering.
- Public widget writes are attributed to the visitor's session; dashboard writes to the user.

## Page Parameters

Pages can define parameters that users fill in, then reference anywhere via `{{params.name}}`.

| Field           | Description                             |
| --------------- | --------------------------------------- |
| `name`          | Reference name (used in `{{params.*}}`) |
| `label`         | Display label                           |
| `param_type`    | Input type (text, number, select, etc.) |
| `default_value` | Default value                           |
| `options`       | Options for select-type parameters      |
| `required`      | Whether the parameter is required       |
| `sort_order`    | Display order                           |

Add parameters through a guided dialog (Label → Reference name → Type → Options → Default → Required) — no JSON authoring.

</TechnicalDetails>

## Version History

Every save can be snapshotted. The **History** panel lists versions with a human-readable summary, lets you **Save version** on demand, and **Restore** any prior snapshot. Restoring auto-checkpoints the current state first, then atomically rewrites pages, widgets, parameters, and queries inside a transaction — so a restore is fully reversible and Container nesting survives the round-trip.

## Presentation Mode and Export

Apps support two display modes:

| Mode         | Best for                                             |
| ------------ | ---------------------------------------------------- |
| Web page     | A scrolling app or portal with page tabs             |
| Presentation | A full-screen slide deck with one app page per slide |

Presentation mode is designed for customer demos and reports: app chrome is hidden, slides are preloaded for smooth navigation, and controls stay out of the content area. Web page mode keeps the same clean runtime surface while preserving page navigation.

When export controls are enabled in App Settings, public runtimes can export the current app as:

- **PDF** — browser print export with app chrome hidden.
- **PPTX** — PowerPoint export with each app page or slide captured as a deck slide.

Export controls are enabled by default and can be hidden from both web and presentation views.

## Locked and Frozen Apps

Locking an app freezes editing for the builder and preserves runtime data for customer-facing presentations. Prompt and data-source widgets serve their frozen result while locked, so published demos do not shift because of fresh LLM or data-source runs. Unlock the app when you want to edit layout, change queries, or refresh frozen outputs.

## Access Control

Each app has a single `auth_method` that gates the public URL:

| `auth_method`       | Behavior                                                           |
| ------------------- | ------------------------------------------------------------------ |
| `none`              | Fully public — anyone with the link can use the app                |
| `pin`               | Requires a shared PIN. The PIN is hashed before storage            |
| `promptrails_login` | Requires a Promptrails account with access to the owning workspace |

For `promptrails_login`, visitors sign in through an inline login flow on the hosted-app domain. The app verifies that the account belongs to the owning workspace before rendering the app.

## AI Assistant

The built-in assistant can build the canvas for you. It exposes tools to list, add, update, and delete widgets, and to list and add queries on the current page (`list_canvas_widgets`, `add_canvas_widget`, `update_canvas_widget`, `delete_canvas_widget`, `list_canvas_queries`, `add_canvas_query`). All are gated by a page-ownership check, so the assistant can only edit apps in workspaces you have access to.

## Public URLs

Each app has a unique slug that forms its public URL, such as `https://your-app-domain.com/internal-tools`.

Slugs must be unique across all apps. Depending on the access method, the first visit may show the PIN prompt or the inline login form before the app renders.

## Creating an App

Create apps from the PromptRails dashboard:

1. Open **Apps** in your workspace and create a new app — set its name and slug.
2. Add one or more pages to define the navigation structure.
3. On each page, drag widgets from the **Components** panel onto the canvas (or start from a template: Dashboard / Form + Table / Agent chat / Single input).
4. Add **Queries** to fetch or write data, and bind widget properties to them with `{{queryName.data}}`.
5. Add **Parameters** for shared inputs and reference them via `{{params.name}}`.
6. Configure **Access Control** under Settings (`none` / `pin` / `promptrails_login`).
7. Save a **version** and share the public URL.

## Analytics

Apps track execution logs including:

- Which widgets / queries were executed
- Input payloads and output summaries
- Status (success / failure)
- Session tracking
- Error messages

Query runs are persisted separately with per-run duration, input snapshot, and output. Review these from the app detail view to understand how end users are interacting with each page.

## App Fields

| Field               | Type      | Description                            |
| ------------------- | --------- | -------------------------------------- |
| `id`                | KSUID     | Unique app identifier                  |
| `workspace_id`      | KSUID     | Workspace scope                        |
| `name`              | string    | Display name                           |
| `description`       | string    | Optional description                   |
| `slug`              | string    | URL slug (unique)                      |
| `status`            | string    | `active` or `inactive`                 |
| `auth_method`       | string    | `none`, `pin`, or `promptrails_login`  |
| `mode`              | string    | `web_page` or `presentation`           |
| `export_enabled`    | boolean   | Shows PDF/PPTX export controls         |
| `grid_columns`      | integer   | Legacy grid column count (default: 12) |
| `last_published_at` | timestamp | Last publish time                      |
| `created_at`        | timestamp | Creation time                          |
| `updated_at`        | timestamp | Last update time                       |

## Related Topics

- [Agents](/docs/agents) -- Agents used in app widgets and queries
- [Prompts](/docs/prompts) -- Prompts used in app widgets and queries
- [Data Sources](/docs/data-sources) -- Data sources used in app widgets and queries
