Automations
A visual flow builder: a trigger event connected to action steps across your integrations.
An automation is a flow on a canvas: one trigger (a reply arrives, a meeting is booked) runs one or more actions (post to Slack, push to your CRM, tag a contact), with IF conditions in between to branch. No code, though an advanced mode accepts a free-form condition.
The builder
Open the Automations tab. Each automation has a name, an on/off toggle, and a canvas. Off never fires, even from a campaign step.
- The Trigger node sits at the top and cannot be deleted.
- Drag a node's dot onto another node to connect, or onto empty canvas to pick what comes next (AI step, AI switch, condition, action, or Stop) already connected.
- Click a node to edit it. Click a connection and press
Deleteto remove it. - Add places a node; Tidy up auto-arranges.
- Card positions save on their own, and the canvas is collaborative: teammates' cursors, live card movement, their selections outlined in their color. Press
/to chat. See Collaboration.
Save before it runs
Triggers, conditions, and actions only take effect after Save (card positions are the exception). Test saves first, then dry-runs.
Triggers
| Trigger | Fires when |
|---|---|
| Reply received | A contact replies to a campaign email |
| Meeting booked / rescheduled / canceled | A Calendly or Cal.com meeting changes |
| Email bounced | A campaign email bounces |
| Unsubscribed | A contact unsubscribes |
| Warmup health changed | A mailbox's warmup health state changes |
| Deliverability complaint | A spam complaint is recorded |
| Inbound webhook | An external system POSTs to this automation's unique URL |
| Campaign action | A campaign sequence reaches a "Run automation" step |
Each trigger carries its own event data (a reply carries contact email, reply intent, classifier confidence). Those are the values your conditions test and your action text inserts. Full per-trigger variable list: Personalization & expressions.
Campaign action never fires by itself; it runs only from a campaign step, still evaluating your conditions.
Inbound webhook
Pick the trigger and save, and the editor shows a unique URL. Anything that can send HTTP POSTs JSON to it.
- The JSON body becomes the payload:
{"email":"[email protected]"}gives you{{.email}}. A non-object body is exposed as{{.body}}. - The URL is the credential. It carries a high-entropy token, so treat it as a secret, send it over HTTPS, and rotate it (switch the trigger away and back) if it leaks.
- The request returns immediately and the flow runs in the background, so a slow action never blocks the caller.
- Keys starting with an underscore are stripped; the body is capped at 1 MB.
IF conditions
An IF node splits into a yes path (right dot) and a no path (bottom dot). Only the matching branch runs; an unconnected branch just ends, or drag it to a Stop node.
Pick a field, an operator, and a value. Fields depend on the trigger, so they are always meaningful.
| Field type | Operators |
|---|---|
| Text | is, is not, contains, is present |
| Number | ≥, ≤, is, is present |
| Choice (enum) | is, is not, is present |
Three special conditions work with any trigger:
- Ask AI (yes/no): a plain-language question about the event ("Is this reply asking about pricing?") picks the path. Templated, so you can drop in
{{.field}}. One credit per evaluation; if the model can't answer or you are out of credits, the no path runs and History shows why. See AI steps in automations. - Random split: a set percentage (
1%to99%) down the yes path, deterministic per event so re-deliveries never flip it. - Advanced expression: a free-form condition, taken when truthy.
and (gtf .confidence 0.8) (eq .intent "positive")Comparisons (eq, gt, lt), numeric coercion (gtf, ltf, add, sub, mul, div), logic (and, or, not), and text helpers (contains, lower) are available. A broken or empty expression is false, so a malformed condition never silently passes, and it is validated on save.
Actions
Each action node picks a Run target (an integration, or Warmbly built-in) and an Action.
| Integration action | What it does |
|---|---|
| Send a Slack / Discord message | Posts to a channel (Slack needs #channel) |
| Send a webhook | HTTP request to a URL you provide |
| Create / update HubSpot contact, Pipedrive person, Salesforce contact, Close lead | Upserts the record |
Slack, Discord, and webhook actions take an optional message template. Slack and Discord arrive as a branded card in Warmbly's accent color with contact and subject fields, not a plain line.
| Built-in action | What it does |
|---|---|
| Add / remove a tag | Adds or removes a contact category |
| Label the email | Applies inbox labels to the replied-on conversation |
| Create a task | Assigned to the workspace owner |
| Create a deal | In a chosen pipeline and stage |
| Move the deal stage | Moves the contact's most recent open deal in that pipeline |
| Unsubscribe the contact | Only when the event carries a campaign |
| Set variables | Computes named values from templates for later steps to reuse as {{.name}} |
| Fire event | Publishes a CUSTOM_EVENT to the realtime gateway |
| AI step / AI switch | An agent or single-shot AI node, and AI-decided routing. See AI steps |
Three constraints worth knowing: Move the deal stage does nothing if the contact has no open deal in that pipeline, Unsubscribe needs an event carrying a campaign, and Label the email works only on a Reply received automation, since it needs a thread to label.
Fire event is the inverse of the inbound webhook: your app subscribes over the websocket with an API key holding REALTIME_SUBSCRIBE and receives { name, payload }, so you get events without hosting a public URL. See Realtime events.
The on-error branch
Every action has a red dot on its right edge. Drag from it to handle failures (a rejected Slack message, a CRM timeout).
- With an on-error branch connected, a failure follows it and the run is not marked failed, like a try/catch. The normal path is skipped.
- Without one, the failure is recorded in History and the run is marked errored, but the flow continues down the normal path best-effort, so one bad step never blocks the rest.
- A dry run never fails an action, so Test always shows the normal path.
Templating
Text fields (messages, webhook URLs, deal names, task titles) accept trigger variables as {{.key}}, with clickable chips in the builder to insert them:
New reply from {{.contact_email}} on {{.campaign_name}}Unknown tokens render empty rather than failing.
Testing and history
Test saves the canvas and dry-runs it against sample data: no messages sent, no records changed. It shows the exact path taken and a preview of each action. AI steps are the exception, running for real so you see actual output, which spends one credit per AI step.
History shows recent real runs: success or error, start time, and per-step results (error text on failure, a short summary on success). It updates live as the automation fires.
Best-effort by design
Runs are best-effort and bounded against loops, so one failing step won't block the rest. The run is still recorded as errored so you can see what went wrong.