AckdbScoreCriterion
One scoring rule: a field reference + exactly one scoring mode. Field refs — `trait:<key>` / `attribute:<key>` (company grain; person grain: `attribute:is_internal|affiliation_count|key_count`), `identity_link:<type>`, `event:<source>.<bareType>` (aggregate), `contacts` (company grain persona count), `company:trait:<key>` (person grain, best match over ANY active affiliation), `convergence` (both grains: the count of DISTINCT `sources` with activity — or of DISTINCT fired `signals` event refs — inside `windowDays`, scored via `bands`; exactly one of sources | signals).
Properties
groupstringrequiredKey of the group this criterion belongs to.
fieldstringrequiredlabelstringOptional display label for explain/UI.
matcharray ofValue → points entries (first match wins). op: eq | in | present. Legal on value-shaped refs (trait/attribute/identity_link/company:) and, since #438, on `agg: "latest"` event refs: the one aggregate that reduces to a VALUE rather than a volume, so it compares the `valueField` as text where bands/perUnitPoints cast it to numeric. Counting aggregates (count/sum/distinct_contacts) and `contacts` reject match (400), since comparing a count against numbers is what bands is for. Exactly one of match | bands | perUnitPoints(+cap) per criterion. On event metadata the comparison is EXACT and case-sensitive (see /scores/capabilities, `eventValueMatching`): trait values canonicalize case-insensitively (#380), event fields deliberately do not, so seed value pickers from /fields/{key}/values instead of accepting free text.
opstringrequired"eq""in""present"valuesarray ofpointsnumberrequiredmin -100 · max 100bandsarray ofNumeric threshold bands (first match wins). Exactly one of gte/lt per band. Points are ±100, the same bound match entries and perUnitPoints carry.
gtenumberltnumberpointsnumberrequiredmin -100 · max 100aggstringAggregate reducer (event: refs; default count). distinct_contacts is company-grain only. latest (#424) scores the CURRENT value carried in event metadata — the `valueField` of the most recent matching event — instead of a volume.
"count""sum""distinct_contacts""latest"sumFieldstringPromoted NUMERIC metadata field — required when agg=sum.
valueFieldstringPromoted metadata field read off the most recent matching event — REQUIRED when agg=latest, rejected otherwise (#424). Must be promoted on the criterion's event type (entity_events.metadata holds promoted fields only). Its value is cast to numeric; a non-numeric value scores 0 points.
windowDaysintegermin 1 · max 3650Trailing window; absent = all-time. REQUIRED on convergence, with a tighter 1–365 bound.
halfLifeDaysintegermin 1 · max 3650Exponential decay half-life (weight = 2^(−age/halfLife)). Not with distinct_contacts or latest.
perUnitPointsnumbermin -100 · max 100Linear points per unit (with cap).
capnumberSymmetric clamp for perUnitPoints (0 < cap ≤ 100). Required with it.
filterobjectcontacts criterion only: persona filter.
titleContainsarray ofactiveWithinDaysintegersourcesarray ofconvergence criterion only: the distinct sources counted (≥2, each must exist in the schema registry). Double counting with per-source `event:` criteria is intended — convergence is the multiplier on top of per-source activity. Exactly one of `sources` | `signals`.
signalsarray ofconvergence criterion only (#421): user-composed signal list — `event:<source>.<bareType>` refs (2–20, deduped, each must resolve in the schema registry; person grain needs person-scope types). The criterion value is how many listed signals had ≥1 event in the window — a signal firing 40× counts once. Exactly one of `sources` | `signals`.
Example
{
"group": "string",
"field": "event:slack.message",
"label": "string",
"match": [
{
"op": "eq",
"values": [],
"points": 0
}
],
"bands": [
{
"gte": 0,
"lt": 0,
"points": 0
}
],
"agg": "count",
"sumField": "string",
"valueField": "employee_count",
"windowDays": 0,
"halfLifeDays": 0,
"perUnitPoints": 0,
"cap": 0,
"filter": {
"titleContains": [
"string"
],
"activeWithinDays": 0
},
"sources": [
"string"
],
"signals": [
"string"
]
}