How Diff Predicates Decide What Alerts You
A change in a field and an alert are two different things. The predicate is the rule in between - here is every type, a worked example for each, and the gate that quietly blocks the ones people expect to fire.
Why a changed page doesn't always mean an alert
Every monitor runs the same two steps on every check: extract the fields, then compare them against the previous run. What happens after that is a separate decision, made by the monitor's diff predicate. The comparison tells Verid what changed. The predicate decides whether that change is worth telling anyone about.
This is the difference between "the page changed" and "the price dropped by more than 5%." A
monitor with any_field_changes fires on any difference at all. A monitor with
field_decreases_by_percent on the price field stays quiet through ten runs of unrelated text
changes and only fires the moment the number actually drops. Same page, same comparison, two
completely different alert behaviours, chosen entirely by which predicate is attached.
Every run records the comparison it made against the previous run - which fields changed, and their
before/after values - the same shape a webhook delivery carries in its diff block:
{
"fields_changed": ["price"],
"before": { "price": "24.99" },
"after": { "price": "19.99" }
}
Two things about this shape matter for everything below. First, fields_changed only lists fields
whose value actually differs from the prior run - unchanged fields aren't in it. Second, before
and after only ever carry the fields that changed, not the full extracted snapshot. An unchanged
field is simply absent from both, not present with an equal value. That second point explains a
failure mode later in this guide.
If the predicate matches, a delivery goes out to every notification endpoint on the monitor. If it doesn't, the run is still recorded in full - extracted values, the comparison, everything - but nothing is sent.
The nine predicate types
Fields are referenced by the name you gave them in your extraction config.
any_field_changes
{ "type": "any_field_changes" }
Fires whenever at least one field differs from the previous run. The simplest predicate, and the right one for "tell me the instant anything on this page moves." Pairs naturally with the full page hash extraction method.
field_changes
{ "type": "field_changes", "field": "tag_name" }
Fires only when the named field changed. Every other field can move freely without triggering anything. This is the one to reach for when you're tracking one specific value - a release tag, a status string - and don't care what else on the page shifts around it.
field_increases_by_percent
{ "type": "field_increases_by_percent", "field": "price", "threshold": 5 }
Fires when the field changed and the percent increase from the previous value to the new one is
at least threshold. threshold is a plain number, not a fraction - 5 means 5%, not 0.05. The
percent move is measured against the size of the previous value, so a previous value of exactly 0
never satisfies a percent predicate - there's no percentage increase from zero that means anything,
so it's treated as never firing rather than as an error.
field_increases_by_absolute
{ "type": "field_increases_by_absolute", "field": "url_count", "threshold": 1 }
Same gate, but compares the raw increase against threshold with no percentage math. threshold: 1
gives you "one or more new entries," which is the common shape for sitemap URL counts, comment
counts, or subscriber counts.
field_decreases_by_percent
{ "type": "field_decreases_by_percent", "field": "price", "threshold": 10 }
Mirror image of the percent-increase case: fires on a drop of at least threshold percent from the
previous value. Same zero-baseline rule applies. This is the standard price-drop predicate.
field_decreases_by_absolute
{ "type": "field_decreases_by_absolute", "field": "in_stock", "threshold": 5 }
Fires when the value dropped by at least threshold in raw terms. Useful for inventory or
follower-count drops where you care about the amount lost, not a percentage of a number that might
itself be small.
field_matches_regex
{ "type": "field_matches_regex", "field": "status", "pattern": "^(error|failed|degraded)" }
Fires when the field's new value matches the regular expression in pattern. If the field isn't
present in the new value, or pattern itself isn't a valid regular expression, the predicate simply
doesn't fire rather than erroring the run.
field_equals
{ "type": "field_equals", "field": "availability", "value": "In Stock" }
Fires when the field's new value exactly equals the literal value. The comparison is type-strict:
a JSON string "true" never equals the boolean true, no matter how you read the field. Comparing
whole objects (which mostly comes up with JSONPath or AI extraction fields) works, but only if the
object's fields are in the same order as value - it isn't a deep, order-independent comparison.
composite (AND / OR)
{
"type": "composite",
"operator": "AND",
"conditions": [
{ "type": "field_changes", "field": "tag_name" },
{ "type": "field_equals", "field": "prerelease", "value": false }
]
}
Combines any number of the other eight predicates - including other composite predicates, nested
as deep as you need - under AND (every condition must be true) or OR (any one is enough). The
common patterns are "field changed AND another field equals a specific value" (skip prereleases,
skip out-of-stock states) and "dropped by X% OR went out of stock" to catch two different signals
with one monitor instead of two.
The gate that catches everyone
Every one of the eight non-composite predicates only fires for a field that actually changed on that
run. For field_changes and both directions of _by_percent / _by_absolute, that's an explicit,
first check - if the field isn't among the ones that changed, the predicate stops there without even
looking at the value. field_matches_regex and field_equals reach the same result a different way:
since the "new value" they read is only ever populated for fields that changed, an unchanged field
looks the same as a missing one, and both predicates decline to fire on a missing field.
Either way, the practical rule is the same: a value being correct is not enough - it also has to have changed on this specific run. This is the single most common reason a correctly-configured predicate "never fires." The threshold, the regex, the equality check - all of it is fine, but the field held the same value as last time, so the predicate never got as far as evaluating its own condition.
The first run is not silent
It's worth being direct about this because it contradicts a common assumption: the first run of a new monitor can alert. A brand-new monitor has nothing to compare against, so Verid treats every extracted field as "new" on that first check - every field counts as changed, exactly as if it had just flipped from one value to another.
In practice this means an any_field_changes predicate, or a field_changes predicate on a field
that extracts successfully, fires and delivers on the very first check, before there's any real
history to compare against. If you don't want a delivery the moment you save a new monitor, use a
percent- or absolute-threshold predicate instead - those can't fire on a first run, because there's
no previous numeric value to measure the move from.
Reading a run against its predicate
Every run stores its comparison and its delivery outcome together, so you don't have to reconstruct
anything by hand to see why a run did or didn't alert. If a run's diff shows the field you expected
in fields_changed, with the values you expected, and no delivery went out, the predicate's own
condition - a threshold, a regex, an exact-match value - is the next thing to check, not the
extraction.
What to read next
- Percent and absolute thresholds, and why yours may never fire -
the numeric parsing rules behind
_by_percentand_by_absolute, including the currency-string case that trips up price monitors specifically. - Which extraction method should you use? - predicates only ever see the fields your extraction config produces.
- How a Verid check runs, end to end - where predicate evaluation sits in the run pipeline, relative to extraction and delivery.
Ready to try it? Point Verid at your URL and get a signed alert on every change. 200 free runs a month, no credit card.