How-to-config-Decision-Point¶
Decision Point is the connection point between application and Themis (risk) system. When application wants Themis to make a decision. It must provide a decision point name and all input variables needed.
Adding a whole new decision point? This page explains its config shape.
The complete rollout also needs database metadata, rules and callers. After
merge, be-risk-configuration deployment creates missing metadata before each
environment's config sync; it does not create rules or caller behavior. Follow
the decision-point lifecycle guide
and its agent skill
for creation, forks, cutovers and recovery.
How to add a new decision point?¶
Add the decision point and its features in be-risk-configuration, merge the PR,
and let the stage-then-production deployment provision missing metadata before
syncing the same config revision. Author rules and connect callers separately.
Themis loads decision points from following place:
be-risk-configuration/src/{environment}/risk-decision-points-config.json
risk-decision-points-config.json example
{
"DecisionPoints": {
"PSP_Transaction": {
"Id": 5,
"Name": "PSP_Transaction",
"IsActive": true,
"Description": "Decision for PSP funding transactions.",
"Actions": [
"RiskBlockUserAction"
],
"Limits": {},
"ReferencedDecisionPoints": [],
"ExecutionResults": [
"Deny"
]
}
}
}
This file describes the decision point itself — its id, whether it is active, its actions and results. It says nothing about features; those live entirely in risk-features-config.json.
Where do I get id of a decision point?¶
You give whatever number you like. But it is better to avoid duplications.
How to declare features to be used in a decision point?¶
Add one entry under your decision point in
be-risk-configuration/src/{env}/risk-features-config.json
That is the whole step. A feature is identified by its feature path — the same string the rule engine stores in rule XML and that Decide() callers send — so the path is the JSON key. DisplayName is the label Hydra shows in the rule editor, and Description is the help text next to it.
risk-features-config.json example
{
"decisionPoints": {
"Pre_SelfFunding_Transaction": {
"features": {
"input/Pre_SelfFunding_Transaction/msisdn/string?": {
"DisplayName": "Msisdn",
"Description": "User msisdn from the intent"
},
"userId/KycSuccessfullyVerifiedEvent/*/timestamp/first/days_since/double": {
"DisplayName": "userId.KycVerified.Days",
"Description": ""
}
}
}
},
"Features": {
"input/common/isCanary/bool?": {
"DisplayName": "IsCanary",
"Description": "Is canary rule triggered"
}
}
}
Search the file first — if the path is already defined under your decision point, it is already available. The top-level Features block holds globals, which are attached to every decision point automatically; you do not list them per decision point.
Because the path is the key, you cannot define the same feature twice under one decision point with two different labels. That used to be possible and left the rule editor showing one of them arbitrarily; if you want a better label, change DisplayName in place.
How to rename a feature display name¶
Just make a good name from the beginning
But if you want to rename it, change DisplayName on that feature in risk-features-config.json. That is a one-file, one-line change and it is safe — live rules are unaffected, because they store the path, not the label.
Changing the JSON key is a different and breaking operation: it is the feature path, so every live rule referencing it and every Decide() caller sending it stop matching. Treat a key change as you would a schema migration, not a rename.
What is a good feature display name¶
When it is used, feature display name is a part of English like logical expression. So, the general rule is *Make it easy to be read in English sentence.
Here are some examples (I feel are good but it's definitely subjective):
| feature display name | description |
|---|---|
| user.KycVerified.Days | Then you read something like this if user.KycVerified.Days is less than 14 then Deny |
| user.PspTransactionCount.InLast1Day | It starts from aggregation point, the current user. Then the area (Psp) and variable (transaction count). And the velocity period as the last part |
| merchantId.IsBlocked | It is a good name until you would like to know where to block/unblock a merchant |
| merchantId.IsInMerchantBlocklist | A little better than previous one by putting list name MerchantBlocklist in the display name |
| countryCodeAlpha2.IsInCountryCodeBlocklist | Here we put alpha2 in the name. So, it is clear that we put SE in CountryCodeBlocklist to block Sweden. Neither SWE nor 752 will work |