Rules Configuration

Screening is the short questionnaire someone answers while creating a project or an organization — a handful of questions about what the AI system does, who it affects, and where it operates. It can also be re-run later from the assessment itself.

A rule connects an answer given in that questionnaire to something the platform should do next — most commonly, add an assessment to the project and pre-fill the questions that clearly do not apply to it. So instead of every project starting from the same blank checklist, the work a team is given reflects what they said about their system.

Rules are written as JSON and uploaded by an organization administrator. This guide explains every field, shows complete working examples you can copy, and lists the exact error messages you may see and how to fix each one.

Where this fits

Asenion covers four capability areas — Authoring → Assessment → Testing → Runtime Governance — explained on the Overview.

Rules belong to the Assessment stage. They shape which policies a project is assessed against and which questions its team is asked, based on what that team said about the system during screening. They do not produce an outcome or a score of their own; they set up the assessment work that people then carry out.

If a term here is unfamiliar, the Glossary defines the platform’s vocabulary — in particular Policy, Policy Pack, Control Bundle, and control.

Scope of this guide

This guide covers rules with:

  • source: "SCREENING" — the rule runs against screening answers, and
  • evaluatorType: "CONTROL_ANSWER_MATCH" — conditions compare the answer options a user selected.

This is the combination the platform evaluates during project and organization creation. If Asenion enables a different combination for your tenant, you will be given the exact field values and the conditions shape that go with it.

On action types. This guide documents ADD_POLICY_AND_ANSWER, which adds a policy’s assessment to the project and is the most common thing a rule does. Every example here uses it, and it is what you should use.

The actionType field accepts other values, which behave differently and are outside the scope of this guide. If you think you need one, talk to Asenion first — Alerts and Actions at the end explains what they do.


Table of contents


How a rule is put together

Every rule answers four questions:

Question Field
When should it be considered? source — and, optionally, triggerPolicyIdentifier and applicableScreening
What must be true? conditions
Then what should happen? actions and automations
Is it live? status

The sequence at screening time:

User answers the            Engine loads the             Conditions are           Actions run
screening questions  ─────▶ organization's        ─────▶ checked against   ─────▶ for every rule
                            ACTIVE rules                 those answers            that matched

Rules are held per organization. A rule you upload applies to your organization only, and it is considered alongside every other active rule — several rules can match the same screening and all of them take effect.


Before you begin

You need:

  • An account with the manage policies capability in your organization. The same capability controls both the upload screen and the underlying API.
  • The screening policy’s control identifiers and answer option identifiers — these go in conditions.
  • The target policy’s identifier — and, if you want to pre-fill answers, its control bundle, control, and answer option identifiers.

See Finding the right identifiers for where each of these comes from. Identifiers are specific to your own tenant’s configuration, so always copy them from your own policies rather than from an example.

Two policies are involved in most rules. The policy whose answers trigger the rule (the screening questionnaire) is almost never the same as the policy the rule adds. Mixing up identifiers between the two is the most common reason a rule does nothing.


Your first rule

This is a complete, valid rule. It says: when the team answers “No” to the question about whether the system uses a trained model, add the privacy assessment to the project.

{
  "name": "No trained model - add privacy assessment",
  "description": "When screening reports no trained model, add the privacy policy so the team completes it.",
  "source": "SCREENING",
  "evaluatorType": "CONTROL_ANSWER_MATCH",
  "triggerPolicyIdentifier": "com.example.screening.usecase",
  "problemType": "PRIVACY_ASSESSMENT_REQUIRED",
  "status": "ACTIVE",
  "conditions": {
    "mode": "requireAny",
    "controls": {
      "com.example.screening.trainedmodel": [
        "com.example.screening.trainedmodel.no"
      ]
    }
  },
  "actions": [
    {
      "actionType": "ADD_POLICY_AND_ANSWER",
      "targetPolicyIdentifier": "com.example.privacy",
      "uniqueKey": "add-policy:com.example.privacy",
      "description": "Add the privacy assessment."
    }
  ]
}

Reading it line by line:

  • source and evaluatorType say this is a screening rule, matched on answer options.
  • triggerPolicyIdentifier keeps the rule tied to one screening questionnaire, so it is not considered for unrelated ones.
  • problemType is a required label naming why the rule exists. See uniqueKey and problemType.
  • status must be "ACTIVE", or the rule is stored but never evaluated.
  • conditions matches one control against one answer option.
  • The action adds a policy; its uniqueKey is required and must be unique within the rule.

Three fields are easy to leave out. problemType and every action’s uniqueKey are rejected at upload, so you will see those immediately. A missing status is the dangerous one: the rule uploads successfully, is stored as DRAFT, and is never evaluated. All three are covered below.


