---
title: "Register a Policy Decision Workflow"
description: "Register a Policy Decision Workflow that chains rules and blocks into a decision."
---

<div style="padding: 14px 16px; border: 0.5px solid #AFA9EC; border-radius: 12px; background: #EEEDFE; margin-bottom: 1.5rem;">
  <div style="display: flex; align-items: center; gap: 7px; margin-bottom: 8px;">
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
      <path d="M9 2L4 9h4l-1 5 5-7H8l1-5z" stroke="#7f77dd" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
    </svg>
    <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.05em; text-transform: uppercase; color: #534AB7;">TL;DR</span>
  </div>
  <div style="font-size: 13px; color: #3C3489; line-height: 1.65;">
    Build a Policy by chaining decision blocks (Application Decision, Offer Creation, Derived Variable, Offer Filtering, Offer Optimization) into a Decision Workflow, then validate it with Simulation, Comparison, and Single Record Review.
  </div>
</div>

Policy (Prospecting, Underwriting, Customer Management) is the module where you design and manage decision workflows that govern how applications are evaluated across the customer lifecycle: from prospecting and loan decisioning to ongoing customer management.

With **Policy Registry**, you can:

- Define end-to-end decision flows covering approval rules, offer generation, filtering, and optimization for products like credit cards, auto loans, and personal loans.
- Run **Simulation** jobs to test your decision logic against real-world data and review results in built-in dashboards.
- Use **Single Record Review** to trace exactly how a specific application moved through your workflow: which rules passed or failed, what offers were generated, and more.
- Run **Comparison** jobs to simulate champion-challenger scenarios and refine your strategies as market conditions evolve.

## How Decision Workflows Are Structured

Every policy is built around a **Decision Workflow**: a sequence of blocks that define each step in your evaluation process. The standard flow for an underwriting policy looks like this:

1. Evaluate whether the application meets your credit criteria (Application Decision).
2. If approved, generate a set of offer combinations (Offer Creation).
3. Calculate any derived values needed from the offer parameters, such as APR or monthly payment (Derived Variable).
4. Filter out offers that don't meet business or product rules (Offer Filtering).
5. Optionally, auto-select the best offer based on a defined goal (Offer Optimization).

There are five block types available when building your workflow:

<table class="attr-table">
  <thead>
    <tr>
      <th style="width: 22%;">Block Type</th>
      <th>What It Does</th>
      <th style="width: 15%;">How Many Allowed</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="field-cell">Application Decision</td>
      <td>Define rule-based logic to approve, deny, or send applications for manual review based on criteria such as FICO score, DTI, and bureau data.</td>
      <td>Multiple</td>
    </tr>
    <tr>
      <td class="field-cell">Offer Creation</td>
      <td>Configure the parameters for generating offer combinations, such as loan amount, interest rate, and term.</td>
      <td>One (optional)</td>
    </tr>
    <tr>
      <td class="field-cell">Derived Variable</td>
      <td>Compute policy-specific values from offer parameters (for example, APR or monthly payment) that can be referenced in later blocks.</td>
      <td>Multiple</td>
    </tr>
    <tr>
      <td class="field-cell">Offer Filtering</td>
      <td>Apply business or product rules to remove ineligible offers, for example partner restrictions or maximum loan limits.</td>
      <td>Multiple</td>
    </tr>
    <tr>
      <td class="field-cell">Offer Optimization</td>
      <td>Evaluate all generated offers against a defined goal (such as maximizing return) and select the best-fit offer.</td>
      <td>One (optional)</td>
    </tr>
  </tbody>
</table>

:::tip
Offer-related blocks are only available after you have added an Offer Creation block.
:::

## How to Register a Policy Decision Workflow

<details open class="step">
<summary>

#### Choose the Right Module

</summary>

Navigate to the module that matches your policy's purpose, then click **+ New Policy** to begin:

- **Prospecting** - for identifying individuals to target with pre-approval offers or mailers.
- **Underwriting** - for loan application approval or denial workflows.
- **Customer Management** - for generating personalized offers for existing customers.

</details>

<details open class="step">
<summary>

#### Add Metadata

</summary>

Give your policy a name, and set its **Attributes** (the product, entity, and scope it runs against). Optionally add a **Description**, **Group**, and **Keywords** under Properties to help your team find and govern it later.

</details>

<details open class="step">
<summary>

#### Build Your Decision Workflow

</summary>

This is where you design the logic your policy will execute. See the next section for a full walkthrough.

</details>

## Building Your Decision Workflow

When you open the **Decision Workflow** tab, you'll see a canvas with **+** to edit.

:::note
Toggle **Edit Mode** on at the top of the page to add or modify blocks.
:::

