# Automations

> Run steps when a lead replies, gets a label, clicks, bounces or finishes a sequence: notify the team, post to Slack, label, move to a list or campaign, wait and check again.

Canonical: https://ballast.to/docs/automations
Last updated: 2026-11-09

An automation runs steps for you when something happens to a lead or a campaign: a reply, a label, a click, a bounce, a finished sequence. You build it on a canvas from a trigger, actions, waits and yes/no conditions. Every plan has them.

## Build one

1. Open Automations under Outreach and click New automation. Start from a recipe, or pick a trigger and start empty.
2. Click the trigger card to narrow it: only some campaigns, and for a labeled reply, only some labels.
3. Press + under a card, or on a line between two cards, to add a step. Click a card to set it up in the side panel. A card with an amber dot still needs something, and to fix at the top lists every one.
4. Switch it On. It runs for every matching event from then on; nothing that happened before runs.

Changes save by themselves a moment after you stop typing (`Cmd+S` saves at once). While an automation is on, a change that leaves a step unset isn't saved until it's set, so a live flow never runs half-built. Owners and admins build automations; every member can see them and their history.

## Triggers

| Trigger | Runs when |
| --- | --- |
| Reply received | A person answered a campaign email. Out-of-office and automatic replies don't count. |
| Reply labeled | A reply got a label, from AI, a rule, a teammate or the API. |
| Link clicked | A lead clicked a link in a campaign email. Needs click tracking on the campaign. |
| Sequence finished | A lead got the last email of a campaign's sequence. |
| Email bounced | An email to a lead bounced hard. |
| Lead unsubscribed | A lead unsubscribed by link, one-click header or reply. |
| Marked as spam | A lead's email provider reported a campaign email as spam (Yahoo, Outlook and others; Gmail doesn't). |
| Lead won | A lead was marked won, by a teammate, the API or an automation. |
| Campaign paused | A campaign stopped sending: a breaker, its end date, a label or a teammate paused it. |
| Inbound webhook | Another tool posts JSON to this automation's own URL: a Typeform or Tally form, Zapier, Make, n8n or your own code. Each field becomes {{data.name}}. |
| Lead created | A lead was added by a teammate, the API, an automation or a Clay push. File imports and lead source pulls stay quiet. |
| Email sent | A campaign email went out. Narrow it to some steps, like the second email. |
| Mailbox needs attention | A mailbox stopped sending: its login failed, a breaker quarantined it, it's cooling off or its inbox can't be read. |

Each run belongs to the lead (and campaign) of its event. Campaign paused has a campaign and no lead, so lead steps are left out there. An automation runs once per event: two labels on the same reply are two runs.

## Actions

| Action | What it does |
| --- | --- |
| Notify the team | A notification in the bell for every member, emailed by their digest settings. |
| Post to Slack | A message in the channel connected under Integrations. |
| Send to a webhook | A signed POST to one of the workspace's webhook endpoints, retried like any delivery. |
| Add a label to the lead | Runs the label's own triggers too. |
| Remove a label from the lead | Takes the label off the lead, in every campaign. |
| Label the reply | Sets the reply's label in the Unibox, like choosing it by hand. |
| Add to a list | Adds the lead to a list. |
| Remove from a list | Takes the lead out of a list. |
| Add to a campaign | Starts another sequence, with the same checks as Add leads: suppressed, bounced, unsubscribed and invalid leads are skipped. |
| Stop the sequence | Ends the lead's queued emails in this campaign or in every campaign. The address stays usable. |
| Pause the campaign | Pauses the campaign the event came from. |
| Mark as won | Sets the lead won, counted in the won rate. |
| Suppress | Adds the address (or its whole domain) to the suppression list. No campaign emails it again. |
| Add a note | An internal note on the lead, never emailed. |
| Set a custom field | Writes a value into one of the lead's custom fields. |
| Post to Discord | A message in the Discord channel connected under Integrations. |
| HubSpot: create or update the contact | Finds the contact by email or creates it, fills the fields HubSpot has empty and can add a note. |
| HubSpot: create a deal | Creates a deal in the pipeline and stage you pick, on the lead's contact (created if missing). |
| Pipedrive: create or update the person | Finds the person by email or creates them with their organization, and can add a note. |
| Pipedrive: create a deal | Creates a deal in the stage you pick, on the lead's person (created if missing). |
| Send a row to Clay | Posts the lead, the reply and the campaign as one row to a Clay table's webhook. |
| AI step | Writes text, sorts into a category or pulls out fields. Later steps use the result as {{ai.name}}. |
| Create or update lead | Makes a lead from the event's fields, or fills in the one with that email, then can add it to a list, a label and a campaign. Later steps act on it. |

