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. M1, M2, and the non-technical M3 include A/B subject line tests — covering the highest-leverage opens plus each persona's first-deploy conversion email.
In Iterable, A/B tests on journey emails are called Experiments. We run them on M1 and M2 only for v1 — subject lines only, same body — to answer the highest-leverage question first: what gets opens and drives the primary conversion? M3 and M4 run uncontrolled in v1; test those in v2 once we have baseline data.
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 | "Build anything, fast — no terminal required" | "You don't need to be an engineer to ship this" | 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 — NT | Subject line | "Get it live" — a real, shareable URL | "Publish your build and share it with the world" | Click rate (deploy CTA) |
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?
email ends_with .edu AND country = US AND no dashboard_opened in 90dpersona 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. M1, M2, and the non-technical M3 carry A/B subject line experiments (purple split nodes below) — so each persona's first-deploy email gets tested. 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 + country = US + dormant = true (no login in 90d).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.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 "Build anything, fast — no terminal required." CTA: Open Workers & Pages → Create Application → Browse more templates (workers.new/templates). |
| 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 · r2_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 | default:"there"}},
Got an idea for a startup, a waitlist page, or a project you want to pitch? You don't need to be an engineer to ship it.
Here's the fastest way in: open Workers & Pages in your dashboard, click Create Application, then Browse more templates — you'll land on a gallery of starting points you can launch in a click. No command line, no setup.
Pick a template that fits your idea and you're building.
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.
Ready to actually make the thing? Build is Cloudflare's vibe-code platform — describe your app, site, or waitlist in plain English and it builds it for you.
Head to build.cloudflare.dev, describe what you want, and let it do the heavy lifting. No CLI, no setup, no guesswork.
Next step after this: getting it live (that's M3).
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.
You built it in Build — now let's get it live.
Hit deploy and Build publishes your site or app for you. You'll get a real URL you can text to a friend, drop into a pitch deck, or post to collect your first signups.
Nothing to install. The free tier covers your launch.
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 (target: activate within ~90 days).
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
Dynamic list = .edu + country=US + dormant=true. Users already carry persona & entry_segment.
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.)