Skip to main content
See also: Decisioning Gates REST API reference for request/response shapes, status codes, and error semantics.

Overview

A Decisioning Gate is a configurable rule that decides whether — and how strongly — a customer may receive a particular Offer. KaireonAI authors these rules in four ordered stages. All four are stored as decisioning gate records with a stage discriminator, and are evaluated inside the Filter (qualify) stage of a Decision Flow: Both eligibility and fit are hard filters; only match performs soft scoring. Legacy stage values are auto-mapped: qualificationeligibility, applicabilityfit, suitabilitymatch. Hard-filter rules are sorted by priority (highest first) and evaluated in order. The first hard-filter failure short-circuits — remaining rules for that Offer are skipped.
At decision time, Eligibility and Fit are evaluated together as hard filters (a failing offer is removed), and Match is the soft multiplier that scales each surviving offer’s score. Ranking-stage rules are persisted and shown in the studio, but they do not currently affect offer ordering — neither the recommend runtime nor the batch executor reads stage-4 rules. Final ordering is governed by the ranking profile (Scoring Strategies), not by ranking-stage decisioning gates.
All four stages are authored on the Decisioning Gates page in the studio sidebar. Ranking profiles (the weighting configuration that combines objectives) are still configured under Scoring Strategies, but the stage-4 rules themselves now live alongside the other gates.

Authoring in the Studio

The studio surface at /studio/qualification-rules (label: Decisioning Gates) lists every rule in the tenant. A pill row at the top filters by stage:
  • All — every rule regardless of stage
  • Eligibility — hard gates only
  • Fit Filters — product-fit only
  • Match Scoring — soft-scoring multipliers only
  • Ranking — final-ordering rules only
Each rule row exposes the rule type, scope assignments, priority, and the stage classification. Click any row to edit; click + New Decisioning Rule to author a new one.

Selecting a stage

The rule editor surfaces a 4-button stage selector arranged in a 2×2 grid. Pick the stage that best matches the rule’s intent:
  • Use Eligibility for legal/compliance gates that block the offer entirely (hard filter).
  • Use Fit Filters for product-fit checks that should drop the offer when they fail (e.g. customer doesn’t already own the offer) — also a hard filter.
  • Use Match Scoring for soft scoring that multiplies the offer’s score (e.g. propensity threshold, recency boost). This is the only stage that scales rather than drops.
  • Use Ranking to author rules intended to adjust the final ordering after match scoring (e.g. campaign priority boosts, recency-bias adjustments). Note: ranking-stage rules are not yet read at decision time — see the note above.
The default rule type lookup adapts to the stage: segment_required, attribute_condition, metric_condition default to Eligibility; propensity_threshold, recency_check default to Match.

AI parse rule

The new-rule form exposes an AI parse rule button. Paste a natural-language description of the rule (e.g. “only customers in the US with balance over 10,000”) and the assistant returns a draft rule type, config payload, and stage. Review and edit before saving — the parser is a starting point, not the source of truth.

Rule Types

Kaireon ships six rule types you can assign to any of the four stages above (see each tab for which stages a type supports). Each type has its own config shape and runtime evaluation logic. Note the distinction between attribute_condition (tests a customer attribute) and offer_attribute (tests an offer field) — they share the same operator set but read from different sides of the decision.
Stage: eligibility or fit (hard filter)Checks that the customer belongs to the required segment(s). If the customer is missing any required segment, the Offer is dropped.Config JSON — the canonical shape is a single segmentId (what the create-time schema requires):
The engine also accepts a legacy requiredSegments array (AND logic across every listed segment) for back-compat:
Runtime behavior
  1. Read segmentId (or the legacy requiredSegments array) from config.
  2. Compare against the customer’s segment list (auto-resolved from enriched customer.segment field, or provided in the Recommend request).
  3. If the customer is in all of the required segments, the rule passes.
  4. If segment data is present but the customer is missing a required segment, the rule fails with a reason like Missing required segments: high_value.
  5. If no segment data is available at all (not enriched and not provided in the request), the rule follows its onMissing behavior: with the default "skip" it is skipped (fail-open) with the reason Skipped: no segment data available and a warning recorded in the decision trace; with "block" the Offer is dropped. This prevents one segment rule from silently zeroing /recommend for every un-enriched customer — enrich or pass segments to actually enforce it.
  6. An empty requiredSegments array is treated as a pass (no restriction).