Finding the right identifiers

The example above uses invented identifiers. To adapt it you need real ones, and they come from two different policies — mixing them up is the most common mistake.

What you are writing Which policy the identifiers come from
conditions.controls The screening policy — the questionnaire being answered
targetPolicyIdentifier The target policy — the one being added or recommended
suggestedAnswers The target policy

Inside a policy’s configuration, the three identifiers nest like this:

Policy
 └── controlBundles[]
      └── identifier   ◄── controlBundleIdentifier
      └── controls[]
           └── identifier   ◄── controlIdentifier
           └── answerOptions[]
                └── identifier   ◄── answerOptionIdentifier

Step by step for a suggested answer:

  1. Open the target policy’s configuration JSON.
  2. Find the control bundle and copy its identifier.
  3. Inside that bundle, find the control and copy its identifier.
  4. Inside that control, find the answer option you want selected and copy its identifier.

For the trigger side, do the same in the screening policy: copy the control’s identifier and the identifier of each answer option that should match.

The Policy Configuration guide describes the full structure of a policy file.

Copy identifiers from your own configuration, never from an example. Every identifier in this guide is illustrative. Yours will differ, and matching is exact.


Uploading a rule

Rules are uploaded in the product — you do not need to send them to Asenion.

  1. Open Organization Administration.
  2. Select the Upload Rules tab.
  3. Paste the JSON for one rule into the text box.
  4. Select Upload.

The result appears directly under the box: the rule name on success, or the reason it was rejected. Invalid JSON is reported as you type, and the Upload button stays unavailable until the text parses.

Accepted shapes. All three of these work, and all three must contain exactly one rule:

{ "rules": [ { "name": "..." } ] }
[ { "name": "..." } ]
{ "name": "..." }

One rule per upload. A payload with more than one rule is rejected with “Only one rule per upload. Please submit one rule at a time.” Upload rules one after another.

Inactive and draft rules stay in the system but are never evaluated. Taking a live rule out of service means changing its status to "INACTIVE", which is not something the upload screen does — contact Asenion for that.


What the rule actually does in the app

It is worth being clear about what your team will and will not see, because a rule that works correctly is very quiet.

During project creation. The recommended policy appears in the policy selection step, already ticked. Whoever is creating the project can untick it, unless the rule marked that policy as required. What they confirm there is what gets attached.

On the project afterwards. The policy’s assessment appears in the project’s assessment list, ready to be worked through. Any questions the rule pre-filled are already answered, so the team is not asked about things that do not apply to this system. Everything else is left for them to answer.

Nothing else happens. Adding a policy creates no Alert and no Action record, sends no notification, and puts nothing in anyone’s list of work to pick up. Nobody is told the rule fired. The only evidence is the assessment now being on the project.

That last point catches people out, so it is worth saying plainly: if you write a rule expecting it to flag something for someone to follow up on, adding a policy will not do that. It adds work to the project; it does not tell anyone about it. Action types that create Alert and Action records behave very differently — see Alerts and Actions.


Rule fields

Field Type Required What it does
name String Yes Short, human-readable name. Shown wherever the rule is listed.
description String No Longer explanation for your team.
source String Yes "SCREENING" for the rules in this guide.
evaluatorType String Yes "CONTROL_ANSWER_MATCH" for the rules in this guide. Determines the shape of conditions.
conditions Object Yes What must be true for the rule to match. Shape depends on evaluatorType.
actions Array One of these What happens when the rule matches. Put your entry here unless told otherwise.
automations Array One of these Same shape as actions; see Actions and automations.
problemType String Yes Uppercase label naming why the rule exists, e.g. DATA_RETENTION_MISSING.
status String No "ACTIVE", "INACTIVE" or "DRAFT". Defaults to DRAFT, which never runs — set "ACTIVE" to go live.
triggerPolicyIdentifier String No Restricts the rule to one screening policy. Strongly recommended — without it the rule is considered for every screening in your organization.
applicableScreening Array No Restricts the rule to certain creation contexts. See Controlling when a rule applies.
classification String No Risk label this rule proposes. See Risk classification.
severity String No "LOW", "MEDIUM", "HIGH" or "CRITICAL". Only applies to action types that create Alert and Action records, so it has no effect on the rules in this guide.

A rule needs at least one entry across actions and automations. Either array may be empty as long as the other is not.


Writing conditions

For CONTROL_ANSWER_MATCH, conditions name the controls to check and, for each one, the answer options that count as a match.

{
  "mode": "requireAny",
  "controls": {
    "com.example.screening.usecase": [
      "com.example.screening.usecase.biometric_id"
    ]
  }
}
Field Type Required Rules
mode String Yes Exactly "requireAny" or "requireAll"
controls Object Yes Map of control identifier to an array of answer option identifiers. Needs at least one entry, and every array must contain at least one non-empty string.

