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" and evaluatorType: "CONTROL_ANSWER_MATCH". If Asenion enables other combinations for your tenant, you will receive the exact field values and conditions schema 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

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
  1. A user completes an assessment (today, commonly screening — e.g. the Pre-Screening questionnaire).
  2. The rule engine loads all active rules for your organization.
  3. For each rule, it checks whether the event data (e.g. the user’s answers) satisfies the rule’s conditions.
  4. 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

conditions is a mixed-type field — its structure depends on the evaluatorType. This document describes CONTROL_ANSWER_MATCH, where conditions contains mode + controls. Other evaluator types will be documented when available.

actions[].metadata is also a mixed-type field — its structure depends on the actionType. For ADD_POLICY_AND_ANSWER it contains suggestedAnswers and targetPolicyVersion; for RECOMMEND_POLICY it 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 controls is a control identifier from the screening policy identified by triggerPolicyIdentifier.
  • 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 suggestedAnswers to just add a policy with no pre-filled answers.
  • If an identifier in suggestedAnswers doesn’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:

  1. Open the target policy’s configuration JSON (the policy the action is adding).
  2. Find the control bundle → copy its identifier (e.g., "com.abc.fairness").
  3. Inside that bundle, find the control → copy its identifier (e.g., "com.abc.fairness.f1").
  4. 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

  1. Trigger: When a user completes the pre-screening assessment (com.fairly.ai.projectinfo).
  2. Condition: Check if the control project.systrainedmodel was answered with project.systrainedmodel.no (i.e., “No, this system does not have a trained model”).
  3. Action 1: Add the Privacy policy (com.abc.privacy) to the project, and pre-fill controls F1, F2, and F3 with “Not Applicable”.
  4. 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 name and description — makes review faster
  • source matches the rule type — for screening rules covered here, "SCREENING"
  • evaluatorType matches how conditions is written — for answer-based screening rules, "CONTROL_ANSWER_MATCH"
  • triggerPolicyIdentifier is set — so the rule only fires for the correct screening policy
  • conditions has both mode and controls
  • All identifiers are correct (cross-check against your policy configuration):
    • Control identifiers in conditions.controls match your screening policy
    • Answer option identifiers match the options in those controls
    • targetPolicyIdentifier in actions matches a policy that exists in the system
    • controlBundleIdentifier and controlIdentifier in suggested answers match the target policy’s bundles
  • suggestedAnswers (if used) have all three required fields: controlIdentifier, controlBundleIdentifier, and values
  • status is "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