Unknown rule types fail closed. If a rule’s ruleType is not one of the six above (for example a corrupted or future-version rule), the engine blocks the candidate rather than passing it, and logs the event (error in production, warn otherwise). This is deliberate — a misconfigured rule must never silently let unqualified candidates through.

The decision context

Every attribute-reading gate resolves its attribute against the canonical decision context — a flat, entity-namespaced record the engine assembles automatically at the start of every Recommend call (no Enrich node required). The available namespaces: See Decision Flows — the decision context for full assembly semantics, including the deprecated legacy <schemaname>.* aliases.

The attribute picker (Studio) and preview endpoint

You don’t have to guess which keys exist. In Studio → Decisioning Gates, the attribute field of attribute_condition and recency_check rules is an autocomplete picker: type to filter the real assembled namespace and pick a verified key — each suggestion shows the key, its entity (for joined schemas), and its value type. Picking a real key at author time removes the most common cause of silently skipped gates (a gate written against customer.tier when the data actually lands under retail_customers.tier). Free text is still accepted as an advanced fallback — the key just has to exist in the assembled context at decision time, or the rule follows its onMissing behavior. The picker is backed by GET /api/v1/decision-context/preview, which returns the full key catalog ([{ key, type, source, entity?, description? }]) built from your tenant’s metadata — customer schema columns, each active Schema Join’s configured aggregations (plus the raw <entity>[] collection), active Behavioral Metrics, journey state, and the documented request attributes. Pass ?flowKey= to scope the catalog to one flow (applies its Enrich-node excludeJoinIds[] opt-outs).
behavior.<metricKey> reads the same MetricValue rows the metric_condition gate reads — identical freshness, never recomputed at decision time. Use behavior.* in an attribute_condition gate (or a compute formula / scoring predictor) for customer-level metric values; keep using metric_condition when you need per-dimension lookups via dimensionMapping.

Missing data: onMissing

Every rule type’s config accepts an optional onMissing field controlling what happens when the data the rule needs is absent from the decision context (attribute not present, no segment data, metric not computed, no propensity score):
Eligibility and compliance gates should set onMissing: "block". The default "skip" exists so a missing enrichment field doesn’t zero out /recommend for every un-enriched customer — but for legal, regulatory, or do-not-sell gates, absence of the attribute must mean not eligible, not assume eligible.
onMissing is stored inside the rule’s config (no migration needed) and is validated at the API boundary as "skip" | "block". It applies to the missing-data path of all six rule types; when the data is present, the rule evaluates normally regardless of onMissing.

Scopes

Every Decisioning Gate has a scope that controls which Offers it applies to. Narrower scopes let you write rules that target specific parts of your catalog without affecting everything else. If a rule’s scope does not match the candidate Offer, the rule is skipped (treated as a pass).
When scopeId is null for a non-global scope, the rule applies to all entities at that scope level. For example, a rule with scope: "category" and scopeId: null applies to every Category.

Stages: hard filters vs match scoring

The stage field selects how a failing rule affects the candidate. At runtime the engine collapses stages into two behaviors: hard filter (eligibility + fit) and match scoring (match). When multiple match rules apply to the same Offer, their multipliers are multiplied together. For example, if a propensity rule returns 0.8 and a recency rule returns 0.5, the final multiplier is 0.8 x 0.5 = 0.4.

Evaluation Order

During the Filter (qualify) stage of a Decision Flow, rules are evaluated as follows:
  1. Load active Decisioning Gates according to the qualify node’s mode (all, selected, or none).
  2. Classify each rule and split into hard-filter rules (eligibility + fit stages) and match-scoring rules (match stage).
  3. Sort each group by priority descending (highest priority first).
  4. For each candidate Offer:
    • Evaluate all hard-filter rules whose scope matches the candidate. First failure drops the Offer.
    • Evaluate all match-scoring rules whose scope matches the candidate. Accumulate the combined multiplier.
  5. Surviving Offers proceed to the Scoring stage with their match multiplier applied.

