Build a Self-Hosted Onboarding Drip Sequence in n8n
Build an n8n onboarding drip sequence on a Schedule Trigger and Wait nodes, with VIP branching and a re-entrancy guard that stops double-sends.
New users decide whether your product is worth keeping in the first week. A good onboarding drip walks them from signup to first value before they go quiet. The catch: most n8n onboarding drip sequence tutorials hand you a HubSpot sequence node or a Kit integration and call it done. That works until you want the logic on your own self-hosted n8n, where you own the timing, the branching, and the data.
This walks through the self-hosted version. No third-party sequence engine. Just a Schedule Trigger, a few Wait-or-branch decisions, and a state column that keeps the whole thing honest.
The hard part isn't sending email. It's making sure a contact gets exactly the right step at the right time, once, even when the workflow restarts mid-run.
What you can automate in an onboarding flow
A self-hosted onboarding sequence can cover most of the manual nudging a success team does by hand:
- Day-0 welcome email with the one action that drives activation
- Day-2 "have you tried X yet?" check tied to a usage flag
- Day-5 social proof or a short how-to for the feature people skip
- VIP branch: a real human intro email for accounts above a revenue threshold
- Day-9 re-engagement for anyone who hasn't logged in
- Internal Slack alert when a high-value account stalls
- A clean exit that marks the contact "onboarded" and stops the drip
The point isn't volume. It's that each branch fires off observed state, not a blind calendar blast.
Why HubSpot-style sequences hide the logic you need
The library templates lean on HubSpot sequences or a Kit drip because those tools hold the timing and branching internally. You drop a contact in, they handle the rest. Fine, until you need a branch the vendor doesn't expose, or you don't want to pay per contact, or you're already self-hosting everything else.
Here's the opinionated take: for onboarding, a Schedule Trigger that re-reads state every run beats the Wait node for anything longer than a few minutes. A Wait node holds the execution in memory (or in the DB, in queue mode). Redeploy n8n, or hit a crash, and a held execution can vanish. The contact silently never gets day 5. A Schedule-driven design has no held state to lose. It asks, every run: who's due, and what did they last get? That question is answerable from the sheet alone.
The onboarding drip pipeline
Schedule Trigger (hourly)
→ Read contacts (Google Sheets / Postgres)
→ Filter: due for next step AND not already sent
→ Switch: route by days-since-signup + VIP flag
→ Send step email (Gmail / SMTP)
→ Write back: last_step, last_sent_at
→ Slack alert if VIP stalled
Every run is idempotent. It reads the world, acts on whoever's genuinely due, and records what it did. Run it twice by accident and nothing double-fires, because the filter already excludes anyone whose last_step matches the step they'd get next.
1. Trigger and read state
Use a Schedule Trigger on an hourly Cron expression rather than "every minute". A trigger that fires every minute will silently drop runs if the previous execution is still in flight, and onboarding doesn't need minute precision. Hourly is plenty.
The Google Sheets node (or a Postgres SELECT) pulls every active contact plus their signup_at, last_step, last_sent_at, and a vip flag. That sheet is your source of truth. No external sequence state to reconcile.
2. Decide who's actually due
A Code node computes days_since_signup and the next_step each contact should be on. Then a filter keeps only rows where the next step is greater than last_step and enough time has passed since last_sent_at. This is the re-entrancy guard. It's the single most important node in the flow.
// Code node: compute eligibility
const now = Date.now();
return items
.map(({ json }) => {
const days = Math.floor((now - new Date(json.signup_at)) / 864e5);
const next = days >= 9 ? 4 : days >= 5 ? 3 : days >= 2 ? 2 : days >= 0 ? 1 : 0;
return { json: { ...json, days, next } };
})
.filter(({ json }) =>
json.next > Number(json.last_step || 0) &&
now - new Date(json.last_sent_at || 0) > 12 * 36e5
);
The 12 * 36e5 is a 12-hour cooldown in milliseconds. It means even a misfire can't push two emails inside half a day. Tune it to your shortest gap.
3. Route by step and segment
A Switch node branches on next (the step) and reads the vip flag. Standard contacts flow down the templated-email path. VIP accounts route to a separate branch that sends a plainer, "reply to me directly" message from a real mailbox. The branch costs you nothing and it's the difference between an enterprise trial feeling automated and feeling handled.
4. Send and record
Each branch ends in a Gmail or SMTP send, then writes last_step and last_sent_at back to the row. Write the state back even if the send is one node away from the trigger. If you send first and the write fails, the next run re-sends. Order matters: send, then immediately record, and let n8n's error workflow catch the rare write failure.
5. Escalate the stalls
For VIP accounts that hit day 9 without a login flag, a Slack node pings the success channel. That's the human-in-the-loop seam. The drip handles the routine nudges; a person handles the account worth saving.
Onboarding drips break in one predictable way: double-sends. Two overlapping Schedule runs, a manual re-trigger during testing, a redeploy mid-execution. Every one of those re-sends step 3 to someone who already got it. Store last_step and last_sent_at per contact, gate every send on them, and the failure mode disappears. It's boring. It's also why your users don't get three identical welcome emails.
Implementation patterns that hold up
Pattern 1: state-in-sheet, not state-in-execution. Keep timing and progress in the contact row, never in a held Wait node. This is what makes the workflow restart-safe. A redeploy loses in-flight executions; it doesn't lose a Google Sheet.
Schedule → Read sheet → Compute next_step → Filter unsent → Send → Write back
Pattern 2: the VIP fork. One Switch output for accounts above a revenue or plan threshold. Route them to human-from email, lighter automation, and a Slack heads-up. Everyone else gets the templated track.
Switch (vip == true) → human-voice email + Slack alert
Switch (vip == false) → templated step email
Pattern 3: the clean exit. When next exceeds your last real step, set a status of onboarded and let the filter exclude them forever after. No zombie contacts looping in the drip.
For teams that also want to gather signal at the end of onboarding, the User Feedback Loop template pairs naturally here: it ingests feedback from a webhook, Google Forms, or Typeform, runs sentiment with OpenAI, and pings the team on Telegram, so the last onboarding step can route an unhappy reply straight to a human.
n8n nodes you'll use most
| Node | Purpose |
|---|---|
| Schedule Trigger | Hourly Cron scan of the contacts sheet |
| Google Sheets / Postgres | Read and write per-contact onboarding state |
| Code | Compute days-since-signup and the eligibility filter |
| Switch | Branch by step number and VIP flag |
| Gmail / SMTP | Send each step's email |
| Slack | Escalate stalled high-value accounts |
The Schedule Trigger and the Code-node filter do the heavy lifting. The rest is plumbing.
Getting started
- Build a contacts sheet with columns:
email,signup_at,last_step,last_sent_at,vip,status. - Add a Schedule Trigger on an hourly Cron, not "every minute".
- Read the sheet, then add the Code node that computes
nextand filters on the re-entrancy guard. - Wire a Switch on
nextandvip; draft one email per step. - After each send, write
last_stepandlast_sent_atback to the row. - Add the Slack escalation branch for stalled VIP accounts.
- Test with a throwaway row set to
signup_atof five days ago and watch exactly one email fire.
The Efficient Onboarding & Knowledge Retention System ships this end-to-end: a Google Sheets new-hire trigger sends personalized welcome emails with documents attached, spins up a Notion knowledge-base page with team contacts, assigns Trello onboarding tasks with milestone deadlines, and logs every step back to Sheets for tracking. It's part of The Complete n8n Templates Bundle, a one-time lifetime license to the whole catalog plus every template added later, worth it if you run more than one of these automations.
Once the drip runs clean, the next gap is usually what happens after activation. If a user goes quiet a month in, that's a retention problem, and the same Schedule-and-state pattern drives it: see the SaaS subscription churn-prevention build for the winback side, and automating customer onboarding in n8n for the broader onboarding picture. The drip is the front door. Keep it idempotent, keep the state in the sheet, and it'll outlast every redeploy.
Common questions
How do I build a drip sequence in n8n without HubSpot?
How do I stop n8n from sending the same onboarding email twice?
Should onboarding drips use the Wait node or a Schedule Trigger?
Get the workflow templates this guide is built on
Import-ready n8n JSON, step-by-step setup, and tested end-to-end. One-time payment, own it forever.
Get 3 tested n8n templates, free
The full customer package for three real catalog templates — workflow JSON, step-by-step setup guide, credential checklist. Built through the same live-instance release process as everything we sell. Plus new templates and automation guides in your inbox. No spam, unsubscribe anytime.
- 01Smart To-Do List ManagerPre-built n8n workflow template that automates productivity with OpenAI. Live in about 10 minutes.$14
- 02Email Follow-Up AutomatorPre-built n8n workflow template that automates crm with OpenAI. Live in about 15 minutes.$12
- 03Market Trend AnalyzerPre-built n8n workflow template that automates data processing with OpenAI. Live in about 10 minutes.$14
More automation guides

n8n Backup Automation That Actually Verifies the Backup
A Backup You Never Verified Is a Coin Flip Every n8n backup automation tutorial that ranks does the same thing: schedule an export, push the JSON to Google Drive or S3, done. They back up the data and…

How to Build an n8n Log Alerting Workflow (No Grafana Required)
Most n8n Error Setups Tell You Everything, Which Means They Tell You Nothing An n8n log alerting workflow has one job: surface the failures that need a human and quietly file the ones that don't. The…

n8n Uptime Monitoring with Slack Alerts (Without the Alert Storms)
An Uptime Check That Cries Wolf Gets Muted in a Week The point of n8n uptime monitoring with Slack alerts isn't to detect a single outage. It's to be trusted the next hundred times it fires. Most of t…