> 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/digital-twin/simulation-types/best-action.md).

# Best Action

Best Action runs the [Digital Twin](/more-details/digital-twin.md)'s counterfactual reasoning in reverse: given a record you want to change, what is the smallest set of changes that would reach a target outcome?

Where an [optimization](/more-details/digital-twin/simulation-types/optimization.md) searches a whole population for the combination that pushes a metric furthest, Best Action starts from specific records — individual customers, machines, or cases — and finds the minimal action for each one. A [prediction](/more-details/digital-twin/simulation-types/prediction.md) tells you a customer is likely to churn; an [intervention](/more-details/digital-twin/simulation-types/intervention.md) tells you what one change would do across everyone; Best Action tells you the fewest changes that would turn *this* customer from churn to retained, and where each option leaves the odds of the target.

For the workflow that produces a Digital Twin in the first place, see [Step 5: Build Digital Twin](/user-guide/creating-digital-twin.md).

***

## Starting a simulation

Open a Digital Twin from the Digital Twins list, open its **Simulations** row (or click **Run Simulation** in the toolbar), then click **New Simulation**. This opens the [type picker](/more-details/digital-twin/simulation-types.md) — choose **Best Action**.

The setup form has five numbered sections plus a configuration summary at the foot. Only the first two (the target outcomes and the records to find actions for) are required; what can change, the limits on those changes and the change limit refine the search but can be left at their defaults on a first pass.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-8b82ab6aab8a0f0c9b34ecca8a314bb521ecf21e%2Frc-best-action-setup-blank.jpeg?alt=media" alt="The empty Best Action setup form: Target outcomes with the target variable and value unset, and Baseline states reading No records added yet"><figcaption><p>The empty setup form. The stepper at the top walks the sections in order; nothing runs until a target and at least one record are set.</p></figcaption></figure>

***

## Targets and baselines

Two ideas drive every Best Action run.

**The target.** The outcome you want each record to reach — *Churn = No*, *Defect = Pass*, *Approved = Yes*. This is the destination the solver works back from.

**The baseline states.** The records you want to move there — the customers at risk, the cases that failed, the units that fell out of spec. Each row is a starting point; the solver treats them independently and returns one action plan per row. This is what sets Best Action apart from the other simulation types: it operates on concrete records, not the population as a whole.

***

## Step 1: Target outcomes

Set the outcome the solver must reach. Each target is a variable and the value it should take — for example `Churn` set to `No`.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-ac5d64fddb41ec534ce9f4deee8246bafb442e0e%2Frc-best-action-target.png?alt=media" alt="Section 1 of the Best Action form with target variable Churn and target value No, a note reading Match mode: Exact — tolerance and direction modes apply only to numeric targets, and the Baseline states section starting below"><figcaption><p>A target outcome, Churn set to No. Categorical targets carry a success line; numeric targets add tolerance and direction modes. The Baseline states section, covered next, starts below.</p></figcaption></figure>

For a categorical target such as `Churn`, a **Counts as met at** slider sets the success line: how sure the model has to be before a record counts as having reached the target. At its lowest position the chosen value only has to be the most likely outcome; from there the line runs up to a 95% chance or more. Records the model already puts above the line are not searched at all, and every option the solver returns has to clear it.

Numeric targets have no probability to reach, so they carry no success line. They unlock tolerance modes (reach within a band) and direction modes (at least, at most) instead, and moving a target from a categorical variable to a numeric one drops the line. **+ Add target** stacks a second outcome the solver must satisfy at the same time.

***

## Step 2: Who to find actions for

The records to act on. Each row is a starting state the solver will try to nudge to the target. On a static twin a toggle at the top of the step chooses where they come from.

**From a prediction.** The records at risk, taken straight from a completed [Prediction](/more-details/digital-twin/simulation-types/prediction.md) of the step 1 target on this twin. The step lists those runs with their record count and when they finished; picking one previews what it would act on — how many of its records sit below the line, out of how many it scored, and the riskiest few by name. The line is the step 1 success line, or 50% where the target has none, and the records arrive ranked, most at risk first. A run takes at most 500 of them; where more sit below the line, the 500 most at risk are the ones used. If the twin has no completed prediction of the target, the step says so and offers **Start a prediction** instead.

Records ticked on a prediction's **Who's at risk** panel arrive here already chosen — "21 records picked on the prediction page" — and **Use everyone below the line instead** widens the run back to the whole group.

