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

# Assessing Risk in FrankieOne

> This guide provides a comprehensive overview of how to implement and configure the Risk-Based Onboarding Engine in FrankieOne v2.0.0. It covers the core concepts of risk calculation, workflow integration, and the detailed configuration of risk factors.

## How Risk Works in Workflows

The FrankieOne v2 Risk Engine transforms static, check-based verification into a dynamic, intelligence-driven framework. This enables you to design sophisticated, risk-based user journeys where the level of due diligence is directly proportional to the calculated risk of an entity.

### Core Concepts

<AccordionGroup>
  <Accordion title="Entity">
    The subject of the risk assessment, which can be an <code>INDIVIDUAL</code> or an <code>ORGANIZATION</code>.
  </Accordion>

  <Accordion title="Service Profile">
    A container for a specific customer context (e.g., KYC Onboarding). Each service profile is assigned one and only one risk profile.
  </Accordion>

  <Accordion title="Risk Profile">
    The central JSON configuration that governs all risk calculations. It defines the risk <b>levels</b> (e.g., LOW, MEDIUM, HIGH) and the <b>factors</b> to be evaluated.
  </Accordion>

  <Accordion title="Risk Factor">
    A specific rule that extracts data, applies scoring logic, and produces a numerical score. Each factor is an independent unit of risk logic.
  </Accordion>

  <Accordion title="Risk Assessment">
    The final output of a risk evaluation generated at the end of a workflow. It contains the aggregated score, the corresponding risk level, and a detailed list of all contributing factors.
  </Accordion>
</AccordionGroup>

### The Risk Assessment Lifecycle

Risk is assessed in a multi-stage process integrated directly into the workflow execution.

<Steps>
  <Step title="Workflow Start">
    The system loads all <code>VALID</code> risk factors already associated with the entity's service profile into the workflow's context. The initial risk score is based on this pre-existing state.
  </Step>

  <Step title="Data Collection Steps">
    As the workflow progresses, steps like AML, IDV, and Onboarding Fraud checks generate <code>processResults</code>, which serve as the source data for many risk factors.
  </Step>

  <Step title="Risk Calculation">
    A dedicated <code>RISK</code> step is triggered. This step evaluates all factors defined in the risk profile, applies the configured scoring logic (<code>scoreMethod</code>), and aggregates the results to produce a final score for each factor.
  </Step>

  <Step title="Workflow Finish">
    The scores from all evaluated risk factors are summed to produce the final <code>workflowRiskScore</code>. This score is then mapped to a <code>workflowRiskLevel</code>. The complete <code>RiskAssessment</code> is added to the <code>workflowResult</code>.
  </Step>

  <Step title="Persistence">
    The new risk assessment is persisted. Any previous factors whose underlying data has changed are marked as <code>STALE</code>.
  </Step>
</Steps>

### Risk Factor Statuses

Each risk factor has a status to manage its lifecycle and ensure calculation accuracy:

