Users enter from a dynamic List, get classified into a persona lane, and climb the same 4-milestone ladder with persona-tailored copy. Every step matters equally — each moves a student one stage deeper. Always-on Exit Rules and a 7-email cap apply across the whole journey. Every milestone email in both lanes runs an A/B subject line test — M1 optimizes for opens, M2 through M4 for the click that advances the student.
In Iterable, A/B tests on journey emails are called Experiments. We run one on every milestone email in both lanes — subject lines only, same body — so each step earns its opens and clicks. M1 wins on open rate (getting them back in the inbox); M2, M3, and M4 win on click rate (the action that moves them to the next rung).
Note: the subject lines below (and in the canvas variants) are examples/placeholders to illustrate the A/B structure — final copy is TBD.
| What's tested | Variant A (control) | Variant B | Win condition | |
|---|---|---|---|---|
| M1 — Tech | Subject line | "See what students like you are shipping on Cloudflare" | "Your account is still live — see what you can build next" | Unique open rate |
| M1 — NT | Subject line + CTA | "Students are making startups from their dorm. Here's how:" · Preheader: No CS degree. No team. Just an idea and a browser. · CTA: Open Workers & Pages | "Build the thing you've been dreaming" · Preheader: Your idea is closer to real than you think. · CTA: Browse templates | Unique open rate |
| M2 — Tech | Subject line | "Deploy it and drop the link in your portfolio" | "Ship your first Worker — get a live URL in minutes" | Click rate (deploy CTA) |
| M2 — NT | Subject line | "Vibe-code your idea into a real app" | "Build your idea — no code, no terminal, just describe it" | Click rate (Build CTA) |
| M3 — Tech | Subject line | "Make it more than a demo. Add a database." | "Give your Worker a memory with D1" | Click rate (D1 CTA) |
| M3 — NT | Subject line | "Get it live" — a real, shareable URL | "Publish your build and share it with the world" | Click rate (deploy CTA) |
| M4 — Tech | Subject line | "You built it. Now put it in your portfolio." | "Drop your link where recruiters can see it" | Click rate (share link) |
| M4 — NT | Subject line | "Not everyone ships. You did." | "You went from idea to live site. Worth sharing." | Click rate (share link) |
Confirm with Adam: does Iterable's Experiment feature work inside a Journey, or only in standalone campaigns? And can the win condition be set on click rate, not just open rate?
.edu emails from Iterable → country and activity_date (30d+ inactive) filtering applied inside Iterable using existing profile fieldspersona and entry_segment (account_setup / worker_deployed)CS / engineering / IT — peer-builder & portfolio framing
Business / founders / design — no-CLI, ship-fast framing
Users flow top-down from a single Start, hit the persona split, then run down one of two lanes. First Worker deploy sits at a different rung per persona: technical at M2 (they deploy straight away), non-technical at M3 (they get a dedicated "Cloudflare Build" step at M2 first). Every step is equally important; delays skip ahead the moment that step's event fires. Every milestone email in both lanes carries an A/B subject line experiment (the purple split nodes below) — so each step is tested for the action it needs to drive. The variant subject lines shown are examples only — final copy is TBD.
Who: CS, information systems, tech, engineering majors.
Use case: improving skills — coursework, side projects, hackathons, building products.
Motivation: level up real engineering skills, ship fast, and build a portfolio that lands internships & jobs.
Pain points: momentum dies after the assignment/hackathon; unaware of adjacent products to build with next; juggling fragmented free tiers; generic emails that never connect to their project.
Who: business, communication, fine arts, liberal arts majors.
Use case: entrepreneurship — launch a startup MVP, waitlist, or prototype to pitch, validate, or compete.
Motivation: build a product/website/app they're passionate about and can ship fast.
Pain points: developer-first docs with no founder on-ramp; no clear "start here" path; CLI feels like a hard stop; feels built for engineers, not them; no early win to pull them back.
The nudge (follow-up email) is not a separate tile on the canvas — it lives inside the delay tile's settings in Iterable Studio. When you configure a delay tile you set: (1) how long to wait (4 days), and (2) what message to send if no event fires during that window. That message is the nudge.
If the student acts during the 4 days → event fires → they skip the rest of the wait and the nudge, jumping straight to the next milestone email.
If 4 days pass with no action → the nudge sends automatically from inside the delay tile, then they advance to the next milestone.
4 milestones + 3 nudges (after M1, M2, M3) = 7-email cap. M4 (Share) has no nudge — it's the final step.
1–6 are Iterable's native journey-building model (the tiles you assemble in Studio). 7–9 are the data & content inputs that have to be in place first — without them the journey has nothing to read, react to, or send.
.edu emails he has access to in Iterable → country and activity_date (30d+ inactive — momentum is the #1 student killer) filtering happens inside Iterable using profile fields already defined.persona; unknown → non-technical lane.persona, increment emails_sent, set milestone flags.emails_sent = 7 (5 milestones + ≤2 nudges).student_classification (technical/non_technical/unknown) · entry_segment (account_setup/worker_deployed) · activity_date (Iterable's existing last-activity field — Adam to confirm exact field name) · country (field name TBD — Adam to confirm).last_dash_login and dormant — replaced by activity_date (already exists in Iterable).student_classification ← Python script output → Iterable via server-side API key; entry_segment/activity_date ← Internal Data MCP / Joey's SQL.dashboard_opened and others: check Data Schema Management before creating. Send from warehouse via server-side API key.The lanes now diverge: technical deploys at M2, then expands; non-technical gets a dedicated Cloudflare Build step at M2 and deploys at M3. Every step is equally important; first Worker deploy is the primary conversion goal we measure against.
Each advance event is tagged by where the data comes from: PRODUCT = product event from the data warehouse (SQL query → Iterable via API) · CLICK = click tracked by Iterable natively / UTM (no warehouse needed).
| Step | Technical student developer lane | Non-technical student developer lane |
|---|---|---|
| M1 | Return to Dash · dashboard_openedProduct "See what students like you are shipping" → Workers AI template in Dash. CTA: Open the Workers AI template. |
Return to Dash · dashboard_openedProduct "See what's possible" — bring them back to browse templates for inspiration. CTA: Open Workers & Pages → Browse templates (workers.new/templates). No "build fast" yet — that's M2. |
| M2 | First Worker deploy · worker_deployedProduct "Deploy it and drop the link in your portfolio" — GitHub push or wrangler deploy. CTA: Open Dash · Build. |
Cloudflare Build · CTA click → BuildClick "Vibe-code your idea into a real app." CTA: Open Build → build.cloudflare.dev. |
| M3 | Add a second product · D1_uploadedProduct "Make it more than a demo" — D1 / R2 (or try Workers AI). |
First Worker deploy · worker_deployedProduct "Get it live" — Build publishes a real, shareable URL. CTA: Open Dash · Build. |
| M4 | Share · build_sharedClick · UTM "Ship it loud" — X / GitHub README badge / Discord. UTM share link. |
Share · build_sharedClick · UTM "Take it further" — LinkedIn / X + Cloudflare for Students. UTM share link. |
Four milestones × two personas. Subject + preheader shown for each; {{firstName}} and {{featured_build_title}} are Iterable merge fields. Keystone milestone (M2) is the first-Worker-deploy moment.
Hi {{firstName | default:"there"}},
You spun up Cloudflare a while back — then midterms, life, whatever. Good news: your account is still here, exactly where you left it.
Here's what another student just shipped on Workers AI: {{featured_build_title}} — {{featured_build_one_liner}}. Built on the free tier, deployed to the global edge in minutes.
Want to start your next one? We've teed up a Workers AI starter template in your dashboard — fork it and you're building in one click, no boilerplate, no config.
Hi {{firstName}},
You set up a Cloudflare account a while back and you're not alone in wondering what to do with it.
Students just like you are using it to launch waitlist pages, pitch prototypes, and ship startup MVPs, without a developer on the team, without a CS degree, and without touching a single line of code.
It starts with a template. Browse what's out there and something might be exactly what you've been looking for.
A project that's deployed beats a project that's "almost done."
Push straight from your GitHub repo, or run wrangler deploy on a template — either way you get a live URL you can put in your portfolio, your résumé, or a recruiter DM. Shipped work gets noticed.
Free tier covers it: 100K requests/day on Workers.
Hi {{firstName}},
You've seen what's possible through our templates. Now it's time to build something that's actually yours.
Build is Cloudflare's tool that turns your idea into a real, live website. No code, no developer, no setup. Just describe what you want in plain English and it builds it for you from scratch. When you're happy with how it looks, tell Build to deploy it and you'll get a real link you can share.
A waitlist page for your startup. A portfolio that gets you noticed. A landing page for the idea you've been pitching to friends. Whatever it is, describe it and watch it come to life.
You shipped something real. Here's how to make it last past the assignment:
Add D1 (a free serverless database) to store the data your project actually needs, or R2 for file storage with zero egress fees. Feeling ambitious? Add an AI feature with Workers AI.
All on one free tier — one account, no new vendor.
Hi {{firstName}},
You've done the hard part. You built something.
Now all that's left is deploying it. Go back into Build, tell it to deploy, and your idea has a real link on the internet you can share with anyone.
No extra setup. No technical steps. Just deploy and you're live.
You built it, deployed it, made it real. Now let people see it.
Post your build on X, add a "Powered by Cloudflare" badge to your GitHub README, or drop it in the Discord "show your build" channel. It's social proof, a portfolio entry, and the start of your next opportunity.
You shipped something real. Two ways to grow it:
Share it — post your build on LinkedIn or X. Proof you shipped is proof to investors, teammates, and your future self.
Keep building — Cloudflare for Students keeps you in the ecosystem with resources and credits as your project grows.
Every rung uses the same pattern: send milestone email → wait TBD days (listening for the event) → send a nudge follow-up → advance to the next milestone. If the student acts during the wait, the event fires and they skip both the wait and the nudge, going straight to the next email. A nudge only sends when the window lapses with no action. Delay window is TBD — pending research via Internal Data MCP on the student conversion cycle. Dormancy defined as 30d+ inactive (momentum is the #1 student killer — shorter window keeps it alive).
Both lanes. Sends on entry.
Only if no action during delay window.
Sooner if the M1 event fires.
Only if no action during delay window.
Sooner if the M2 event fires.
Only if no action during delay window.
Both lanes → exit. (No nudge — final step.)
One event — worker_deployed — does two jobs: it measures conversion and it triggers the next email. It originates in the Cloudflare product, gets pushed into Iterable, and the delay tile listens for it.
Student deploys a Worker (GitHub push, wrangler deploy, or Build). The deploy is recorded in product data.
Internal Data MCP / Amplitude watches for the account's first deploy and dedupes (so only #1 counts).
Amplitude / BIFire the worker_deployed event + set first_deploy_at on the profile.
The first-deploy delay tile (M2 technical / M3 non-technical) sees the event and advances the user immediately (skips the nudge).
A single Track API call stamps the event on the user. createdAt lets Iterable order it; dataFields are optional context. API key must be Server-side type (Iterable → Integrations → API Keys → type = Server-side). Server-side = data moving between backend platforms. Client-side = happens in the browser (visible in DevTools → cookies).
Iterable evaluates branch/exit criteria continuously, so the event pulls the user forward in near-real-time — no waiting out the full delay.
first_deploy_at IS null so repeat deploys don't re-fire.Pull dormant .edu contacts from BI → enrich against ZoomInfo → classify technical vs. non-technical via keyword matching on job title, department, and field of study. Result is written to the persona profile field and drives the split at entry.
Counts from the proof-of-concept enrichment run 25_062926_PERSON_*.csv (25 contacts classified). Fields used: Job Title, Department, Persona, Classification_Reason.
unknown. Caveat: ZoomInfo coverage on current students is thin, so expect a sizable unknown bucket — worth A/B testing the unknown→non-technical default.Iterable does not enrich or classify anyone. It only reads data fields on each user's profile. Persona is computed upstream (warehouse + ZoomInfo + the keyword classifier), written onto the Iterable profile as a field, and the Filter tile simply routes on that field. Same for entry segment, which comes from product data.
Outside Iterable — runs before the daily list sync
Dormant .edu accounts (email + product signals: account created, Workers deployed, last login).
Internal Data MCPMatch each email to job title, department, and field-of-study signals.
ZoomInfo exportScore titles/majors against keyword lists → assign persona + entry_segment.
Write fields onto each profile via CSV import, Users API, or reverse-ETL (Hightouch/Census).
/api/users/bulkUpdateInside Iterable — at journey runtime
Adam pulls all .edu emails from Iterable. country and activity_date (30d+ inactive) filtering happens inside Iterable using existing profile fields.
Criteria: persona = technical → Technical lane. Else → Non-technical lane (unknown defaults here).
Criteria: entry_segment = worker_deployed → skip the deploy step they've already done, go to the next milestone.
One call per user (or batched via bulkUpdate). The dataFields are what the Filter tiles read.
No code — you pick the field and value in the tile's criteria builder. It evaluates the stored profile field; it does not call ZoomInfo.
persona is already on the profile when the user reaches the Filter tile. Iterable can't enrich mid-journey — it only reads what's there. (A Journey Webhook could call an external API live, but batch enrichment upstream is the right pattern here.)