The rows are read from the prediction when the run starts rather than copied into the setup, so the run names that prediction as its parent in the twin's lineage bar, and reopening it leads back to where its records came from.

**Enter records.** The records supplied directly, in one of three ways:

* **Field Input** — enter records by hand in a table, one column per record. Category and boolean variables offer a dropdown of their known values; numeric, date and free-text variables are typed in. Best for a handful of cases.
* **File Upload** — drop in a CSV. The file must carry every variable in the twin as a column; the panel lists the exact set it expects.
* **Dataset** — pull rows straight from an existing [dataset](/user-guide/data-views.md), the natural choice when the records you care about already live in the workspace.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-31be30d0416105e81af7e6bc69a5fe2554331a65%2Frc-best-action-baseline-fields.png?alt=media" alt="The Baseline states section on the Field Input tab, showing an Input Records table with a column per record and a value set for every variable — customerID, gender, SeniorCitizen, Partner, tenure, Churn, and so on"><figcaption><p>Field Input. Each record is a column; set a value for every variable to define a starting state.</p></figcaption></figure>

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-89403f1a376a788959881162a26b2f1dc510b64c%2Frc-best-action-baseline-upload.png?alt=media" alt="The Baseline states section on the File Upload tab, with a dashed drop zone reading Click to Upload CSV File and the list of required columns the file must contain"><figcaption><p>File Upload. The CSV must contain every variable the twin uses as a column.</p></figcaption></figure>

The more representative the baseline records, the more actionable the result — a row that is already close to the target needs only a small change, which is exactly the recommendation Best Action is designed to surface.

***

## Step 3: What can change

