Guides
A step-by-step guide to adding Code nodes in Thoughtly automations and mid-call agent flows — with real examples for phone normalization, lead scoring, area code routing, and custom business rules.
Last updated
Thoughtly’s built-in integrations, branching conditions, and AI steps handle most automation logic out of the box. But every lead-conversion operation eventually hits an edge case that no pre-built step covers: a phone number that needs reformatting before the dialer fires, a lead score that depends on three fields and a lookup table, or a routing decision that changes by area code.
That is where Code nodes come in. The Code integration lets you run sandboxed JavaScript inside any Thoughtly automation or mid-call agent action — no external serverless environment, no DevOps overhead. You write a short script, it receives data from prior steps, and it returns a structured result that downstream steps can use immediately.
This guide walks through exactly how to set up Code nodes in both automation workflows and live agent call flows. You will learn the inputs/outputs model, sandbox constraints, and five practical patterns that insurance, mortgage, home services, and other high-volume lead teams use daily.
A Code node is a step or action that executes a small JavaScript snippet inside Thoughtly’s restricted sandbox. It exists for last-mile customization — the cases where built-in steps almost solve the problem, but you need to transform, validate, format, or calculate something that no pre-built integration covers.
Code nodes appear in two places inside Thoughtly:
In Tools → Automations, you can add a Code step anywhere in an automation flow — after a trigger, between CRMCRMThe system of record for leads, contacts, deals, and activity. Thoughtly reads from and writes to your CRM continuously. lookups, before an outbound call, or after a call completes. The step receives outputs from the trigger and any prior steps through the data picker, runs your script, and passes the return value to the next step in the flow.
Common automation uses: normalize phone numbers before dialing, calculate a lead score from CRM fields, choose a local-presence number based on area code, reshape a webhook payload into the format a downstream integration expects, or apply time-of-day routing logic.
In the Agent Builder, open a Speak node, go to Actions, and click + Add new action. Select Code from the integration list. The script runs during a live call — the agent speaks, the Code action fires, variables update from the result, and rule-based outcomes evaluate immediately. Shorter timeout limits apply during live calls to protect call quality.
Common mid-call uses: validate a caller-provided email address on the spot, calculate a quote estimate from variables collected during the conversation, or format a confirmation message that the agent reads back to the caller.
The Code sandbox enforces several limits to protect platform stability and call quality:
| Constraint | What it means |
|---|---|
| Execution timeout | Scripts that run too long are killed. During live calls, the limit is shorter. |
| Memory cap | Large data structures or string concatenation can hit this limit. |
| Code size cap | Keeps snippets small and reviewable. |
| No network calls | fetch, XMLHttpRequest, and WebSocket are blocked. Use a Webhook step for external APIs. |
| No Node.js built-ins | require, process, and dynamic imports are blocked. |
| No code generation | eval, new Function, and dynamic import() are blocked. |
| No infinite loops | The sandbox detects and terminates obvious infinite loops. |
If you need to call an external API, use a Webhook or integration step before or after the Code step and pass the data through the data picker.
Open Tools → Automations and either create a new automation or open an existing one. Make sure the automation already has a trigger configured — a Webhook, an On Call Completed trigger, a CRM event from HubSpot or Salesforce, a Recurring Schedule, or any integration trigger.
Click the canvas to add a new step. In the step picker, select Code. The step opens with three tabs: Account/Configure, Output, and Next Step.
In the Configure tab you will see a code editor field — this is where your JavaScript goes. There is also an optional Timeout field. Leave it at the default unless you have a specific reason to shorten the execution window.
Give your Code step a clear Display Name (for example, “Normalize Phone” or “Score Lead”). This label appears in the step list and makes your automation easier to scan when you have multiple Code steps in the same flow.
Your Code step receives data from prior nodes through an inputs object. Each key in inputs corresponds to a previous node ID — the trigger, a CRM step, another Code step, and so on.
Use the data picker (the lightning-bolt icon in the code editor) to insert references. The picker shows the trigger and every prior step as a tree. Click a field to insert its reference path into your code:
// Data from the trigger
const phone = inputs['trigger'].phone
// Data from a prior CRM step
const firstName = inputs['create_contact'].first_name
// Data from a scheduling step
const slot = inputs['check_availability'].selected_timeYou do not need to import anything or configure an external connection. The data picker handles the wiring automatically.
Every Code step must return a JSON-serializable value. That return value becomes the step output and is immediately available to all downstream steps through the data picker.
Here is a minimal example that normalizes a phone number to E.164 format before an outbound call:
const phone = inputs['trigger'].phone
const digits = phone.replace(/\D/g, '')
return {
e164: digits.startsWith('1') ? `+${digits}` : `+1${digits}`,
}Key rules for the code you write:
After your Code step runs, its return value is available in the data picker for every subsequent step. Click the lightning-bolt icon in any downstream step’s configuration, select the Code step from the list, and pick the field you need.
For example, if your Code step returned an E.164-formatted phone number, the downstream Call Phone Number step references it as:
{{ steps.normalize_phone.e164 }}You can also use Code step outputs in Conditions (If/Else, Switch, Filter) for branching. This is one of the most powerful patterns: compute a value in Code, then branch on it deterministically. For instance, a Code step that returns a lead score can feed an If/Else condition that routes qualified leads to an outbound call and unqualified leads to a nurture email.
Only switch the automation from Draft to Live after you have verified the output schema and confirmed that downstream steps are mapping the correct fields.
Code nodes are not limited to pre-call or post-call automations. You can run JavaScript during a live phone call as a mid-call agent action. Here is how:
When a Speak node has actions attached, the flow changes: the agent speaks, actions run (with interruptions disabled by default), variables update from action results, outcomes evaluate, and the next node executes — all without waiting for another caller reply. Use rule-based outcomes that check the Code action’s output fields directly. Prompt-based outcomes cannot automatically “see” internal values unless you surface them in the spoken conversation.
If the Code action takes more than an instant, add a short spoken phrase before it fires — something like “One moment while I check that for you” — so the caller does not hear dead air. Use an uninterrupted message for this so the agent is not cut off mid-sentence.
The following five examples cover the most common Code node patterns for teams running high-volume inbound and outbound lead operations.
Leads from web forms, CRMs, and third-party aggregators arrive in every format imaginable: (555) 123-4567, 555.123.4567, +15551234567, 5551234567. The Call Phone Number step expects a clean E.164 number. A Code step between the trigger and the call step strips formatting and prepends the country code:
const phone = inputs['trigger'].phone
const digits = phone.replace(/\D/g, '')
return {
e164: digits.startsWith('1') ? `+${digits}` : `+1${digits}`,
}The downstream Call Phone Number step maps its phone number input to the Code step’s e164 output field. No more failed dials from inconsistent formatting.
After a call completes or a form submission fires a webhook, you often need a numeric score to decide whether the lead gets a same-day callback, a nurture sequence, or a warm transfer. A Code step can compute a weighted score from fields your agent or CRM step captured:
const lead = inputs['create_or_update_contact']
const score =
(lead.budget >= 50000 ? 40 : 20) +
(lead.timeline === 'immediate' ? 30 : 10) +
(lead.company_size >= 100 ? 30 : 10)
return {
score,
qualified: score >= 70,
}The downstream If/Else condition checks qualified == true to route the lead to an outbound call or a slower nurture path. Adjust the weights and thresholds to match your team’s qualification criteria — for insurance leads you might weight urgency and coverage type; for mortgage leads, loan amount and pre-approval status.
Outbound answer rates improve significantly when the caller ID matches the prospect’s area code. A Code step can extract the area code and map it to the right local-presence number in your Thoughtly phone number inventory:
const digits = inputs['trigger'].phone.replace(/\D/g, '')
const areaCode = digits.length === 11
? digits.slice(1, 4)
: digits.slice(0, 3)
const region = {
'212': 'new_york',
'646': 'new_york',
'310': 'los_angeles',
'415': 'san_francisco',
'713': 'houston',
'305': 'miami',
}[areaCode] || 'default'
return { areaCode, region }Pair this with a Switch condition downstream that branches by region and assigns the correct Thoughtly phone number for each outbound call.
When your agent collects an email during a live call, a Code action can validate the format before the conversation continues. If the format is invalid, a rule-based outcome loops back to ask the caller to repeat the address:
const email = inputs['trigger'].email
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
return { email, valid }The Speak node’s outcomes check valid == true to proceed, or loop back to the same node with a message like “I didn’t catch a valid email address — could you spell that out for me?” Limit retries to avoid frustrating the caller (two or three attempts, then offer to skip and follow up by phone).
After your agent collects details and books an appointment, a Code step can assemble a confirmation message that a Send SMS step delivers immediately:
const firstName = inputs['create_contact'].first_name
const appointmentDate = inputs['check_availability'].selected_time
return {
message: `Hi ${firstName}, your appointment is confirmed for ${appointmentDate}. Reply STOP to cancel.`,
}Map the Code step’s message output to the Send SMS step’s body field. This keeps the message logic in one place and makes it easy to update the format without editing the SMS step directly.
return 'qualified' gives downstream steps nothing to branch on. Return an object with named fields: return { qualified: true, score: 85 }.Once your Code-enhanced automations or agent flows are live, track these metrics to confirm they are working as intended:
The Code integration is available in Thoughtly automations and agent actions. Check your current plan’s feature access in your Thoughtly account settings, or contact Thoughtly support to confirm Code node availability for your tier.
No. The sandbox blocks all network access, including fetch, XMLHttpRequest, and WebSocket. If you need external data, use a Webhook step before or after the Code step and pass the result through the data picker.
The automation run fails at that step. The error is logged in the automation run history. If you have downstream steps that depend on the Code output, they will not execute. Design your script defensively — check for null or undefined inputs before operating on them, and return a predictable error object that downstream Conditions can handle gracefully.
Thoughtly enforces execution timeout limits to prevent slow scripts from blocking workflows or degrading call quality. During live calls, the timeout is shorter than in post-call automations. Keep your scripts focused and avoid unnecessary computation. If your logic requires significant processing, consider breaking it into multiple Code steps or offloading heavy work to an external service via a Webhook step.
Yes. You can place a Code step inside a Loop step to transform, score, or validate each item in an array. The Code step receives the current loop item through the data picker. Be mindful that each loop iteration counts as a separate automation step for credit/usage purposes.