The two levels of logic

There are two levels, and they behave differently:

  • Within one control, the listed options are always OR. If you list three options for a control, selecting any one of them makes that control match.
  • Across controls, mode chooses.
mode Across controls Use it when
requireAny OR — the rule matches if at least one control matches Any single answer is reason enough
requireAll AND — the rule matches only if every listed control matches Several things must be true together

requireAny — either answer is enough:

{
  "mode": "requireAny",
  "controls": {
    "com.example.screening.usecase": ["com.example.screening.usecase.biometric_id"],
    "com.example.screening.sector": ["com.example.screening.sector.healthcare"]
  }
}

requireAll — both must be true:

{
  "mode": "requireAll",
  "controls": {
    "com.example.screening.lifecycle": ["com.example.screening.lifecycle.deployed"],
    "com.example.screening.context": ["com.example.screening.context.high_risk"]
  }
}

How matching actually works

  • Matching is exact, case-sensitive string comparison against the answer option identifiers the user selected. There is no partial matching, no wildcards, and no case-insensitive fallback — a single character out of place means the control does not match.
  • A control that was never answered does not match. Under requireAll that alone stops the rule from matching. Under requireAny it simply contributes nothing, and another control can still make the rule match.
  • Conditions only work against questions that offer fixed choices. A control matches on the answer option the user picked, so free-text, document-upload and card questions cannot be used in conditions — they have no options to match.
  • Each answer option needs its own identifier. Option identifiers are optional in a policy’s configuration, and matching is far more reliable when they are set. Where an option has no identifier the platform falls back to its stored value, and an option whose value is zero is dropped entirely — so a “No” scored 0 with no identifier can never match. If the options you need are unidentified, ask Asenion to add identifiers before writing the rule.
  • No other operators exist. There is no negation, no “none of”, no numeric comparison or threshold, and no nesting of condition groups. The two levels above are the whole language. To express “if not X”, list the answer options that are the case instead.
  • Any extra key you add inside conditions is stored but ignored.

Actions and automations

A rule carries two arrays. Entries in both have exactly the same shape and the same field rules, both are validated identically, both require a uniqueKey, and a rule needs at least one entry between them.

The two arrays separate work a person is asked to do (actions) from work the platform carries out on its own (automations). Whether that separation has any effect depends on the action type:

  • For ADD_POLICY_AND_ANSWER it makes no difference at all. Both arrays are read together and every entry is treated the same way.
  • For action types that create Action records, it is recorded on the record and is visible to the team — see Where actions and automations differ.

When it makes no difference, put the entry in actions — it is the conventional place and keeps your rules consistent with each other.


The ADD_POLICY_AND_ANSWER action

ADD_POLICY_AND_ANSWER adds the target policy’s assessment to the project, and optionally pre-fills answers to questions that clearly do not apply.

Fields available on an entry:

Field Type Required Description
actionType String Yes "ADD_POLICY_AND_ANSWER"
targetPolicyIdentifier String Yes The policy to add
uniqueKey String Yes Stable key, unique within the rule — see uniqueKey and problemType
description String No Explains this entry. Recorded on the rule; for ADD_POLICY_AND_ANSWER nothing is surfaced to the team, so treat it as a note for whoever reads the rule next.
metadata Object No suggestedAnswers and targetPolicyVersion — see below
required Boolean No When true, the policy cannot be unchecked during policy selection

Write the action type exactly as shown. A misspelled value is rejected at upload with an Unknown actionType error.

Adding a policy

{
  "actionType": "ADD_POLICY_AND_ANSWER",
  "targetPolicyIdentifier": "com.example.privacy",
  "uniqueKey": "add-policy:com.example.privacy",
  "required": true,
  "metadata": {
    "suggestedAnswers": [
      {
        "controlIdentifier": "com.example.privacy.dm1",
        "controlBundleIdentifier": "com.example.privacy.dataminimisation",
        "values": [
          {
            "answerOptionIdentifier": "com.example.privacy.dm1.not_applicable",
            "value": "1"
          }
        ]
      }
    ]
  }
}

metadata supports:

Field Type Description
suggestedAnswers Array Answers to pre-fill
targetPolicyVersion String Pin a specific policy version, written exactly as it appears in the policy configuration (for example "1.0.1"). Omit to use the latest.

Each suggested answer needs three identifiers, all taken from the target policy:

Field Required Description
controlIdentifier Yes The question to pre-fill
controlBundleIdentifier Yes The control bundle that question belongs to
values[].answerOptionIdentifier Yes The option to select
values[].value No Marks the option as selected. Use the string "1".

