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, andevaluatorType:"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
- Before you begin
- Your first rule
- Finding the right identifiers
- Uploading a rule
- What the rule actually does in the app
- Rule fields
- Writing conditions
- Actions and automations
- The ADD_POLICY_AND_ANSWER action
- uniqueKey and problemType
- Risk classification
- Controlling when a rule applies
- What happens when a rule runs twice
- More examples
- Troubleshooting
- Quick reference
- Checklist before you upload
- Alerts and Actions — background, not needed to write a rule
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:
sourceandevaluatorTypesay this is a screening rule, matched on answer options.triggerPolicyIdentifierkeeps the rule tied to one screening questionnaire, so it is not considered for unrelated ones.problemTypeis a required label naming why the rule exists. See uniqueKey and problemType.statusmust be"ACTIVE", or the rule is stored but never evaluated.conditionsmatches one control against one answer option.- The action adds a policy; its
uniqueKeyis required and must be unique within the rule.
Three fields are easy to leave out.
problemTypeand every action’suniqueKeyare rejected at upload, so you will see those immediately. A missingstatusis the dangerous one: the rule uploads successfully, is stored asDRAFT, 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:
- Open the target policy’s configuration JSON.
- Find the control bundle and copy its
identifier. - Inside that bundle, find the control and copy its
identifier. - 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.
- Open Organization Administration.
- Select the Upload Rules tab.
- Paste the JSON for one rule into the text box.
- 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,
modechooses.
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
requireAllthat alone stops the rule from matching. UnderrequireAnyit 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” scored0with 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
conditionsis 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_ANSWERit 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
actionsandautomationsdiffer.
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 actionTypeerror.
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
suggestedAnswersout entirely to just add the policy. - If an identifier in
suggestedAnswersmatches 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:
- Rules with no
applicableScreening(broadest) - Rules set to
"ALL" - 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:
- Trigger — someone completes the screening questionnaire.
- Condition — the control for “does this system use a trained model” was answered “no”.
- First action — adds the privacy assessment and marks two of its data-minimisation questions as not applicable.
- 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:
- Is
status"ACTIVE"? This is by far the most common cause. A rule with nostatusis stored asDRAFT, and draft and inactive rules are never evaluated. - Does
triggerPolicyIdentifiermatch the screening actually being answered? If it names a different policy, the rule is not considered. - Does
applicableScreeninginclude 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. - Are the control identifiers exactly right? Matching is case-sensitive and exact. Copy and paste them rather than retyping.
- Are the answer option identifiers right? Conditions match on the answer option identifier, not on the answer text shown on screen.
- 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
suggestedAnswersdoes not match anything in the target policy — these are skipped without an error. Re-checkcontrolBundleIdentifier,controlIdentifierandanswerOptionIdentifieragainst the target policy’s configuration. - The questions were already answered. Pre-filling only writes to questions that are still unanswered.
controlBundleIdentifieris 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
nameis clear, anddescriptionexplains the intent for whoever reads it nextsourceis"SCREENING"andevaluatorTypeis"CONTROL_ANSWER_MATCH"statusis"ACTIVE"— otherwise the rule is stored as a draft and never runsproblemTypeis present and uppercase- Every entry in
actionsandautomationshas auniqueKey, lowercase and unique within the rule conditionshas bothmodeandcontrols, and every control lists at least one answer optiontriggerPolicyIdentifieris set, so the rule is only considered for the intended screeningapplicableScreeningvalues are spelled exactly as documented, if used- Identifiers are copied from your own configuration and cross-checked:
- Control identifiers in
conditions.controlsexist in the screening policy - Answer option identifiers exist on those controls
targetPolicyIdentifiermatches a policy in the systemcontrolBundleIdentifierandcontrolIdentifierin suggested answers exist in the target policy
- Control identifiers in
- Each entry’s
actionTypeis 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
actionsarray 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 theactionsForProjectquery 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
problemTypeand 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:
- Look for an existing record with this identity. For an Alert that is the four parts above; for an Action, its
uniqueKeyplus the Alert it sits under. - 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
problemTypeon the same target share one Alert — they resolve to the same identity. Give them differentproblemTypevalues and you get two Alerts for the same target. - Two rules that also use the same
uniqueKeyshare one Action. Same problem, same remedy, one item. - Two entries in one rule with different
uniqueKeys produce two separate Actions. This is whyuniqueKeyis 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’suniqueKey, 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
RESOLVEDonce 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
actionsbecomes an Action of kindUSER_ACTION— work for a person; - an entry in
automationsbecomes an Action of kindAUTOMATION— 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.