**Variables the solver may change** lists every variable in the twin, ticked to begin with. Untick the fields a business cannot move (a customer's age bracket, their elapsed tenure, their demographics) and they are held fixed for every record, so no recommendation can propose touching them. Target variables are never listed, since the solver has to be free to predict them; variables the twin itself cannot change are listed but disabled, with the reason.

***

## Step 4 (optional): Limits on those changes

Narrows the values a changeable variable may take: a **Range** between a low and a high, or a **Set** of allowed values. A variable with no entry here can take any value it was trained on. Holding a variable still belongs to the previous step, so this one has no fixed option.

**Auto Generate Constraints** proposes sensible bounds from the variables in the twin. It fills only the variables that have no entry yet, leaving your own limits and the variables you unticked alone, and it never constrains a target. Each suggestion can then be edited by hand.

***

## Step 5: Limit changes

The defining control of Best Action. **Max changes** caps how many variables the solver may change in any single record's recommendation. A low cap yields simple, actionable plans — change one or two things — while a higher cap lets the solver reach harder targets at the cost of asking more of the operator.

This is the knob that makes the result a *next best action* rather than a wholesale redesign: it forces the solver to find the shortest path to the target, not merely a path.

**Recommend by** decides which of the options that reach the target becomes each record's plan. **Fewest changes**, the default, takes the smallest set of changes that gets there and breaks ties on the stronger result; the search stops one size past the smallest plan that works. **Strongest result** takes the option that clears the target by the widest margin and searches every size up to the cap, so the run takes longer. The result page carries the same toggle, so the choice can be changed after the run without starting a new one.

***

## Configuration Summary

The card at the foot of the form mirrors back the run in plain English: the target and its success line ("Churn should be No (at a 70% chance or more)"), the number of starting records, the change cap and how each record's plan is picked, alongside the version of the twin the simulation will run against.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-6f799d6802c43b992dec24a15b08b3326ae97fba%2Frc-best-action-new-simulation-ready.jpeg?alt=media" alt="The populated Best Action form with target Churn = No, match mode Exact, and a Baseline states table of customer records on the Field Input tab"><figcaption><p>The populated form: target Churn = No and the baseline records in place. <strong>Run Simulation</strong> launches the search across every baseline record.</p></figcaption></figure>

***

## Reading the result

A completed run carries a green **Completed** pill and the actions **Create app** and **Edit Config**; its duration is shown on the run's row in the list. To get a PDF, tick the run in the list, click **Export 1 as report**, and use **Export PDF** in the report editor.

**Best actions across this group** opens with a sentence counting how many records need a change to meet the goal, how many already meet it, and the change that gets most of them there. **Export plan** beside it writes one row per record as CSV or JSON: the record, its status, the value it sits at now, its plan, how many changes that plan makes, whether the plan reaches the target, the value after, and the shift between the two.

Every record carries a status against the target:

* **Already there**: the model already puts the record at the goal, so it was not searched. Any options it shows are ways to raise it further.
* **Reached with a change**: at least one option gets the record there.
* **Can't reach**: nothing the search found gets the record all the way there, so the record shows the closest option instead.
* **Not evaluated**: the engine could not search this record, so its empty result is not evidence that no action exists.

**Changes that get there** gives one row to each set of changes the run returned, single or combined, ranked by how many of the records that need a change it gets there, then by how few changes it makes. Each row carries that count, the average shift, and the range the target ends up in. A record counts in a row only when that exact set reaches the target, so a combination's result is never credited to one of the changes inside it.

Below it, **Per record** lists one block per baseline record, most at risk first, with filter chips for each status and a **By record** / **Compare** toggle. A run with more than three records opens on **Needs a change**, which is every record that does not meet the goal today. Each block is named after the record's id column where the data has one, falling back to "Record 1", "Record 2", … and shows one plan: the option **Recommend by** selects, with the rest under **Other ways**. **Left out** collects the options the page will not recommend, being those that change nothing, lower the chance, get no closer, take a numeric target out of its range, or add changes that do no better than one of their own parts.

**Compare** turns the list into a grid: the records down the side, every change and combination the run returned across the top, and each record's value now and its plan at either end. A cell is where that set of changes leaves the record, green where it gets there, red where it makes things worse, with a ring on the record's plan and a dot where the run did not return that set for that record.

Because the recommendations are per record, the result is a worklist rather than a single headline: records fixed by one change are the cheapest wins, while records that need several changes can be triaged or set aside.

<figure><img src="https://1662811113-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBXg3gZLR0e2Q8SzeQmql%2Fuploads%2Fgit-blob-3fa6827736b564e683598597f4c8fc1253027255%2Frc-best-action-result.png?alt=media" alt="A completed Best Action run: the Best actions across this group table listing each change with how many records it fixes and where the target&#x27;s odds end up, above the Per record breakdown with its By record and Compare toggle, where Record 1 lists Option 1, 2 and 3, each marked DO ALL 2 and trailed by 56% to 94%"><figcaption><p>A completed run: the cross-record summary and its table of changes above, the per-record list below.</p></figcaption></figure>

***

## Past simulations

Past runs appear in the twin's run list, which the **Simulations** row on the twin's Home opens. The workflow is the same as for any other simulation type — see [Prediction › Past simulations](/more-details/digital-twin/simulation-types/prediction.md#past-simulations).

***

## Outside the UI

This simulation runs from the Python SDK and from the REST API, with the same configuration the wizard collects.

```python
>>> twin.score(rows, targets=[{"variable": "Churn", "value": "No"}])
```

Over REST it is a scenario on `POST /api/v1/workspaces/{workspaceId}/simulations` with `"type": "counterfactual"`, or `temporal_counterfactual` / `panel_counterfactual`. The SDK verb runs the batch scoring endpoint, which returns paged per-row verdicts and keeps any non-variable column as the row label. A static run can take its records from a completed prediction instead of carrying them: `scenario.samplesFrom` on the simulations endpoint, or `fromRunId` on the scoring endpoint.

See [Working with Digital Twins](/api-and-integrations/sdk-getting-started/sdk-working-with-twins.md) for the SDK, and [Running Simulations over REST](/api-and-integrations/rest-running-simulations.md) for the request body.

***

## Other Simulation Types

* [Prediction](/more-details/digital-twin/simulation-types/prediction.md) — predict an outcome for a specific input.
* [Intervention](/more-details/digital-twin/simulation-types/intervention.md) — change a single variable and observe propagation.
* [Optimization](/more-details/digital-twin/simulation-types/optimization.md) — find the input combination that maximizes or minimizes a target.
* [Explanation](/more-details/digital-twin/simulation-types/explanation.md) — understand the drivers and impacts behind an outcome.
* [Root Cause Analysis](/more-details/digital-twin/simulation-types/root-cause-analysis.md) — diagnose the cause of a specific abnormal value.
* [Anomaly Scan & Diagnosis](/more-details/digital-twin/simulation-types/anomaly-scan.md) — scan every variable for anomalies and diagnose each one.

See [Step 6: Run Simulations](/user-guide/simulations.md) — general overview.
