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
What Are Call Flows?
Before You Build Your First Call Flow
When to Use Call Flows (vs. Simple Routing Plans)
The Visual Call Flow Builder
Understanding Node Types
Step-by-Step: Creating Your First Call Flow
Step-by-Step: Building Conditional Routing
Connecting Call Flows to Campaigns
Testing and Debugging Call Flows
Common Call Flow Patterns
Troubleshooting
Migration Guides
Frequently Asked Questions (FAQ)
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
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
Naming rules:
Be consistent with casing — avoid mixing formats (e.g., don't use vip-customer in one place and VIP-Customer in another). Moja normalizes tags to UPPERCASE internally, so any casing will work, but consistent naming prevents confusion when reading your Call Flow configuration.
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
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:
Document your tag taxonomy — Create a spreadsheet with approved tag names
Train your team — Everyone uses the same names
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:
Create an RTB Group in RTB Groups section
Add your buyers to the group with bid parameters
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:
Create a "Tag Dictionary" spreadsheet
Share with your team, publishers, webhook developers
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:
How It Works
Drag nodes from the sidebar onto the canvas
Connect nodes by dragging from one node's output handle to another node's input handle
Configure each node by clicking it and filling in the properties panel
Save your flow when complete
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:
Caller clicks your ad or fills a form
Your system sends incoming webhook with caller data to Moja
Caller dials your Moja tracking number
Call Flow hits this node and waits up to X seconds
If webhook data arrives → flow continues immediately with data attached
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: is-homeowner, high-value, 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:
Call Flow reaches this node
Call is handed to the selected Routing Plan
Routing Plan applies its rules (geo, capacity, schedule, etc.)
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:
Call Flow routes to a Routing Plan (using this node)
Routing Plan selects a Target
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:
Add Debug nodes adjacent to key decision points in your flow (not in the direct flow path)
Save and test your Call Flow with a real call
Check the call log after the call completes
Debug logs show the exact values at that point in the flow
Best practices:
Place Debug nodes adjacent to If Statement nodes to see what's being compared — do not place them inline between the preceding node and the If Statement
Place Debug nodes adjacent to Variable nodes to verify data was read correctly
⚠️ Debug nodes should be branched off to the side, not in the direct flow of your routing logic
Remove or disable Debug nodes in production (they add log noise)
Always use bracket syntax: [variable_name] not variable_name
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
Click Call flows in the main sidebar
You'll see the Call Flows listing page
Step 2: Create a New Call Flow
Click Create (top right)
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
Find If Statement in the sidebar (blue)
Drag it onto the canvas
The Start node will automatically connect to your If Statement node
Step 5: Configure the If Statement
Click the If Statement node to select it
A configuration dialog will appear
Fill in the fields:
Name: "Check if California"
Variable A: [CALLER_STATE]
Operator: ==
Variable B: CA
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):
Drag Routing Plan node onto the canvas
Position it to the right of the If Statement node, slightly above
Click to configure:
Routing Plan: Select "CA Agents" from the dropdown
Save the node configuration
For the FALSE path (Not California):
Drag another Routing Plan node onto the canvas
Position it below the first Routing Plan node
Click to configure:
Routing Plan: Select "National Agents"
Save the node configuration
Step 7: Connect the Nodes
Find the TRUE output handle on the If Statement node (usually the top output)
Click and drag from that handle to the input handle of the "CA Agents" Routing Plan node
Find the FALSE output handle on the If Statement node
Drag it to the input handle of the "National Agents" Routing Plan node
Your flow now looks like:
Step 8: Save Your Call Flow
Click Save in the top toolbar
Give your Call Flow a name: "Geographic Routing - CA vs National"
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:
Use the Debug node technique (see "Testing and Debugging" section)
Or attach it to a test campaign and make a test call
Verify It Worked
After attaching to a campaign and making test calls:
Navigate to Reporting or Call Tracking
Find your test calls
Check the Routing Path field—it should show which branch was taken
Calls from CA numbers should route to CA Agents
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:
Wait for incoming webhook data with customer information
Route VIP customers to senior agents
Route active customers to standard queue
Route new leads to a qualification IVR first
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"
You've created your first Call Flow (follow the previous section)
Step 1: Create a New Call Flow
Navigate to Call flows
Click Create
You'll see the blank canvas with a Start node
Step 2: Add a Wait For Pre Call Webhooks Node
Drag Wait For Pre Call Webhooks from the sidebar (purple)
Connect the Start node to this node
Click the node to configure:
Name: "Wait for Customer Data"
Wait Duration: 5 seconds
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
Drag Variable node onto the canvas (teal)
Connect the Wait node to this Variable node
Click to configure:
Variable Name: customer_tier
Variable Value: [customer_tier] (reads from incoming webhook)
Save the node configuration
Step 4: Add Debug Node (Testing)
Let's add a Debug node to verify webhook data is arriving:
Drag Debug node onto the canvas (red)
Place it adjacent to the Variable node — do not connect it inline between Variable and If Statement
Click to configure:
Variables to Log: [customer_tier], [CALLER_ID], [CALLER_STATE]
Save the node configuration
💡 Tip: You can remove this Debug node later once you've verified it's working. Remember to use bracket syntax [variable] in Debug nodes.
Step 5: Add First If Statement (Check for VIP)
Drag If Statement node (blue)
Connect the Variable node to this If Statement (main flow path)
Click to configure:
Name: "Check if VIP"
Variable A: [customer_tier]
Operator: ==
Variable B: vip
Save the node configuration
Step 6: Add Tagging Node for VIP Path (TRUE)
Drag Tagging node onto canvas (cyan)
Position it in the TRUE path area
Click to configure:
Action: Add
Tags: vip-customer, high-priority
Save the node configuration
Step 7: Add Routing Plan for VIP (TRUE Path)
Drag Routing Plan node (pink)
Connect the Tagging node to this Routing Plan
Click to configure:
Routing Plan: Select "Senior Agents"
Save the node configuration
Step 8: Add Second If Statement (FALSE Path - Check for Active)
Now handle the FALSE path from the VIP check:
Drag another If Statement node
Connect the FALSE output of the first If Statement to this new If Statement
Click to configure:
Name: "Check if Active Customer"
Variable A: [customer_tier]
Operator: ==
Variable B: active
Save the node configuration
Step 9: Add Routing for Active Customers (TRUE)
Drag Routing Plan node
Connect the TRUE output of the "Active Customer" If Statement to this node
Click to configure:
Routing Plan: "Standard Queue"
Save the node configuration
Step 10: Add Routing for New Leads (FALSE)
Drag Routing Plan node
Connect the FALSE output of the "Active Customer" If Statement to this node
Click to configure:
Routing Plan: "New Lead"
Save the node configuration
Step 11: Review Your Complete Flow
Your Call Flow should now look like:
Step 12: Save Your Call Flow
Click Save in the toolbar
Name it: "Insurance Routing - VIP, Active, New"
Click Create
Verify It Worked
After attaching to a campaign and sending test incoming webhook data:
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
Send webhook with customer_tier: "active"
Make a test call
Should route to Standard Queue
Send webhook with customer_tier: "new" (or any other value)
Make a test call
Should route to New Lead
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
Click Campaigns in the main sidebar
Select the campaign you want to use this Call Flow with
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:
Scroll to the Incoming Webhooks section in campaign settings
Verify your incoming webhook is listed and attached
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
Scroll to the Routing Configuration section
You'll see two radio buttons:
Routing Plan (default)
Call Flow
Select Call Flow
Step 4: Select Your Call Flow
A dropdown will appear: Call Flow
Click the dropdown
Select your Call Flow from the list (e.g., "Insurance Routing - VIP, Active, New")
Step 5: Save Campaign
Scroll to the bottom
Click Save Changes
Wait for the success notification: "Campaign updated successfully"
Verify It's Connected
Make a test call to a tracking number in that campaign
Navigate to Call Tracking or Reporting
Find the test call
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:
Edit the campaign
Change Execution Mode back to Routing Plan
Select your Routing Plan
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:
Add Debug nodes at key decision points in your flow
Configure them to log the variables you're checking:
Variables: [CALLER_STATE], [customer_tier], [routing_decision]
Save your Call Flow
Attach to a test campaign
Make test calls
Navigate to Call Tracking or Reporting
Find your test call and view the Call Log
Look for debug output:
[Call Flow Debug] CALLER_STATE=CA, customer_tier=vip, routing_decision=senior_agents
Best practices:
Place Debug nodes adjacent to (not inline before) If Statement nodes to see what's being compared
Place Debug nodes adjacent to Variable nodes to verify data was read correctly
⚠️ Do not place Debug nodes in the direct flow path (e.g., Variable → Debug → If Statement). Branch them off as a separate connection.
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:
Send test incoming webhooks with controlled data (use your webhook integration method)
Immediately call your tracking number from the same phone number
Verify the call routed as expected
Repeat with different data values to test each branch
Test matrix example:
Strategy 3: Trace Call Flow Execution
Best for: Understanding which path the call took
How:
Use Tagging nodes to mark which path was taken
Example
After test calls, filter by tags in Reporting
Verify the correct path was taken
Strategy 4: Test Edge Cases
Don't just test the happy path—test these scenarios:
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:
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
Check campaign status:
Ensure campaign Active checkbox is checked
Inactive campaigns don't process calls
Check tracking number assignment:
Verify the test call is going to a number assigned to this campaign
Navigate to Numbers and check campaign association
Check Call Flow saved state:
Open the Call Flow in the builder
Ensure there are no unsaved changes
Re-save if necessary
Check for disconnected nodes:
Every node (except end nodes) should have an outgoing connection
No orphaned nodes that can't be reached from Start
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
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
Check for data type mismatches:
[CALLER_STATE] might be "CA" (string) not just CA
Use exact string matches: "CA" with quotes if needed
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
Check for whitespace:
"vip " (with trailing space) ≠ "vip"
Use .trim() in JavaScript node if needed:
tags.CUSTOMER_TIER = tags.CUSTOMER_TIER.trim();
Check operator:
Use == for equality, not = (assignment)
Use != for not equal
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:
Check incoming webhook timing:
Webhook must be sent before caller dials
If webhook arrives after call starts, Wait node may timeout
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
Check caller_id format:
Webhook caller_id must match exactly: +15551234567
Common mistakes: missing +, different format
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
Increase Wait timeout:
Edit the Wait For Pre Call Webhooks node
Try 8-10 seconds instead of 5
Check webhook lifespan:
Default: 3600 seconds (1 hour)
If webhook was sent more than 1 hour ago, it's expired
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:
Check Debug node placement strategy:
Adjacent to Variable nodes — Verify data was read (not inline in main flow)
Adjacent to If Statement nodes — See what's being compared (connect as a separate branch, not between nodes)
After HTTP Request nodes — Verify API response
After JavaScript nodes — Confirm calculations are correct
⚠️ Important: Debug nodes should NOT be placed in the direct flow path between nodes (e.g., Variable → Debug → If Statement). Instead, branch them off to the side so they don't interrupt your main routing logic.
Trace the execution path with tags:
Then filter call logs by tags to see which path was taken.
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
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:
Verify tags are being set:
Add Debug node after Tagging node
Log all tags to confirm they're applied
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.
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;
Check outgoing webhook configuration:
Navigate to Webhooks → Outgoing
Verify webhook is attached to the campaign
Check webhook payload template includes tags
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:
Test endpoint separately (optional for reference):
Verify endpoint is reachable and returns data using your preferred testing method
Check timeout:
Endpoint must respond within 10 seconds
Optimize your API or cache data if possible
Check authentication:
Verify headers are correct: {"Authorization": "Bearer YOUR_KEY"}
Check for typos in API keys
Check URL format:
Both HTTP and HTTPS are supported
Variable interpolation: [CALLER_ID] should be replaced automatically
Check response handling:
Use Debug node to log the response variable
Response may contain error messages (e.g., {"error": "Invalid API key"})
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:
Test JavaScript separately:
Copy your JS code to a browser console or Node.js REPL
Test with sample data
Fix syntax errors
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
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
Keep it simple:
Avoid complex logic that's hard to debug
Break into multiple nodes if needed
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:
Check for infinite loops:
Ensure every path leads to a routing decision (Routing Plan or Hangup)
No circular connections between nodes
Check Wait node timeout:
If Wait For Pre Call Webhooks timeout is too high (> 10 seconds), calls may feel stuck
Reduce to 5 seconds
Check HTTP Request timeouts:
If external API is slow (> 10 seconds), request will timeout
Optimize API or handle timeouts gracefully
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:
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
Check Tagging node configuration:
Action should be Add (not Remove)
Tags field should not be empty
Check call completion:
Tags may not appear until call fully completes
Refresh the call log view
Check for Remove tags nodes:
If a downstream Tagging node removes the tag, it won't appear
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
Common Ringba Tag Naming Issues
Problem: Ringba allows freeform tag names → inconsistency → broken logic
Solution in Moja:
Establish tag naming conventions before building flows (see "Before You Build" section)
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 → 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 → 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
Run platforms in parallel for 1-2 weeks — Compare call routing accuracy
Start with one campaign — Don't migrate everything at once
Document your old platform's logic — Screenshot flows, export configs
Create a Tag Dictionary before migrating — Prevent naming inconsistency
Test edge cases — What happens when webhook is missing? API is down?
Train your team — Moja's visual builder is different from form-based routing
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:
Route to a Routing Plan (using Routing Plan node)
Routing Plan selects a Target
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
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:
Email: [email protected]
In-app chat: Click the support icon in the bottom right
Appendix: Quick Reference
Node Type Summary Table
Common Tag Reference
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)
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