Skip to main content

Call Flows - Complete Guide

D
Written by Denise Abdullah
Updated yesterday

Call Flows - Complete Guide

For: Network Operators | Advertisers with Complex Routing Needs

Time: ~30 minutes to build your first Call Flow

Last Updated: March 27, 2026

What You'll Accomplish

By the end of this guide, you'll understand how to build sophisticated, event-driven call routing using Moja's visual Call Flow builder. You'll learn when to use Call Flows (versus simple Routing Plans), how to leverage different node types for conditional logic, and how to integrate with webhooks for data-driven routing decisions.


Table of Contents

1. What Are Call Flows?

2. Before You Build Your First Call Flow

3. When to Use Call Flows (vs. Simple Routing Plans)

4. The Visual Call Flow Builder

5. Understanding Node Types

6. Step-by-Step: Creating Your First Call Flow

7. Step-by-Step: Building Conditional Routing

8. Connecting Call Flows to Campaigns

9. Testing and Debugging Call Flows

10. Common Call Flow Patterns

11. Troubleshooting

12. Migration Guides

13. Frequently Asked Questions (FAQ)

14. What's Next?


What Are Call Flows?

Call Flows are Moja's visual, node-based builder for creating event-driven call routing logic. Think of Call Flows as a flowchart that executes in real-time as calls arrive—you can branch, make decisions, fetch data, tag calls, and route dynamically based on any combination of conditions.

The Difference: Routing Plans vs. Call Flows

Feature

Routing Plans

Call Flows

Complexity

Simple, linear routing

Complex, branching logic

Visual Editor

Form-based

Drag-and-drop node canvas

Conditional Logic

Limited (geo, time-based)

Full if/then branching

External Data

No

Yes (HTTP requests, webhooks)

Custom Variables

No

Yes (set, read, manipulate)

JavaScript Logic

No

Yes (custom JS execution)

Debugging

Basic call logs

Debug nodes + step logging

Use Case

"Route CA calls to Target A, TX to Target B"

"If VIP customer from webhook data, call their account manager; else route to IVR based on product interest"

In short:

* Routing Plans are for straightforward routing (geo-based, time-based, round-robin)

* Call Flows are for sophisticated, data-driven routing with conditional branching


Before You Build Your First Call Flow

Before diving into the Call Flow builder, understanding these foundational concepts will save you hours of troubleshooting and help you avoid common pitfalls.

Understanding the Moja Hierarchy

Call flows don't exist in isolation—they're part of a larger routing architecture. Here's how the pieces fit together:

Key Relationships:

* Campaigns own Call Flows (one campaign = one Call Flow OR one Routing Plan, not both)

* Incoming webhooks must be attached to the campaign to be available in Call Flows

* Call Flows make routing decisions but ultimately point to Routing Plans

* Routing Plans connect to Targets (your buyers/destinations)

* Targets can be reused across multiple Routing Plans/campaigns

Real-world example:

⚠️ Important: Incoming webhooks must be attached to the campaign to make their data available in Call Flows. Navigate to your campaign settings → Incoming Webhooks section to verify the webhook is connected.

Tag Naming Best Practices

Tags are critical for reporting, filtering, and downstream logic. Inconsistent tag naming will break yomuur Call Flows. Establish naming conventions before building.

✅ DO: Use Consistent, Structured Naming

Category

Good Example

Why It Works

Geography

caller-state-ca, caller-state-tx

Prefix + location

Customer tier

tier-vip, tier-active, tier-new

Prefix + value

Campaign source

source-google, source-facebook

Prefix + platform

Call quality

quality-verified, quality-suspicious

Prefix + assessment

Product interest

product-medicare, product-auto-insurance

Prefix + product

Naming rules:

* Use lowercase consistently — avoid mixed case (e.g., use vip-customer, not VIP-Customer). Moja normalizes tags to uppercase internally, but naming should remain lowercase for consistency.

* Hyphens or underscores for spaces — both high-value and high_value work. Underscores are easier for regex operations.

* Descriptive prefixes — tier-vip not just vip (prevents collision)

* No special characters except hyphens and underscores

* Consistent abbreviations — If you use ca for California, don't also use calif or california

ℹ️ Note: Moja automatically normalizes custom tag names to UPPERCASE internally. So customer-tier, Customer-Tier, and CUSTOMER-TIER all resolve to the same tag. However, maintain lowercase naming in your documentation and processes for consistency.

Understanding System Tags vs. Custom Tags

System Tags are predefined by Moja and automatically populated (e.g., CALLER_ID, CALLER_STATE, CAMPAIGN_ID). These:

* Do NOT need to be defined by you in the Custom Tags section

* Are always available in Call Flows

* Cannot be deleted or renamed

Custom Tags are created by you for your specific business logic (e.g., customer-tier, lead-score). These:

* Must be predefined in Webhooks → Custom Tags before you can reference them in Call Flows

* Can be set via incoming webhooks, Tagging nodes, or JavaScript nodes

* Appear in reporting alongside system tags

Action: Before building Call Flows that use custom tags, navigate to Webhooks → Custom Tags and create your tag definitions there first.

❌ DON'T: Create Tag Chaos

Problem

Bad Examples

Why It Fails

Inconsistent casing

VIP, vip, Vip

While Moja normalizes to uppercase, using different cases in your docs/webhooks creates confusion

Inconsistent formats

zip_code, zipcode, zip-code, ZipCode, ZIP, zip

Impossible to filter/query consistently

Vague names

tag1, test, good

What do these mean in 6 months?

Spaces/special chars

high value!, vip (customer)

May break downstream systems

Real-World Example: Fixing Tag Chaos

Before (broken logic):

// Your CRM sends different zip formats

// Call Flow tries to check zip code

if (tags.zip_code == "90210") { ... } // But the tag might be:

// zip, ZIP, zipcode, zip_code, ZipCode, caller_zip, caller-zip-code

After (clean, works every time):

// Standardize on ONE format: caller-zip

// Normalize in JavaScript node if needed

tags.CALLER_ZIP = tags.CALLER_ZIP || tags.ZIP_CODE || tags.ZIP || tags.ZIPCODE;

if (tags.CALLER_ZIP == "90210") { ... } // Always works

Action Items Before Building:

1. Document your tag taxonomy — Create a spreadsheet with approved tag names

2. Train your team — Everyone uses the same names

3. Validate incoming data — If publishers/webhooks send data, normalize it in a JavaScript node:

// Normalize zip code variations

tags.CALLER_ZIP = tags.CALLER_ZIP || tags.ZIP_CODE || tags.ZIP || tags.ZIPCODE;

RTB vs Static Routing: Decision Tree

💬 Customer Confusion: "Setting up RTB versus static publishers — the distinction isn't intuitive." — Cobras Solutions (Connor)

One of the most common questions: "Should I use RTB (Real-Time Bidding) or static routing?" The answer depends on your business model. Use this decision tree:

Static Routing Example (Simple)

Use case: You have 3 buyers. CA calls go to Buyer A ($50/call), TX calls go to Buyer B ($45/call), all others go to Buyer C ($40/call).

Pricing: Fixed, pre-negotiated per buyer.

RTB Example (Using RTB Groups)

Use case: You have 5 buyers who bid on each call. You want to maximize revenue by routing to the highest bidder.

Best Practice: Use Moja's built-in RTB Groups feature rather than building custom RTB logic in Call Flows.

Setup:

1. Create an RTB Group in RTB Groups section

2. Add your buyers to the group with bid parameters

3. In your Call Flow, route to a Routing Plan that uses the RTB Group

Why use RTB Groups instead of custom logic:

* Built-in bid management and timeout handling

* Automatic highest-bidder selection

* Built-in analytics and reporting

* Simpler to maintain than custom HTTP Request logic

Pricing: Dynamic—highest bidder wins. Could be $30 one call, $75 the next.

Key Difference:

* Static: You control routing logic explicitly in Call Flow

* RTB: Buyers control routing via bids; use RTB Groups for automatic handling

Most Common Setup: Hybrid — Use RTB Groups for high-volume campaigns, static routing for premium/exclusive buyers.

Common Pitfalls to Avoid

Before you start building, be aware of these traps that catch most first-time Call Flow builders:

