How to Set Up Login Streak Milestones → Gamified Rewards + Engagement in n8n
Login streaks are one of the highest-ROI retention mechanics you can ship, and almost nobody automates them. You already know the pattern: a user logs in seven days in a row, then nine, then they miss
Login streaks are one of the highest-ROI retention mechanics you can ship, and almost nobody automates them. You already know the pattern: a user logs in seven days in a row, then nine, then they miss a day and never come back. The moment to intervene isn't when they churn — it's when they hit a milestone and you reward it, turning a passive habit into a deliberate one. This article shows you how to build a fully automated Login Streak Milestones engine in n8n that detects 7-, 30-, 90-, and 365-day streaks and delivers the right reward — a badge, a discount code, or a plan upgrade — over email and Slack, with zero manual work.
The Problem: Streaks Die in Silence
Most SaaS and community products track login activity but do nothing with it. The data sits in an analytics table while the behavioral opportunity evaporates. Three failures repeat across teams:
- No detection layer. You know when someone logged in, but you never compute the consecutive-day count. A streak is a stateful concept, and event logs are stateless.
- No trigger at the right moment. Even teams that calculate streaks send a generic monthly newsletter instead of a reward the instant a milestone lands. Timing is the entire value — a badge at day 7 feels earned; the same badge a week later feels random.
- Manual reward fulfillment. Someone in ops copy-pastes a Gumroad discount code into an email. It doesn't scale past a few dozen users, so it quietly stops happening.
The cost is invisible but real: your most engaged users — the ones building a habit — get no signal that the habit matters. Gamification research is blunt here. Variable, milestone-based reinforcement is what converts a streak from fragile to sticky. If you're not closing that loop automatically, you're leaking your best cohort.
The Solution: A Stateful Streak Engine in n8n
The workflow does four things on a schedule: pull login events, compute each user's current consecutive-day streak, match that number against your milestone thresholds (7 / 30 / 90 / 365), and fire the correct reward through email and Slack — but only once per milestone per user. That last constraint, idempotency, is what separates a real system from a spam cannon.
The architecture is deliberately boring, because reliability beats cleverness here:
- Detection: a scheduled run reads login timestamps and reduces them to a streak integer per user.
- Decision: a routing node maps the streak to a milestone tier and its reward payload.
- Delivery: parallel branches send a templated email and a Slack notification.
- Memory: a store records which milestones each user has already received so nobody gets the day-7 badge twice.
Step-by-Step Setup in n8n
Here is the node-by-node build. Assume your login events live in Postgres, Airtable, or any table your app already writes to.
1. Schedule Trigger. Add a Schedule Trigger node set to run once daily, early morning (e.g. cron 0 6 * * *). Daily granularity matches how streaks are defined — you're asking "did they log in yesterday?" not reacting in real time.
2. Fetch login events. Add a Postgres (or Airtable / HTTP Request) node. Query the last 400 days of logins so a 365-day streak is computable: SELECT user_id, login_date FROM logins WHERE login_date > NOW() - INTERVAL '400 days' ORDER BY user_id, login_date. Return distinct user/day pairs to avoid double-counting multiple logins in one day.
3. Compute the streak. Add a Code node (JavaScript). Group rows by user_id, sort dates descending, and walk backward from today counting consecutive days until you hit a gap. The core loop:
const byUser = {};
for (const item of $input.all()) {
const { user_id, login_date } = item.json;
(byUser[user_id] ??= []).push(login_date.slice(0,10));
}
const out = [];
for (const [user_id, dates] of Object.entries(byUser)) {
const set = new Set(dates);
let streak = 0;
let d = new Date();
while (set.has(d.toISOString().slice(0,10))) {
streak++; d.setDate(d.getDate() - 1);
}
out.push({ json: { user_id, streak } });
}
return out;
Allow one day of grace by starting the cursor at "yesterday" if you don't want to penalize users who haven't logged in yet today.
4. Filter to milestone hits. Add a Filter node that passes only rows where streak equals exactly 7, 30, 90, or 365. Checking for equality — not "greater than" — means each user triggers a milestone on the precise day they reach it.
5. Deduplicate against history. Add a lookup (Postgres SELECT or a Data Store / Airtable read) keyed on user_id + milestone. Route through an IF node: if a row already exists for this user and this milestone, stop. Otherwise continue and immediately write the record so re-runs are safe.
6. Route to the right reward. Add a Switch node on streak with four outputs. Attach a Set node to each defining the reward payload — for example: 7 → badge "Week Warrior"; 30 → 15% discount code; 90 → 25% code; 365 → a free plan upgrade. For discount codes, call your Gumroad/Stripe offer-code API from an HTTP Request node to mint a unique code per user rather than sharing one public code.
7. Deliver over email + Slack. After each reward branch, merge into two delivery nodes running in parallel:
Send Email(SMTP) or theGmailnode: templated HTML using expressions likeCongrats on your {{$json.streak}}-day streak!and the reward value injected from the Set node.Slacknode: post to a#winschannel or DM the user via their Slack ID, so your team sees engagement momentum and power users feel publicly recognized.
Wire both to a final Postgres INSERT logging user_id, milestone, reward, and sent_at — closing the idempotency loop from step 5.
The Benefits: Retention You Can Measure
Once this is live, the payoffs compound:
- Perfectly timed reinforcement. Rewards land the day a habit crystallizes, which is exactly when behavioral science says they stick.
- Zero ops overhead. The workflow runs daily forever. No one mints codes or writes emails by hand.
- A visible retention signal. Your
#winsSlack channel becomes a live feed of engaged users — useful for the whole team, and a morale boost. - Escalating value. A badge is cheap; a 365-day upgrade is generous — and only your most loyal users ever trigger it, so the expensive rewards are perfectly self-targeting.
- Full auditability. Every reward is logged, so you can measure whether milestone-hitters churn less than the baseline cohort.
Common Pitfalls to Avoid
Skipping idempotency. The number-one failure. Without the dedup step, every daily run at streak-day-7 would re-send if your streak logic ever includes ">=". Always store what you sent and check before sending.
Timezone drift. "Consecutive days" depends on which day boundary you use. Normalize all login timestamps to a single timezone (UTC or the user's) before slicing to a date, or a user in UTC+10 will see phantom broken streaks.
Counting duplicate logins. If a user logs in five times on Monday, that's still one day. Deduplicate to distinct user/date pairs in the query or the Code node, or your streaks inflate.
Sharing one discount code. A single public code gets leaked and abused. Mint per-user, single-use codes through your commerce API so the reward stays exclusive.
No grace window. If your run fires before a user has logged in "today," a strict same-day check resets valid streaks to zero. Anchor the count on yesterday, or run late enough in the day to be safe.
Silent delivery failures. Add an Error Trigger workflow or an error branch that alerts your team on Slack if the email node fails — otherwise rewards silently vanish and you won't know until a user complains.
Build it once, and your most engaged users get recognized automatically, every day, forever. That's the whole point of automation: turn a retention insight you already have into a system that acts on it without you.
Ja construimos isso pra voce
Nao comece do zero. O Login Streak Milestones → Gamified Rewards + Engagement e um workflow n8n pronto para instalar — conecta suas ferramentas em minutos, sem codigo.
Instalar por $79 →