How it behaves:

  • If the policy is already on the project, it is not added a second time.
  • A pre-filled answer is only written where the question is still unanswered. A person’s answer is never overwritten, and neither is an answer another rule already set.
  • You can leave suggestedAnswers out entirely to just add the policy.
  • If an identifier in suggestedAnswers matches nothing in the target policy, that answer is skipped without an error and the rest continue. This is why checking identifiers against your policy configuration matters — a typo here is invisible at upload time and only shows up as a question that was not pre-filled.

Whether the policy can be unchecked

The required flag sets how much choice the person creating the project has:

{
  "actionType": "ADD_POLICY_AND_ANSWER",
  "targetPolicyIdentifier": "com.example.euaiact",
  "uniqueKey": "add-policy:com.example.euaiact",
  "required": false
}

With required: false — or with the flag left out — the policy is offered during policy selection and whoever is creating the project can uncheck it. With required: true it cannot be unchecked.


uniqueKey and problemType

Both are required on every new rule, and both have a strict format — the upload is rejected without them.

What they are for is identifying the Alert and the Action a rule raises. ADD_POLICY_AND_ANSWER raises neither, so for the rules in this guide the two fields are recorded and never read: they change nothing about what your rule does. They matter for action types that do create those records, where they decide whether two rules converge on one item or produce two — see Alerts and Actions.

Give them accurate, stable values anyway. They are how a rule is recognised later, and vague or accidentally-reused values make a set of rules hard to maintain.

uniqueKey

Required on every entry in actions and automations.

  • Format: must start with a lowercase letter or digit, and may then contain lowercase letters, digits, periods, hyphens, underscores and colons.
  • Must be unique within the rule. Two entries in the same rule cannot share a key.
  • Keep it stable. Treat it as the entry’s permanent name and change it only when you mean to describe something genuinely different.

Good keys describe the entry: add-policy:com.example.privacy, add-policy:com.example.fairness.

Example Valid? Why
add-policy:com.example.privacy Yes Lowercase, allowed punctuation
add_policy_privacy Yes Underscores allowed
Add-Policy No Uppercase letters
-add-policy No Cannot start with a hyphen

problemType

Required on every new rule. An uppercase identifier — letters, digits and underscores, starting with a letter or digit — that names the underlying reason the rule exists, for example DATA_RETENTION_MISSING or PRIVACY_ASSESSMENT_REQUIRED.

Name the reason rather than the action — PRIVACY_ASSESSMENT_REQUIRED rather than ADD_PRIVACY_POLICY — and reuse the same value across rules that exist for the same reason.

Example Valid?
DATA_RETENTION_MISSING Yes
PRIVACY_ASSESSMENT_REQUIRED Yes
Data_Retention No — must be uppercase
_MISSING No — cannot start with an underscore

Risk classification

A rule can propose a risk label for the project by setting classification at the top level of the rule.

{
  "name": "High-risk use case",
  "source": "SCREENING",
  "evaluatorType": "CONTROL_ANSWER_MATCH",
  "triggerPolicyIdentifier": "com.example.screening.usecase",
  "problemType": "HIGH_RISK_USE_CASE",
  "status": "ACTIVE",
  "classification": "high-risk",
  "conditions": {
    "mode": "requireAny",
    "controls": {
      "com.example.screening.usecase": ["com.example.screening.usecase.biometric_id"]
    }
  },
  "actions": [
    {
      "actionType": "ADD_POLICY_AND_ANSWER",
      "targetPolicyIdentifier": "com.example.euaiact",
      "uniqueKey": "add-policy:com.example.euaiact",
      "required": true
    }
  ]
}

When several matching rules propose different labels, the most severe one is used:

Order Value
Most severe "prohibited"
  "high-risk"
  "limited-risk"
Least severe "minimal-risk"

If no matching rule proposes a label, the result is "cannot-determine".

The label is a starting point drawn from what the team reported during screening. It records how the system was described; it is not a finding about the system, and it can be revisited as the assessment proceeds.


Controlling when a rule applies

Two optional fields narrow when a rule is considered. Both are worth setting — without them, a rule is considered for every screening in your organization.

triggerPolicyIdentifier

Ties the rule to one screening questionnaire.

"triggerPolicyIdentifier": "com.example.screening.usecase"

A rule without this field is considered for every screening. A rule with it is considered only for that screening policy.

applicableScreening

Restricts the rule to certain creation contexts:

Value Applies when creating
"AI_SYSTEM" An AI system or project
"ORGANIZATION" An organization
"FUNCTIONAL_MODEL" A functional model
"VENDOR_MODEL" A vendor model
"ALL" Any of the above
"applicableScreening": ["AI_SYSTEM"]

