Skip to content

Screening rule fields

A workflow template can carry screening rules. A rule watches one value from the verification, compares it with an operator, and takes an action when the comparison holds. You send rules on the screening_rules array of POST /v1/workflow-templates and PATCH /v1/workflow-templates/{id}.

condition.field is a string on the wire, so this page is the discovery path: it lists every field name the rule engine can resolve, every operator, every action, and every rule type. A field that is not on this page is refused at write time, and the refusal names the field.

Rules replace, they do not merge. Sending screening_rules on a PATCH replaces the template’s whole rule set. Omitting it leaves the existing rules untouched, and sending [] clears them. A template holds at most 100 rules.

For the no-code version of the same feature, see Add fraud signals to a workflow.

A condition is either a single field comparison or a group of conditions combined with AND or OR.

// A field condition.
{ "kind": "field", "field": "fraud.botScore", "operator": "lte", "value": 10 }
// A group. `children` holds 1 to 20 conditions and groups can nest.
{
"kind": "group",
"op": "AND",
"children": [
{ "kind": "field", "field": "fraud.asnIsDatacenter", "operator": "eq", "value": true },
{ "kind": "field", "field": "fraud.ipIsTorExit", "operator": "eq", "value": true },
],
}

field is 1 to 120 characters. value is a string, a number, a boolean, an array of strings, or an array of numbers, and an array holds at most 1000 entries.

A whole rule looks like this:

{
"name": "Datacenter connection",
"ruleType": "ip_reputation",
"condition": {
"kind": "field",
"field": "fraud.asnIsDatacenter",
"operator": "eq",
"value": true
},
"action": "review",
"priority": 100,
"isActive": true
}

priority is an integer from 0 to 10000 and defaults to 100. isActive defaults to true. scoreAdjustment is a number from -1 to 1 and is only meaningful with the adjust_score action.

Operator Holds when
eq The resolved value equals value.
neq The resolved value does not equal value.
gt The resolved value is greater than value.
gte The resolved value is greater than or equal to value.
lt The resolved value is less than value.
lte The resolved value is less than or equal to value.
in value is an array and it contains the resolved value.
not_in value is an array and it does not contain the resolved value.
contains The resolved value contains value.
exists The field resolved to a value at all. value is ignored.

Two notes on contains and array-valued fields. When the field resolves to an array (applicant.givenNames is the one to watch), contains tests exact membership of one element, not a substring: applicant.givenNames contains "Maria" matches ["Jose", "Maria"] and does not match ["Mariana"]. For substring matching on a name, use applicant.name. On the same array field, eq and in compare by reference and therefore never hold, and neq and not_in hold for every applicant who has given names, which makes them always-true rules rather than useful ones.

A field that resolves to nothing (the applicant never supplied it, or the signal was not collected) makes the rule not fire, except under exists, which reports false.

Field Type What it resolves to
applicant.legalName string The captured legal name, verbatim. Empty for an applicant whose name arrived as separate components.
applicant.name string The name the platform holds: the captured legal name, or the composition of the name components.
applicant.familyName string The primary identifier from the document. Can be more than one word, and holds a mononym in full.
applicant.givenNames string[] The ordered secondary identifiers from the document.
applicant.email string The applicant’s email address.
applicant.email.domain string The lowercased domain part of the applicant’s email address.
applicant.dateOfBirth string The applicant’s date of birth.
applicant.age number The applicant’s age in whole years.
applicant.address.country string The two-letter country code of the applicant’s address.
applicant.phone string The applicant’s phone number.
finding.count number How many findings the verification has produced.
finding.severity.high.count number How many of those findings are high severity.
finding.severity.medium.count number How many are medium severity.
finding.severity.low.count number How many are low severity.

These resolve only when the workflow template has device intelligence switched on. On a workflow without it they resolve to nothing, so a rule built on them does not fire.

Field Type What it resolves to
fraud.botScore number How human the connection looks. Lower means more automated.
fraud.threatScore number The reputation score of the connecting address.
fraud.country string The two-letter country code of the connecting address.
fraud.asn number The autonomous system number the connection came from.
fraud.asOrganization string The name of the network that owns that autonomous system.
fraud.asnIsDatacenter boolean The connection came from a datacenter network rather than a consumer one.
fraud.verifiedBotCategory string The category of a recognized, declared bot. Absent for ordinary traffic.
fraud.ja4 string The TLS fingerprint of the connection.
fraud.ipReputation boolean The connecting address is rated poor: datacenter, proxy, exit node, or a bad reputation score.
fraud.ipIsTorExit boolean The connecting address is a known anonymizing exit node.
fraud.repeatApplicant boolean This person has been seen applying before.
fraud.emailDisposable boolean The email address belongs to a disposable-address service.
fraud.emailDomainDga boolean The email domain looks machine-generated.
fraud.emailUsernameGrammarInvalid boolean The part before the @ does not read like a real name.
fraud.geoTimezoneMismatch boolean The browser’s timezone does not match the timezone of the connecting address.
fraud.deviceTouchPointMismatch boolean The device claims a form factor its touch capability contradicts.
fraud.countryMismatch boolean The country of the connecting address differs from the country the applicant declared.
fraud.impossibleTravel boolean Two attempts from locations too far apart, too fast, for one person.
fraud.ringSize number How many verifications link together as one coordinated group.
fraud.ringTruncated boolean The group was larger than the walk limit, so fraud.ringSize is a floor rather than a total.
fraud.captureAbsent boolean No connection context was captured at all for this verification.
fraud.networkContextChanged boolean The network at submit differs materially from the network the applicant started on.
device.velocity.distinctIps number How many distinct addresses this device has been seen on.
device.velocity.sessionCount number How many verifications this device has started.
device.behavior.pasteCount number How many times the applicant pasted into the form.
device.behavior.totalSeconds number How long the applicant spent on the form.
device.behavior.minStepSeconds number The shortest time spent on any one step.
device.behavior.headless boolean The browser is running under automation control.
device.behavior.collectionAbsent boolean No behavioral measurements arrived for this verification.

