Skip to main content
The decisioning engine is the core runtime that powers every Recommend API call. It takes a customer context, resolves the right Decision Flow, executes a pipeline of filtering, scoring, and ranking stages, and returns a personalized set of ranked offers with computed values. KaireonAI uses a composable pipeline model with 16 node types across 3 phases. The pipeline shares the formula engine, scoring engines, and portfolio optimization logic across all flows.

Request Lifecycle

Every call to POST /api/v1/recommend follows this lifecycle.

Flow Resolution

When the request does not include a decisionFlowKey, the engine resolves the flow automatically using FlowRoute records. Resolution uses most-specific-match-wins:
  1. Channel + Placement — exact match on both
  2. Channel only — matches any placement for that channel
  3. Tenant default — fallback when no channel/placement match exists
If no flow is resolved and no key is provided, the engine falls back to a legacy decisioning path that applies decisioning gates, contact policies, and priority-weighted scoring directly without a flow config.

Multi-Placement Requests

When the request body includes a placements array, the engine resolves each placement independently. With deduplicate: true, placements are resolved sequentially and each subsequent placement excludes offers already selected by previous placements. Without deduplication, all placements resolve in parallel.

Composable Pipeline

The composable pipeline uses an ordered array of nodes. Each node has a type, id, and config object. The runner validates the pipeline structure before execution, then processes nodes sequentially.

Three Phases

Nodes are organized into three logical phases. The pipeline validator enforces that Phase 1 nodes appear before Phase 2, and Phase 2 before Phase 3.
All 16 node types are fully functional — no stubs. The call_flow node (shown in amber) can be inserted at any phase to invoke a sub-flow, with a max nesting depth of 2 and circular reference detection. The enrich node queries schema tables with Redis caching, the qualify node evaluates decisioning gates with AND/OR logic trees.

16 Node Types

Execution Model

The pipeline runner maintains a mutable candidates array and a groupResult map as shared pipeline state. Each node reads from and writes to these structures:
  1. Validation — Before any node runs, the runner checks that the pipeline has the required nodes and that phase ordering is respected. A bad pipeline is rejected up front rather than failing partway through execution.
  2. Sequential loop — Nodes execute in array order via a for...of loop with a switch on node.type.
  3. Early return — The response node returns the assembled result immediately, terminating the loop.
  4. Trace accumulationtraceSummary counters are updated at key nodes (inventory, qualify, contact_policy, rank).

Formula Engine

The formula engine (lib/formula-engine.ts) provides safe expression evaluation with no dynamic code execution. Every formula goes through three stages:

Pipeline

1. Tokenizer — Scans the input string character by character, producing typed tokens for numbers, string literals, identifiers, operators (+, -, *, /, %, >, <, >=, <=, ==, !=), punctuation ((, ), ,, ?, :), and end-of-input. 2. Parser — Recursive-descent parser that builds an AST respecting operator precedence: 3. Evaluator — Tree-walks the AST, resolving identifiers from a variable map. Null propagation: if any operand resolves to null/undefined, the result is null. Division by zero returns null.

Built-in Functions

The engine ships six built-in functions. See the Formula Reference for full signatures and worked examples.
  • min — minimum of two numbers
  • max — maximum of two numbers
  • round — round to nearest integer, or to N decimal places when a second argument is supplied
  • abs — absolute value of a number
  • coalesce — first non-null value (minimum two arguments)
  • concat — string concatenation (null propagates)

Variable Namespaces

Formulas resolve identifiers from three namespaces: Example formula:
This returns a discounted rate for high-value loans, falling back to the base rate otherwise.

Scoring

The engine supports multiple scoring methods, selected per flow configuration.

Scoring Methods

Model Resolution Hierarchy

When method is propensity or formula, the engine resolves the scoring model through this hierarchy:
  1. Active experiment — If an experiment references the configured modelKey, the engine uses experiment-aware traffic splitting to select champion vs. challenger models. Assignment is tracked via the experimentAssignmentTotal counter.
  2. Direct model lookup — Falls back to algorithmModel.findFirst({ key: modelKey, status: "active" }).
  3. Pre-computed propensity scores — If no model is found, checks attributes.propensityScores[modelKey] from the request body.
  4. Priority-weighted fallback — Last resort: uses priority / 100 as the score.

Scoring Failure Fallback

The scoring stage never lets a model error break a decision. When a model raises an error (or an external scoring endpoint fails), the engine catches it and applies a fallback:
  • External endpoints: the candidate score becomes 0.5 * fitMultiplier (the 0.5 default is a constant in the scoring stage, not an environment variable).
  • Missing model: scoring falls through the model-resolution hierarchy to priority_weighted (priority / 100).
  • Flag: degradedScoring is set to true on the response (and on each affected candidate) so callers can tell the decision ran in a degraded state.
There is no stateful per-model circuit breaker or cooldown window in the scoring stage — failures are handled per request via this fallback. (A separate general-purpose circuit breaker guards outbound integrations such as webhooks, connector tests, and audit forwarding — see Operations.)
When explain=true is passed to the Recommend API, each decision includes a degraded boolean that surfaces whether that specific offer was scored using the fallback. The response also includes a rankingScores breakdown with propensity, relevance, impact, emphasis, composite so you can inspect exactly how the PRIE formula was evaluated per offer. See Recommend API — Decision Explanations for the full response shape.

Portfolio Optimization

Multi-objective portfolio optimization computes a weighted composite score across five dimensions:

Dimensions

Default Weights

By default, only conversion has a non-zero weight (1.0), making the optimized score equal to the conversion probability. Configure weights via Portfolio Optimization profiles in Studio or via the Optimize pipeline node to enable multi-objective optimization. If the total weight sums to zero, the engine falls back to the raw conversion score.

Decision Traces

The engine produces a traceSummary on every decision for lightweight observability, plus an optional debugTrace with full diagnostic detail.

Trace Summary (always captured)

Debug Trace (when debug=true)

Includes everything in the trace summary, plus:
  • qualificationReasons — Per-offer disqualification details (rule ID, reason)
  • contactPolicyReasons — Per-offer/creative suppression details (policy ID, reason)
  • featureContributions — Per-result score explainability (model type, base score, top factors)
  • afterConsent, afterGuardrails — Additional stage counters

Sampling and Storage

Decision traces are persisted to the decision-trace store and surfaced in the Runs and Decision Health views. The tenant settings decisionTraceEnabled and decisionTraceSampleRate control whether and how often traces are written.
Set decisionTraceSampleRate to 1.0 (100%) during development and testing. In production, reduce to 0.010.05 (1–5%) to balance observability with storage costs.

Policy Snapshots

On every decision, the engine persists a policy snapshot (fire-and-forget) containing the current decisioning gates, contact policies, and guardrail rules along with a policyVersionHash. This enables forensic replay: given a decision trace, you can reconstruct the exact policy state that was active when the decision was made.

Worked Example

This example traces a pipeline executing a Recommend request for a banking cross-sell use case. The flow is configured with 10 nodes.

Pipeline Config

Execution Trace

Response Shape


Next Steps

Recommend API

Full request/response reference for the Recommend endpoint.

Decision Flows

Configure Decision Flows in Studio.

Composable Pipeline

Build flows from 16 modular node blocks.

Algorithms & Models

Scoring engines used in the Score stage.