Leave the field out (or use "ALL") to apply the rule everywhere. Values are read case-insensitively, and a value that is not on this list is dropped without an error — so check spelling, or the restriction you intended silently will not be applied.

Which rule wins when several match

Several rules can match one screening, and every one of them takes effect — a policy recommended by any of them is added. Precedence settles one thing only: when two matching rules recommend the same policy but disagree about required, whose setting is used.

The more specific rule wins, in this order:

  1. Rules with no applicableScreening (broadest)
  2. Rules set to "ALL"
  3. Rules naming the exact context (most specific — these override the two above)

Within the same tier, a newer rule wins over an older one. So you can keep a broad default that recommends a policy optionally, and layer a rule for one context that makes the same policy required.


What happens when a rule runs twice

Screening can be re-run, and rules are designed for that. Running the same rule again does not add the policy a second time or overwrite anyone’s work:

  • The policy is matched on its identifier. If it is already on the project, it is not added again. When the action pins targetPolicyVersion, the version has to match too — otherwise any version already on the project counts as a match.
  • A question that already has an answer is left alone. Neither a person’s answer nor one a rule set on an earlier run is overwritten. Only questions still unanswered are pre-filled.
  • Nothing is written when nothing changed. If the policy was already there and every question was already answered, the run leaves the project untouched.

This is checked against the project itself at the moment the rule runs, so it holds however many times screening is re-run, and no matter which rule added the policy first.


More examples

Add a policy without pre-filling answers

The simplest useful rule:

{
  "name": "Trained model - add fairness assessment",
  "description": "When the system uses a trained model, add the fairness assessment.",
  "source": "SCREENING",
  "evaluatorType": "CONTROL_ANSWER_MATCH",
  "triggerPolicyIdentifier": "com.example.screening.usecase",
  "problemType": "FAIRNESS_ASSESSMENT_REQUIRED",
  "status": "ACTIVE",
  "conditions": {
    "mode": "requireAny",
    "controls": {
      "com.example.screening.trainedmodel": ["com.example.screening.trainedmodel.yes"]
    }
  },
  "actions": [
    {
      "actionType": "ADD_POLICY_AND_ANSWER",
      "targetPolicyIdentifier": "com.example.fairness",
      "uniqueKey": "add-policy:com.example.fairness"
    }
  ]
}

Any one of several answers triggers the rule

Listing several options for one control means any of them is enough:

{
  "name": "External-facing system - add privacy assessment",
  "description": "Add the privacy assessment when the system serves customers or the public.",
  "source": "SCREENING",
  "evaluatorType": "CONTROL_ANSWER_MATCH",
  "triggerPolicyIdentifier": "com.example.screening.usecase",
  "problemType": "PRIVACY_ASSESSMENT_REQUIRED",
  "status": "ACTIVE",
  "conditions": {
    "mode": "requireAny",
    "controls": {
      "com.example.screening.audience": [
        "com.example.screening.audience.customers",
        "com.example.screening.audience.general_public"
      ]
    }
  },
  "actions": [
    {
      "actionType": "ADD_POLICY_AND_ANSWER",
      "targetPolicyIdentifier": "com.example.privacy",
      "uniqueKey": "add-policy:com.example.privacy"
    }
  ]
}

Several conditions must all be true

{
  "name": "Deployed and high-risk - add post-deployment review",
  "description": "When the system is deployed AND in a high-risk context, add the post-deployment review assessment.",
  "source": "SCREENING",
  "evaluatorType": "CONTROL_ANSWER_MATCH",
  "triggerPolicyIdentifier": "com.example.screening.usecase",
  "problemType": "POST_DEPLOYMENT_REVIEW_REQUIRED",
  "status": "ACTIVE",
  "applicableScreening": ["AI_SYSTEM"],
  "conditions": {
    "mode": "requireAll",
    "controls": {
      "com.example.screening.lifecycle": ["com.example.screening.lifecycle.deployed"],
      "com.example.screening.context": ["com.example.screening.context.high_risk"]
    }
  },
  "actions": [
    {
      "actionType": "ADD_POLICY_AND_ANSWER",
      "targetPolicyIdentifier": "com.example.postdeployment",
      "uniqueKey": "add-policy:com.example.postdeployment",
      "required": true
    }
  ]
}

One rule, several policies, with answers pre-filled

A common pattern: when a screening answer shows something does not apply, add the related policies and mark the questions that do not apply, so the team is not asked to answer them.