⚠️ Pitfall 1: Tag Naming Inconsistency Breaking Logic

The Trap: Different parts of your system use different tag names for the same concept.

Example of Failure:

// Your webhook sends: caller_zip = "90210"

// Your Call Flow checks: if (tags.ZIP_CODE == "90210") { ... } ← NEVER MATCHES

// Your reporting filters by: ZipCode = "90210" ← NEVER FINDS DATA

Solution: Establish a single source of truth for tag names. Document it. Enforce it.

Action Items:

1. Create a "Tag Dictionary" spreadsheet

2. Share with your team, publishers, webhook developers

3. Add validation in Call Flows:

// Normalize all zip code variations to ONE tag

tags.CALLER_ZIP = tags.CALLER_ZIP || tags.ZIP_CODE || tags.ZIP || tags.ZIPCODE;

⚠️ Pitfall 2: Forgetting Fallback Paths

The Trap: Building Call Flows that assume data will always be available.

Example of Failure:

Problem: What if the webhook never arrives? customer_tier is null → matches FALSE path → routes to standard queue. VIP customers might get standard service if webhook is delayed.

Better Design:

Key Lesson: Always design for missing/bad data. Every If Statement should handle both TRUE and FALSE paths gracefully.


When to Use Call Flows (vs. Simple Routing Plans)

Use Call Flows When You Need To:

✅ Make routing decisions based on external data

Example: Wait for incoming webhook with customer tier, then route VIPs to priority agents

✅ Implement conditional branching logic

Example: If caller from California AND age > 65, route to Medicare specialist

✅ Fetch data from your systems mid-call

Example: HTTP request to your CRM to check customer status before routing

✅ Set custom tags dynamically during routing

Example: Tag calls as "high-value" if certain conditions are met

✅ Execute custom JavaScript logic

Example: Calculate a score based on multiple variables and route accordingly

✅ Wait for pre-call webhook data before routing

Example: Caller clicks an ad, your system sends data via webhook, then caller dials—Call Flow waits for that data

✅ Debug complex routing with visibility into each step

Example: Log variable values at each decision point to troubleshoot routing issues

Use Simple Routing Plans When:

❌ Your routing is straightforward (geo-based, time-based, round-robin)

❌ You don't need conditional branching

❌ You don't need to integrate with external systems

❌ Speed to implementation matters more than flexibility

💡 Tip: Start with a Routing Plan for your first campaign. When you outgrow it, migrate to a Call Flow. Moja makes it easy to switch execution modes in your Campaign settings.


The Visual Call Flow Builder

Accessing Call Flows

UI Path: Call flows (main sidebar)

Permissions: Admin and Member roles only

The Builder Interface

When you open the Call Flow builder, you'll see:

Section

Purpose

Canvas (center)

Visual workspace where you drag, drop, and connect nodes

Node Sidebar (left)

Available node types you can drag onto the canvas

Properties Panel (right)

Configure selected node settings

Toolbar (top)

Save, test, zoom controls, layout options

Mini-map (bottom right)

Navigate large flows easily

How It Works

1. Drag nodes from the sidebar onto the canvas

2. Connect nodes by dragging from one node's output handle to another node's input handle

3. Configure each node by clicking it and filling in the properties panel

4. Save your flow when complete

5. Attach to a Campaign to make it live

Visual flow structure:

Every Call Flow:

* Starts automatically when a call arrives in a campaign using that Call Flow

* Executes nodes sequentially following the connections you've drawn

* Ends when a routing decision is made (Routing Plan node or Hangup node)


Understanding Node Types

Moja provides several node types, each with a specific purpose. Here's the complete reference:

Logic Nodes

If Statement

Color: Blue

Purpose: Conditional branching—split your flow based on whether a condition is true or false

When to use:

* Route differently based on caller state/zip

* Check if a variable meets a threshold

* Branch based on webhook data or variable values

Configuration:

* Variable A: Left side of comparison (use bracket syntax: [caller_state])

* Operator: Comparison type (==, !=, <, >, <=, >=, ~= for regex)

* Variable B: Right side of comparison (can be a static value or another bracketed variable)

Example:

Variable A: [customer_tier]

Operator: ==

Variable B: vip

Output paths:

* TRUE: Condition matched—follow this path

* FALSE: Condition not matched—follow this path

Best practices:

* Use exact string matches for states: [CALLER_STATE] == "CA"

* For numeric comparisons, use >, <, >=, <=

* Chain multiple If Statement nodes for complex AND/OR logic

* Use ~= for regex matching (Moja-specific syntax)

* Always use bracket syntax for variable references: [variable_name]


Variable

Color: Teal

Purpose: Read, set, or manipulate variables during the call flow

When to use:

* Store incoming webhook data in a reusable variable

* Set a flag that later nodes can check

* Read system tags like CALLER_ID, CALLER_STATE, CAMPAIGN_ID

Configuration:

* Variable Name: Name of the variable to set or read

* Variable Value: Value to assign (can reference other variables with [TAG_NAME])

Example use cases:

* Read: customer_name from incoming webhook data

* Set: routing_priority = high

* Manipulate: Combine multiple values into one variable

Common system tags you can read:

* [CALLER_ID] — Caller's phone number

* [CALLER_STATE] — Caller's state (editable)

* [CALLER_ZIP] — Caller's ZIP code (editable)

* [CAMPAIGN_ID] — Campaign ID

* [CAMPAIGN_NAME] — Campaign name

* Custom data from incoming webhooks (use [custom_tag_name] syntax)

Tip: For the full list of 100+ available system tags, go to Webhooks → Custom Tags in the Moja portal. System tags are automatically available—you don't need to define them yourself.


Javascript

Color: Orange

Purpose: Execute custom JavaScript code during call flow execution

When to use:

* Complex calculations or string manipulations

* Multi-step logic that's cumbersome with multiple If Statement nodes

* Advanced data transformations

How it works: The JavaScript node has access to two objects:

* tags — All system tags and custom tags (read and modify)

* variables — Custom variables set by other nodes

To set values that downstream nodes can use, modify these objects directly:

// Modify tags and variables objects directly

tags.GENDER = "Male";

variables.lead_score = 0;

// Read existing values and calculate

if (tags.CALLER_STATE == "CA") variables.lead_score += 10;

if (variables.customer_tier == "vip") variables.lead_score += 20;

// String manipulation

tags.CALLER_ID_FORMATTED = tags.CALLER_ID.replace("+1", "");

// Normalize tag variations

tags.CALLER_ZIP = tags.CALLER_ZIP || tags.ZIP_CODE || tags.ZIP || tags.ZIPCODE;

Tag Access in JavaScript:

* System tags are automatically in UPPERCASE: tags.CALLER_STATE, tags.CAMPAIGN_ID

* Custom tags are normalized to UPPERCASE: tags.CUSTOMER_TIER

* Use tags.TAG_NAME syntax (not bracket syntax)

To reference tags in downstream If Statement nodes:

* Use bracket syntax: [CALLER_STATE], [customer_tier]

Best practices:

* Keep it simple—complex logic is harder to debug

* Store results in tags or variables for downstream If Statement nodes

* Test your JS separately before adding to Call Flow

* Comment your code for future maintenance

* Use tags for values you want to appear in call logs and reporting

* Use variables for temporary calculation values

Capabilities:

* fetch() is available for HTTP requests within JavaScript

* Async/await supported

* Standard JavaScript Math, String, Array, Date methods

Limitations:

* Execution timeout: 5 seconds max

* No require(), no file system access

* For complex HTTP workflows, prefer the HTTP Request node


Data Nodes

Wait For Pre Call Webhooks

Color: Purple

Purpose: Pause call processing until incoming webhook data arrives (or timeout expires)

When to use:

* You send caller data via incoming webhook before the caller dials

* You need guaranteed data availability before making routing decisions

* Pre-call enrichment from your CRM, database, or external systems

Configuration:

* Wait Duration: Seconds to wait (configurable, no maximum limit)

How it works:

1. Caller clicks your ad or fills a form

2. Your system sends incoming webhook with caller data to Moja

3. Caller dials your Moja tracking number

4. Call Flow hits this node and waits up to X seconds

