Skip to main content

Overview

Steps execute in order from top to bottom. Each step reads from and writes to a shared context object that carries data through the pipeline. You can add up to 20 steps per automation. To add a step, click + Add step in the pipeline builder and select the step type.

Step Types

Transform

Reshape and map data from one format to another. Transforms are typically used to:
  • Map incoming webhook fields to contact fields before an Upsert Contact step
  • Map call results to CRM column identifiers before a Destination step
Configuration: Add one or more field mappings. Each mapping has: Example — mapping webhook fields for a contact: Transform results are written to mapped.* in the context and are available to all subsequent steps.

Code

Reshape data with a small piece of Python when field mappings are not enough — splitting a name, building a list, tidying a date, or deciding what to write based on several fields at once. Choose Code from the transform dropdown. The Code step is only available to team members with the manage settings permission. Configuration: Your code goes inside one top-level def run():. Return True to carry on, False to stop the branch. Reading and writing the context: Doing work: set_call_result(data) — pass a dictionary of your own fields and they are stored on the call, in a field of their own, separate from the AI’s summary and structured data. Use it when the automation, not the model, is the authority on how a call turned out. The values are returned with the call by the API, so reporting can read them directly instead of picking apart a summary. The step must be running on a call — that is, the automation’s trigger is Call Completed or Call Scheduled. Fields are merged in, so a second call adds to what is there rather than replacing everything.
integration_request(...) — read data from a connected integration using the credentials and address already stored against it. You give it a path, not a full web address; an absolute URL is refused, so the stored credentials can never be pointed at another site. It is read-only and paginated, so large collections come back a page at a time. An error from the far end comes back as data rather than stopping the step, which lets a loop set one record aside and keep going. enqueue_automation(automation_id, payload) — start another automation and hand it a payload, which arrives there as trigger.*. Use it to give each record its own run, so one bad record fails and retries on its own instead of taking the whole batch down with it. You cannot enqueue the automation that is running, an automation in another team, or a disabled one. Writing the code:
  • One top-level def run():. No nested definitions, no classes, no decorators, no lambdas.
  • No imports.
  • Indent with spaces, not tabs. The editor converts pasted tabs for you and the Tab key inserts four spaces.
  • Errors show as you type, each with its line number — for example Line 12 — Tabs are not allowed, indent with spaces.
  • There is no attribute access in the sandbox, so build lists by joining them together rather than with .append.
Limits: Going over the 2 MB limit fails that one function call, not the whole run — so page through large reads rather than asking for everything at once. Going over the call or change limits stops the run.

LLM

Ask an AI model to read some data and give you a structured answer — classifying a call, tidying free text, or pulling a few fields out of a long note. Choose LLM from the transform dropdown. Configuration: Placeholders in the prompt are filled in from the run’s live context before the model sees it — call.*, contact.* and trigger.* all work — so the prompt can quote the actual transcript, the contact’s details, or the incoming webhook payload rather than being written in the abstract. Each field in the Output Schema needs a description. It is what tells the model what you actually want, so write it as though you were explaining the field to a new team member.

Upsert Contact

Create a new contact in Voxworks or update an existing one. The step matches on either phone number or email to determine whether to create or update. Configuration: After this step runs, contact.* is available in the context for all subsequent steps. Example: Match on phone_number, with mappings: If a contact with that phone number already exists, their name fields are updated. If not, a new contact is created.

Upsert Object

Create or update a typed data object and optionally link it to the contact. Objects store CRM identifiers and external data that your automation needs to reference later — for example, a Monday.com item ID or a HubSpot deal ID. Configuration: After this step runs, the object’s data is available at objects.{type_name}.*. Merge or replace. By default the fields you supply are merged into the record, leaving anything you did not mention untouched. From a Code step you can ask for replace instead, which writes exactly the fields you supply and drops the rest — use it when the incoming data is the whole truth about that record. The name field updates the record’s display name. Example: Object type: monday_item, match on CRM_ID = trigger.crm_id, link to contact: yes.

Create Call

Trigger an outbound call using a specific Script. This step creates a call record and schedules it for immediate execution (or at a specified time). Configuration: Important: Pipeline split. Any steps placed after Create Call become post-call steps. They do not execute until the call finishes and the AI has generated the summary and structured data. The pipeline builder shows a visual divider: “after call completes”. After the call completes, call.* is populated with the full call record including summary, transcript, structured data, and objectives.

Destination

Push data to an external system. There are four destination types:

Integration Sync

Sync data to a connected CRM (e.g., Monday.com). Uses field mappings to map call outputs and object data to integration-specific column identifiers. The target field names correspond to column IDs in your CRM. You’ll find these in your CRM’s column settings.

Email

Send an email to the contact, your team members, or a specific address. Email subjects and bodies support {{variable}} interpolation. Placeholders in the subject and body are filled from the call, the contact and your team, so a post-call email can carry the summary, the collected fields and the contact’s details without any extra mapping. Values that run to several lines keep their line breaks in the branded HTML email, and any characters that would otherwise break the layout are handled for you. Dates and times are shown in your account’s timezone rather than UTC. A placeholder that has no value comes out as an empty string.

SMS

Send a text message to the contact or to team members.

Webhook

Send an HTTP request to any URL. Useful for integrating with systems that don’t have a native integration.

Condition

Branch the pipeline based on a field value. If the condition is true, the on_true steps run. If false, the optional on_false steps run (or the condition is skipped). Configuration: Example — notify team on low rating:
  • Field: call.structured_data.rating
  • Operator: lt
  • Value: 3
  • On true: SMS to team — “Low rating call: {{contact.full_name}}”
  • On false: (skip)
Conditions can be nested up to 2 levels deep.

Source

Fetch data from an external API or connected integration. The response is written to source.* in the context. Configuration (HTTP): Configuration (Integration Fetch): The fetched data is resolved at runtime using object identifiers from the context (e.g., the contact’s linked CRM item ID).

Limits