{
  "name": "No trained model - add privacy and safeguards with N/A answers",
  "description": "When screening reports no trained model, add the privacy and safeguards assessments and mark the model-specific questions as not applicable.",
  "source": "SCREENING",
  "evaluatorType": "CONTROL_ANSWER_MATCH",
  "triggerPolicyIdentifier": "com.example.screening.usecase",
  "problemType": "MODEL_QUESTIONS_NOT_APPLICABLE",
  "status": "ACTIVE",
  "conditions": {
    "mode": "requireAny",
    "controls": {
      "com.example.screening.trainedmodel": ["com.example.screening.trainedmodel.no"]
    }
  },
  "actions": [
    {
      "actionType": "ADD_POLICY_AND_ANSWER",
      "targetPolicyIdentifier": "com.example.privacy",
      "uniqueKey": "add-policy:com.example.privacy",
      "description": "Add the privacy assessment with its model-specific questions marked not applicable.",
      "metadata": {
        "suggestedAnswers": [
          {
            "controlIdentifier": "com.example.privacy.dm1",
            "controlBundleIdentifier": "com.example.privacy.dataminimisation",
            "values": [
              {
                "answerOptionIdentifier": "com.example.privacy.dm1.not_applicable",
                "value": "1"
              }
            ]
          },
          {
            "controlIdentifier": "com.example.privacy.dm2",
            "controlBundleIdentifier": "com.example.privacy.dataminimisation",
            "values": [
              {
                "answerOptionIdentifier": "com.example.privacy.dm2.not_applicable",
                "value": "1"
              }
            ]
          }
        ]
      }
    },
    {
      "actionType": "ADD_POLICY_AND_ANSWER",
      "targetPolicyIdentifier": "com.example.safeguards",
      "uniqueKey": "add-policy:com.example.safeguards",
      "metadata": {
        "suggestedAnswers": [
          {
            "controlIdentifier": "com.example.safeguards.s4",
            "controlBundleIdentifier": "com.example.safeguards.controls",
            "values": [
              {
                "answerOptionIdentifier": "com.example.safeguards.s4.not_applicable",
                "value": "1"
              }
            ]
          }
        ]
      }
    }
  ]
}

What this rule does:

  1. Trigger — someone completes the screening questionnaire.
  2. Condition — the control for “does this system use a trained model” was answered “no”.
  3. First action — adds the privacy assessment and marks two of its data-minimisation questions as not applicable.
  4. Second action — adds the safeguards assessment and marks one of its questions as not applicable.

Re-running screening will not add either policy a second time, or overwrite answers already given — see What happens when a rule runs twice.


Troubleshooting

The upload was rejected

Message Cause Fix
Missing or invalid "name" name absent or empty Add a non-empty name
Invalid "source" (must be SCREENING or RISK_SCORE) Unrecognized source Use "SCREENING"
Missing or invalid "evaluatorType" evaluatorType absent Add "CONTROL_ANSWER_MATCH"
Missing or invalid "conditions" conditions absent or not an object Add the conditions object
Provide at least one entry in "actions" or "automations" Both arrays empty or absent Add at least one entry
Only one rule per upload. Please submit one rule at a time. Payload holds several rules Upload them one at a time
No rule found. Use { "rules": [ {...} ] } or a single rule object. Shape not recognized Use one of the accepted shapes
uniqueKey is required on all actions and automations An entry has no uniqueKey Add a uniqueKey to every entry
Invalid uniqueKey '...' Wrong format Lowercase letters, digits, ., -, _, : — starting with a lowercase letter or digit
Duplicate uniqueKey '...' Two entries share a key Make each key unique within the rule
problemType is required when creating a new rule problemType absent Add an uppercase identifier, e.g. DATA_RETENTION_MISSING
Invalid problemType '...' Wrong format Uppercase letters, digits and underscores only
ADD_POLICY_AND_ANSWER requires targetPolicyIdentifier Target missing Add targetPolicyIdentifier
Unknown actionType: ... actionType misspelled or not recognized Check the spelling against the documented value
Invalid mode: "..." mode missing or misspelled Use exactly "requireAny" or "requireAll"
conditions.controls must be a non-empty object controls missing, empty, or an array Provide a map with at least one control
conditions.controls["..."] must be a non-empty array of strings A control maps to an empty array List at least one answer option identifier
No Evaluator registered for type: ... Unrecognized evaluatorType Use "CONTROL_ANSWER_MATCH"

The rule uploaded but nothing happens

Work down this list:

  1. Is status "ACTIVE"? This is by far the most common cause. A rule with no status is stored as DRAFT, and draft and inactive rules are never evaluated.
  2. Does triggerPolicyIdentifier match the screening actually being answered? If it names a different policy, the rule is not considered.
  3. Does applicableScreening include the context you are testing? A misspelled value is dropped silently. If that empties the array, the rule stops being restricted at all and is considered for every context — the opposite of what you intended.
  4. Are the control identifiers exactly right? Matching is case-sensitive and exact. Copy and paste them rather than retyping.
  5. Are the answer option identifiers right? Conditions match on the answer option identifier, not on the answer text shown on screen.
  6. With requireAll, was every listed control answered? An unanswered control does not match, which stops the whole rule.