The two *Absent fields report an absence of evidence rather than a bad signal, so treat them as a reason to look, not as a finding.

Four more families take a name you fill in. They follow a fixed shape and anything outside that shape is refused.

Shape Resolves to
check.<checkType>.outcome The outcome of that check on this verification: pass, fail, review, or error.
any.finding.<subjectType>.<field> The first defined value of <field> across the findings with that subject type.
all.finding.<subjectType>.<field> Every defined value of <field> across those findings, as an array.
count_where.finding.<subjectType>.<field> How many of those findings have a truthy <field>.
session.metadata.<key> The value of that key in the workflow template’s metadata. Letters, digits, and _ only.

<checkType> is a check type, exactly as it appears in the workflow document and in the session read. <subjectType> is a finding’s subject_type, and <field> is either a top-level finding field (id, subjectType, severity, sourceAuthority, findingType) or a key inside that finding’s evidence.

Worked examples:

Example Meaning
check.credit_history.outcome The outcome of the credit-history check.
check.id_verification.outcome The outcome of the identity check.
any.finding.credit_record.recordType The type of the first adverse record on the credit file.
all.finding.person.severity The severities of every watchlist finding about the person.
count_where.finding.credit_record.recordType How many adverse records the credit file produced.
session.metadata.regionRiskScore The regionRiskScore key on the workflow template’s metadata.

session.metadata.<key> resolves strings, numbers, booleans, and arrays of strings or numbers. A key whose value is an object, or an array mixing types, fails the verification rather than resolving quietly, so keep template metadata flat.

Action What the rule does when it holds
tag Records a reviewer-visible annotation. It does not route the verification and does not drive its outcome.
review Routes the verification to manual review.
adjust_score Adds scoreAdjustment to the weighing that decides whether the verification needs manual review.
approve Marks the verification approved.
decline Declines the verification. Refused at write time for most rules: see below.

ruleType labels what a rule is about. It does not restrict which fields the rule may read: any rule type can carry any resolvable field. What it does decide is whether decline is allowed.

Rule type May carry decline?
country_block Yes
score_threshold Yes
age_restriction Yes
geolocation_mismatch Yes
watchlist_hit Yes
adverse_media_hit Yes
custom Yes
repeat_applicant No
device_velocity No
ip_reputation No
bot_signal No
impossible_travel No
identity_ring No
email_risk No
behavior_anomaly No
geo_mismatch No
credit_history No

“Yes” here means the write is accepted. It does not mean a rule declines an applicant by itself: a fired decline is floored to manual review before it reaches the verification’s outcome, in both regions, for every rule type. What the write-time refusal adds is that a rule cannot be saved claiming an action the engine would not honor.

Separately from the rule type, a rule whose condition touches a sensitive attribute cannot carry decline, whatever its rule type. Declining on age, country, postal code, gender, or a name pattern carries fair-lending and fair-housing exposure (ECOA and Regulation B, and Fair Housing Act disparate impact), so the platform refuses to save the rule and asks for tag or review instead. A reviewer’s manual decline is then a documented human decision rather than an automated one.

Sensitive attribute Authorable in a rule?
applicant.age Yes
applicant.address.country Yes
applicant.legalName Yes
applicant.name Yes
applicant.familyName Yes
applicant.givenNames Yes
applicant.address.postalCode No
applicant.gender No

The last two are not resolvable fields at all, so a rule cannot read them. They are listed as sensitive anyway, so that if either ever becomes readable it is already covered.

Every refusal below is an HTTP 422 carrying the standard error envelope, with error.code set to validation_error. The envelope also carries type, doc_url, and request_id, which are the same on every error; the Error reference shows them in full. The examples here show only the fields that differ per refusal.

The offending rule is identified by its index in the array you sent.

{
"error": {
"code": "validation_error",
"message": "Invalid request",
"details": {
"issues": [
{
"code": "custom",
"message": "Decline action is not allowed on a credit history rule. A credit report is a consumer report, so declining on it is an adverse action a person has to take and notice (FCRA 15 U.S.C. 1681m). Use action: 'tag', 'review', or 'adjust_score' instead, then decline from the review queue where the required notices are shown.",
"path": ["screening_rules", 0, "action"]
}
]
}
}
}

A fraud rule type refused for the same reason carries the other message, and the wording is deliberately different because the reason is:

{
"code": "custom",
"message": "Decline action is not allowed on a fraud rule type (no auto-decline of applicants, EU Art. 22 / D9). Use action: 'tag', 'review', or 'adjust_score' instead.",
"path": ["screening_rules", 0, "action"]
}

A rule refused for touching a sensitive attribute carries a third message, also on path: ["screening_rules", <index>, "action"], naming the attributes listed above and pointing at tag or review.

In all three cases the fix is the same: change action, save, and let a reviewer make the decline from the review queue. For what a decline on a credit report obliges the reviewer to do, see Background-check decisions (FCRA).

A condition.field that is not on this page (including inside a nested group) is refused before anything is written, and the response names the field:

{
"error": {
"code": "validation_error",
"message": "Unresolvable screening-rule field",
"details": { "field": "applicant.creditScore" }
}
}

Only the field path is returned, never an applicant value. Fix the field name against the tables above and send the write again. The refusal is all-or-nothing: no part of the template is saved and the existing rules are untouched.