- Add to a campaign follows the same rules as adding leads by hand and checks the plan's active contacts. A lead it skips (suppressed, bounced, already in) shows as skipped with the reason, and the flow carries on.
- Post to Slack uses the channel connected in [Integrations](https://ballast.to/docs/integrations#slack). Send to a webhook uses one of your [webhook endpoints](https://ballast.to/docs/webhooks): signed, retried and in its delivery log, with the type `automation.step`.
- Notify the team lands in every member's bell under Automations and in their email digest, as each person's [notification settings](https://ballast.to/docs/team#notifications) say.

### Text with variables

Titles, messages, notes and field values take variables like `{{lead.first_name}}`, `{{campaign.name}}` or `{{reply.text}}`, with a fallback after a bar: `{{lead.first_name|there}}`. The side panel shows the ones the trigger has as buttons. Custom fields are `{{custom.deal_stage}}`.

## HubSpot, Pipedrive, Discord and Clay

Connect HubSpot (a service key), Pipedrive (your API token) or a Discord channel in [Integrations](https://ballast.to/docs/integrations#automation-apps), then add their steps. Ballast writes only when a step runs: nothing syncs in the background.

- HubSpot: create or update the contact finds the contact by email or creates it. A contact that exists only gets the fields it has empty (name, company, job title, phone, website, country), so a step never overwrites what your team typed. An optional note lands on its timeline.
- Pipedrive: create or update the person finds the person by email or creates them, with their organization from the lead's company. A person who exists is left as is. An optional note is added to them.
- Create a deal (HubSpot or Pipedrive) does the same first, then opens a deal in the pipeline and stage you pick, read live from your CRM. Every run opens a new deal, so put it after a condition that runs once per lead, like a reply labeled Interested.
- Post to Discord posts the message to the connected channel. Mentions like @everyone are sent as plain text.
- Send a row to Clay posts one row to a Clay table's webhook URL (add a Webhook source to the table). The lead's fields, the campaign, the reply and earlier AI results become columns.

A CRM or Clay that is down or limiting requests is tried again up to 3 times. A key that stops working fails the step and marks the integration so you see it in Integrations.

## AI steps and Ask AI

An AI step does one of three things with the lead, the campaign, the reply and earlier AI results:

| Mode | What you get |
| --- | --- |
| Write text | Writes a short text from your instruction, like a summary of the reply or a first line for Slack. |
| Sort into a category | Picks one of the categories you list. An If / else after it sends each category its own way. |
| Pull out fields | Reads values out of the reply or the lead, like a phone number or a meeting date. |

The result is a variable later steps use: `{{ai.text}}` by default for writing, `{{ai.category}}` for sorting, and one per field you name for pulling out, like `{{ai.meeting_date}}`. An If / else can check it with AI step's result, which is how a category sends leads different ways.

Ask AI is a rule inside an If / else: write a yes or no question ("Does the reply ask for a call?") and pick whether Yes means the AI said yes or no. An answer that is neither fails the rule, and the history says so.

- They run on your own OpenAI or Gemini key when one is connected in [Integrations](https://ballast.to/docs/integrations#ai-provider). Otherwise on Ballast's keys, up to 1,000 AI calls a day per workspace, not counted against Write with AI.
- What a lead wrote is sent to the model as data, never as instructions: a reply that says "ignore your instructions" is read as text.
- Only steps you add send anything to an AI provider. AI labels on replies stay a separate switch in Settings.

## Leads from forms: Inbound webhook

The Inbound webhook trigger gives the automation its own URL. Anything that can send an HTTP POST can start it: a Typeform or Tally webhook, Zapier's Webhooks by Zapier, Make's HTTP module, n8n's HTTP Request node, or your own code. Facebook, LinkedIn and TikTok lead forms reach it through Zapier, Make or n8n.

- Send JSON (or a form). Each field becomes a variable: `{"email":"jane@acme.com","answers":{"team_size":12}}` gives `{{data.email}}` and `{{data.answers.team_size}}`. Keys starting with an underscore are dropped; the body can be up to 1 MB.
- The URL answers at once with 202; the flow runs in the background. The last request is kept, even while the automation is off, so the builder shows its fields and Test can run on it.
- The URL is the password: keep it out of public pages. Replace it from the trigger if it leaks; the old one stops working at once.

Add Create or update lead to turn the request into a lead: the email and each field are variables, a blank value never erases what the lead has, and the lead can go into a list, get a label and start a campaign. A lead that exists is matched by email, so posting the same person twice updates them. New leads count toward the plan's active contacts, go through the same suppression and verification checks as Add lead, and start Lead created automations. The steps after it act on that lead.

## Test a flow

Press Test in the builder. The flow is saved, then runs once on sample data: a made-up lead (Jane Doe at Example Co) or, for an inbound webhook, the last request the URL received or JSON you paste. Name a real lead instead to see which way your conditions go for that person. Nothing is sent or changed: each action says what it would do, waits are passed over, and conditions read the lead as it is now. The canvas marks the steps the test reached, with Would run, Yes, No, Skipped or Failed, and fades the rest.

AI steps and Ask AI run for real in a test, so you see the model's answer. They count toward the day's AI calls on Ballast's keys.

## When a step fails

By default a failed step stops the run, and History says why. Turn on If this step fails in an action's panel to give it an On error path: the canvas splits into Then and On error, and a failure goes down On error instead of stopping the run, like telling the team when HubSpot is down. The steps under Then don't run in that case.

Slack, Discord, HubSpot, Pipedrive and Clay are tried again up to 3 times over a few minutes when a failure looks temporary (a rate limit, an outage) before the step counts as failed.

## Waits

A wait holds the run for minutes, hours or days (up to 90 days), then the next step runs. Nothing is sent while it waits. The run history shows when it carries on.

## Conditions

An If / else splits the path into Yes and No. It checks the lead and the reply as they are when the run gets there, so a wait followed by a condition sees what changed in the meantime: a reply, a label, a won deal.

| Check | Compares |
| --- | --- |
| The reply's label | The label the reply has now, or none. |
| The reply's text | Words in the latest reply. |
| The reply is urgent | Whether AI labels marked the reply urgent (needs AI labels on). |
| Campaign | The campaign of the event. |
| Lead status | Active, replied, interested, won, bounced, unsubscribed or suppressed. |
| Lead has replied | Whether a person ever replied to any campaign (out-of-office doesn't count). |
| Lead has the label | Whether the lead has a label, in any campaign. |
| Lead is in the list | Whether the lead is in a list. |
| Lead field | Any lead field or custom field: is, contains, is empty, or at least / at most a number. |
| Lead's email provider | Google, Microsoft, Yahoo and the other email providers. |
| Random split | A share of runs, from 1% to 99%, for testing two paths. |
| AI step's result | What an earlier AI step wrote: is, contains, is empty, or a number. |
| Ask AI | A yes or no question the AI answers from the lead, the reply and earlier AI results. |
| Event field | A value the event carried: a field posted to an inbound webhook ({{data.team\_size}}), or the mailbox and its problem. |

Lead fields, AI results and event fields also compare as numbers with is at least and is at most ("1,200" and "50%" read as numbers). Several rules can go in one condition, matched all together or any one. A condition ends its path: the steps that follow go under Yes or No. Adding a condition above existing steps puts them under its Yes path.

## History and failures

- The History tab lists every run with the lead, the campaign, each step and what it did: done, skipped (with why), yes or no, waiting, failed. Runs are kept 90 days.
- A step that fails stops its run, and the run shows the reason, unless the step has an error path. Slack and network blips are tried again up to 3 times over a few minutes first.
- Turning an automation off, changing its trigger or deleting it stops its runs in progress. A run waiting at a step you removed stops too.
- An action can cause another event (marking a lead won starts automations on Lead won). That chain goes 3 automations deep at most, so two automations can't feed each other forever.

## Recipes

| Recipe | What it sets up |
| --- | --- |
| Interested reply: tell the team, stop the rest | When a reply is labeled positive, post it to Slack and end the lead's other sequences. |
| No reply: try another campaign | Three days after the last email, a lead who never replied starts a second sequence. |
| Clicked but quiet: nudge the team | Two days after a click, if the lead still hasn't replied, a teammate gets a note to follow up. |
| Won: move to Customers | A won lead goes into a customers list and out of every sequence. |
| Spam complaint: pause and look | A spam complaint pauses the campaign and tells the team, before it hurts the domain. |
| Urgent reply: Slack right away | A reply AI marks urgent goes to Slack within the minute. Needs AI labels on. |

Label triggers ([Labels](https://ballast.to/docs/leads#labels)) still work and run first. Use them for what a label always means, like suppressing on Unsubscribe; use an automation for anything that waits, checks or tells someone.

## Limits

- 100 automations per workspace and 60 steps each, on every plan.
- Waits up to 90 days each.
- Every automation is in the API and in MCP for your AI: `list_automations`, `get_automation_options`, `create_automation`, `update_automation`, `list_automation_runs` and more in the full toolset. See the [API reference](https://ballast.to/docs/api/automations) and [MCP](https://ballast.to/docs/mcp).

Ballast is cold email software with every feature on every plan. Connect unlimited mailboxes, send sequences at a pace your domains survive, and handle replies in one inbox, priced by the emails you send.