The policy was added but the answers were not pre-filled

  • An identifier in suggestedAnswers does not match anything in the target policy — these are skipped without an error. Re-check controlBundleIdentifier, controlIdentifier and answerOptionIdentifier against the target policy’s configuration.
  • The questions were already answered. Pre-filling only writes to questions that are still unanswered.
  • controlBundleIdentifier is missing. It is required on every suggested answer.

Quick reference

Rule

Field Required Values
name Yes Any non-empty string
description No Any string
source Yes "SCREENING"
evaluatorType Yes "CONTROL_ANSWER_MATCH"
conditions Yes { "mode": ..., "controls": { ... } }
actions / automations At least one entry between them Array of entries
problemType Yes Uppercase identifier
status No (defaults to DRAFT) "ACTIVE", "INACTIVE", "DRAFT"
triggerPolicyIdentifier No Screening policy identifier. Strongly recommended
applicableScreening No "AI_SYSTEM", "ORGANIZATION", "FUNCTIONAL_MODEL", "VENDOR_MODEL", "ALL"
classification No "prohibited", "high-risk", "limited-risk", "minimal-risk"
severity No "LOW", "MEDIUM", "HIGH", "CRITICAL" — no effect on the rules in this guide

Action or automation entry

Field Required Notes
actionType Yes Exact spelling — an unrecognized value is rejected at upload
targetPolicyIdentifier Yes The policy to add
uniqueKey Yes Lowercase, unique within the rule
description No A note on the entry; not surfaced to the team for this action type
metadata No suggestedAnswers, targetPolicyVersion
required No true stops the policy being unchecked during policy selection

Conditions

Field Required Notes
mode Yes "requireAny" (OR across controls) or "requireAll" (AND across controls)
controls Yes Map of control identifier to answer option identifiers; OR within each control

Suggested answer

Field Required
controlIdentifier Yes
controlBundleIdentifier Yes
values[].answerOptionIdentifier Yes
values[].value No — use the string "1"

Checklist before you upload

  • name is clear, and description explains the intent for whoever reads it next
  • source is "SCREENING" and evaluatorType is "CONTROL_ANSWER_MATCH"
  • status is "ACTIVE" — otherwise the rule is stored as a draft and never runs
  • problemType is present and uppercase
  • Every entry in actions and automations has a uniqueKey, lowercase and unique within the rule
  • conditions has both mode and controls, and every control lists at least one answer option
  • triggerPolicyIdentifier is set, so the rule is only considered for the intended screening
  • applicableScreening values are spelled exactly as documented, if used
  • Identifiers are copied from your own configuration and cross-checked:
    • Control identifiers in conditions.controls exist in the screening policy
    • Answer option identifiers exist on those controls
    • targetPolicyIdentifier matches a policy in the system
    • controlBundleIdentifier and controlIdentifier in suggested answers exist in the target policy
  • Each entry’s actionType is spelled exactly, and has the target field that type requires
  • No existing rule already does this — the rules already loaded for your organization can be listed through the API, so check before adding a near-duplicate
  • Walk through one screening path end to end and confirm the conditions would match what you expect. The platform can evaluate a set of screening answers and report which rules matched and why, without creating a project — ask Asenion for the API details if you want to check a rule before it goes live.

Alerts and Actions

Appendix — background only. Nothing here is needed to write the rules this guide covers. It explains what other action types create, so that the fields those types rely on make sense.

This guide documents ADD_POLICY_AND_ANSWER, which changes the project directly and leaves no separate record behind. The actionType field accepts other values, and some of them behave in a fundamentally different way: instead of changing the project, they make the platform create records your team then works through.

A note on the word “action”. It is used for two different things. A rule’s actions array holds definitions — what you write in the JSON. An Action is a record the platform creates on a project when a rule matched. This section is about the records. They are what the actionsForProject query returns.

The two records

Record What it represents Its states
Alert Something needs attention on this project OPEN, RESOLVED, DISMISSED
Action One piece of work under that alert PENDING, IN_PROGRESS, DONE, DISMISSED, REJECTED, FAILED

An Alert carries a severity and the set of rules currently contributing to it. An Action can be assigned to a person, given a due date, commented on, started, resolved or dismissed.

How the relationships work