| Status         | Description                                                                                                                                                                                        |
| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **VALID**      | The default status. The factor is current and contributes to the risk score.                                                                                                                       |
| **STALE**      | The underlying data used to generate the factor has changed (e.g., an entity's address was updated). Stale factors are excluded from future calculations and are replaced by new, `VALID` factors. |
| **OVERRIDDEN** | A user has manually overridden the factor's score. The `manualOverrideScore` will be used instead of the system-calculated score.                                                                  |
| **DISCARDED**  | The factor was generated during a workflow but later deemed irrelevant within the same execution. It is excluded from the final risk assessment.                                                   |

***

## Risk Profile Configuration

The entire risk engine is driven by the `risk_profiles.json` configuration file. Each profile defines the `levels` and `factors` for risk calculation.

### `levels` Schema

The `levels` array defines the qualitative risk bands and their corresponding numerical score ranges.

```json theme={null}
"levels": [
    {
        "label": "LOW",
        "range": { "min": 0, "max": 40 }
    },
    {
        "label": "MEDIUM",
        "range": { "min": 41, "max": 70 }
    },
    {
        "label": "HIGH",
        "range": { "min": 71, "max": 90 },
        "extra": {
            "GenerateIssue": {
                "category": "RISK",
                "issue": "RISK_THRESHOLD_HIGH",
                "severity": "REVIEW"
            }
        }
    },
    {
        "label": "UNACCEPTABLE",
        "range": { "min": 91 },
        "extra": {
            "GenerateIssue": {
                "category": "RISK",
                "issue": "RISK_THRESHOLD_UNACCEPTABLE",
                "severity": "BLOCK"
            }
        }
    }
]
```

* **label**: The name of the risk level (e.g., `LOW`, `HIGH`).
* **range**: Defines the numerical score boundaries. `min` and `max` are inclusive.
* **extra.GenerateIssue**: An optional object that instructs the engine to generate a workflow issue when the risk level is reached.

### `factors` Schema

The `factors` array lists all risk factors to be evaluated. Each factor object is a rule for calculating a specific risk score component.

```json theme={null}
{
    "name": "fraud_email",
    "description": "Fraud Email Signal",
    "default": "LOW",
    "scoreMethod": "lookup",
    "aggregate": "max",
    "defaultScore": {
        "name": "default",
        "score": 0,
        "value": "LOW",
        "flags": ["include_zero"]
    },
    "scores": [
        { "value": "LOW", "score": 0, "flags": ["include_zero"] },
        { "value": "MEDIUM", "score": 10 },
        { "value": "HIGH", "score": 20 },
        { "value": "UNACCEPTABLE", "score": 30 },
        { "value": "UNKNOWN", "score": 40 }
    ]
}
```

* **name**: The unique identifier for the risk factor (e.g., `fraud_email`).
* **description**: A human-readable description for display and auditing.
* **handler**: The name of the internal service responsible for data extraction and scoring logic (e.g., `jurisdiction_lookup`).
* **scoreMethod**: The method for assigning a score (`lookup`, `lookup_range`, `bool`).
* **aggregate**: The method for aggregating multiple scores into a single factor score (`sum`, `max`, `min`, `average`, `count`). This applies only when a handler returns multiple values.
* **scores**: An array of objects defining the value-to-score mapping.
* **defaultScore**: A fallback score to apply if the input data cannot be mapped via the `scores` array.

***

## Risk Factor Library

This section provides a comprehensive catalogue of the risk factors available in the FrankieOne platform. Each factor is designed to assess a specific element of risk during the onboarding process.

### KYC Risk Factors (Individuals)

These factors are applicable to `INDIVIDUAL` entities and focus on identity attributes, jurisdiction, and AML signals.

<AccordionGroup>
  <Accordion title="entity_age">
    Calculates risk based on the individual's age at the time of workflow execution.

    <ul>
      <li><b>Handler</b>: <code>entity\_age</code></li>
      <li><b>Score Method</b>: <code>lookup\_range</code></li>
      <li><b>Data Source</b>: <code>individual.dateOfBirth.normalized</code></li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "entity_age",
          "description": "Calculates risk based on the entity's age.",
          "handler": "entity_age",
          "scoreMethod": "lookup_range",
          "scores": [
              { "name": "Minor", "range": { "max": 17 }, "score": 100 },
              { "name": "Young Adult", "range": { "min": 18, "max": 25 }, "score": 15 },
              { "name": "Standard Adult", "range": { "min": 26 }, "score": 0 }
          ],
          "defaultScore": { "value": "N/A", "score": 80 }
      }
    ```
  </Accordion>

  <Accordion title="document_type">
    Applies risk based on the type(s) of identity documents provided. It aggregates scores if multiple documents are present.

    <ul>
      <li><b>Handler</b>: <code>document\_type\_lookup</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Aggregate Method</b>: <code>max</code> (Typically used to take the score of the riskiest document)</li>
      <li><b>Data Source</b>: <code>individual.documents.IDENTITY\[].type</code></li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "document_type",
          "description": "Scores risk based on the provided document type.",
          "handler": "document_type_lookup",
          "scoreMethod": "lookup",
          "aggregate": "max",
          "scores": [
              { "value": "PASSPORT", "score": 5 },
              { "value": "DRIVERS_LICENSE", "score": 10 },
              { "value": "UTILITY_BILL", "score": 40 }
          ],
          "defaultScore": { "value": "No Documents", "score": 0 }
      }
    ```
  </Accordion>

  <Accordion title="nationality_risk">
    Scores risk based on the provided nationality.

    <ul>
      <li><b>Handler</b>: <code>jurisdiction\_lookup</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Aggregate Method</b>: <code>max</code></li>
      <li><b>Data Source</b>: <code>individual.nationality</code></li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "nationality_risk",
          "description": "Scores risk based on the provided nationality.",
          "handler": "jurisdiction_lookup",
          "config": { "source": "nationality" },
          "scoreMethod": "lookup",
          "aggregate": "max",
          "scores": [
              { "value": "IRN", "score": 100 },
              { "value": "RUS", "score": 50 },
              { "value": "AUS", "score": 0 }
          ],
          "defaultScore": { "value": "Other", "score": 30 }
      }
    ```
  </Accordion>

  <Accordion title="residential_country_risk">
    Scores risk based on the current residential address country.

    <ul>
      <li><b>Handler</b>: <code>jurisdiction\_lookup</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Aggregate Method</b>: <code>max</code></li>
      <li><b>Data Source</b>: <code>individual.addresses\[]</code> where <code>type</code> is <code>RESIDENTIAL</code></li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "residential_country_risk",
          "description": "Risk based on the current residential address country.",
          "handler": "jurisdiction_lookup",
          "config": { "source": "address", "addressType": "RESIDENTIAL" },
          "scoreMethod": "lookup",
          "aggregate": "max",
          "scores": [
              { "value": "NGA", "score": 70 },
              { "value": "AUS", "score": 5 }
          ],
          "defaultScore": { "value": "Other", "score": 30 }
      }
    ```
  </Accordion>

  <Accordion title="AML Factors (is_pep, has_sanctions, etc.)">
    These boolean factors check for the presence of corresponding data in AML `processResults`.

    <ul>
      <li><b>Handlers</b>: <code>is\_pep</code>, <code>has\_sanctions</code>, <code>has\_adverse\_media</code>, <code>on\_watchlist</code></li>
      <li><b>Score Method</b>: <code>bool</code></li>
      <li><b>Data Source</b>: <code>processResults\[]</code> where <code>supplementaryData.type</code> is <code>AML</code></li>
    </ul>

    <b>Example Configuration (for `is_pep`):</b>

    ```json theme={null}
      {
          "name": "is_pep",
          "description": "Entity Has PEP Hits",
          "handler": "is_pep",
          "scoreMethod": "bool",
          "scores": [
              { "value": true, "score": 50 }
          ]
      }
    ```
  </Accordion>

  <Accordion title="pep_level">
    Scores risk based on the numerical PEP classification level (1-4).

    <ul>
      <li><b>Handler</b>: <code>pep\_level\_lookup</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Aggregate Method</b>: <code>max</code></li>
      <li><b>Data Source</b>: <code>processResults\[].supplementaryData.pepData\[].level</code></li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "pep_level",
          "description": "Risk based on the PEP classification level",
          "handler": "pep_level_lookup",
          "scoreMethod": "lookup",
          "aggregate": "max",
          "scores": [
              { "value": "1", "score": 100 },
              { "value": "2", "score": 80 },
              { "value": "3", "score": 50 },
              { "value": "4", "score": 30 }
          ],
          "defaultScore": { "value": "N/A", "score": 0 }
      }
    ```
  </Accordion>

  <Accordion title="workflow_attempts">
    Scores risk based on the number of times an entity has executed a specific workflow.

    <ul>
      <li><b>Handler</b>: <code>workflow\_attempts\_counter</code></li>
      <li><b>Score Method</b>: <code>lookup\_range</code></li>
      <li><b>Data Source</b>: Historical workflow execution records for the entity.</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "workflow_attempts",
          "description": "Total onboarding attempts for this workflow.",
          "handler": "workflow_attempts_counter",
          "scoreMethod": "lookup_range",
          "scores": [
              { "name": "First Attempt", "range": { "max": 1 }, "score": 0 },
              { "name": "Multiple Attempts", "range": { "min": 2, "max": 3 }, "score": 30 },
              { "name": "High Attempts", "range": { "min": 4 }, "score": 70 }
          ]
      }
    ```
  </Accordion>

  <Accordion title="custom_attribute_risk">
    Scores risk based on custom key-value data provided by the client in the <code>individual.customAttributes</code> object.

    <ul>
      <li><b>Handler</b>: <code>custom\_attribute\_lookup</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Data Source</b>: <code>individual.customAttributes</code></li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "product_type_risk",
          "description": "Scores risk based on the client-provided 'product_type' attribute.",
          "handler": "custom_attribute_lookup",
          "config": {
              "attributeName": "product_type"
          },
          "scoreMethod": "lookup",
          "scores": [
              { "value": "Card Present", "score": 5 },
              { "value": "Online Payments", "score": 20 }
          ],
          "defaultScore": { "value": "Other", "score": 10 }
      }
    ```
  </Accordion>

  <Accordion title="unresolved_duplicates">
    Scores risk based on the number of potential duplicate profiles for the entity that have not yet been resolved.

    <ul>
      <li><b>Handler</b>: <code>unresolved\_duplicates</code></li>
      <li><b>Score Method</b>: <code>lookup\_range</code></li>
      <li><b>Data Source</b>: Duplicate check process results.</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "unresolved_duplicates",
          "description": "Unresolved duplicates",
          "handler": "unresolved_duplicates",
          "scoreMethod": "lookup_range",
          "scores": [
              {
                  "name": "Any unresolved duplicates",
                  "range": { "min": 1 },
                  "score": 20
              }
          ]
      }
    ```
  </Accordion>

  <Accordion title="true_positive_duplicates">
    Scores risk based on the number of duplicate profiles that have been confirmed as `TRUE_POSITIVE`.

    <ul>
      <li><b>Handler</b>: <code>true\_positive\_duplicates</code></li>
      <li><b>Score Method</b>: <code>lookup\_range</code></li>
      <li><b>Data Source</b>: Duplicate check process results.</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "true_positive_duplicates",
          "description": "Resolved duplicates",
          "handler": "true_positive_duplicates",
          "scoreMethod": "lookup_range",
          "scores": [
              {
                  "name": "Any resolved duplicates",
                  "range": { "min": 1 },
                  "score": 2
              }
          ]
      }
    ```
  </Accordion>
</AccordionGroup>

### Onboarding Fraud Risk Factors

This section details the risk factors derived from Onboarding Fraud checks. These checks provide signals for device, IP address, phone number, and email address risk.

<AccordionGroup>
  <Accordion title="fraud_count_session">
    This factor scores risk based on the number of sessions used during an onboarding workflow. An excessive number of sessions can indicate suspicious behavior.

    <ul>
      <li><b>Handler</b>: <code>fraud\_count\_session</code></li>
      <li><b>Score Method</b>: <code>lookup\_range</code></li>
      <li><b>Returns</b>: A single integer value representing the session count.</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "fraud_count_session",
          "description": "Fraud Session Count",
          "scoreMethod": "lookup",
          "defaultScore": {
              "name": "Default",
              "value": "0",
              "score": 0,
              "flags": ["include_zero"]
          },
          "scores": [
              {
                  "name": "Up to 2",
                  "range": { "max": 2 },
                  "score": 0
              },
              {
                  "name": "Between 3-5",
                  "range": { "min": 3, "max": 5 },
                  "score": 10
              },
              {
                  "name": "Greater than 5",
                  "range": { "min": 6 },
                  "score": 20
              }
          ]
      }
    ```
  </Accordion>

  <Accordion title="fraud_ip_address">
    This factor aggregates risk signals from IP address checks across one or more sessions.

    <ul>
      <li><b>Handler</b>: <code>fraud\_ip\_address</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Aggregate</b>: <code>max</code> (to capture the highest risk signal from any session)</li>
      <li><b>Returns</b>: An array of risk levels (e.g., <code>\["HIGH", "LOW"]</code>).</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "fraud_ip_address",
          "description": "Fraud IP Signal",
          "aggregate": "max",
          "defaultScore": {
              "name": "default",
              "value": "LOW",
              "score": 0,
              "flags": ["include_zero"]
          },
          "scores": [
              { "value": "LOW", "score": 0, "flags": ["include_zero"]},
              { "value": "MEDIUM", "score": 10 },
              { "value": "HIGH", "score": 20 },
              { "value": "UNACCEPTABLE", "score": 30 },
              { "value": "UNKNOWN", "score": 40 }
          ]
      }
    ```
  </Accordion>

  <Accordion title="fraud_device">
    This factor aggregates risk signals from device checks across one or more sessions.

    <ul>
      <li><b>Handler</b>: <code>fraud\_device</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Aggregate</b>: <code>max</code> (to capture the highest risk signal from any device)</li>
      <li><b>Returns</b>: An array of risk levels (e.g., <code>\["HIGH", "LOW"]</code>).</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "fraud_device",
          "description": "Fraud Device Signal",
          "aggregate": "max",
          "defaultScore": {
              "name": "default",
              "score": 0,
              "value": "LOW",
              "flags": ["include_zero"]
          },
          "scores": [
              { "value": "LOW", "score": 0, "flags": ["include_zero"] },
              { "value": "MEDIUM", "score": 10 },
              { "value": "HIGH", "score": 20 },
              { "value": "UNACCEPTABLE", "score": 30 },
              { "value": "UNKNOWN", "score": 40 }
          ]
      }
    ```
  </Accordion>

  <Accordion title="fraud_email">
    This factor scores risk based on the fraud signal for the selected entity email address.

    <ul>
      <li><b>Handler</b>: <code>fraud\_email</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Returns</b>: A single risk level value (e.g., <code>"MEDIUM"</code>).</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "fraud_email",
          "description": "Fraud Email Signal",
          "defaultScore": {
              "name": "default",
              "score": 0,
              "value": "LOW",
              "flags": ["include_zero"]
          },
          "scores": [
              { "value": "LOW", "score": 0, "flags": ["include_zero"] },
              { "value": "MEDIUM", "score": 10 },
              { "value": "HIGH", "score": 20 },
              { "value": "UNACCEPTABLE", "score": 30 },
              { "value": "UNKNOWN", "score": 40 }
          ]
      }
    ```
  </Accordion>

  <Accordion title="fraud_phone_number">
    This factor scores risk based on the fraud signal for the selected entity phone number.

    <ul>
      <li><b>Handler</b>: <code>fraud\_phone\_number</code></li>
      <li><b>Score Method</b>: <code>lookup</code></li>
      <li><b>Returns</b>: A single risk level value (e.g., <code>"HIGH"</code>).</li>
    </ul>

    <b>Example Configuration:</b>

    ```json theme={null}
      {
          "name": "fraud_phone_number",
          "description": "Fraud Phone Signal",
          "defaultScore": {
              "name": "default",
              "score": 0,
              "value": "LOW",
              "flags": ["include_zero"]
          },
          "scores": [
              { "value": "LOW", "score": 0, "flags": ["include_zero"] },
              { "value": "MEDIUM", "score": 10 },
              { "value": "HIGH", "score": 20 },
              { "value": "UNACCEPTABLE", "score": 30 },
              { "value": "UNKNOWN", "score": 40 }
          ]
      }
    ```
  </Accordion>
</AccordionGroup>

***

## Risk in Workflows: The Fraud Step

The Onboarding Fraud solution is integrated into workflows via a dedicated **Fraud Step**.

### Step Results

The Fraud Step can return one of three results:

| Result        | Description                                                                                   |
| :------------ | :-------------------------------------------------------------------------------------------- |
| **CLEAR**     | All underlying checks passed without raising any concerns. No operator action is required.    |
| **HIT**       | One or more underlying checks returned results that should be reviewed by an operator.        |
| **UNCHECKED** | The step was unable to generate any results, likely due to a data validation or system issue. |

### Impact of Resolving Results on Risk Calculation

When an operator resolves a `HIT`, their action directly impacts how risk is calculated on subsequent workflow runs.

| Operator Action/Status              | Effect on Risk Calculation                                                                                                               |
| :---------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
| **No manualStatus** (Unresolved)    | The risk from the provider is used as is.                                                                                                |
| <code>FALSE\_POSITIVE</code>        | The risk for the result is ignored entirely and excluded from the calculation.                                                           |
| <code>TRUE\_POSITIVE\_ACCEPT</code> | The risk for the result is forced to <code>LOW</code> to reflect that the signal is real but the risk has been accepted by the operator. |
| <code>TRUE\_POSITIVE\_REJECT</code> | The risk for the result remains as it was from the provider, reflecting that the signal is real and has been rejected by the operator.   |

***

## Advanced Configuration

### Aggregation Methods

Aggregation applies only when a factor's handler yields multiple input values (e.g., multiple device risk scores). The engine first calculates an "item score" for each value and then collapses them into a single "factor score" using one of the following methods:

| Method      | Description                                                                                                                                         |
| :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
| **max**     | The final factor score is the highest score from all evaluated items.                                                                               |
| **sum**     | The final factor score is the sum of all item scores.                                                                                               |
| **min**     | The final factor score is the lowest score from all evaluated items.                                                                                |
| **average** | The final factor score is the mathematical average of all item scores.                                                                              |
| **count**   | The final score is determined by the total number of input items, which is then mapped against the ranges defined in the <code>scores</code> array. |

### Connector-Level HIT/CLEAR Mapping

For some third-party providers, you can configure how their risk levels map to FrankieOne's `HIT` or `CLEAR` results at the connector level. This allows for fine-tuning of your risk appetite.

By default, the mapping is:

* `HIGH`, `MEDIUM`, `UNACCEPTABLE` -> **HIT**
* `LOW` -> **CLEAR**

You can override this behavior in the connector configuration. For example, to treat `MEDIUM` risk as `CLEAR`:

```json theme={null}
"connector": {
    "provider": {
        "...rest of provider config": "",
        "v2": {
            "riskLevelPROResultMapping": {
                "FRAUD_DEVICE": {
                    "MEDIUM": "CLEAR"
                },
                "FRAUD_IP_ADDRESS": {
                    "MEDIUM": "CLEAR"
                },
                "FRAUD_EMAIL_ADDRESS": {
                    "MEDIUM": "CLEAR"
                },
                "FRAUD_PHONE_NUMBER": {
                    "MEDIUM": "CLEAR"
                }
            }
        },
        "...rest of provider config": ""
    }
}
```
