Overview
The Composable Pipeline is the next-generation execution model for Decision Flows. Instead of a fixed sequence of stages, you assemble a pipeline from typed node blocks arranged across three ordered phases. Each node performs one job — load inventory, filter candidates, score offers, rank results — and you choose which nodes to include and how to configure them. Existing v1 flows continue to work unchanged. When you save a flow withversion: 2 in its config, KaireonAI switches to the composable pipeline engine automatically.
Three Phases
Every composable pipeline organizes its nodes into three phases. Nodes within a phase run in position order; phases always execute in sequence.| Phase | Name | Purpose | Allowed Nodes |
|---|---|---|---|
| 1 | Narrow | Load candidates and reduce the set | inventory, filter, match_creatives, enrich, qualify, contact_policy, call_flow |
| 2 | Score & Rank | Score, rank, and group survivors | score, rank, group, call_flow |
| 3 | Output | Compute final values and format response | compute, set_properties, response |
Node Types
Phase 1 — Narrow
Inventory
Inventory
Loads candidate offers from the database. Every pipeline must start with exactly one Inventory node.Config:
scope — "all" loads every active offer, or "category" restricts to a specific category.Filter
Filter
Removes candidates that fail condition-based rules. Supports 13 operators across four namespaces.Operators:
eq, neq, gt, gte, lt, lte, in, not_in, contains, starts_with, regex, is_null, is_not_nullNamespaces: offer.*, customer.*, request.*, channel.*Config:conditions— Array of{ field, operator, value }rulescombinator—"AND"(all must pass) or"OR"(any can pass)
Match Creatives
Match Creatives
Attaches eligible creative assets to each candidate offer based on channel targeting and audience rules.Config:
requireCreative— Whether to remove candidates without a creative (defaulttrue)placementMatchMode—"exact","any", or"none"(default"any")
Enrich
Enrich
Loads customer data from schema tables. The loaded fields become available as
customer.* variables in all downstream nodes.Config: sources — Array of enrichment sources, each with:schemaId— Which schema table to querylookupKey— The lookup field (default"customer_id")fields— Which columns to loadprefix— Namespace prefix for loaded fields (default"customer")cacheTtlSeconds— Redis cache duration (default 60)optional— Whether to continue if the lookup fails (defaulttrue)
Qualify
Qualify
Applies qualification rules with AND/OR logic trees. Rules can be combined into nested groups for complex eligibility logic.Config:This means: customer must pass age AND region AND (premium OR loyalty).
mode—"all"(run every rule),"selected"(pick specific rules), or"none"(skip)qualificationRuleIds— Array of rule IDs when mode is"selected"logic— Optional AND/OR logic group for combining rule results
Contact Policy
Contact Policy
Enforces frequency and timing guardrails to prevent over-contacting customers.Config:
mode—"all","selected", or"none"contactPolicyIds— Array of policy IDs when mode is"selected"
Call Flow (Phase 1)
Call Flow (Phase 1)
Delegates to another Decision Flow as a sub-pipeline. Useful for reusable filtering or qualification logic.Config:
flowId— The target Decision Flow to callmergeMode—"append"(add sub-flow results to candidates) or"replace"(use sub-flow results only)
Phase 2 — Score & Rank
Score
Score
Runs scoring models against each candidate. Every pipeline must have exactly one Score node.Config:Traffic is split deterministically using a hash of the customer ID, so each customer always sees the same model variant.
method—"priority_weighted","propensity", or"formula"defaultModel— The model key to use when no override matchesoverrides— Array of model overrides scoped to offer, category, or channeloverridePriority— Resolution order, e.g.["offer", "category", "channel", "default"]formula— Weight-based blending when method is"priority_weighted":Weights must sum to 1.0.
Rank
Rank
Produces the final ordered list using multi-objective arbitration. One Rank node allowed per pipeline.Config:
method—"topN","diversity","round_robin", or"explore_exploit"maxCandidates— How many offers to return (1–50, default 5)maxPerCategory— Optional cap on offers per categorymaxPerChannel— Optional cap on offers per channelexplorationRate— Optional 0–1 value for explore/exploit strategies
Group
Group
Allocates ranked candidates across multiple named placements (e.g., hero banner, sidebar, email). Must appear after the Rank node.Config:
placements— Array of{ placementId, count }definitionsallocationStrategy—"priority_fill"assigns best-scoring offers to each placement in order, preventing duplicates across placements
placements object instead of a flat array:Phase 3 — Output
Compute
Compute
Evaluates formula-based computed fields for each candidate offer. One Compute node allowed.Config:
overrides— Flow-level formula overrides (array of{ name, formula, outputType })extras— Additional computed fields beyond what categories define (same shape)
Set Properties
Set Properties
Attaches static or derived key-value pairs to each candidate before the response is assembled.Config:
properties — Array of { key, value } pairs. Each property can also include a formula field for dynamically computed values.Response
Response
Formats the final output. Every pipeline must end with exactly one Response node.Config:
includeDebugTrace— Whether to include debug trace data (defaultfalse)responseFormat—"standard"(flat array) or"grouped"(placements object, requires Group node)
Stub Node Summary
The following nodes have limited functionality in the initial v2 release. They are structurally valid and can be included in pipeline configs, but their runtime behavior is incomplete:| Node | Current Behavior | Impact |
|---|---|---|
| enrich | Logs warning, does not query schema tables | customer.* variables are empty in downstream nodes |
| call_flow | Logs warning, skips sub-flow execution | Sub-flows are not invoked; candidates pass through unchanged |
| qualify (with logic tree) | Evaluates logic tree but hardcodes segment_match rule type | Rule evaluation may not match expected behavior for non-segment rules |
| contact_policy | Passes empty rules and empty history | No candidates are filtered by contact policies |
Worked Example
This example walks through 8 offers being processed by a v2 pipeline with grouping and computed fields.Pipeline Config
Step-by-Step Execution
Step 1 — Inventory: Loads 8 active offers from the database.| Offer | Priority | Weight | base_rate |
|---|---|---|---|
| Premium Card | 90 | 100 | 14.99 |
| Travel Rewards | 80 | 80 | 17.99 |
| Cash Back | 70 | 90 | 15.49 |
| Student Card | 25 | 100 | 22.99 |
| Balance Transfer | 60 | 70 | 12.99 |
| Secured Card | 20 | 100 | 24.99 |
| Business Platinum | 85 | 60 | 16.99 |
| Everyday Card | 40 | 50 | 19.99 |
offer.priority >= 30): Removes Student Card (25) and Secured Card (20). 6 remain.
Step 3 — Score (priority_weighted): Each candidate gets score = (priority/100) * (weight/100).
| Offer | Score |
|---|---|
| Premium Card | 0.90 |
| Travel Rewards | 0.64 |
| Cash Back | 0.63 |
| Business Platinum | 0.51 |
| Balance Transfer | 0.42 |
| Everyday Card | 0.20 |
| Placement | Offers |
|---|---|
hero (1 slot) | Premium Card (0.90) |
sidebar (3 slots) | Travel Rewards (0.64), Cash Back (0.63), Business Platinum (0.51) |
display_rate = round(base_rate * 0.9, 2) for each candidate.
| Offer | display_rate |
|---|---|
| Premium Card | 13.49 |
| Travel Rewards | 16.19 |
| Cash Back | 13.94 |
| Business Platinum | 15.29 |
Pipeline Validation
KaireonAI validates the pipeline structure when you save a flow. Invalid pipelines are rejected with specific error codes:| Code | Rule |
|---|---|
EMPTY_PIPELINE | Pipeline must contain at least one node |
MISSING_INVENTORY | Must start with an Inventory node |
MISSING_RESPONSE | Must end with a Response node |
MISSING_SCORE | Must contain a Score node |
DUPLICATE_SINGLETON | Only one of each: inventory, score, rank, group, compute, response |
PHASE_ORDER_VIOLATION | Phases must be non-decreasing (1 -> 2 -> 3) |
FILTER_WRONG_PHASE | Filter nodes must be in Phase 1 |
GROUP_BEFORE_RANK | Group node must appear after the Rank node |
CALL_FLOW_WRONG_PHASE | Call Flow nodes must be in Phase 1 or 2 |
CALL_FLOW_MAX_DEPTH | Sub-flow nesting cannot exceed 2 levels |
CALL_FLOW_CIRCULAR | Circular call_flow references are not allowed |
INVALID_NODE_CONFIG | A node’s config doesn’t match its type-specific schema |
V2 Config Format
A v2 flow config uses this JSON structure:version: 2 field is what tells KaireonAI to use the composable pipeline engine. Without it, the flow runs through the v1 fixed-stage engine.
Migrating from V1
Existing v1 flows work without changes. When you’re ready to migrate, use the built-in migration utility that converts a v1 config to v2 format:- The v1
inventorystage maps to an Inventory node filter.qualificationModemaps to a Qualify nodefilter.contactPolicyModemaps to a Contact Policy nodescoring.methodandscoring.modelKeymap to a Score noderanking.methodandranking.maxCandidatesmap to a Rank node- Enrichment and compute configs (if present) map to Enrich and Compute nodes
- A Response node is always added at the end
API
Saving a V2 flow
Use the standard Decision Flows API. ThedraftConfig field accepts both v1 and v2 formats:
Executing a V2 flow
V2 flows are executed through the same Recommend API as v1 flows — no changes needed on the caller side:version: 2, the engine uses the composable pipeline. Otherwise it uses the v1 engine.