Conditional Logic & Routing

Document information
  • Canonical URL: /docs/03_writing-step-code/50_conditional-logic-routing
  • Version: 2026-05-08
  • Tags: code-snippets, routing, logic

Conditional routing is how ProcessFlow decides what should happen next after a step evaluates incoming data. This guide shows practical routing patterns that keep decisions understandable, testable, and safe under production load. When each branch is explicit and includes clear context, operators can troubleshoot route outcomes quickly.

Routing decision pipeline showing input evaluation, rule matching, and explicit route selection for ProcessFlow branches.

Return route values consistently

ProcessFlow branches are driven by the route field in a step response, so route naming and output shape should stay consistent across all decision points. A stable contract makes downstream branching predictable and easier to reason about. Include a short decision context in data so logs and run history explain why the branch was chosen.

const approved = Boolean(process_input.result?.approved ?? false);

return {
    success: true,
    route: approved ? "approved" : "rejected",
    data: {
        approved: approved,
        reason: approved ? "Validation checks passed" : "Validation checks failed",
    },
};

Evaluate multi-condition rules with explicit logic

Routing rules become brittle when condition behavior is hidden or inconsistent across fields. A safer approach is to evaluate each condition independently, collect per-rule outcomes, and apply one explicit logical operator (AND or OR). This gives you traceable decisions and makes rule updates low risk.

const payload = (process_input.result?.data ?? {}) as Record<string, unknown>;
const rules = (process_input.result?.rules ?? []) as Array<Record<string, unknown>>;
const logic = String(process_input.result?.logic ?? "AND").toUpperCase();

function evaluate(data: Record<string, unknown>, rule: Record<string, unknown>): boolean {
    const value = data[String(rule.field ?? "")] ?? null;
    const target = rule.value ?? null;
    const operator = String(rule.operator ?? "equals");

    switch (operator) {
        case "equals":
            return value == target;
        case "greater_than":
            return Number.isFinite(Number(value)) && Number.isFinite(Number(target)) && Number(value) > Number(target);
        case "in":
            return Array.isArray(target) && target.includes(value);
        default:
            return false;
    }
}

const results = rules.map((rule) => evaluate(payload, rule));
const passed = logic === "OR" ? results.includes(true) : !results.includes(false);

return {
    success: true,
    route: passed ? "pass" : "fail",
    data: {
        logic: logic,
        rules_total: rules.length,
        rules_passed: results.filter(Boolean).length,
    },
};

Route approval workflows by threshold

Approval routing often combines amount thresholds, request type, and urgency. Keeping that logic in one ordered decision block avoids conflicting outcomes between branches. This pattern routes to the lowest valid approval level first, then adds an urgency prefix when escalation is required.

const amount = Number(process_input.result?.amount ?? 0);
const type = String(process_input.result?.type ?? "standard");
const priority = String(process_input.result?.priority ?? "normal");

let route = "executive_approval";
let reason = "Default executive path";

if (amount <= 100 && ["supplies", "maintenance"].includes(type)) {
    route = "auto_approve";
    reason = "Low amount and eligible type";
} else if (amount <= 5000) {
    route = "manager_approval";
    reason = "Within manager threshold";
} else if (amount <= 25000) {
    route = "director_approval";
    reason = "Within director threshold";
}

if (priority === "urgent" && route !== "auto_approve") {
    route = `urgent_${route}`;
    reason = `${reason} with urgent escalation`;
}

return { success: true, route, data: { reason } };

Keep routing logic aligned across languages

Teams often share routing rules between ProcessFlow snippets and service applications, so aligned examples reduce ambiguity in code reviews. The snippets below evaluate a score and return the same route names across TypeScript and JavaScript. Matching behavior across runtimes avoids branch drift in production.

function routeByScore(score: number): { route: string; reason: string } {
  if (score >= 90) return { route: "priority_review", reason: "High score threshold met" };
  if (score >= 60) return { route: "standard_review", reason: "Medium score threshold met" };
  return { route: "reject", reason: "Score below minimum threshold" };
}

Persist routing outcomes through an API

If your workflow stores route decisions externally, send a concise payload with the selected route and decision context so audit trails remain complete. Including tenant headers and a workflow run identifier makes replay and incident review easier for support teams. This pattern also helps analytics teams measure branch volumes over time.

curl -X POST "https://api.example.com/api/v1/workflow-routes" \
  -H "X-API-Key: <API_KEY>" \
  -H "X-Tenant-ID: <TENANT_ID>" \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_run_id": "wf_01J2EXAMPLE",
    "route": "manager_approval",
    "reason": "Within manager threshold",
    "metadata": {
      "amount": 3200,
      "request_type": "equipment"
    }
  }'
{
  "success": true,
  "data": {
    "decision_id": "dec_01J2EXAMPLE",
    "stored_route": "manager_approval",
    "status": "recorded"
  }
}

Prevent routing regressions

Routing regressions usually come from unclear default paths, non-deterministic condition checks, or route names that change between versions. You can avoid these failures by always defining a fallback route, keeping route labels semantic, and returning decision metadata for each evaluation. With these guardrails, branch behavior stays stable as rules evolve.

See also

Conditional routing works best when every decision can be explained in one sentence and reproduced with the same input. If your routes stay explicit and well-scoped, ProcessFlow branching remains reliable even as business rules grow.