5. If webhook data arrives → flow continues immediately with data attached

6. If timeout expires → flow continues without webhook data (use If Statement to handle missing data)

Timing Diagram:

Example scenario:

practices:

* Set timeout long enough for your webhook to arrive (typically 5-10 seconds)

* Always have a fallback path for missing webhook data

* Use Debug nodes after this to verify data arrived

* Critical: Ensure incoming webhook is attached to the campaign (see "Before You Build" section)

⚠️ Important: The incoming webhook must be attached to the campaign for this node to access the data. Verify in campaign settings → Incoming Webhooks section.

📖 See the Webhooks Guide for detailed information on sending incoming webhook data to Moja.


HTTP Request

Color: Purple

Purpose: Make HTTP requests to external APIs during call flow execution

When to use:

* Fetch data from your CRM mid-call

* Send notifications to Slack or other services

* Query external databases for routing decisions

* Log call flow events to your analytics platform

Configuration:

* Method: GET, POST, PUT, DELETE

* URL: Endpoint to call (supports both HTTP and HTTPS)

* Headers: Custom headers (for authentication, content-type)

* Body: Request payload (for POST/PUT)

* Response Variable: Store the Response Variable Name (usable in other nodes) and Response Body Variable Name (whole JSON string)

Example: Fetch customer data

Method: GET

Headers: {"Authorization": "Bearer YOUR_API_KEY"}

Store Response In: crm_data

Example: Send Slack notification

Method: POST

Headers: {"Content-Type": "application/json"}

Body: {"text": "VIP call incoming: [customer_name] | [CALLER_ID]"}

Best practices:

* Keep requests fast (< 2 seconds response time)

