Playbook rules
4 min read
Rules decide the outcome of an onboarding from the risk signals and data collected along the way, so decisioning fits your own risk model. A Rules node holds groups of rules written as CEL (Common Expression Language) expressions and writes an outcome value that the rest of the playbook acts on.
When you create a playbook from a template, its Rules nodes come preloaded with a default set of rules. You can keep that set or customize it in the rules editor.
Rule evaluation
Rules express your custom logic for risk decisioning or data collection. For example, rules can:
- 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 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), a 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:
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).
Each namespace holds a number of features, for example risk_signal.watchlist_hit_ofac, vault.id.country, and data.charge_risk_score.
The rules editor suggests available features as you type. Press CTRL + SPACE to see the variables available in the current scope.
Risk signal expressions
Input features in the risk_signal namespace are booleans (true or false) that say whether a verification check triggered that risk signal. The simplest rule is therefore a single risk signal:
Rule
This rule evaluates to true if the verification checks yielded this risk signal.
The ! operator negates a risk signal, so the following evaluates to true if the verification checks did not yield this risk signal:
Rule1!risk_signal.watchlist_hit_ofac
The complete list of risk signals is in the Risk Signal Glossary.
Vault data expressions
Input features in the vault namespace are strings. They map one-to-one to the vault fields: id.zip is available to rules as vault.id.zip, and business.country as vault.business.country.
Since rules are boolean expressions and vault data features are strings, a vault data feature must be compared to a specific value. CEL uses == for equality:
Rule1vault.id.country == "US"
This evaluates to true if the country entered during onboarding is US.
Vault data also supports inequality checks with the != operator:
Rule1vault.id.country != "CA"
You can also check membership in a list with the in operator:
Rule1vault.id.country in ["US", "CA", "MX"]
Compound rule expressions
Expressions can be combined with the logical operators ! (not), && (and), and || (or), and grouped with parentheses to express more complex conditions. For example:
Rule1risk_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 this rule without parentheses:
Rule1!risk_signal.attested_device_apple || vault.id.country != "US" && !risk_signal.device_high_risk
Formatted with parentheses to clarify the order of operations, the rule above is equivalent to:
Rule1(!risk_signal.attested_device_apple) 2|| 3( 4 vault.id.country != "US" 5 && 6 (!risk_signal.device_high_risk) 7)
Because CEL is a full expression language, rules can also use ternaries (? :), string functions, and helpers such as size(...) when you need them.
Best practices
To keep your rules easy to understand, split top-level || expressions into multiple rules within the same group. Take this compound rule:
Rule1!risk_signal.document_ocr_name_matches || !risk_signal.document_selfie_matches
It can be split into two rules in the same group:
Rule1!risk_signal.document_ocr_name_matches
Rule1!risk_signal.document_selfie_matches
Since a group matches if any one of its rules matches, the two forms express the same logic.
Next steps
Building playbooks shows where Rules nodes sit in a playbook and the other nodes they work with.