<figure>
  <img src="/images/how-tos/register-policy-decision-workflow/create-a-policy-05-decision-workflow.png" alt="Decision Workflow canvas with multiple blocks connected end to end." class="screenshot" loading="lazy" />
  <figcaption>The Decision Workflow canvas chains blocks together; click <strong>+</strong> between blocks to insert a new one.</figcaption>
</figure>

### Adding Blocks

Click the connecting dots on the canvas to insert a new block. The platform shows only the block types that are valid at that position: the options depend on what comes before and what comes after. A few rules of thumb cover most cases:

- **Application Decision blocks come first.** They precede Offer Creation and never follow an offer-related block.
- **Offer Creation can appear at most once**, and only after the Application Decision phase. Once an Offer Filtering block exists, no further Offer Creation is allowed.
- **Offer Filtering blocks follow Offer Creation** and can be stacked. They cannot appear before Offer Creation or after Offer Optimization.
- **Offer Optimization can appear at most once** and sits at the end of the offer phase. Nothing offer-related can follow it.
- **Derived Variable can be inserted almost anywhere**: it produces a value, not a decision. It's the only block type that can be added after Offer Optimization.

### Configuring Each Block

Click any block to open its configuration panel. What you configure depends on the block type.

#### Application Decision and Offer Filtering

