SendSetsDocs

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 Delete to 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

TriggerFires when
Reply receivedA contact replies to a campaign email
Contact createdA new contact is added by hand, through the API, by a form or by an automation (bulk file imports and sheet syncs stay quiet, see below)
Form submittedA hosted form receives a submission
Meeting booked / rescheduled / canceledA Calendly or Cal.com meeting changes
Email bouncedA campaign email bounces
UnsubscribedA contact unsubscribes
Warmup health changedA mailbox's warmup health state changes
Deliverability complaintA spam complaint is recorded
Inbound webhookAn external system POSTs to this automation's unique URL
Campaign actionA 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.

Contact created fires once per new contact, with the contact's fields and its first-touch source, so a condition can branch on source (a form, the API, an automation) or on any field. A contact that already existed and was only updated does not fire it. Bulk arrivals are deliberately silent: a file import or a Google Sheets sync of ten thousand rows would otherwise run ten thousand flows and flood every webhook, so those paths never raise it, and neither does a single API request adding more than 100 contacts.

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":"a@b.com"} 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 typeOperators
Textis, 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% to 99%) 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 SendSets built-in) and an Action.

Integration actionWhat it does
Send a Slack / Discord messagePosts to a channel (Slack needs #channel)
Send a webhookHTTP request to a URL you provide
Create / update HubSpot contact, Pipedrive person, Salesforce contact, Close leadUpserts the record

Slack, Discord, and webhook actions take an optional message template. Slack and Discord arrive as a branded card in SendSets's accent color with contact and subject fields, not a plain line.

Built-in actionWhat it does
Create or update contactMakes a contact from the event's fields, or enriches the one with that email, then tags it and enrols it in a campaign. See Lead intake
Add to campaignEnrols the event's contact in a campaign; a finished campaign restarts through the usual launch checks (a refusal is noted in its activity log and the lead waits), one waiting for leads picks up straight away

Saving an automation with either of these actions turns on the picked campaign's Keep running for new leads setting, so the campaign waits for the next run instead of finishing between them. | 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.

Add to campaign and every other contact action need a contact to act on: the event must carry contact_id or contact_email, which every SendSets trigger does. On an inbound webhook, put Create or update contact first and the contact it writes becomes the event's contact for the steps after it.

Lead intake

Any system that can send an HTTP request can create contacts in SendSets without an API client: an Inbound webhook trigger followed by a Create or update contact action.

  1. Pick the Inbound webhook trigger, save, and copy the URL.
  2. Add Create or update contact. Each field is a template over the JSON the caller sends: an email of {{.email}}, a first name of {{.first_name}}, a custom field team_size set to {{.answers.team_size}}. Pick the tags and the campaign the lead lands in.
  3. Point the sender at the URL. Zapier's Webhooks by Zapier, Make's HTTP module, n8n's HTTP Request node, a Typeform or Tally webhook, or your own code.

The action matches an existing contact by email, so re-sending the same lead updates it instead of duplicating it. A blank rendered value never erases a field the contact already has. If the contact already exists decides whether an existing contact is enriched and enrolled (the default) or left alone. New contacts carry automation as their first-touch source, with the automation's name as the detail.

Facebook, Instagram, LinkedIn and TikTok lead forms

Meta, LinkedIn and TikTok deliver lead-form submissions to Zapier, Make and n8n in real time. Either route them through the SendSets app there with its Create or Update Contact and Add to Campaign actions, or send them to an inbound webhook automation as above. Both end the same way: a contact with the ad's answers as custom fields, tagged and enrolled in the campaign that follows up from your mailbox. See Zapier, Make and n8n.

Lead-form campaigns produce leads in bursts, and the follow-up still goes out from your mailboxes under their daily caps and spacing. A day of two hundred leads on one mailbox capped at fifty is a four-day send; the campaign's recipients-versus-capacity estimate shows it. Add mailboxes rather than raising caps.

A flow that creates a contact can be the reason a Contact created flow runs. That is allowed, and bounded: an event raised by an automation's own action carries how many hops led to it, and after five hops no further automation runs, so two flows cannot feed each other forever.

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.

On this page