# Playbook Rules

Footprint’s rules engine enables you to customize user onboarding decisioning to fit your business’s risk model.

When you create a playbook from a template, rules nodes are preloaded with a default set of rules. Businesses may find this default set of rules sufficient, or they may choose to customize the onboarding flow using the Footprint rules editor. Rules are written as [CEL (Common Expression Language)](https://github.com/google/cel-spec) expressions.

## Rule evaluation

Rules enable businesses to express custom logic for risk decisioning or data collection. For example, rules can be used for each of the following:

* Assign final user statuses based on different risk signals.
* Split an onboarding flow based on user attributes (e.g. different onboarding flows for U.S. and international users).
* Define Enhanced Due Diligence (EDD) or step-up flows.

A rules node sets an outcome value based on the data and risk signals available during onboarding. Rules are organized into groups, where each group has a value and a set of associated rules. Groups are evaluated in the order they are displayed (top-to-bottom), and a group matches if any of its associated rules evaluates to `true`. The first matching group's value is used, and the result is written to an onboarding data key (e.g. `data.kyc_outcome`) that the rest of the playbook can act on. If no group matches, the default value is used. A default value is required and is always displayed last.

## Rules as CEL expressions

Rules are expressions written in [CEL (Common Expression Language)](https://github.com/google/cel-spec) — a simple, type-safe language from Google — that evaluate during onboarding to either `true` or `false`. They are composed of input features, comparison expressions, and boolean operators.

Input features have the format `<namespace>.<feature>`. There are three namespaces of features:

* `risk_signal`: Risk signals derived from verification checks
* `vault`: Data vaulted during onboarding
* `data`: Values computed earlier in the onboarding flow (for example, by an upstream rules or expression node)

Within each of the namespaces is a number of features (for example, `risk_signal.watchlist_hit_ofac`, `vault.id.country`, and `data.charge_risk_score`).

Features available to a rule are suggested as you type in the rules editor. Press `CTRL + SPACE` to see the variables available in the current scope.

### Risk signal expressions

Risk signal input features in the `risk_signal` namespace take on boolean values (`true` or `false`), indicating whether that risk signal was triggered by a verification check. Therefore, the simplest rule is a single risk signal. For example:

```js filename="Rule"
risk_signal.watchlist_hit_ofac
```

This rule evaluates to `true` if the verification checks yielded this risk signal.

The `!` operator negates a risk signal, meaning the following evaluates to `true` if the verification checks did not yield this risk signal:

```js filename="Rule"
!risk_signal.watchlist_hit_ofac
```

The complete list of risk signals available can be found in the [Risk Signal Glossary](https://dashboard.onefootprint.com/home?rsg=open).

### Vault data expressions

Vault data input features in the `vault` namespace take on string values. The features in this namespace map one-to-one to the vault fields listed [here](https://docs.onefootprint.com/articles/vault/fields). For example `id.zip` is available to rules as the feature `vault.id.zip` and `business.country` is available as `vault.business.country`.

Since rules are boolean expressions and vault data features are strings, vault data features must be compared to a specific value. CEL uses `==` for equality:

```js filename="Rule"
vault.id.country == "US"
```

This would evaluate to `true` if the country entered during onboarding is `US`.

Vault data also supports inequality checks with the `!=` operator:

```js filename="Rule"
vault.id.country != "CA"
```

You can also check membership in a list with the `in` operator:

```js filename="Rule"
vault.id.country in ["US", "CA", "MX"]
```

### Compound rule expressions

Expressions can be composed together with the logical operators `!` (not), `&&` (and), and `||` (or), and grouped using parentheses to express more complex conditions. For example:

```js filename="Rule"
risk_signal.device_high_risk && (!risk_signal.attested_device_apple || vault.id.country != "US")
```

The `&&` operator takes precedence over the `||` operator, and the `!` operator has the highest precedence. Take for example this rule without parentheses:

```js filename="Rule"
!risk_signal.attested_device_apple || vault.id.country != "US" && !risk_signal.device_high_risk
```

Formatted with parentheses to clarify the order of operations, the above rule is equivalent to:

```js filename="Rule"
(!risk_signal.attested_device_apple)
||
(
	vault.id.country != "US"
	&&
	(!risk_signal.device_high_risk)
)
```

Because CEL is a full expression language, rules can also use ternaries (`? :`), string functions, and helpers like `size(...)` when you need them.

## Best practices

To keep your rules easy to understand, you may split up top-level `||` expressions into multiple rules within the same group. Take for example this compound rule:

```js filename="Rule"
!risk_signal.document_ocr_name_matches || !risk_signal.document_selfie_matches
```

This rule can be split up into two rules in the same group:

```js filename="Rule"
!risk_signal.document_ocr_name_matches
```

```js filename="Rule"
!risk_signal.document_selfie_matches
```

Since a group matches if any one of its associated rules matches, these two rule sets express the same logic.