Rules Configuration Guide
Rules let you automate compliance workflows on the Asenion platform. A rule ties when something in the platform should trigger automated actions — for example when configured conditions on assessments or project data are satisfied. Today, most customer rules are built around screening assessments: when the user’s answers match your conditions, the platform can run actions such as attaching a policy or marking controls as Not Applicable.
Scope: This guide describes rules that use
source:"SCREENING"andevaluatorType:"CONTROL_ANSWER_MATCH". If Asenion enables other combinations for your tenant, you will receive the exact field values andconditionsschema for that rule type.
This guide explains how to write your own rules as JSON. Once ready, send them to the Asenion team for review and activation.
Table of Contents
- How Rules Work
- Rule Structure
- Writing Conditions
- Choosing an Action Type
- Writing Suggested Answers
- Risk Classification
- Complete Example (ABC)
- More Examples
- Quick Reference
- Checklist Before Submitting
How Rules Work
The flow below is how screening rules work today. Other rule types may use different triggers while still following the same idea: match conditions, then run actions.
Assessment event Rule engine checks Actions are
(e.g. screening answers) ─────▶ conditions against ──────▶ executed
the event data automatically
- A user completes an assessment (today, commonly screening — e.g. the Pre-Screening questionnaire).
- The rule engine loads all active rules for your organization.
- For each rule, it checks whether the event data (e.g. the user’s answers) satisfies the rule’s conditions.
- If conditions match, the rule’s actions are executed — adding policies, pre-filling answers, etc.
Rules are safe to re-run. If a policy is already attached to a project, the rule won’t duplicate it. If a control already has an answer, the rule won’t overwrite it.
Rule Structure
Every rule is a JSON object with these fields:
{
"name": "Short descriptive name of the rule",
"description": "Longer explanation of what this rule does and why.",
"source": "SCREENING",
"evaluatorType": "CONTROL_ANSWER_MATCH",
"triggerPolicyIdentifier": "com.fairly.ai.projectinfo",
"classification": "high-risk",
"conditions": { ... },
"actions": [ ... ],
"status": "ACTIVE"
}
| Field | Type | Required | What it does |
|---|---|---|---|
name | String | Yes | A short, human-readable name for the rule |
description | String | No | A longer explanation — helpful for your team and for Asenion’s review |
source | String | Yes | Supported in this guide: "SCREENING" — rule runs in the screening-answers context. Additional sources may be introduced; use the value Asenion specifies for your rule type. |
evaluatorType | String | Yes | Supported in this guide: "CONTROL_ANSWER_MATCH" — conditions compare selected answer options. Other evaluators may use a different conditions shape; follow the contract for your evaluatorType. |
triggerPolicyIdentifier | String | Recommended | The identifier of the screening policy whose answers trigger this rule (e.g., "com.fairly.ai.projectinfo") |
conditions | Object (mixed) | Yes | What answers must match for this rule to fire (see Writing Conditions) |
actions | Array | Yes | What to do when conditions match (see Choosing an Action Type) |
status | String | Yes | Set to "ACTIVE" |
classification | String | No | Risk classification label (e.g., "high-risk") — see Risk Classification |
conditionsis a mixed-type field — its structure depends on theevaluatorType. This document describesCONTROL_ANSWER_MATCH, whereconditionscontainsmode+controls. Other evaluator types will be documented when available.
actions[].metadatais also a mixed-type field — its structure depends on theactionType. ForADD_POLICY_AND_ANSWERit containssuggestedAnswersandtargetPolicyVersion; forRECOMMEND_POLICYit is not used.
Tip: Set triggerPolicyIdentifier so your rule only fires for the correct screening policy, not for every assessment in the system.
Writing Conditions
Conditions define when the rule fires. For CONTROL_ANSWER_MATCH, they check which answer options the user selected in the screening assessment.
{
"mode": "requireAny",
"controls": {
"control.identifier.here": ["answer.option.identifier.here"]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
mode | String | Yes | "requireAny" or "requireAll" (see below) |
controls | Object | Yes | A map of control identifiers to arrays of answer option identifiers |
How to read this:
- Each key in
controlsis a control identifier from the screening policy identified bytriggerPolicyIdentifier. - Each value is an array of answer option identifiers. A control matches if the user selected any of the listed options.
Where do I find these identifiers? See How to Find the Right Identifiers.
Example — trigger when user answers “No” to the trained model question:
{
"mode": "requireAny",
"controls": {
"project.systrainedmodel": ["project.systrainedmodel.no"]
}
}
Condition Modes: requireAny vs requireAll
The mode field controls how multiple controls in the controls map are combined:
| Mode | Meaning | Use when… |
|---|---|---|
requireAny | Rule fires if at least one control matches | Any single answer is enough to trigger the rule |
requireAll | Rule fires only if every control matches | Multiple conditions must all be true simultaneously |
requireAny (OR logic) — the rule fires if either condition is true (use case is biometric or sector is healthcare):
{
"mode": "requireAny",
"controls": {
"project.usecase": ["project.usecase.biometric_identification"],
"project.sector": ["project.sector.healthcare"]
}
}
requireAll (AND logic) — the rule fires only if both conditions are true:
{
"mode": "requireAll",
"controls": {
"project.lifecycle": ["project.lifecycle.deployed"],
"project.context": ["project.context.high_risk"]
}
}
Choosing an Action Type
Actions define what happens when the rule fires. Each rule can have one or more actions. Two action types are available:
| Action Type | What it does | Requires user action? |
|---|---|---|
ADD_POLICY_AND_ANSWER | Adds a policy to the project and optionally pre-fills specific answers | No — fully automatic |
RECOMMEND_POLICY | Suggests a policy; user decides whether to accept | Yes — user reviews |
ADD_POLICY_AND_ANSWER — Auto-add a policy with pre-filled answers
This is the most common action type. It automatically attaches a policy to the project and can pre-set answers on specific controls.
{
"actionType": "ADD_POLICY_AND_ANSWER",
"targetPolicyIdentifier": "com.yourorg.privacy",
"metadata": {
"suggestedAnswers": [
{
"controlIdentifier": "com.yourorg.fairness.f1",
"controlBundleIdentifier": "com.yourorg.fairness",
"values": [
{
"answerOptionIdentifier": "com.yourorg.fairness.f1.not_applicable",
"value": "1"
}
]
}
]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
actionType | String | Yes | "ADD_POLICY_AND_ANSWER" |
targetPolicyIdentifier | String | Yes | Identifier of the policy to add to the project |
metadata | Object (mixed) | No | Action-specific payload — structure depends on actionType (see below) |
metadata.suggestedAnswers | Array | No | Answers to pre-fill (see Writing Suggested Answers) |
metadata.targetPolicyVersion | String | No | Specific policy version to add (defaults to the latest version) |
Important behaviour:
- If the policy is already attached to the project, it won’t be added again.
- Pre-filled answers only apply to controls that haven’t been answered yet — existing user answers are never overwritten.
- You can use this action without
suggestedAnswersto just add a policy with no pre-filled answers. - If an identifier in
suggestedAnswersdoesn’t match any control or answer option in the target policy, that answer is silently skipped — the rule continues with the remaining answers. This is why verifying identifiers against your policy config is critical.
RECOMMEND_POLICY — Suggest a policy for human review
Creates a recommendation that appears in the project’s action list. A user must review and accept it.
{
"actionType": "RECOMMEND_POLICY",
"targetPolicyIdentifier": "com.yourorg.euaiact"
}
| Field | Type | Required | Description |
|---|---|---|---|
actionType | String | Yes | "RECOMMEND_POLICY" |
targetPolicyIdentifier | String | Yes | Identifier of the policy to recommend |
Use this when you want a human to decide whether the policy should be added, rather than adding it automatically.
Writing Suggested Answers
Suggested answers (used with ADD_POLICY_AND_ANSWER) tell the system which control answers to pre-fill. Each suggested answer targets one specific control within a specific control bundle.
{
"controlIdentifier": "com.yourorg.fairness.f1",
"controlBundleIdentifier": "com.yourorg.fairness",
"values": [
{
"answerOptionIdentifier": "com.yourorg.fairness.f1.not_applicable",
"value": "1"
}
]
}
| Field | Type | Required | Description |
|---|---|---|---|
controlIdentifier | String | Yes | The control to answer |
controlBundleIdentifier | String | Yes | The control bundle containing this control |
values | Array | Yes | Array of answer values to set |
values[].answerOptionIdentifier | String | Yes | The answer option to select |
values[].value | String | No | Typically "1" to indicate the option is selected |
How to Find the Right Identifiers
A rule involves two different policies, and identifiers come from different places:
| What you’re writing | Where identifiers come from |
|---|---|
conditions.controls (what triggers the rule) | The screening policy (e.g., com.fairly.ai.projectinfo) |
suggestedAnswers (what gets pre-filled) | The target policy being added (e.g., com.abc.privacy) |
For suggested answers, you need three identifiers. All come from the target policy configuration (see the Policy Configuration Guide):
Target Policy (the one being added)
└── controlBundles[]
└── identifier ◄── This is your controlBundleIdentifier
└── controls[]
└── identifier ◄── This is your controlIdentifier
└── answerOptions[]
└── identifier ◄── This is your answerOptionIdentifier
Step by step:
- Open the target policy’s configuration JSON (the policy the action is adding).
- Find the control bundle → copy its
identifier(e.g.,"com.abc.fairness"). - Inside that bundle, find the control → copy its
identifier(e.g.,"com.abc.fairness.f1"). - Inside that control, find the answer option you want to select → copy its
identifier(e.g.,"com.abc.fairness.f1.not_applicable").
These three identifiers go into the suggested answer object.
Risk Classification
You can assign a classification to a rule to enable automatic risk classification during screening. This is a top-level field on the rule (alongside name, source, etc.).
When multiple rules pass, the highest severity wins:
| Severity | Value |
|---|---|
| Highest | "prohibited" |
"high-risk" | |
"limited-risk" | |
| Lowest | "minimal-risk" |
{
"name": "High-risk use case → Recommend EU AI Act policy",
"source": "SCREENING",
"evaluatorType": "CONTROL_ANSWER_MATCH",
"classification": "high-risk",
"conditions": {
"mode": "requireAny",
"controls": {
"project.usecase": ["project.usecase.biometric_identification"]
}
},
"actions": [ ... ],
"status": "ACTIVE"
}
If no passing rule has a classification, the result is "cannot-determine".
Complete Example (ABC)
This real-world rule from ABC demonstrates a common pattern: when a screening answer indicates something doesn’t apply, automatically add related policies and mark specific controls as Not Applicable.
{
"name": "Pre-screen: No trained model → Add Privacy (F1,F2,F3 N/A) and Safeguards (S4 N/A)",
"description": "When Pre-Screening project.systrainedmodel is answered No, add Privacy and set F1, F2, F3 to Not Applicable; add Safeguards and set S4 to Not Applicable.",
"source": "SCREENING",
"evaluatorType": "CONTROL_ANSWER_MATCH",
"triggerPolicyIdentifier": "com.fairly.ai.projectinfo",
"conditions": {
"mode": "requireAny",
"controls": {
"project.systrainedmodel": ["project.systrainedmodel.no"]
}
},
"actions": [
{
"actionType": "ADD_POLICY_AND_ANSWER",
"targetPolicyIdentifier": "com.abc.privacy",
"metadata": {
"suggestedAnswers": [
{
"controlIdentifier": "com.abc.fairness.fi",
"controlBundleIdentifier": "com.abc.fairness",
"values": [
{
"answerOptionIdentifier": "com.abc.fairness.fi.not_applicable",
"value": "1"
}
]
},
{
"controlIdentifier": "com.abc.fairness.f2",
"controlBundleIdentifier": "com.abc.fairness",
"values": [
{
"answerOptionIdentifier": "com.abc.fairness.f2.not_applicable",
"value": "1"
}
]
},
{
"controlIdentifier": "com.abc.fairness.f3",
"controlBundleIdentifier": "com.abc.fairness",
"values": [
{
"answerOptionIdentifier": "com.abc.fairness.f3.not_applicable",
"value": "1"
}
]
}
]
}
},
{
"actionType": "ADD_POLICY_AND_ANSWER",
"targetPolicyIdentifier": "com.abc.safeguards",
"metadata": {
"suggestedAnswers": [
{
"controlIdentifier": "com.abc.safety.S4",
"controlBundleIdentifier": "com.abc.safety",
"values": [
{
"answerOptionIdentifier": "com.abc.safety.S4.not_applicable",
"value": "1"
}
]
}
]
}
}
],
"status": "ACTIVE"
}
What this rule does, step by step
- Trigger: When a user completes the pre-screening assessment (
com.fairly.ai.projectinfo). - Condition: Check if the control
project.systrainedmodelwas answered withproject.systrainedmodel.no(i.e., “No, this system does not have a trained model”). - Action 1: Add the Privacy policy (
com.abc.privacy) to the project, and pre-fill controls F1, F2, and F3 with “Not Applicable”. - Action 2: Add the Safeguards policy (
com.abc.safeguards) to the project, and pre-fill control S4 with “Not Applicable”.
Key patterns
| Pattern | How it’s used |
|---|---|
| Trigger scoping | triggerPolicyIdentifier ensures this rule only fires for the project-info screening policy |
| One rule, multiple policies | A single rule adds two different policies in one go |
| Pre-filling “Not Applicable” | Reduces manual work — controls that don’t apply are auto-answered |
| Safe to re-run | If policies are already attached or controls already answered, nothing changes |
More Examples
Add a policy without pre-filling answers
The simplest possible rule — just adds a policy when a condition matches:
{
"name": "Trained model → Add Fairness policy",
"description": "When the system has a trained model, add the Fairness assessment policy.",
"source": "SCREENING",
"evaluatorType": "CONTROL_ANSWER_MATCH",
"triggerPolicyIdentifier": "com.fairly.ai.projectinfo",
"conditions": {
"mode": "requireAny",
"controls": {
"project.systrainedmodel": ["project.systrainedmodel.yes"]
}
},
"actions": [
{
"actionType": "ADD_POLICY_AND_ANSWER",
"targetPolicyIdentifier": "com.yourorg.fairness"
}
],
"status": "ACTIVE"
}
Recommend a policy for human review
Instead of auto-adding, suggest a policy and let the user decide:
{
"name": "High-risk use case → Recommend EU AI Act policy",
"description": "Recommend the EU AI Act compliance policy when the system is used for high-risk purposes.",
"source": "SCREENING",
"evaluatorType": "CONTROL_ANSWER_MATCH",
"triggerPolicyIdentifier": "com.fairly.ai.projectinfo",
"classification": "high-risk",
"conditions": {
"mode": "requireAny",
"controls": {
"project.usecase": [
"project.usecase.biometric_identification",
"project.usecase.critical_infrastructure",
"project.usecase.employment_screening"
]
}
},
"actions": [
{
"actionType": "RECOMMEND_POLICY",
"targetPolicyIdentifier": "com.fairly.ai.euaiact"
}
],
"status": "ACTIVE"
}
Multiple conditions must all be true (AND logic)
Use "requireAll" when the rule should only fire if every condition matches:
{
"name": "Deployed + High-risk → Add post-market monitoring",
"description": "When the AI system is deployed AND in a high-risk context, add the post-market monitoring policy.",
"source": "SCREENING",
"evaluatorType": "CONTROL_ANSWER_MATCH",
"triggerPolicyIdentifier": "com.fairly.ai.projectinfo",
"conditions": {
"mode": "requireAll",
"controls": {
"project.lifecycle": ["project.lifecycle.deployed"],
"project.context": ["project.context.high_risk"]
}
},
"actions": [
{
"actionType": "ADD_POLICY_AND_ANSWER",
"targetPolicyIdentifier": "com.yourorg.postmarket"
}
],
"status": "ACTIVE"
}
One condition triggers multiple answer options (OR within a control)
When you list multiple answer options for one control, matching any of them is enough:
{
"name": "External-facing system → Add Privacy policy",
"description": "Add Privacy policy if the system serves customers or the general public.",
"source": "SCREENING",
"evaluatorType": "CONTROL_ANSWER_MATCH",
"triggerPolicyIdentifier": "com.fairly.ai.projectinfo",
"conditions": {
"mode": "requireAny",
"controls": {
"project.audience": [
"project.audience.customers",
"project.audience.general_public"
]
}
},
"actions": [
{
"actionType": "ADD_POLICY_AND_ANSWER",
"targetPolicyIdentifier": "com.yourorg.privacy"
}
],
"status": "ACTIVE"
}
Quick Reference
Rule Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Short rule name |
description | String | No | Detailed explanation |
source | String | Yes | Screening rules: "SCREENING" (see Rule Structure for other types) |
evaluatorType | String | Yes | Answer-based screening rules: "CONTROL_ANSWER_MATCH" (see Rule Structure for other types) |
triggerPolicyIdentifier | String | Recommended | Screening policy identifier that triggers this rule |
conditions | Object (mixed) | Yes | Structure depends on evaluatorType; for CONTROL_ANSWER_MATCH: { "mode": "...", "controls": { ... } } |
actions | Array | Yes | Array of action objects |
status | String | Yes | "ACTIVE" |
classification | String | No | "prohibited", "high-risk", "limited-risk", or "minimal-risk" |
Action Fields
| Field | Type | Required for | Description |
|---|---|---|---|
actionType | String | Both | "ADD_POLICY_AND_ANSWER" or "RECOMMEND_POLICY" |
targetPolicyIdentifier | String | Both | Identifier of the policy to add or recommend |
metadata | Object (mixed) | — | Structure depends on actionType; not used for RECOMMEND_POLICY |
metadata.suggestedAnswers | Array | — | Only for ADD_POLICY_AND_ANSWER; answers to pre-fill |
metadata.targetPolicyVersion | String | — | Only for ADD_POLICY_AND_ANSWER; specific version (default: latest) |
Suggested Answer Fields
| Field | Type | Required | Description |
|---|---|---|---|
controlIdentifier | String | Yes | Control to answer |
controlBundleIdentifier | String | Yes | Bundle containing the control |
values[].answerOptionIdentifier | String | Yes | Answer option to select |
values[].value | String | No | Typically "1" |
Conditions Fields
| Field | Type | Required | Description |
|---|---|---|---|
mode | String | Yes | "requireAny" (OR) or "requireAll" (AND) |
controls | Object | Yes | Map of control identifiers to arrays of answer option identifiers |
Checklist Before Submitting
Use this checklist when preparing your rules to send to the Asenion team:
- Every rule has a clear
nameanddescription— makes review faster sourcematches the rule type — for screening rules covered here,"SCREENING"evaluatorTypematches howconditionsis written — for answer-based screening rules,"CONTROL_ANSWER_MATCH"triggerPolicyIdentifieris set — so the rule only fires for the correct screening policyconditionshas bothmodeandcontrols- All identifiers are correct (cross-check against your policy configuration):
- Control identifiers in
conditions.controlsmatch your screening policy - Answer option identifiers match the options in those controls
targetPolicyIdentifierin actions matches a policy that exists in the systemcontrolBundleIdentifierandcontrolIdentifierin suggested answers match the target policy’s bundles
- Control identifiers in
suggestedAnswers(if used) have all three required fields:controlIdentifier,controlBundleIdentifier, andvaluesstatusis"ACTIVE"- No duplicate rules — check if a similar rule already exists
- Walk through a scenario mentally: pick a user path (e.g. answering the screening questions) and verify the conditions would match what you expect