* Handle timeouts gracefully (don't block call routing)

* Both HTTP and HTTPS endpoints are supported

* Store responses in variables for use in downstream nodes

* Test endpoints separately before adding to Call Flow

Limitations:

* Request timeout: 10 seconds (hardcoded, not configurable)

* If request fails or times out, variable is set to null and flow continues — always check for null before using response data

HTTP Response Format:

* Successful responses stored as JSON object in two variables: responseVar (full HTTP response) and responseBodyVar (body only)

* Access nested fields: [crm_data.account_status]

* On any error (timeout, HTTP error, connection failure): variable set to null


Tagging

Color: Cyan

Purpose: Add or remove tags from the call

When to use:

* Mark calls that meet certain criteria for filtering/reporting

* Tag high-value calls for manual review

* Apply labels based on routing decisions

* Track which path through the Call Flow was taken

Configuration:

* Action: Add or Remove tags

* Tags: Comma-separated tag names

Example:

Action: Add

Tags: high-value, vip-customer, premium-route

Use cases:

* Tag calls routed to VIP agents as vip-call

* Tag calls from specific states as california, texas

* Tag calls that converted as converted (for reporting)

* Remove tags that are no longer relevant: Action: Remove, Tags: new-lead

Best practices:

* Use consistent naming conventions: lowercase, hyphens or underscores for spaces (underscores preferred for regex)

* Don't over-tag—only tag what you'll filter/report on

* Tags appear in call logs and are included in outgoing webhooks

* Use tags to track A/B tests or routing experiments

* Remember: Custom tags must be predefined in Webhooks → Custom Tags before use

ℹ️ Note: System tags (like CALLER_ID, CALLER_STATE) are automatically available and don't need to be defined. Custom tags you create must be predefined in the Custom Tags section before you can use them.


Routing Nodes

Routing Plan

Color: Pink

Purpose: Route the call to a specific Routing Plan (which in turn routes to Targets)

When to use:

* Final routing decision in your Call Flow

* You want to use an existing Routing Plan's logic (geo, capacity, etc.)

* You need to route to multiple Targets with failover/priority logic

Configuration:

* Routing Plan: Select from your existing Routing Plans

How it works:

1. Call Flow reaches this node

2. Call is handed to the selected Routing Plan

3. Routing Plan applies its rules (geo, capacity, schedule, etc.)

4. Call connects to a Target (buyer/destination)

Example:

About Call Transfers: There is no standalone "Transfer" node in Call Flows. Instead, transfers happen through this workflow:

1. Call Flow routes to a Routing Plan (using this node)

2. Routing Plan selects a Target

3. If the Target is configured as a SIP endpoint, the call is transferred via SIP

Best practices:

* Create reusable Routing Plans for common destinations

* Name Routing Plans clearly: "VIP Queue", "After-Hours Voicemail", "CA Medicare Agents"

* Use Routing Plans for the final routing step (don't nest Call Flows and Routing Plans deeply)

💡 Tip: You can click "Create new" in the Routing Plan selector to create a Routing Plan on the fly while building your Call Flow.


Hangup

Color: Dark Red

Purpose: Immediately end the call

When to use:

* Reject low-quality callers (e.g., blacklisted number)

* Business is closed and no voicemail available

* Caller didn't meet qualification criteria

* Error state where routing isn't possible (e.g., HTTP request returned invalid response)

Configuration:

* Reason: (Optional) Note why the call is being hung up (for logging)

Example use case:

Best practices:

* Always log the reason for hanging up (helps with debugging)

* Use sparingly—hanging up on callers hurts conversion rates

* Consider routing to a message node instead (if you add that feature in the future)

* Don't use for TCPA checks — TCPA filtering happens at the routing plan level, not in Call Flows

⚠️ Note: Compliance checks like TCPA screening should be configured at the routing plan or campaign level, not implemented as Hangup logic in Call Flows.


Utility Nodes

Debug

Color: Red

Purpose: Log variable and tag values for troubleshooting

When to use:

* Testing new Call Flows

* Troubleshooting why a condition isn't matching

* Verifying webhook data is arriving correctly

* Debugging variable values at specific points in the flow

Configuration:

* Variables/Tags to Log: Comma-separated list using bracket syntax

Example:

Variables/Tags: [CALLER_STATE], [customer_tier], [crm_response]

How to use:

1. Add Debug nodes at key decision points in your flow

2. Save and test your Call Flow with a real call

3. Check the call log after the call completes

4. Debug logs show the exact values at that point in the flow

Best practices:

* Place Debug nodes before If Statement nodes to see what's being compared

* Place Debug nodes after Variable nodes to verify data was read correctly

* Remove or disable Debug nodes in production (they add log noise)

* Always use bracket syntax: [variable_name] not variable_name

Output: Debug nodes write to the call log:

[Call Flow Debug] CALLER_STATE=CA, customer_tier=vip, crm_response={"status":"active"}


Step-by-Step: Creating Your First Call Flow

Let's build a simple Call Flow that routes calls based on the caller's state.

Business scenario:

* Calls from California → Route to CA Agents

* Calls from all other states → Route to National Agents

Before You Start

* You have at least two Routing Plans created:

* "CA Agents" (routes to California targets)

* "National Agents" (routes to all other targets)

* You know the UI path: Call flows (main sidebar)

* You're logged in as Admin or Member role


Step 1: Navigate to Call Flows

1. Click Call flows in the main sidebar

2. You'll see the Call Flows listing page


Step 2: Create a New Call Flow

1. Click Create (top right)

2. You'll see the visual Call Flow builder


Step 3: Understand the Canvas

You'll see:

* Canvas (center) — Empty canvas with a Start node

* Node Sidebar (left) — Available node types

* Toolbar (top) — Save, zoom, layout controls


Step 4: Add an If Statement Node

1. Find If Statement in the sidebar (blue)

2. Drag it onto the canvas

3. The Start node will automatically connect to your If Statement node


Step 5: Configure the If Statement

1. Click the If Statement node to select it

2. A configuration dialog will appear

3. Fill in the fields:

* Name: "Check if California"

* Variable A: [CALLER_STATE]

* Operator: ==

* Variable B: CA

4. Save the node configuration


Step 6: Add Routing Plan Nodes

Now we'll add two Routing Plan nodes—one for each outcome (TRUE/FALSE).

For the TRUE path (California):

1. Drag Routing Plan node onto the canvas

2. Position it to the right of the If Statement node, slightly above

3. Click to configure:

* Routing Plan: Select "CA Agents" from the dropdown

4. Save the node configuration

For the FALSE path (Not California):

1. Drag another Routing Plan node onto the canvas

2. Position it below the first Routing Plan node

3. Click to configure:

* Routing Plan: Select "National Agents"

4. Save the node configuration


Step 7: Connect the Nodes

1. Find the TRUE output handle on the If Statement node (usually the top output)

2. Click and drag from that handle to the input handle of the "CA Agents" Routing Plan node

3. Find the FALSE output handle on the If Statement node

4. Drag it to the input handle of the "National Agents" Routing Plan node

Your flow now looks like:


Step 8: Save Your Call Flow

1. Click Save in the top toolbar

2. Give your Call Flow a name: "Geographic Routing - CA vs National"

3. Click Create

Success! You've created your first Call Flow.


Step 9: Test Before Going Live (Optional)

Before attaching this to a campaign, you can test it:

1. Use the Debug node technique (see "Testing and Debugging" section)

2. Or attach it to a test campaign and make a test call


Verify It Worked

After attaching to a campaign and making test calls:

1. Navigate to Reporting or Call Tracking

2. Find your test calls

3. Check the Routing Path field—it should show which branch was taken

4. Calls from CA numbers should route to CA Agents

5. All other calls should route to National Agents


Step-by-Step: Building Conditional Routing

Now let's build a more sophisticated Call Flow that combines incoming webhook data with conditional routing.

Business scenario: You run an insurance lead generation campaign. You want to:

1. Wait for incoming webhook data with customer information

2. Route VIP customers to senior agents

3. Route active customers to standard queue

4. Route new leads to a qualification IVR first

5. Tag high-value calls for manual review


Before You Start

* You've set up an incoming webhook (see Webhooks Guide)

* Critical: Incoming webhook is attached to your campaign (Campaign settings → Incoming Webhooks)

* Your webhook sends: customer_tier (values: "vip", "active", "new")

* Custom tag customer_tier is predefined in Webhooks → Custom Tags

* You have three Routing Plans:

* "Senior Agents"

* "Standard Queue"

* "New Lead IVR"

* You've created your first Call Flow (follow the previous section)


Step 1: Create a New Call Flow

1. Navigate to Call flows

2. Click Create

3. You'll see the blank canvas with a Start node


Step 2: Add a Wait For Pre Call Webhooks Node

1. Drag Wait For Pre Call Webhooks from the sidebar (purple)

2. Connect the Start node to this node

3. Click the node to configure:

* Name: "Wait for Customer Data"

* Wait Duration: 5 seconds

4. Save the node configuration

Why this matters: This ensures your incoming webhook data has time to arrive before routing decisions are made.


Step 3: Add a Variable Node to Read Webhook Data

1. Drag Variable node onto the canvas (teal)

2. Connect the Wait node to this Variable node

3. Click to configure:

* Variable Name: customer_tier

* Variable Value: [customer_tier] (reads from incoming webhook)

4. Save the node configuration


Step 4: Add Debug Node (Testing — Adjacent, Not Inline)

Add a Debug node to verify webhook data is arriving. Important: Debug nodes should be placed adjacent to your flow for testing, not inline between nodes.

1. Drag Debug node onto the canvas (red)

2. Place it next to the Variable node (not between Variable and the next node)

3. Connect the Variable node to the Debug node as a separate branch

4. Click to configure:

* Variables to Log: [customer_tier], [CALLER_ID], [CALLER_STATE]

5. Save the node configuration

💡 Tip: Debug nodes are for testing only. Place them adjacent to the nodes you want to inspect — not in the main flow path. Remove or disconnect them before going to production.


Step 5: Add First If Statement (Check for VIP)

1. Drag If Statement node (blue)

2. Connect the Variable node to this If Statement (main flow path)

3. Click to configure:

* Name: "Check if VIP"

* Variable A: [customer_tier]

* Operator: ==

* Variable B: vip

4. Save the node configuration


Step 6: Add Tagging Node for VIP Path (TRUE)

1. Drag Tagging node onto canvas (cyan)

2. Position it in the TRUE path area

3. Click to configure:

* Action: Add

* Tags: vip-customer, high-priority

4. Save the node configuration


Step 7: Add Routing Plan for VIP (TRUE Path)

1. Drag Routing Plan node (pink)

2. Connect the Tagging node to this Routing Plan

3. Click to configure:

* Routing Plan: Select "Senior Agents"

4. Save the node configuration


Step 8: Add Second If Statement (FALSE Path - Check for Active)

Now handle the FALSE path from the VIP check:

1. Drag another If Statement node

2. Connect the FALSE output of the first If Statement to this new If Statement

3. Click to configure:

* Name: "Check if Active Customer"

* Variable A: [customer_tier]

* Operator: ==

* Variable B: active

4. Save the node configuration


Step 9: Add Routing for Active Customers (TRUE)

1. Drag Routing Plan node

2. Connect the TRUE output of the "Active Customer" If Statement to this node

3. Click to configure:

* Routing Plan: "Standard Queue"

4. Save the node configuration


Step 10: Add Routing for New Leads (FALSE)

1. Drag Routing Plan node

2. Connect the FALSE output of the "Active Customer" If Statement to this node

3. Click to configure:

* Routing Plan: "New Lead IVR"

4. Save the node configuration


Step 11: Review Your Complete Flow

Your Call Flow should now look like:


Step 12: Save Your Call Flow

1. Click Save in the toolbar

2. Name it: "Insurance Routing - VIP, Active, New"

3. Click Create


Verify It Worked

After attaching to a campaign and sending test incoming webhook data:

1. Send webhook with customer_tier: "vip"

* Make a test call

* Check call log: should route to Senior Agents

* Should have tags: vip-customer, high-priority

2. Send webhook with customer_tier: "active"

* Make a test call

* Should route to Standard Queue

3. Send webhook with customer_tier: "new" (or any other value)

* Make a test call

* Should route to New Lead IVR


Connecting Call Flows to Campaigns

Once you've built your Call Flow, you need to attach it to a Campaign for it to execute.

⚠️ Critical Prerequisite: If your Call Flow uses incoming webhooks, ensure the webhook is attached to the campaign before making test calls.

Step-by-Step: Attach Call Flow to Campaign

Step 1: Navigate to Your Campaign

1. Click Campaigns in the main sidebar

2. Select the campaign you want to use this Call Flow with

3. Click to open the campaign edit view


Step 2: Verify Incoming Webhook Attachment (If Applicable)

If your Call Flow uses the "Wait For Pre Call Webhooks" node:

1. Scroll to the Incoming Webhooks section in campaign settings

2. Verify your incoming webhook is listed and attached

3. If not attached, add it now

Why this matters: The Wait node can only access webhook data if the webhook is connected to the campaign.


Step 3: Change Execution Mode

1. Scroll to the Routing Configuration section

2. You'll see two radio buttons:

* Routing Plan (default)

* Call Flow

3. Select Call Flow


Step 4: Select Your Call Flow

1. A dropdown will appear: Call Flow

2. Click the dropdown

3. Select your Call Flow from the list (e.g., "Insurance Routing - VIP, Active, New")


Step 5: Save Campaign

1. Scroll to the bottom

2. Click Save Changes

3. Wait for the success notification: "Campaign updated successfully"


Verify It's Connected

1. Make a test call to a tracking number in that campaign

2. Navigate to Call Tracking or Reporting

3. Find the test call

4. Check the Routing Details—it should show your Call Flow was used


Switch Back to Routing Plan (If Needed)

To revert to a simple Routing Plan:

1. Edit the campaign

2. Change Execution Mode back to Routing Plan

3. Select your Routing Plan

4. Click Save Changes

💡 Tip: Campaigns are automatically active when created—no need to activate separately. If your Call Flow isn't executing, check that the campaign itself is active (Status checkbox).


Testing and Debugging Call Flows

Testing Call Flows is critical—routing mistakes cost money and hurt caller experience. Here's how to test effectively.

Strategy 1: Use Debug Nodes

Best for: Verifying variable values and flow paths

How:

1. Add Debug nodes at key decision points in your flow

2. Configure them to log the variables you're checking:

Variables: [CALLER_STATE], [customer_tier], [routing_decision]

3. Save your Call Flow

4. Attach to a test campaign

5. Make test calls

6. Navigate to Call Tracking or Reporting

7. Find your test call and view the Call Log

8. Look for debug output:

[Call Flow Debug] CALLER_STATE=CA, customer_tier=vip, routing_decision=senior_agents

Best practices:

* Place Debug nodes before If Statement nodes to see what's being compared

* Place Debug nodes after Variable nodes to verify data was read correctly

* Remove or disable Debug nodes in production (they add log noise)

* Always use bracket syntax: [variable] not variable


Strategy 2: Test with Known Data

Best for: Verifying specific conditions

How:

1. Send test incoming webhooks with controlled data (use your webhook integration method)

2. Immediately call your tracking number from the same phone number

3. Verify the call routed as expected

4. Repeat with different data values to test each branch

Test matrix example:

Test

Webhook Data

Expected Route

Result

1

customer_tier: "vip"

Senior Agents

✅ Pass

2

customer_tier: "active"

Standard Queue

✅ Pass

3

customer_tier: "new"

New Lead IVR

✅ Pass

4

(no webhook)

New Lead IVR

✅ Pass (fallback)


Strategy 3: Trace Call Flow Execution

Best for: Understanding which path the call took

How:

1. Use Tagging nodes to mark which path was taken

2. Example

3. After test calls, filter by tags in Reporting

4. Verify the correct path was taken


Strategy 4: Test Edge Cases

Don't just test the happy path—test these scenarios:

Edge Case

What to Test

Missing webhook data

Does flow handle customer_tier being empty/undefined?

Webhook timeout

What happens if webhook takes > 10 seconds to arrive?

Invalid values

What if customer_tier = "unknown" or a typo?

Null values

What if CALLER_STATE is null?

HTTP request failure

What if external API is down?

Multiple If Statement FALSE paths

Does flow reach the final fallback?

Best practice: Always have a fallback path for unexpected data.


Common Testing Issues

Issue: Call Flow doesn't execute at all

Check:

* Campaign is using Call Flow execution mode (not Routing Plan)

* Correct Call Flow is selected in campaign settings

* Campaign is Active (Status checkbox is checked)

* Test call is going to a number in that campaign

* If using webhooks: Incoming webhook is attached to campaign


Issue: Conditional logic not working as expected

Check:

* Variable names use bracket syntax: [customer_tier] not customer_tier

* String comparisons use exact values: "vip" not "VIP" (unless normalized)

* Operator is correct: == for equality, not =

* Debug nodes show the variable has the value you expect


Issue: Webhook data not available in Call Flow

Check:

* Incoming webhook was sent before the call arrived

* caller_id in webhook matches the calling number exactly (format: +15551234567)

* Incoming webhook is attached to the campaign (most common issue)

* Wait For Pre Call Webhooks node has sufficient timeout (5-10 seconds)

* Check Webhooks → Incoming Requests to verify webhook was received


Issue: HTTP Request node failing

Check:

* Endpoint URL is correct (HTTP or HTTPS both supported)

* Endpoint responds within 10 seconds

* Authentication headers are correct

* Test the endpoint separately with curl or Postman

* Check response variable in Debug node (may contain error message)


Common Call Flow Patterns

Here are proven Call Flow patterns you can adapt for your use cases.


Pattern 1: Geo-Routing by State

Scenario: Simple geographic routing—California callers go to one buyer, everyone else to another.

When to use: Your buyers have geographic preferences or state licenses.

Variations:

* Multi-state routing: Chain If Statements for TX, FL, NY, etc.

* Zip code routing: Use [CALLER_ZIP] instead of [CALLER_STATE]

* Area code routing: Use regex on [CALLER_ID]: [CALLER_ID] ~= ^\+1415.* for San Francisco


Pattern 2: Quality Filtering via CRM Lookup

Scenario: Before routing, check if the caller is a known customer in your CRM. Route VIPs to premium buyer, unknowns to standard buyer.

When to use: You have a CRM/database with customer quality data and want to maximize revenue by routing high-value callers to buyers who pay more.

Best practices:

* Keep CRM API response time < 2 seconds

* Handle API timeouts gracefully (route to default buyer if CRM is down)

* Cache frequently-accessed customer data


Pattern 3: Wait for Webhook Data, Then Route Based on Score

Scenario: Caller clicks your ad, your system sends enrichment data (credit score, age, zip, etc.) via webhook, then caller dials. Route based on that data.

Timeline:

Call Flow:

When to use: You have pre-call data (from ad click, form fill, etc.) that affects routing decisions and buyer payouts.

Best practices:

* Set Wait timeout to 8-10 seconds if your enrichment API is slow

* Always handle missing webhook data (timeout path)

* Tag calls with the score/tier for reporting

* Ensure incoming webhook is attached to campaign

* Use HTTPS webhooks only


Pattern 4: VIP Customer Fast Lane

Scenario: Incoming webhook identifies VIP customers, route them to priority agents

Use when: You have customer LTV or tier data and want to prioritize high-value callers.


Pattern 5: State-Based Routing with Exceptions

Scenario: Route by state, but VIP customers from any state go to a special queue

Use when: You have both tier-based and geo-based routing logic.


Pattern 6: Pre-Call Qualification via External API

Scenario: Check caller's credit score from your API before routing

Use when: You need to qualify callers against external data before routing.

⚠️ Warning: Hanging up on callers can hurt conversion. Consider routing to a "call you back" message instead.


Pattern 7: Business Hours Routing

Scenario: Route to live agents during business hours, voicemail after hours

Note: Moja doesn't have a built-in call_hour system tag. Use a JavaScript node to get the current time:

Use when: You want time-based routing beyond what basic Routing Plans offer.

💡 Tip: Adjust the UTC offset for your timezone. For complex time logic (weekends, holidays), extend the JavaScript node.


Pattern 8: A/B Testing Routing Strategies

Scenario: Route 50% of calls to Strategy A, 50% to Strategy B

Use when: You want to test different routing approaches and measure performance.

Analysis: Filter reports by test-group-a and test-group-b tags to compare conversion rates.


Pattern 9: Multi-Criteria Routing with Scoring

Scenario: Calculate a lead score based on multiple factors, route accordingly

Use when: You have complex scoring logic that's easier to express in JavaScript than multiple If Statement nodes.


Pattern 10: Webhook Data Enrichment + HTTP Request Combo

Scenario: Wait for incoming webhook with basic data, then enrich with your API

Use when: You have a multi-stage data fetch (webhook provides ID, then fetch full profile).


Pattern 11: Fallback Routing When Data Missing

Scenario: Use webhook data if available, otherwise use basic geo routing

Use when: Webhook data may not always be available (e.g., caller didn't come from your landing page).


Troubleshooting

This section addresses the most common customer pain points and troubleshooting scenarios.

Problem: Call Flow Not Executing

Symptoms: Calls bypass your Call Flow and go straight to a default route (or fail)

Diagnostic steps:

1. Check campaign execution mode:

* Navigate to your campaign

* Verify Execution Mode is set to Call Flow (not Routing Plan)

* Verify the correct Call Flow is selected

2. Check campaign status:

* Ensure campaign Active checkbox is checked

* Inactive campaigns don't process calls

3. Check tracking number assignment:

* Verify the test call is going to a number assigned to this campaign

* Navigate to Numbers and check campaign association

4. Check Call Flow saved state:

* Open the Call Flow in the builder

* Ensure there are no unsaved changes

* Re-save if necessary

5. Check for disconnected nodes:

* Every node (except end nodes) should have an outgoing connection

* No orphaned nodes that can't be reached from Start

6. Check incoming webhook attachment (if applicable):

* If your flow uses Wait For Pre Call Webhooks, verify the webhook is attached to the campaign

* Campaign settings → Incoming Webhooks section


Problem: "Why Isn't My If Statement Working?"

Symptoms: Calls always go down the FALSE branch even when you expect TRUE

Diagnostic steps:

* * Check call log to see actual values

1. Check variable syntax:

* Use brackets: [CALLER_STATE] not CALLER_STATE

* Common mistake: Forgetting the brackets → variable name is treated as literal string

Example of broken logic:

Variable A: CALLER_STATE ← WRONG (missing brackets)

Operator: ==

Variable B: CA

This compares the string "CALLER_STATE" to "CA" → always FALSEFixed:Variable A: [CALLER_STATE] ← RIGHT (with brackets)

Operator: ==

Variable B: CA

2. Check for data type mismatches:

* [CALLER_STATE] might be "CA" (string) not just CA

* Use exact string matches: "CA" with quotes if needed

3. Check for case sensitivity:

* While Moja normalizes tags to UPPERCASE internally, ensure your comparison values match

* For custom variables, use consistent casing or normalize in JavaScript

4. Check for whitespace:

* "vip " (with trailing space) ≠ "vip"

* Use .trim() in JavaScript node if needed:

tags.CUSTOMER_TIER = tags.CUSTOMER_TIER.trim();

5. Check operator:

* Use == for equality, not = (assignment)

* Use != for not equal

6. Verify variable exists:

* If Variable A doesn't exist, comparison may fail

* Use Debug to confirm variable has a value


Problem: "Webhook Data Not Available"

Symptoms: Variables from incoming webhook are empty or null in Call Flow

Diagnostic steps:

1. Check incoming webhook timing:

* Webhook must be sent before caller dials

* If webhook arrives after call starts, Wait node may timeout

2. Verify webhook was received:

* Navigate to Webhooks → Incoming Requests

* Find your webhook request

* Check status (should be 200/success)

* Verify payload contains the expected data

3. Check caller_id format:

* Webhook caller_id must match exactly: +15551234567

* Common mistakes: missing +, different format

4. Check campaign attachment:

* Most common issue: Incoming webhook must be attached to the campaign

* Navigate to campaign → check Incoming Webhooks section

* If not listed, attach it now

5. Increase Wait timeout:

* Edit the Wait For Pre Call Webhooks node

* Try 8-10 seconds instead of 5

6. Check webhook lifespan:

* Default: 3600 seconds (1 hour)

* If webhook was sent more than 1 hour ago, it's expired

7. Verify variable names:

* If webhook sends customer_tier, read it as [customer_tier]

* Variable names are case-insensitive but use consistent naming


Problem: "Calls Not Routing to Expected Buyer"

Symptoms: Calls route to wrong destination, or routing logic seems random. Where do Debug nodes belong?

Diagnostic steps:

1. 2. Check Debug node placement strategy:

* After Variable nodes — Verify data was read

* Before If Statement nodes — See what's being compared

* After HTTP Request nodes — Verify API response

* After JavaScript nodes — Confirm calculations are correct

3. Trace the execution path with tags:

4. 5. Then filter call logs by tags to see which path was taken.

6. Check for missing FALSE paths:

* Every If Statement should have both TRUE and FALSE connected

* Missing FALSE path → call has nowhere to go → unpredictable behavior

7. Verify Routing Plan configuration:

* Open the Routing Plan node

* Confirm it's pointing to the correct Routing Plan

* Check the Routing Plan itself has valid targets


Problem: "Tags Not Passing to Webhook"

Symptoms: Outgoing webhooks don't include expected tags, or tags have different names than expected

Root cause: Tag naming inconsistency between Call Flow, webhooks, and downstream systems

Diagnostic steps:

1. Verify tags are being set:

* Add Debug node after Tagging node

* Log all tags to confirm they're applied

2. Check tag naming consistency:

* In Call Flow: caller-zip

* In webhook payload: caller_zip or callerZip or zip?

* In your system: zipcode or ZIP or zip_code?

These are all DIFFERENT tags. Pick one format and stick to it everywhere.

3. Standardize tag names:

* Create a "Tag Dictionary" document

* Use the same format everywhere: caller-zip (lowercase, hyphens)

* Normalize incoming data in JavaScript node:

// Normalize all zip code variations to ONE tag

tags.CALLER_ZIP = tags.CALLER_ZIP || tags.ZIP_CODE || tags.ZIP || tags.ZIPCODE;

4. Check outgoing webhook configuration:

* Navigate to Webhooks → Outgoing

* Verify webhook is attached to the campaign

* Check webhook payload template includes tags

5. Test with a webhook debugging tool:

* Use webhook.site or similar

* Send a test call

* Inspect the exact payload your system receives

* Compare tag names to what you expect


Problem: HTTP Request Node Failing

Symptoms: HTTP Request node returns errors or empty response

Diagnostic steps:

1. Test endpoint separately (optional for reference):

* Verify endpoint is reachable and returns data using your preferred testing method

2. Check timeout:

* Endpoint must respond within 10 seconds

* Optimize your API or cache data if possible

3. Check authentication:

* Verify headers are correct: {"Authorization": "Bearer YOUR_KEY"}

* Check for typos in API keys

4. Check URL format:

* Both HTTP and HTTPS are supported

* Variable interpolation: [CALLER_ID] should be replaced automatically

5. Check response handling:

* Use Debug node to log the response variable

* Response may contain error messages (e.g., {"error": "Invalid API key"})

6. Check for SSL/certificate issues:

* Ensure endpoint has valid SSL certificate (if using HTTPS)

* Self-signed certs may be rejected


Problem: JavaScript Node Not Working

Symptoms: Variables set in JavaScript node are empty/wrong downstream

Diagnostic steps:

1. Test JavaScript separately:

* Copy your JS code to a browser console or Node.js REPL

* Test with sample data

* Fix syntax errors

2. Check variable assignment:

* Modify the tags and variables objects directly

* Using var, let, or const for local calculations is fine — Moja reads from tags and variables when the node completes

Example:// ✅ CORRECT: Modify tags and variables objects

var score = 0; // local variable is fine

if (tags.CALLER_STATE == "CA") score += 10;

variables.lead_score = score; // This is what Moja reads

tags.PRIORITY = "high"; // Can also set tags

3. Check for runtime errors:

* Add Debug node after JavaScript node

* Log the variables you set using bracket syntax: [lead_score], [PRIORITY]

* If empty, check for JS errors

4. Keep it simple:

* Avoid complex logic that's hard to debug

* Break into multiple nodes if needed

5. Check variable scope:

* System tags are available via tags.TAG_NAME (e.g., tags.CALLER_STATE)

* Custom variables are available via variables.name (e.g., variables.customer_tier)


Problem: Call Flow Hangs or Times Out

Symptoms: Calls get stuck and don't route

Diagnostic steps:

1. Check for infinite loops:

* Ensure every path leads to a routing decision (Routing Plan or Hangup)

* No circular connections between nodes

2. Check Wait node timeout:

* If Wait For Pre Call Webhooks timeout is too high (> 10 seconds), calls may feel stuck

* Reduce to 5 seconds

3. Check HTTP Request timeouts:

* If external API is slow (> 10 seconds), request will timeout

* Optimize API or handle timeouts gracefully

4. Check for missing connections:

* Every If Statement should have both TRUE and FALSE paths connected

* Missing connections = call has nowhere to go


Problem: Tags Not Appearing in Call Logs

Symptoms: Tagging nodes seem to execute, but tags don't show in reporting

Diagnostic steps:

1. Check tag syntax:

* Tags should be lowercase, hyphen-separated or underscore separated: high-value or high_value, not High Value

* No special characters except hyphens and underscores

2. Check Tagging node configuration:

* Action should be Add (not Remove)

* Tags field should not be empty

3. Check call completion:

* Tags may not appear until call fully completes

* Refresh the call log view

4. Check for Remove tags nodes:

* If a downstream Tagging node removes the tag, it won't appear

5. Check custom tag definition:

* Custom tags must be predefined in Webhooks → Custom Tags

* System tags are automatically available


Migration Guides

Switching from another call tracking platform? These guides map common patterns from other platforms to Moja Call Flows.


Migrating from Ringba

Ringba users typically have these patterns:

Ringba Pattern → Moja Call Flow Equivalent

Ringba Feature

Moja Equivalent

How to Build

IVR Menus

IVR (separate feature)

Configure IVR menu routing at the campaign/IVR level. Note: IVR responses are not automatically available as Call Flow variables.

Geo-Routing Rules

If Statement on [CALLER_STATE]

[If Statement: [CALLER_STATE] == "CA"] → Route to CA buyer

Tag-Based Routing

Variable + If Statement

Read tag from webhook, then branch: [If Statement: [campaign_type] == "medicare"]

Concurrency Caps

Routing Plan settings

Set concurrency limits in Routing Plan, not Call Flow

Ping/Post (RTB)

RTB Groups

Use Moja's built-in RTB Groups feature instead of custom HTTP logic

Call Distribution (Round Robin)

Routing Plan

Use Routing Plan with Round Robin distribution, not Call Flow

Time-Based Routing

JavaScript + If Statement

Use JavaScript to get current hour (new Date().getUTCHours()), then branch on result. See Pattern 7.

Duplicate Detection

HTTP Request to your system

Call your deduplication API, then branch based on response

Common Ringba Tag Naming Issues

Problem: Ringba allows freeform tag names → inconsistency → broken logic

Solution in Moja:

1. Establish tag naming conventions before building flows (see "Before You Build" section)

2. Use JavaScript node to normalize incoming tags:

// Normalize all zip variations to ONE format

tags.CALLER_ZIP = tags.CALLER_ZIP || tags.ZIP_CODE || tags.ZIP || tags.ZIPCODE;

Ringba → Moja Migration Checklist

* Export Ringba call logs to identify all tags used (create a Tag Dictionary)

* Map Ringba IVR menus to Moja IVRs (separate feature) + Call Flow routing

* Recreate routing rules as If Statement nodes

* Convert Ringba "Number Pools" to Moja Dynamic Number Insertion (DNI)

* Migrate webhook integrations (Ringba → Moja incoming/outgoing webhooks)

* Test with parallel traffic (run both platforms simultaneously for 1-2 weeks)


Migrating from Retriever (Invoca)

Retriever/Invoca users typically have these patterns:

Retriever Pattern → Moja Call Flow Equivalent

Retriever Feature

Moja Equivalent

How to Build

Signal Tags

Tagging Node

Use Tagging node to add labels: [Tagging: Add "converted"]

Dynamic Routing

Call Flow

Use If Statement nodes to branch based on conditions

Webhooks (Pre-Call)

Wait For Pre Call Webhooks

Use Wait node to receive data before routing

Webhooks (Post-Call)

Outgoing Webhooks

Configure at campaign level, not in Call Flow

Campaign Hierarchy

Campaign → Call Flow → Routing Plan → Target

See "Before You Build" section for hierarchy diagram

Buyer Connection

Target

Create Targets for each buyer, then reference in Routing Plans

Call Recording

Campaign-level setting

Enable recording in Campaign settings, not Call Flow

Transcription + AI

QAI (Moja's AI analysis)

Enable QAI in Campaign settings for transcription + sentiment analysis

Retriever → Moja Migration Checklist

* Map Retriever "Campaigns" to Moja Campaigns (1:1 mapping)

* Convert Retriever "Publisher" structure to Moja Publishers

* Migrate Retriever "Buyer Connections" to Moja Targets

* Recreate dynamic routing rules as Call Flows

* Convert Retriever "Signal Tags" to Moja Tagging nodes

* Migrate pre-call webhooks to Moja incoming webhooks

* Migrate post-call webhooks to Moja outgoing webhooks

* Test QAI transcription accuracy vs Retriever's transcription


Migrating from Invoca

Invoca users typically have these patterns:

Invoca Pattern → Moja Call Flow Equivalent

Invoca Feature

Moja Equivalent

How to Build

Signal AI (Automated Tagging)

QAI + Tagging

Use Moja QAI for AI tagging, or JavaScript node for custom logic

Custom Data Parameters

Incoming Webhooks

Send custom data via incoming webhook, read in Call Flow with Variable node

Routing Priority

Routing Plan with Priority

Set priority order in Routing Plan, not Call Flow

Network Integration

HTTP Request node

Call external APIs with HTTP Request node

Call Whisper

Whisper node (coming soon)

Not yet available—use Routing Plan whisper settings

Repeat Caller Detection

HTTP Request to CRM

Check caller history via API: [HTTP Request: GET crm.com/callers/[CALLER_ID]]

Destination Match

If Statement + Routing Plan

Branch based on condition, then route to specific target

Invoca → Moja Migration Checklist

* Export Invoca "Custom Data" fields → Map to Moja incoming webhook fields

* Convert Invoca "Signal AI" rules to Moja QAI + Tagging nodes

* Migrate Invoca "Routing Profiles" to Moja Call Flows

* Recreate Invoca "Network Integrations" with HTTP Request nodes

* Map Invoca "Destination Groups" to Moja Routing Plans

* Test repeat caller detection logic

* Migrate compliance settings (TCPA, DNC lists)


General Migration Best Practices

1. Run platforms in parallel for 1-2 weeks — Compare call routing accuracy

2. Start with one campaign — Don't migrate everything at once

3. Document your old platform's logic — Screenshot flows, export configs

4. Create a Tag Dictionary before migrating — Prevent naming inconsistency

5. Test edge cases — What happens when webhook is missing? API is down?

6. Train your team — Moja's visual builder is different from form-based routing

7. Leverage Moja support — Schedule migration planning call with your account manager


Frequently Asked Questions (FAQ)

General

Q: How many nodes can I add to a Call Flow?

A: There's no strict limit, but we recommend keeping flows under 20-30 nodes for performance and maintainability. If your flow is becoming very complex, consider breaking it into multiple Call Flows or using JavaScript nodes to consolidate logic.

Q: Can I reuse a Call Flow across multiple campaigns?

A: Yes! The same Call Flow can be selected in multiple campaigns. This is useful for common routing logic (e.g., "VIP Routing" applied to all your premium campaigns).

Q: Can I clone a Call Flow?

A: Not directly in the UI currently. Workaround: Open the Call Flow you want to clone, manually recreate the nodes in a new Call Flow, or contact support to request cloning.

Q: Can I export/import Call Flows?

A: Not currently supported. This is a planned feature—contact your account manager if this is critical for your workflow.


Execution & Performance

Q: How fast do Call Flows execute?

A: Most Call Flows execute in under 1 second. HTTP Request nodes and Wait For Pre Call Webhooks nodes add latency (5-10 seconds max). Design flows to minimize caller wait time.

Q: Do Call Flows execute in parallel or sequentially?

A: Sequentially. The call follows one path through the flow from Start to a routing decision. Nodes execute one after another in the order you've connected them.

Q: What happens if a node fails (e.g., HTTP request times out)?

A: The flow continues. Failed nodes typically set an error value in the response variable. Always design fallback paths for node failures.

Q: Can I pause or stop a Call Flow mid-execution?

A: No. Once a call enters a Call Flow, it executes to completion (Routing Plan or Hangup node). You can't interrupt mid-flow. Design your flows with explicit exit paths (Hangup nodes) if you need to terminate early.


Data & Variables

Q: What system tags are available?

A: Moja provides 100+ system tags you can use in call flows. Common examples:

* [CALLER_ID] — Caller's phone number (+15551234567)

* [CALLER_STATE] — Caller's state (editable)

* [CALLER_ZIP] — Caller's ZIP code (editable)

* [CAMPAIGN_ID] — Campaign identifier

* [CAMPAIGN_NAME] — Campaign name

* [BUYER_NAME] — Buyer name

* [GCLID] — Google Click ID

* [UTM_SOURCE] — UTM source from landing page

To view the full, current list: Go to Webhooks → Custom Tags in the Moja portal.

Note: System tags are automatically available and do not need to be defined by you. Custom tags you create must be predefined in the Custom Tags section before use.

Q: Can I set variables that persist across multiple calls from the same caller?

A: No. Variables are scoped to a single call. If you need cross-call persistence, use incoming webhooks to send caller context from your system on each call.

Q: Can I do math in If Statement conditions?

A: Not directly. Use a JavaScript node first to calculate the result, then use If Statement to check it:

Q: Are tags and variables case-sensitive?

A: Tags: Moja automatically normalizes custom tag names to UPPERCASE internally, so customer-tier, Customer-Tier, and CUSTOMER-TIER all resolve to the same tag. However, maintain lowercase naming in your documentation for consistency.

Variables: Case-insensitive comparisons are used internally.

Best practice: Use consistent naming conventions even though the system is case-insensitive.


Integration & Webhooks

Q: Can I use outgoing webhooks with Call Flows?

A: Yes! Outgoing webhooks are configured at the campaign level, not the Call Flow level. Attach outgoing webhooks to your campaign, and they'll fire for calls processed by that Call Flow.

Q: Can Call Flows trigger outgoing webhooks at specific points in the flow?

A: Use the HTTP Request node to POST to any endpoint (including your own webhook receivers). For Moja's built-in outgoing webhooks, they fire based on call events (e.g., "On Conversion"), not Call Flow nodes.

Q: What happens if the Wait For Pre Call Webhooks node times out?

A: The flow continues without webhook data. Downstream nodes that reference webhook variables will see empty/null values. Always design fallback paths for missing webhook data.

✅ Confirmed: The Wait node proceeds immediately when the webhook is received — it doesn't wait the full timeout duration. If the webhook doesn't arrive before timeout, the flow continues with empty values.

Q: Can I send data from a Call Flow back to my system?

A: Yes, use the HTTP Request node to POST data to your endpoint. Include call variables in the request body:

Method: POST

Body: {"caller_id": "[CALLER_ID]", "route_taken": "vip"}


Debugging & Testing

Q: Can I test a Call Flow without making an actual call?

A: Not directly. You must make a test call. Best practice: Use a dedicated test campaign and test tracking numbers to avoid polluting production data.

Q: How do I see which path a call took through a Call Flow?

A: Use Tagging nodes to mark each path:

If Statement TRUE → Tagging: Add "flow-path-a"

If Statement FALSE → Tagging: Add "flow-path-b"

Then filter call logs by tag.

Q: Can I see a visual representation of a call's flow after the call completes?

A: Not currently. This is a planned feature. Use Debug nodes and tags to trace execution for now.

Q: Debug nodes aren't logging anything. Why?

A: Check:

* Debug node is connected in the flow

* Call actually executed the flow (check campaign execution mode)

* You're looking at the correct call log entry

* Variables you're logging use bracket syntax: [variable_name]

* Variables you're logging actually exist (typo in variable name?)


Advanced

Q: Can I nest Call Flows (call one Call Flow from another)?

A: Not directly. Call Flows are terminal—they execute to a routing decision. Workaround: Use Routing Plan nodes to add additional routing logic (Routing Plans can be chained).

Q: Can I use regex in If Statement conditions?

A: Yes, use the ~= operator:

Variable A: [CALLER_ID]

Operator: ~=

Variable B: ^\+1415.*

This checks if CALLER_ID starts with +1415 (San Francisco area code).

Q: Can I route to different campaigns from a Call Flow?

A: No. Call Flows route to Routing Plans within the same campaign. Routing Plans then route to Targets (which may belong to different buyers/campaigns indirectly). If you need cross-campaign routing, use Routing Plans with Targets assigned to multiple campaigns.

Q: Can I play custom audio messages in a Call Flow?

A: Not currently with a dedicated node. Workaround: Route to an IVR that plays the message, then transfers to the final destination.

Q: Can I transfer calls mid-flow?

A: There is no standalone "Transfer" node in Call Flows. Transfers happen through this workflow:

1. Route to a Routing Plan (using Routing Plan node)

2. Routing Plan selects a Target

3. If the Target is configured with a SIP endpoint, the call is transferred via SIP

Configure SIP transfer settings at the Target level, not in the Call Flow.


What's Next?

Now that you understand Call Flows, explore these related features to build even more powerful routing logic:

For Data-Driven Routing

* Webhooks Guide — Send and receive real-time data to enrich Call Flows

* Custom Tags — Create custom tags for advanced filtering and reporting (Webhooks → Custom Tags)

* HTTP Integrations — Connect Call Flows to your CRM, databases, and external APIs

For Conversion Optimization

* IVR Setup — Pre-qualify callers before Call Flow routing

* RTB Groups — Use built-in RTB Groups for Real-Time Bidding instead of custom logic

* Conversion Rules — Define what qualifies as a conversion for your campaigns

For Advanced Routing

* Routing Plans — Combine with Call Flows for sophisticated fallback/priority logic

* Targets — Configure Targets with concurrency, caps, and schedules

* Number Pools (DNI) — Dynamically assign tracking numbers for attribution

For Analytics & Reporting

* Reporting Dashboard — Filter by tags and analyze Call Flow performance

* QAI Analysis — Transcribe and analyze calls routed through Call Flows

* Call Logs — Deep dive into individual call routing decisions


Need Help?

Support:

* In-app chat: Click the support icon in the bottom right


Appendix: Quick Reference

Node Type Summary Table

Node

Color

Category

Purpose

Output Paths

If Statement

Blue

Logic

Conditional branching

TRUE, FALSE

Variable

Teal

Logic

Set/read variables

Single

Javascript

Orange

Logic

Custom JS execution

Single

Wait For Pre Call Webhooks

Purple

Data

Pause for webhook data

Single

HTTP Request

Purple

Data

Call external APIs

Single

Tagging

Cyan

Data

Add/remove tags

Single

Routing Plan

Pink

Routing

Route to Routing Plan

Terminal (end)

Hangup

Dark Red

Routing

End call immediately

Terminal (end)

Debug

Red

Utility

Log variables for testing

Single


Common Tag Reference

Tag

Format

Example

Notes

[CALLER_ID]

E.164

+15551234567

System tag (editable)

[CALLER_STATE]

2-letter

CA

Derived from area code (editable)

[CALLER_ZIP]

5-digit

90210

Derived from area code (editable)

[CAMPAIGN_ID]

UUID

cmp_12345...

System tag (read-only)

[CAMPAIGN_NAME]

String

Medicare Q1

System tag (read-only)

[BUYER_NAME]

String

Acme Insurance

System tag (read-only)

[GCLID]

String

EAIaIQobChM...

Google Click ID (read-only)

[UTM_SOURCE]

String

google

From landing page (read-only)

[customer_tier]

Custom

vip

From incoming webhook (must predefine)

[customer_name]

Custom

John Doe

From incoming webhook (must predefine)

Full list: See Webhooks → Custom Tags in the Moja portal for all 100+ available system tags.

Remember: System tags are automatically available. Custom tags must be predefined before use.


Operator Reference (If Statement)

Operator

Meaning

Example Use

==

Equals

[CALLER_STATE] == "CA"

!=

Not equals

[customer_tier] != "banned"

<

Less than

[credit_score] < 600

>

Greater than

[customer_ltv] > 10000

<=

Less than or equal

[DURATION] <= 120

>=

Greater than or equal

[age] >= 65

~=

Regex match

[CALLER_ID] ~= ^\+1415.*


Variable Syntax Reference

In If Statement nodes:

* Always use bracket syntax: [CALLER_STATE], [customer_tier]

In JavaScript nodes:

* System tags: tags.CALLER_STATE (automatically UPPERCASE)

* Custom tags: tags.CUSTOMER_TIER (normalized to UPPERCASE)

* Variables: variables.lead_score

In Debug nodes:

* Use bracket syntax: [CALLER_STATE], [lead_score]

Best practice: Be consistent. Use brackets [variable] in all node configuration fields.


Troubleshooting Checklist

When a Call Flow isn't working:

* Campaign execution mode is set to Call Flow (not Routing Plan)

* Correct Call Flow is selected in campaign settings

* Campaign is Active (Status checkbox checked)

* Call Flow has been Saved

* All nodes are connected (no orphaned nodes)

* Variable names use bracket syntax: [CALLER_STATE] not CALLER_STATE

* If Statement operators are correct (== not =)

* Incoming webhook attached to campaign (if using webhook data) ← Most common issue

* Webhook caller_id format matches exactly

* Wait For Pre Call Webhooks timeout is sufficient (5-10 sec)

* HTTP Request endpoints respond within 10 seconds

* Debug nodes added to verify variable values

* Debug nodes use bracket syntax: [variable]

* Test calls going to correct tracking number/campaign

* Tag naming is consistent (lowercase, hyphens, no variations)

* Custom tags predefined in Webhooks → Custom Tags


Did this answer your question?