> For the complete documentation index, see [llms.txt](https://docs.rootcause.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rootcause.ai/more-details/applications.md).

# Applications

The rest of the platform is where you build and validate models. **Applications** is where you package one into something an audience can use: a dashboard people open, an endpoint another system calls, a flow that runs on its own, an AI assistant scoped to your data — or any combination of those in a single application.

An application composes what the workspace already holds — its data sources, Datasets and Digital Twins — and its audience never has to understand the models underneath.

Open **Applications** in the workspace's top navigation bar. The list has four columns: **Name**, **Owner**, **Status** (Draft, Published or Archived) and **Updated**, with a search box that matches on the name. **New application** starts the builder; **Import** brings one in from elsewhere.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-c3c3b3ab09b1744d8287f2894178fd9e56d69305%2Frc-applications-list.png?alt=media" alt="The Applications page of a workspace listing two draft applications, Churn Score API and Customer Health, under the columns Name, Owner, Status and Updated, with the New application button highlighted"><figcaption><p>The Applications list. <strong>New application</strong> opens the builder.</p></figcaption></figure>

***

## What an application can be

An application is not declared to be one thing before it is built — there is no type to pick. You build a flow, and what the finished thing is follows from what you put in it, so one application can be several of these at once.

|                   | How you build it                                                                                                                                                                                                                        | Examples                                                                       |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| **A dashboard**   | Curated answers for people to read: compose tiles, charts and tables on the **Dashboard** canvas, powered by the live flow underneath.                                                                                                  | Operations health for the on-call team; a weekly revenue snapshot              |
| **An endpoint**   | A programmable endpoint other systems can call: an **API Request** block takes the call, the flow runs, an **API Response** block returns a typed payload.                                                                              | Score a transaction for fraud risk; resolve a customer ID to a profile bundle  |
| **An automation** | A flow started by an event block — a schedule, a webhook, a dataset arriving, a twin being trained — that writes its results back into the workspace or out to another system.                                                          | Nightly data quality check; hourly model retrain pipeline                      |
| **An assistant**  | An LLM-driven assistant with a goal, a scope and a set of tools it is allowed to use, switched on with **Offer an assistant** in the **Publish** panel, then configured on the **Scope** and **Tools** modes that appear once it is on. | A support copilot scoped to one team; an investigation agent over the ontology |

{% hint style="info" %}
An application that carries an **API Request** block is an endpoint you build on your workspace's data. It is not the platform's own REST API (see [API Access](/api-and-integrations/api-access.md)), and it is not an [OAuth App](/api-and-integrations/api-access/oauth-apps.md), which is a third-party client that authenticates against RootCause.
{% endhint %}

***

## Create an application

The builder is one page with three steps, **Basics**, **Start** and **Create**. The progress dots in the top right corner jump between them.

1. **Name your application**: a name, a one-line description, and optional tags. The name is required; the builder will not create anything without one. The slug shown under the name becomes part of the published link.
2. **How do you want to start?**
   * **Blank canvas**: an empty editor.
   * **From a template**: a prewired scaffold you bind to your own data. The built-in templates come first; templates published in your organisation follow, each with a **Bind to this workspace** step that matches what it needs to this workspace's datasets and twins.
3. **Review & create**: a recap of what will be created, then **Create application**.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-d9cbcc5fdcddac680201c7590d5aceb13cc62c5d%2Frc-applications-builder-start.png?alt=media" alt="The New application builder: step 1 Name your application with Name, Description and Tags filled in, step 2 How do you want to start? with the Blank canvas and From a template cards highlighted, and step 3 Review and create with a recap and the Create application button"><figcaption><p>The three steps on one page. Step 2 is where you choose between an empty editor and a template.</p></figcaption></figure>

Six templates ship today, and every one of them is offered whatever you are building.

| Template                    | What it builds                                                                                                                                                              |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Dataset review**          | A named dataset with its source evidence and a full row count, ready for the business measures and review policies you add. Needs no trained twin.                          |
| **Echo API**                | A working endpoint that returns the JSON you send — an **API Request** wired straight to an **API Response**, to put your own processing between.                           |
| **Model monitoring**        | Forecast plus historical accuracy for a live temporal twin. Re-runs when the twin assimilates new data.                                                                     |
| **Static model monitoring** | Scores a static twin on new labelled rows for the target columns you pick.                                                                                                  |
| **Walk-forward study**      | An expanding-window backtest over a dataset: borrows a twin's causal graph, trains or updates at each origin, and scores by lead time.                                      |
| **Frozen model evaluation** | Scores one trained version you pick against held-out actuals, without training, assimilation or auto-update. The latest version is never substituted for the one you chose. |

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-6f49cbdfeb943b98efd308a07daa3db91ee237ff%2Frc-applications-builder-templates.png?alt=media" alt="Step 2 of the builder with From a template selected, listing the curated templates Dataset review, Echo API, Model monitoring, Static model monitoring, Walk-forward study and Frozen model evaluation"><figcaption><p>From a template: the six built-in templates. Templates published in your organisation are listed below them.</p></figcaption></figure>

Creating an application makes a **draft** and opens it in the editor. From a blank canvas it also opens the **Workspace chat** beside the editor, since there is nothing on any canvas yet: describe what the application should do and the chat builds the modules, wiring and widgets into the draft for you. That is how you get an AI-built starting point; there is no separate prompt step in the builder. The Workspace chat is the platform's building agent, and it is always there behind the chat button in the top bar. It is not the assistant an application can offer its own viewers, which is switched on separately (see [The editor](#the-editor)). The **Echo API** template does not open the chat for you; it starts with its request and response already wired. The one exception to the draft rule is **Model monitoring**, which is published to the workspace as it is created, because the twin events that drive it only reach a published application.

### Importing one instead

**Import**, beside **New application**, takes an application that already exists somewhere else: a `.rcapp.json` export, a template your organisation has published, or one of the platform's own. You are shown what it needs before anything is created, and you bind its datasets and twins onto this workspace as you import. The same dialog can import into an existing application rather than a new one.

***

## The editor

Every application is powered by a **flow**: a graph of modules (inputs, transforms, simulations, outputs) wired together on a canvas. Every application gets the same three modes, switched from the **Mode** control at the top of the editor: **Dashboard**, **Flow** and **Test**. An application opens on its **Dashboard**.

| Mode          | What it is for                                                                                                                                                                |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Dashboard** | What viewers see: pages, sections, widgets and the controls they use.                                                                                                         |
| **Flow**      | The data recipe underneath: blocks and the wiring between them. What starts a run is a block too, from the **Events** group of the palette; there is no separate trigger tab. |
| **Test**      | Runs the draft with a payload or an arriving dataset, and shows each run and its timeline.                                                                                    |

Turning on **Offer an assistant** (under **More options** in the **Publish** panel) adds three more modes: **Scope**, **Tools** and **Try assistant**. They are the same for every application, and they disappear again when the assistant is switched off. See [In a chat](#consuming-a-published-application) below.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-a622cf97713c09104f8a4ce905668c98f078a2e5%2Frc-applications-editor-modes.png?alt=media" alt="The editor of a new blank application: the Mode control reads Dashboard, Flow, Test, the empty dashboard canvas says Compose your dashboard, the right-hand panel lists Add widget, Layout, Settings, Credentials, Links, App-wide values and Recent runs, and the Workspace chat is open on the right asking What should we change in Customer Health?"><figcaption><p>A new blank application: the three modes every application has, and the Workspace chat ready to build the flow.</p></figcaption></figure>

The right-hand panel carries everything that is not the canvas: the application's **Settings**, its **Credentials**, its **Links** to other applications, its **App-wide values**, its **Recent runs** (**History** once it is published), and the **Publish** panel.

**Test graph** executes the whole flow against the current draft. Select a module and it becomes **Test to here**; **Run to here** is also on the module's inspector and its right-click menu. Each module then shows its status and a preview of what came out of it.

The **Test options** dropdown beside the button carries two switches that decide what a draft run is allowed to do:

* **Side effects** — **Suppressed** by default, so a test run does not write to your datasets, call external APIs or send notifications. Switch it to **Live** when you want the real thing.
* **Cached results** — **Reusing** by default; switch to **Recompute** to ignore cached module output and run everything again.

### The flow palette

Blocks are grouped by what they do.

| Group               | What it holds                                                                                                                                                                                                                                                                 |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Events**          | What starts the flow: a schedule, a webhook, a dataset arriving, a twin being trained, an interval tick, a widget being clicked, or an event another application emitted.                                                                                                     |
| **Inputs**          | Where data comes in: a saved dataset, a connector import, and the viewer-facing inputs a dashboard widget binds to (scenario controls, table entry, configuration, a time range).                                                                                             |
| **Data Processing** | Reshaping, branching and guarding data: filters, joins, aggregations, conditionals that route rows down different branches, batching, and quality gates.                                                                                                                      |
| **Primitives**      | Single-purpose steps: select, filter, join, union, aggregate, compute, window, resample, pivot, sort, cast and fill; the scalar and JSON operations; and the control steps that switch between cases, wait for inputs to arrive, delay, or poll an endpoint until it answers. |
| **Intelligence**    | The Digital Twin work: intervention, counterfactual, forecast, optimisation, root cause analysis, anomaly detection, training and updating, plus LLM calls, sandboxed Python, and loops.                                                                                      |
| **Outputs**         | Where results land: back into a dataset, out to a connector table or an external API, a Slack or webhook notification, or an event other applications can subscribe to.                                                                                                       |
| **Library**         | Reusable pieces published by your workspace or organisation — see [Reuse](#reuse) below.                                                                                                                                                                                      |

Every block is available in every application: the palette hides nothing and greys nothing out, so an **API Request** or a viewer-input block can go on any flow.

### The dashboard

A dashboard is built from **pages**, each page from **sections**, and each section from **widgets** — charts, tables, KPIs, stat grids, forecasts, text and images, and the interactive ones. Sections use a small set of layout presets rather than a free-form canvas, so a dashboard stays readable at any width.

Within a preset, a widget is sized by the grips on its edges in edit mode. The bottom edge sets its height, anywhere from 120 to 1600 pixels. In a **Stack** section the right edge sets its width as a span out of twelve columns, so widgets sit side by side and wrap onto a new row when the row is full; the other presets offer the height grip only. The widget resizes live as you drag and the size is saved on release; double-clicking a grip resets that axis — to the widget's default height, or to full width. Dragging a widget still reorders it, and once a **Stack** section has widths set, dropping onto the left or right half of another widget places it there in the row.

Sizes are held on the section, so the same widget can differ page to page. They survive export, import and duplication, and an embed through a **Sub-app**. On a narrow screen every widget is full width. A viewer reading the published dashboard gets the sizes and no grips.

A **Stat grid** carries a **Columns** setting of one to four, two by default. At two and above its tiles divide the height the widget has, in rows of that many. At **1, stacked** each stat takes a row of its own at a fixed tile height, and a grid left at its default height grows with the number of stats rather than packing them into a set box — which is how a grid in a narrow column reads as a list rather than a mosaic.

Five widgets let a viewer feed the flow rather than only read it. Each binds to an input module on the flow:

* **Scenario controls** expose simulation parameters within bounds you set.
* **Data input** lets a viewer type or paste the rows the flow runs on.
* **Config controls** expose a configuration the flow reads — what the Walk-forward study template uses. A field set to **Hidden** keeps its value, its default and its URL parameter but draws no control: for a value the page embedding the dashboard has already chosen, such as the unit it navigated to.
* **Time range** asks the viewer over what window, as a row of duration pills. Its settings are **Durations**, **Default**, **Custom range** and **Custom date and time range**. A new one offers `1h`, `6h`, `24h`, `7d` and `30d`, opens on `6h`, and has **Custom range** on and **Custom date and time range** off.
* **Upload** takes a file from the viewer.

**Buttons** are separate: a button does not carry data, it fires an event the flow starts from, so a viewer can re-run a branch on demand.

### Time range settings

| Setting                        | What it takes                                                                                                                                                                    |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Durations**                  | One per line: a whole number and a unit of `m`, `h`, `d` or `w`, up to ten years. `30d = Last month` labels the pill. Duplicates and lines that are not durations offer no pill. |
| **Default**                    | The pill a viewer who has picked nothing gets. The first duration when unset.                                                                                                    |
| **Custom range**               | Adds a **Custom** pill for a window typed to the day.                                                                                                                            |
| **Custom date and time range** | The same, to the minute. With both on, the viewer chooses the precision.                                                                                                         |

Both ends of the window are UTC, and reach the flow on the module's `range` port.

### App-wide values

A control publishes its value to the whole application when its settings carry a **Name** under **Available app-wide**. Every module then reads it as `{{config.<name>}}` anywhere in its own settings, queries included, with no wire between them. With no name, only what is wired to the control reads it.

Controls that can publish: **Config Input**, **Scenario Input**, **Table Input**, **Time Range Input**. A name starts with a lowercase letter, then letters, digits and underscores.

Structured values are read by path. A time range named `range` publishes:

| Token                       | Value                                                       |
| --------------------------- | ----------------------------------------------------------- |
| `{{config.range.from}}`     | Start of the window, UTC                                    |
| `{{config.range.to}}`       | End of the window, UTC                                      |
| `{{config.range.duration}}` | The duration pill, such as `6h`. Empty for a custom window. |

In a **Data Operations** SQL query the value is rendered as a SQL literal, quotes included: write `WHERE ts >= {{config.range.from}}`, not `WHERE ts >= '{{config.range.from}}'`. This applies to Data Operations queries only; a widget's own metric SQL receives the value as plain text.

**App-wide values** in the right-hand panel lists each published name, the control publishing it and the modules reading it.

Every module that reads a name re-runs when the viewer changes it.

### Values from a wired step

**API Call**, **Poll**, **Call LLM** and **Connector Import** read a value into their settings with a `{{...}}` token, the same syntax as an app-wide value. A token here names **the module's own input**, not the step upstream of it: the first segment is the input the value arrives on, and the rest is a path within it. An **API Call** whose `payload` input carries an earlier step's response reads a field of it as `{{payload.exportId}}`; a **Poll** reads `{{request.id}}`, a **Call LLM** prompt reads `{{context.region}}`. With no path, `{{payload}}` puts the whole value in, rendered as JSON. **Connector Import** also reads a payload key bare, as `{{weeksBack}}`.

Tokens are read in the URL, the body, and the header and query parameter keys and values of **API Call** and **Poll**; in the system and user prompts of **Call LLM**; and in the table name or query of **Connector Import**.

**Insert variable** under each of those fields writes the token for you. It lists the steps wired directly into this module, since a step further up the flow never reaches it, and each row names the input the value arrives on and the token it writes. A JSON input opens to pick a field rather than type a path: the fields a **Config Input** declares, or the keys the port carried on the last run. A field a dot path cannot name, one holding a dot, braces, or a leading or trailing space, is not offered. A table input opens to its columns only in **Per Row** mode, where `{{rows.<column>}}` reads the row in hand; in **Once** mode it offers the whole table.

A token that will not resolve is flagged under the field: its first segment names none of the module's inputs, or names an input nothing is wired into, or is `{{secret}}` on an **API Call** or **Poll** with no credential chosen under **Authentication**. `{{config.…}}` and `{{item.…}}` are always accepted. Where the token names a step wired into the module instead of the input it arrives on, the warning carries a one-click **Use `{{...}}`** rewrite to the form that resolves.

**API Call** and **Poll** do not send a request that still holds a token nothing resolves. The run fails naming the field, the token and the reason: `The URL uses {{payload.host}}, but nothing arrived on this module's "payload" input, so the request was not sent.` A test run with **Side effects** on **Suppressed** makes the same check against what is wired, so the problem surfaces in the editor rather than on the first live call.

***

## Publishing

The **Status** column tracks the lifecycle: **Draft** (still being built), **Published** (live and consumable), or **Archived** (retired).

Publishing happens in one panel in the editor's sidebar, and access is the only real decision in it.

* **Who can open it** — one row carrying the level in force and what it means; open it and every level is a described row of its own: **Only me**, **Workspace**, **Organisation**, **Any platform user** (anyone with a platform account, whatever organisation they belong to), or **Public**. Where viewer sign-in is configured, **Platform users and app viewers** appears as a further row below **Any platform user**: it admits every signed-in account, including the free viewer accounts that can only open shared links, which **Any platform user** excludes. Under the row sits a line naming who the application is already shared with and an **Add people** button. An application's current access stays selected when you republish.
* Everything else sits under **More options**, which grows the card downward in place: people and email domains, the version number, release notes, embedding, the assistant and sub-app toggles, the events it sends, and **Available as**.
* Each publish **snapshots an immutable version**, so later edits to the draft never disturb what consumers are using. The panel prefills the next patch version, with major/minor/patch buttons and optional release notes.
* **Available as** is a derived line, not a choice: it follows from the blocks in the flow, the access above, and the assistant and sub-app toggles beside it. Every application is available as a **Web page**; an **API Request** block adds **API**, schedule and webhook blocks add their own, and the assistant toggle adds **Agent tool** once the application is shared beyond you.
* The platform validates before you publish. Blocking problems are listed under **Cannot publish** and the button stays disabled until they are fixed — an application with nothing to publish at all (no widget, no **API Request**, no event block), an **API Request** with no **API Response** to answer through, more than one **API Request**, a widget bound to a module that is gone, a cron expression that does not parse, two controls publishing the same app-wide name or one colliding with a **Config Input** field, two inputs anywhere in the application sharing one **URL parameter** (the reason names every field and module holding it), a **URL parameter** that is not a name a link can carry, a module reading a `{{config.…}}` name nothing publishes. Non-blocking notes are listed separately and do not stop a publish. When the blocker is something collapsed out of sight — a sub-app with nothing exposed, or an embed list that is not exact origins — the reason is named beside the **Publish** button.
* **Only let specific websites embed it** is off until the application carries a list, and while it is off any site may frame the application. Turn it on and list the origins allowed to frame it and drive its shared inputs, one per line. Each must be an exact origin — `https://host` or `http://host:3000`, with no path and no wildcard — or the publish is blocked. A list left empty restricts nothing, and turning the toggle back off discards what you typed.

Published versions are listed with their access level and date. Any of them can be opened and archived, and a live application can be unpublished back to draft.

***

## Consuming a published application

**In the browser**, every application gets a link at `/p/<slug>`, with an embeddable variant for an iframe. Public ones carry the organisation's logo in the header — the "public apps" the [organisation branding settings](/more-details/organisation-administration.md#organisation-details) refer to.

A **table** on a published page hands the viewer the whole result rather than the rows on screen: its download button writes every row of the run behind it as CSV, in query order, with the columns the table hides left out and any on-screen filtering ignored. It is offered on a table fed by a run — one fed by a dataset or a data view has none — and it is refused exactly where the page is, so a public application serves it to anonymous visitors and a narrower one only to those who could already open the page.

**Over REST**, an application whose published version carries an **API Request** block is called at `POST /api/v1/apps/<slug>`. The author chooses on that block whether the endpoint answers synchronously with the response payload, or acknowledges with `202` and a status URL the caller polls. A call that leaves out a value the flow reads is refused before any module runs: where a **Data Operations** block takes its parameters straight from the **API Request** block and its SQL reads `{{params.<key>}}`, the endpoint answers `400` with the code `missing_request_params` and an error naming every field the call did not carry — `missing_request_params: the request has no body.seniorCitizen and body.partner; this application reads them from every request.` Each name is the part of the request the value comes from and its path within it, so a body field reads `body.seniorCitizen` and one taken from the query string reads `query.region`. Only a block the flow runs on every request is asked for up front — one reached through nothing but the **API Request**, a **Config Input**, a **Dataset Source** and other **Data Operations** blocks. A block behind a **Conditional** or a **Switch**, or downstream of anything that may leave its output unwritten on a given run — a **Merge**, a **Code** block, a loop, a **Box** — may never run, so its fields are not demanded and a call that omits them starts a run that fails inside the flow. The publish panel issues and revokes API keys, which callers send as `Authorization: Bearer <key>` or `x-api-key`.

{% hint style="warning" %}
Whether that endpoint demands a key follows from **Who can open it**. Published as **Public**, it answers any caller that knows its address, with no key at all — treat that as making the flow callable by anyone with the URL, and do not put anything behind it that you would not expose. At every narrower access level a key is required. The gate is the published snapshot, so an application only starts serving REST once it has been published with the **API Request** block in it.
{% endhint %}

**On a schedule or an event**, an application runs when its cron fires, when something POSTs to its webhook URL, or when the platform event it subscribes to fires. Events only fire for published applications.

**In a chat**, an application answers as an assistant once **Offer an assistant** is on. You give it a goal and instructions in **Scope** mode, choose its model, and set a **tool policy** that decides how far it may go:

* **read-only** — it answers from what it can read and never acts.
* **tool-use** — it may also set controls and open pages, but never presses a button.
* **agent** — it may additionally press the buttons it has been allowed, asking the viewer to confirm first.

**Try assistant** runs the real assistant against the draft, so you can try the configuration before publishing.

***

## Reuse

Two ways to build something once and use it in several places:

* **Sub-apps** — publishing with **Offer as a sub-app** turned on lets other applications drop this one in as a single block. The terminals you expose become its ports, and the host pins a specific version, so republishing cannot silently change what the host sees.
* **Boxes** — a reusable group of blocks published to the workspace or organisation library, with its own parameters and versions. Boxes appear in the palette under **Library**. What a box drops in as depends on how much it holds. A box whose body holds two or more blocks drops in as a single **Box** node linked to the library entry, pinned to a version and carrying its declared parameter defaults. A box whose body holds exactly one block drops in as an ordinary node of that block's own type, with the box's parameter defaults already baked into its settings: a starting point stamped out from the box rather than a linked instance, so there is no version to follow and no box to open.

Applications can also talk to each other without being nested: one publishes a named event, another subscribes to it. The **Links** panel shows both directions, along with the datasets and twins the application shares with its neighbours.

***

## Applications and the rest of the workspace

* Simulations run by an application appear in the twin's simulation history alongside the ones people started, each carrying an **App** badge. Set the **Ran by** filter to **Apps** to see only those, or to **People** to leave them out.
* Roles carry a dedicated **Applications** permission area controlling who can build and manage them. See [Organisation Administration](/more-details/organisation-administration.md).
* An application's lineage is visible from the data side: a Dataset's lineage traces through the Digital Twin to the reports and applications built on it.
