Campaigns A visual builder for branching sequences

Draw the whole sequence,
branch by branch.

Email steps, waits, and branches that route every contact on what they actually do: opened, clicked, replied. Add A/B variants to any step, then send the whole thing through a pool of mailboxes in the recipient time zone.

The builder

The sequence is a map, not a list.

You lay the whole follow-up out on a canvas. Every email is a step, every gap is a wait you set in days, and a branch sends each contact down its own path the moment they open, click, or reply. This is the real builder, drawn out.

Add step Stop on reply
First touch Start
Email
Quick question, {{.FirstName}}
On reply
wait 2 days
if opened ≥ 1? instant
then · opened
Case study nudge
Email
Saw {{.Company}} shipped {{.Trigger}}
On reply
wait 3 days
Last call
Email
Should I close this out?
Ends here
else · no open · 4d
Move deal stage
Action
HubSpot · to Nurture
Ends here
4 steps · 1 branch · 1 action
Step types

Four kinds of node.

Everything on the canvas is one of four nodes. Drop them in, wire them up, and the sequence runs exactly as it reads.

Email step
sends copy

Sends one templated email. Holds the subject, the body, any A/B variants, and the wait before whatever comes next.

Branch
routes contacts

Splits the path on a signal: opened, clicked, replied, or a share at random. Then and else can each point anywhere.

Action
side effect

No email. Add a tag, create a task or deal, move a CRM stage, notify a channel, or unsubscribe the contact.

Stop
ends here

Closes the sequence for that contact. Reached on a break-up step, a met goal, or an else with nowhere left to go.

Branching & timing

Route on what they do.

A branch takes one signal and sends the contact down its own path. Reply, open, and click can fire instantly, the moment the event lands, instead of waiting for the next scheduled step. The rest evaluate at the step on a window you set.

  • Then and else can each point at any step, an action, or a stop.
  • Waits are set per step, from 0 to 60 days, counted in business days.
Signal
Timing
What it catches
replied
instant
The moment a reply lands. Usually pulls the contact off the rest of the sequence.
opened
instant
As the open returns, or after a set number of days if you would rather wait and decide.
clicked
instant
A click on any tracked link, instantly or inside a window you choose.
not opened
window
No open inside the window. The classic break-up trigger.
random %
window
Send a share of contacts down a different path. A/B at the level of the whole flow.
always
window
An unconditional else. Everyone who reaches it takes this path.
Templating

Real personalisation, with safe defaults.

Every step is a Go template, the same syntax used across the rest of Warmbly. Pull a field with a leading dot, give it a default so a blank never embarrasses you, and wrap an optional line in an if so it only shows when the field is set.

Available fields
{{.FirstName}}{{.Company}}{{.Role}}{{.Trigger}}{{.City}}
first touch · body
Hi {{.FirstName}},
Saw {{if .Trigger}}{{.Trigger}} at {{end}}{{ .Company | default "your team" }} and the {{.Role}} role lined up with what we tend to help land.
Worth a 20-minute look next week?
Dana
Renders for Maya, with no Trigger on file
Hi Maya,
Saw Northwind and the VP RevOps role lined up with what we tend to help land.
Worth a 20-minute look next week?
Dana

Her Trigger was blank, so the if skips the "at" clause and the sentence still reads clean.

A/B variants

Test the copy on any step.

Add variants to an email step and the volume splits by weight. Each contact is pinned to one variant by a stable split, so a retry never flips them, and we report which one earned the reply.

  • Weights from 1 to 100, the original always carries the rest.
  • Pause a losing variant without touching the rest of the step.
A/B variants
3 variants, split by weight
Original original
Quick question, {{.FirstName}}
60%
Variant B
{{.Company}} + a 20-minute look
25%
Variant C
worth closing the loop?
15%
Sender pool

One sequence, many mailboxes.

A sequence sends through a pool of mailboxes, not one. The scheduler round-robins contacts across the pool with an eye on each mailbox daily cap and current health band, so no single mailbox ever carries the campaign.

Mailbox
Role
Today
Cap
Headroom
ben@acme.com
AE
50
19
sara@acme.com
AE
50
22
mark@acme.com
BDR
50
16
kai@acme.com
BDR
50
24
Round-robin · health-aware · cap-aware Pool headroom: 81 sends today
Auto-stop rules

Six reasons a contact stops getting mail.

Suppression carries across every sequence in the workspace. A bounced or opted-out contact never gets picked back up by another sequence by accident.

Rule 01 stop
Replied

The reply pulls the contact off the sequence the instant it lands. Everyone else keeps going.

Rule 02 stop
Goal met

A booked meeting or a won deal closes the sequence for that contact automatically.

Rule 03 stop
Hard bounce

The recipient is suppressed across the whole workspace. Nothing else ever queues to them.

Rule 04 stop
Unsubscribed

One-click unsubscribe, RFC 8058. Suppression applies before the confirmation page loads.

Rule 05 stop
Reply: STOP

Opt-out phrases in a reply are caught and suppressed without anyone in the loop.

Rule 06 stop
Manual suppress

An operator or an uploaded list suppresses the contact across every sequence at once.

Limits

The numbers a sequence respects.

Hard limits from the codebase. Override what you can per step, but the per-mailbox cap is enforced server-side regardless.

01 · Wait between steps

Set per step. Same-day follow-ups read as automation, so most steps sit a few business days apart.

0 to 60 days
02 · Branch signals

Route on a real engagement signal, inside a window you set or the instant it happens.

open · click · reply
03 · A/B split

Add variants to any email step. Volume splits by weight and we track which one earns the reply.

weight 1 to 100
04 · Per-mailbox cap

Default cold budget per mailbox. Raise it toward 100 once the mailbox has earned it.

50 / day
05 · Send spacing

At least ten minutes between two sends from the same mailbox. No bursts.

600s minimum
06 · Business hours

Steps land inside the recipient working window, not the sender clock.

recipient TZ
Source: internal/scheduler/campaign_scheduler.go · internal/config/constants.go
Campaign FAQ

Five questions about how sequences run.

Each branch takes one path on one signal: opened, clicked, replied, or a random share. Chain branches to combine them. The then and else paths can each point at any step, an action, or a stop.

Reply, open and click can route the moment they happen instead of waiting for the next scheduled step. A reply can pull the contact off the sequence and notify the owner within seconds.

Each contact is assigned a variant at the step by a stable weighted split, so a retry always lands on the same copy. We report which variant earned the reply.

A pool of your mailboxes, not one. The scheduler round-robins across the pool under each mailbox cap, the 600s spacing rule, and its current health band, so no single mailbox carries the campaign.

A reply, a met goal, a bounce, an unsubscribe, a STOP reply, or a manual suppression. Suppression is workspace-wide, so another sequence never picks the contact back up by accident.

Build your first sequence in five minutes.

Connect a mailbox, pick a contact list, and draw the first step.