act · sequences and steps
Step components
The building blocks of every sequence.
In Sequences and steps
Every sequence is built from the same seventeen step components: messages, public posts, value deliveries and control steps like waits and branches. A step is defined once, reused across sequences, and versioned, so a result is always tied to the exact step that produced it. This page specifies each component and the life of one step from due to done.
sequence library → steps · the parts → templates · the words → next-best-action · the choice → contact policy · the limits
A step is one action — One message, one wait, one branch. Never two sends in one step.
Exit is checked before every send — If they replied, asked to stop or converted, the next step never runs.
Every send emits a touch_id — That id is how a reply, a click or a signup finds its way back to the step.
in industry terms
Steps are the components; a sequence is an ordered list of them.
Sales engagement tools call these steps or touches in a cadence: email, call, LinkedIn task, wait. VibeDraft adds public steps (replies and posts) and value steps (the brief, the waitlist), because a solo founder's outreach happens in public before it happens in private.
definition — Step · Type, channel, sender, wait, condition, exit rules, template. Versioned.
run — Step instance · One step for one lead in one campaign: due time, policy check, draft, send, outcome.
output — Touch · What actually went out, with a touch_id, the final text and the time.
how it works
The life of one step, from due to done.
A step instance moves through the same states every time. Each state change is an event, so the drafts and the blocks are training data, not just the sends.
- Due — wait elapsed
- Exit check — replied? stopped?
- Policy — caps, quiet hours
- Draft — template + voice
- Founder — edit, send, skip
- Sent — touch_id
- Outcome — reply, click, join met
- Exit — reason logged blocked
- Blocked — rule logged
exit check — Before anything else · Replied, asked to stop, converted, bounced or out of steps ends it here.
policy — The contact policy decides · A blocked step is a row with the rule that blocked it, never a silent skip.
draft — Template plus voice · Slots filled from the brief, the lead and the signal, drafted in the founder's voice.
founder — You send · Shipped, shipped edited, or discarded: each is a label for the drafter.
the object
The step, and one run of it.
Two records: the step definition, reused across sequences, and the step instance, one per lead.
| Field | Type | What it holds |
|---|---|---|
step_id | string | step.<type>[.<variant>], never reused for something else. |
version | integer | Bumped when the template, wait, condition or exits change. |
type | enum | one of the 17 components below. |
channel | enum | reddit_thread · x_reply · x_dm · reddit_chat · email · threads · instagram · tiktok · none |
sender | enum | you · scheduled · system |
wait | offset | "day 0", "+3d", "on reply"; plus a send window in local time. |
condition | expression | When it runs at all, in the segment rule grammar. |
exits | list | replied · stop · converted · bounced · out_of_steps |
template | id + variants | See Templates and variants. |
instance.state | enum | due · exited · blocked · drafted · sent · skipped · outcome |
instance.touch_id | id | Set on send. The join key for everything after. |
one record, as data
{
"step_id": "step.dm.brief",
"version": 3,
"type": "dm",
"channel": "x_dm",
"sender": "you",
"wait": "on positive reply",
"condition": "dms_open = true and dms_sent = 0",
"exits": [
"replied",
"stop",
"converted",
"out_of_steps"
],
"template": {
"id": "tmpl.dm.brief",
"variants": [
"A",
"B"
]
},
"instance": {
"campaign_id": "camp.lead-to-waitlist.2026-10-20",
"lead_id": "lead_…",
"state": "sent",
"touch_id": "touch_…",
"sent_at": "2026-10-21T15:12:00Z"
}
}the catalog
Seventeen components, specified.
Message steps reach a person; public steps post to an audience; value steps deliver what was promised; control steps shape the flow.
step.public_reply — Message
Public reply
Answer in the thread where they posted. Visible to everyone, so it has to help on its own.
- channel
- Reddit thread, X reply, Threads reply
- sender
- you
- wait
- day 0, within the trigger's freshness window
- runs if
- thread still open; not removed
- exits on
- they reply, the thread locks, a mod removes it
- records
- touch_id, thread url, final text, edits, removal
step.light_touch — Message
Light touch
A short second reply or reaction after they answer: thanks, a follow-up question.
- channel
- same thread
- sender
- you
- wait
- after they answer
- runs if
- reply_class in (positive, question)
- exits on
- no answer in 3 days
- records
- touch_id, reply class it answered
step.dm — Message
Direct message
The one private message. Carries the offer, not a hello.
- channel
- X DM, Reddit chat, Threads (no DM API)
- sender
- you
- wait
- after a public exchange, or on request
- runs if
- DMs open; never sent a DM before
- exits on
- replied, asked to stop, out of steps
- records
- touch_id, offer_id, link id, delivered or not
Threads has no API for DMs; that step is a task for the founder.
step.follow_up_dm — Message
Follow-up DM
One nudge if the DM got no answer.
- channel
- same as the DM
- sender
- you
- wait
- +3 to 4 days
- runs if
- no reply to the DM; under the unanswered cap
- exits on
- reply, stop, cap reached
- records
- touch_id, days since DM
step.email — Message
For people who gave you an address: waitlist, trial, customers.
- channel
- sender
- scheduled or you
- wait
- per sequence
- runs if
- has an email and consent; not unsubscribed
- exits on
- reply, unsubscribe, bounce
- records
- touch_id, open/click if tracked, bounce
step.email_follow_up — Message
Email follow-up
The second email, shorter, one ask.
- channel
- sender
- scheduled
- wait
- +2 to 4 days
- runs if
- no reply; not unsubscribed
- exits on
- reply, unsubscribe, conversion
- records
- touch_id
step.comment_keyword — Message
Comment-a-keyword reply
The reply to someone who commented your keyword: confirm, then deliver.
- channel
- the post's thread, then DM
- sender
- you
- wait
- within hours of the comment
- runs if
- commented the keyword
- exits on
- delivered, asked to stop
- records
- touch_id, delivery time
step.quote_post — Public
Quote post
Quote their public post with something useful, when replying would bury it.
- channel
- X, Threads
- sender
- you or scheduled
- wait
- day 0
- runs if
- post is public and recent
- exits on
- n/a
- records
- post id, metrics snapshots
step.own_post — Public
Own post
A post on your account that the sequence depends on: the offer post, a launch post.
- channel
- X, Threads, Instagram, TikTok
- sender
- scheduled
- wait
- per sequence
- runs if
- n/a
- exits on
- n/a
- records
- post id, metrics at 10 min, 30 min, 1 h, 1 day, 7 days
step.send_brief — Value
Send their brief
- channel
- inside a DM or email
- sender
- you
- wait
- with the DM
- runs if
- brief generated for their URL
- exits on
- opened, ignored
- records
- brief id, opened or not
step.add_waitlist — Value
Add to waitlist
Turns a yes into a waitlist row, with the touch that earned it.
- channel
- waitlist form or reply
- sender
- system
- wait
- on yes
- runs if
- said yes, or clicked the link
- exits on
- joined
- records
- first and last touch_id on the waitlist row
step.offer_call — Value
Offer a call
Fifteen minutes to set them up. Big ask: warm leads only.
- channel
- DM or email
- sender
- you
- wait
- after a positive reply
- runs if
- warm; trial or asked how
- exits on
- booked, declined, no answer
- records
- touch_id, booked or not
step.tracked_link — Value
Tracked link
A short link whose clicks carry the touch_id back.
- channel
- inside any message
- sender
- system
- wait
- n/a
- runs if
- any step with a link
- exits on
- n/a
- records
- link id, clicks with time
step.wait — Control
Wait
Do nothing for a while. Waits are steps so their length can be tested.
- channel
- none
- sender
- system
- wait
- +N hours or days
- runs if
- always
- exits on
- n/a
- records
- scheduled vs actual wake time
step.branch — Control
Branch on reply class
Pick the next step from how they answered: positive, question, not now, stop.
- channel
- none
- sender
- system
- wait
- on reply
- runs if
- a reply arrived
- exits on
- n/a
- records
- reply class, which branch, classifier version
step.task — Control
Task for the founder
Something only a person can do: find an email, record a video, check a profile.
- channel
- none
- sender
- you
- wait
- per sequence
- runs if
- n/a
- exits on
- done, skipped
- records
- task, done or skipped, minutes
step.exit — Control
Stop / exit
Ends the sequence for this person and says why.
- channel
- none
- sender
- system
- wait
- any time
- runs if
- an exit condition is met
- exits on
- n/a
- records
- exit reason: replied, stop, converted, bounced, out of steps
illustrative example
Lead to waitlist, as components.
A four-step sequence, written out with the components it uses. The founder sends every message.
day 0 · step.public_reply
the one thing that fixes what they posted, in the thread
Exit if the mods remove it or the thread locks.
on reply · step.branch — positive or question → light touch; not now → exit; stop → exit and never-contact
step.light_touch — a short follow-up in the thread
step.dm + step.send_brief — one private message with their brief and the waitlist, through a tracked link
+3 to 4 days · step.follow_up_dm — one nudge, then step.exit (out of steps)
failure modes
What goes wrong, and how to catch it.
How each one shows up, and the check that catches it before it costs a week.
| Failure | How it shows up | How to catch it |
|---|---|---|
| Double send | The same DM goes out twice | Step instances are unique per (campaign, lead, step); sending checks the touch log |
| Exit not checked | A nudge after they already said no | Exit check runs immediately before the draft and again before the send |
| Wrong timezone | Messages arrive at 3am their time | Send windows use the lead's timezone when known, the founder's otherwise |
| Channel unavailable | A DM step on an account with closed DMs | Condition checks DMs open; otherwise branch to a public step or a task |
| Untracked step | Replies arrive and can't be matched to a step | No send without a touch_id; tracked links on every link |
data to capture
Record it so a model can learn from it later.
Follows the guide's eight data-spine rules: log the decision, version what decided, freeze the inputs, keep the negatives, log the odds, treat every human touch as a label, keep one chain of ids, keep the raw.
record
- every step instance and every state it passed through, with times
- the draft, the final text and the decision (shipped, edited, discarded)
- the exit or block reason when a step didn't send
- the touch_id on every send, and the outcome joined to it
label
reply, click and conversion per step instance; the founder's decision on each draft
trains
step-level response models (which step type works for which trigger) and a send-time model
test
waits, step order, channel per step · randomized per lead within a campaign arm