Three relationships, and each exists for a reason:

  • One rule can produce many Alerts. A rule that acts on several targets raises one Alert per target, so unrelated problems never get merged into a single item.
  • Many rules can share one Alert. Rules that name the same problemType and act on the same target all contribute to the same Alert, which keeps the list of them. Two rules noticing the same problem produce one item for the team, not two.
  • One Alert holds many Actions. The Alert is the problem; the Actions beneath it are the separate pieces of work needed to deal with it.

That middle relationship is the reason problemType is required on every rule.

What identifies an Alert

An Alert is identified by these four things together:

Part Where it comes from
Scope What the Alert belongs to — a project
targetType What the action type acts on, e.g. POLICY
targetId The target identifier — the policy or control the entry names
problemType Taken straight from the rule

For a rule with problemType: "DATA_RETENTION_MISSING" acting on the policy com.example.dataretention in one project, those four values together are that Alert’s identity. The same combination in a different project is a different Alert — which is what you want, since one project’s problem is not another’s.

These are stored as separate fields rather than combined into a single key. That is deliberate: it means a new dimension can be added later without changing how existing Alerts are identified.

Nothing about the rule itself is part of the identity. That is what allows two different rules to arrive at the same identity and join the same Alert. Change either the target or the problemType and you get a different identity, and therefore a different Alert.

An Action’s identity is simpler: its uniqueKey, within the Alert it belongs to.

How duplicates are prevented, practically

Screening can be re-run any number of times, so the platform never simply inserts a record. For both Alerts and Actions it does the same two-step:

  1. Look for an existing record with this identity. For an Alert that is the four parts above; for an Action, its uniqueKey plus the Alert it sits under.
  2. Not there? Create it. Found it? Reuse that same record rather than making a second one.

What “reuse” means differs slightly between the two, and the difference is deliberate:

  • An Action is left exactly as it is. Its status, assignee, due date and comments all survive. Work someone has already done is never reset by a later run.
  • An Alert is reused and refreshed. It stays the same record and keeps the time it was first raised, but its last-evaluated time moves forward, and its severity can be raised if a rule now contributing to it is more severe than before.

Underneath, the database holds a uniqueness constraint on those same keys. If two runs happen at the same instant and both get as far as trying to create the record, the database refuses the second one; the platform catches that, fetches the record the first run created, and carries on with it. The constraint is what makes this safe rather than merely likely.

Five consequences worth knowing:

  • Re-running never duplicates what already exists.
  • Two rules with the same problemType on the same target share one Alert — they resolve to the same identity. Give them different problemType values and you get two Alerts for the same target.
  • Two rules that also use the same uniqueKey share one Action. Same problem, same remedy, one item.
  • Two entries in one rule with different uniqueKeys produce two separate Actions. This is why uniqueKey is required rather than derived from the action: without distinct keys, a second Action on the same target would be treated as a repeat of the first and quietly dropped.
  • Changing a rule’s identity later creates a new record and leaves the old one behind. Identity is computed from values in your rule, so editing a live rule’s problemType, an entry’s uniqueKey, or the target it acts on means the next run computes a different identity, finds nothing matching, and creates a fresh Alert or Action. The previous one is not updated, not closed, and not removed — it simply stays where it is, and the team now sees both. Treat those three values as permanent once a rule is live. The record left behind is not cleaned up automatically and will not close on its own, so it has to be dismissed by hand.

severity also becomes meaningful here: it is carried onto the Alert and its Actions, defaulting to MEDIUM, and where several rules share one Alert the highest severity among them applies. Note that it only moves upwards — once an Alert has been raised to a higher severity it keeps it, even if the rule that raised it later stops matching.

What happens over time

  • The rule matches — the Alert opens and its Actions are created as PENDING.
  • Screening is re-run and it still matches — the existing Alert and Actions are reused, untouched.
  • The rule stops matching — that rule is withdrawn from the Alert. The Alert is only RESOLVED once no contributing rule still matches, and its Actions are then dismissed. One rule going quiet does not close a problem another rule still sees.
  • It matches again later — the dismissed Actions are reopened rather than duplicated, so their history survives.

Where actions and automations differ

For action types that create Actions, the array you choose is recorded on the record:

  • an entry in actions becomes an Action of kind USER_ACTION — work for a person;
  • an entry in automations becomes an Action of kind AUTOMATION — work the platform carries out.

An Action of kind AUTOMATION can be rejected by a user. Rejecting it converts it to USER_ACTION and returns it to PENDING, so the work stays on the list but is now owned by a person. Only AUTOMATION Actions can be rejected this way.

Which action type to use

Use ADD_POLICY_AND_ANSWER — the action type this guide documents and supports. It covers adding assessment work to a project, and it creates none of the records above.

The other values the schema accepts are outside the scope of this guide. If you have a case that needs one, talk to Asenion before you build on it so we can confirm the behaviour for your tenant and cover it properly.