Decision Flow Filter Modes

Setting the filter mode to none bypasses all Decisioning Gates. Offers may be recommended to ineligible customers. Use with caution.

AND/OR Logic Trees

For advanced scenarios, Kaireon supports recursive AND/OR logic groups via a nested logic-group structure. This allows you to compose rules into arbitrarily nested boolean expressions.
The example above evaluates as:
Vacuous truth rules:
  • An empty AND group (no ruleIds, no sub-groups) evaluates to true.
  • An empty OR group evaluates to false.
The evaluator walks the tree recursively: each group collects boolean results from its ruleIds (via the qualification engine) and its nested groups, then applies the group’s operator ("AND" = every result must be true; "OR" = at least one must be true).

Field Reference

All fields accepted by the POST /api/v1/qualification-rules endpoint:

Worked Example

Customer C-4821 requests a decision for the “Gold Card Upgrade” Offer. Three Decisioning Gates are active.

Rule definitions

Pass scenario (credit_score = 745)

Customer context:
  • Segments: ["premium", "high_value"]
  • customer.credit_score: 745
  • monthly_impressions metric value: 8
The Offer survives and proceeds to scoring. Debug trace (abbreviated):
No entries in qualificationReasons for this Offer because all three rules passed.

Fail scenario (credit_score = 680)

Same customer but with customer.credit_score = 680. Rule 2 fails. The engine short-circuits — Rule 3 is not evaluated. The Offer is dropped. Debug trace (abbreviated):

Migrating from legacy stage names

Tenants whose data was created before this rename may have rows with legacy stage values. The classifier transparently maps them so existing data keeps working: To physically migrate the rows in the database (recommended for tidiness), run:
The script defaults to dry-run; pass --apply to mutate. It returns a JSON summary of how many rows were updated per legacy value.

API Quick Reference

Create a Decisioning Gate

Response: 201 Created with the full rule object including id, createdAt, and updatedAt. The stage field defaults to eligibility when omitted.

List Decisioning Gates

Returns a paginated list sorted by priority (descending), then by creation date (descending). Supports cursor-based pagination via cursor and limit query parameters, and stage filtering via ?stage=eligibility|fit|match|ranking. Each returned row is enriched with a classifier-derived decisioningStage field so the UI can render the stage label without re-classifying client-side.

Update a Decisioning Gate

Send the rule id plus any fields to update. Only provided fields are changed.

Delete a Decisioning Gate

Soft-deletes the rule (retains the record with a deletedAt timestamp). Returns { "deleted": true, "warnings": [...] }. If the rule is referenced by any Decision Flow’s draftConfig, the response includes a warnings array listing affected flows (ghost reference check). For complete request/response schemas, see the API Reference.

Effective Rules — inheritance view per offer

Each offer rolls up rules from four scope levels:
A decisioning gate (or contact policy) attached to a category applies to every offer in that category; a rule attached to a subcategory narrows that further; a rule attached to an offer applies only to that offer; a rule with scope = "global" applies to every offer in the tenant. Channel and creative scopes are intentionally excluded from this view. They evaluate at decision time and require a specific channel/creative the offer is being delivered through. Operators inspect those via Decision Traces.

Where to find it

Open any offer in /studio/actions, click into the detail view, and click Effective Rules in the top action bar. The page renders two tables — Contact Policies and Decisioning Rules — each annotated with the matched scope (global / category / subcategory / offer).

API

Returns:
The endpoint requires any of the admin, editor, or viewer roles. Both lists are sorted by priority descending. The matchedScope field tells the UI why the rule applies (e.g., “rule X applies because of category Y”); legacy single-scope rows that haven’t been migrated to the multi-scope scopes[] relation still resolve correctly.

Contact Policies

Frequency caps and cooldown rules that limit how often a customer is contacted.

Decision Flows

Orchestrate Decisioning Gates, scoring, and ranking into a complete decision pipeline.

Behavioral Metrics

Define the metrics used by metric_condition rules.

Composable Pipeline

The v2 pipeline includes a dedicated qualify node for inline qualification.