Both block types share the rule builder. Each rule is a pass/fail condition over registered Data Elements, Features, or upstream Derived Variables. Combine rules with AND/OR, then map the pass and fail outcomes in [Setting Pass/Fail Paths](#setting-passfail-paths) below.

<figure>
  <img src="/images/how-tos/register-policy-decision-workflow/create-a-policy-06-rule-builder.png" alt="Rule builder used to define pass/fail conditions inside an Application Decision block." class="screenshot" loading="lazy" />
  <figcaption>The rule builder is shared between Application Decision and Offer Filtering blocks.</figcaption>
</figure>

#### Offer Creation

Define the possible values for each offer parameter (loan amount, interest rate, term, and so on). The platform generates every offer combination by taking the **Cartesian product** of the values you provide.

For example, with these parameters:

- `potential_loan_amount`: `10,000`, `15,000`
- `potential_term`: `24`, `36`
- `potential_interest_rate`: `0.05`

the platform generates 4 offers (2 × 2 × 1):

<table class="attr-table">
  <thead>
    <tr>
      <th style="width: 12%;">Offer</th>
      <th>Potential Loan Amount</th>
      <th>Potential Term</th>
      <th>Potential Interest Rate</th>
    </tr>
  </thead>
  <tbody>
    <tr><td class="field-cell">1</td><td>10000</td><td>24</td><td>0.05</td></tr>
    <tr><td class="field-cell">2</td><td>10000</td><td>36</td><td>0.05</td></tr>
    <tr><td class="field-cell">3</td><td>15000</td><td>24</td><td>0.05</td></tr>
    <tr><td class="field-cell">4</td><td>15000</td><td>36</td><td>0.05</td></tr>
  </tbody>
</table>

These offers then flow into any Derived Variable, Offer Filtering, and Offer Optimization blocks that follow.

#### Derived Variable

Define a new calculated value that downstream blocks can reference. Fill in:

- **Type**: the output type (for example, `Numerical`, `String`, `Boolean`).
- **Alias** (required): the identifier the rest of the workflow uses to refer to this value.
- **Description** (required): a one-line plain-language explanation.
- **Input**: objects the definition reads. The dropdown lets you pick from offer config parameters, global variables defined in upstream Application Decision blocks, global functions, Data Elements, Features, and Models.
- **Definition**: the Python expression that returns the value.

**Example: compute monthly payment from the Offer Creation parameters above.**

<table class="attr-table">
  <thead>
    <tr>
      <th style="width: 22%;">Field</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="field-cell">Type</td>
      <td><code>Numerical</code></td>
    </tr>
    <tr>
      <td class="field-cell">Alias</td>
      <td><code>monthly_payment</code></td>
    </tr>
    <tr>
      <td class="field-cell">Description</td>
      <td>Monthly payment for the offer, derived from loan amount, term, and interest rate.</td>
    </tr>
    <tr>
      <td class="field-cell">Input</td>
      <td><code>potential_loan_amount</code>, <code>potential_term</code>, <code>potential_interest_rate</code></td>
    </tr>
    <tr>
      <td class="field-cell">Definition</td>
      <td><code>r = potential_interest_rate / 12</code><br><code>n = potential_term</code><br><code>p = potential_loan_amount</code><br><code>return p * r * (1 + r) ** n / ((1 + r) ** n - 1)</code></td>
    </tr>
  </tbody>
</table>

Downstream blocks (Offer Filtering, Offer Optimization) can then reference `monthly_payment` like any other input (for example, to filter out offers whose monthly payment exceeds a borrower's DTI cap).

#### Offer Optimization

Define the objective (for example, maximise return) and the selection logic. The block evaluates every remaining offer against the objective and picks one as the best-fit offer.

Offer presentation order is configured separately in the **Offer Display** tab, not as a workflow block.

### Setting Pass/Fail Paths

Most blocks require you to define what happens when an application passes or fails the block's rules. For each outcome, you can configure:

- **Auto Denial**: the application is declined at this step.
- **Manual Review**: the application is routed for human review.
- **Auto Approval**: the application is approved at this step.
- **Continue**: the application moves to the next block.

Offer Creation and Derived Variable blocks do not have pass/fail paths: all records pass through them automatically.

:::caution
The platform validates that every block has all required outcome paths defined before letting you save.
:::

### A Simple Example

Here is what a two-block underwriting workflow might look like:

- **Block 1: Minimum Credit Eligibility Check (Application Decision)**: Applications with a FICO score below the threshold are auto-denied. Those that pass continue to Block 2.
- **Block 2: Bureau Derogatory Limit Check (Application Decision)**: Applications with too many derogatory marks are sent to manual review. Those that pass are auto-approved.

You can expand this by adding an Offer Creation block after the Application Decision blocks, then layering in filtering and optimization logic.

To delete a block, click the delete option on the block. Note that deleting the **Offer Creation** block will also remove all offer-related blocks that follow it.

## What You Can Do with a Policy

Once your workflow is saved, you have several options for testing and evaluating it:

### Simulation Jobs

Run a **Simulation** job to execute your policy against a dataset and review the results in built-in dashboards. Dashboards include metrics like approval rates, offer distribution, and per-block outcomes.

### Single Record Review

Use **Single Record Review** to trace how a single application moves through your decision workflow. You can see which rules passed or failed at each block, what offers were generated, what Derived Variable values were calculated, and the final outcome.

## How to Read the Policy Output

Each simulation produces a row per application (or per application × offer, once offers are generated). The columns are emitted by the blocks in your workflow:

<table class="attr-table">
  <thead>
    <tr>
      <th style="width: 30%;">Column</th>
      <th>Emitted by</th>
      <th>What it contains</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="field-cell">application_decision_box_1, application_decision_box_2, …</td>
      <td>Each Application Decision block, in order</td>
      <td>The per-block outcome for that application: <code>AUTO_DENIAL</code>, <code>MANUAL_REVIEW</code>, <code>AUTO_APPROVAL</code>, or empty if the application never reached the block.</td>
    </tr>
    <tr>
      <td class="field-cell">application_decision_output</td>
      <td>Roll-up across all Application Decision blocks</td>
      <td>The effective outcome from the Application Decision phase. Taken from the last non-empty <code>application_decision_box_n</code> column.</td>
    </tr>
    <tr>
      <td class="field-cell">potential_loan_amount, potential_term, potential_interest_rate, …</td>
      <td>Offer Creation</td>
      <td>One column per offer parameter you configured. For applications that reach Offer Creation, each generated offer is a row.</td>
    </tr>
    <tr>
      <td class="field-cell">Derived Variable aliases (e.g. <code>monthly_payment</code>, <code>apr</code>)</td>
      <td>Each Derived Variable block</td>
      <td>One column per Derived Variable, named with the alias you chose. Appears in the order the blocks are defined.</td>
    </tr>
    <tr>
      <td class="field-cell">offer_filtering_box_1, offer_filtering_box_2, …</td>
      <td>Each Offer Filtering block, in order</td>
      <td>The per-block outcome on the offer: <code>AUTO_DENIAL</code>, <code>MANUAL_REVIEW</code>, <code>AUTO_APPROVAL</code>, or empty if the offer never reached the block.</td>
    </tr>
    <tr>
      <td class="field-cell">offer_filtering_output</td>
      <td>Roll-up across all Offer Filtering blocks</td>
      <td>The effective outcome from the Offer Filtering phase, taken from the last non-empty <code>offer_filtering_box_n</code> column.</td>
    </tr>
    <tr>
      <td class="field-cell">is_optimized</td>
      <td>Offer Optimization</td>
      <td><code>True</code> for the offer the optimization block selected as best-fit, <code>False</code> for the rest.</td>
    </tr>
  </tbody>
</table>

The same columns appear in **Single Record Review**, presented per-block so you can trace exactly where an application or offer was decided.

---

Next: [Run a Simulation →](/user-guide/how-tos/run-a-simulation/)
