Cloudflare
Iterable Lifecycle Journey Re-engaging Dormant .edu Student Builders
GROWTH MARKETING · DRAFT v1

Re-engaging Dormant
.edu Student Builders

14,049Dormant .edu accounts (90d inactive)
21%Current active rate
~10%Target 30-day retention lift
7 maxEmails per user (capped)

The Mapping

Journey workflow in Iterable Studio

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.

Entry Source
Filter (Yes/No split)
Message (Email)
Delay
User Update
Exit Rule
A/B Experiment (Iterable)
A/B Testing Plan

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.

EmailWhat's testedVariant A (control)Variant BWin condition
M1 — TechSubject 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 — NTSubject line"Build anything, fast — no terminal required""You don't need to be an engineer to ship this"Unique open rate
M2 — TechSubject 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 — NTSubject line"Vibe-code your idea into a real app""Build your idea — no code, no terminal, just describe it"Click rate (Build CTA)
M3 — NTSubject 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?

Entry Source · List Dormant .edu Student Builders
Dynamic list, re-evaluated daily. email ends_with .edu AND country = US AND no dashboard_opened in 90d
User Update Stamp persona & entry_segment
From BI + ZoomInfo enrichment → persona and entry_segment (account_setup / worker_deployed)
Filter · Persona Split Technical  /  Non-technical
Unknown → routed to Non-technical (lower-friction default)

Technical lane

CS / engineering / IT — peer-builder & portfolio framing

Filter · Entry Segment Segment B skips M2
Already deployed a Worker → skip the deploy rung, go to M3
▶ start
Message · M1Return to Dash A/B
"See what students like you are shipping" → Workers AI template in DashTesting subject line · win condition: open rate
TBD-day wait → nudge if no action · skip on dashboard_opened
Message · M2First Worker deploy A/B
"Deploy it and drop the link in your portfolio"Testing subject line · win condition: click rate
TBD-day wait → nudge if no action · skip on worker_deployed
Message · M3Add a second product
"Make it more than a demo" — D1 / R2 (or try Workers AI)
TBD-day wait → nudge if no action · skip on r2_uploaded
Message · M4Share the build
"Ship it loud" — X / GitHub badge / Discord

Non-technical lane

Business / founders / design — no-CLI, ship-fast framing

Filter · Entry Segment Segment B skips to deploy
Already deployed a Worker → skip M2 (Build), go to M3
▶ start
Message · M1Return to Dash A/B
"Build anything, fast" → Workers & Pages → Create Application → Browse templatesTesting subject line · win condition: open rate
TBD-day wait → nudge if no action · skip on dashboard_opened
Message · M2Cloudflare Build A/B
"Vibe-code your idea into a real app" → build.cloudflare.devTesting subject line · win condition: click rate
TBD-day wait → nudge if no action · skip on CTA click → Build
Message · M3First Worker deploy
"Get it live" — Build deploys a real URL
TBD-day wait → nudge if no action · skip on worker_deployed
Message · M4Share the build
"Take it further" — LinkedIn / X + CF for Students
Exit Rules · Always-on Eject immediately when…
Unsubscribed Hard bounce Spam complaint All milestones complete 7-email cap reached

In Iterable

How the journey looks on the Studio canvas

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.

Abbreviation key: Tech = Technical student developer (CS / engineering / IT) · NT = Non-technical student developer (business / founders / design) · M1–M4 = milestone messaging events (transactional emails we need to hit at each step of the journey)
First Worker deploy = the primary conversion goal. A Worker deploy means the student pushes code live and gets a working URL — the clearest signal a dormant account is reactivating (the one-pager's activation milestone). Every milestone matters equally in the flow; we just measure success against worker_deployed. We chose it over "first inference" because it's a standard product signal that's far simpler to track than a custom AI event while the team is still standing up segmentation and event feeds. (Workers AI stays in the copy as a "what to build next" idea, not a tracked milestone.)
Tile ID 10970801
Start · Entry Source (List)
Dormant .edu Student Builders · daily eval
Tile ID 10970802
User Update
Set persona & entry_segment (BI + ZoomInfo)
Tile ID 10970803
Filter · Persona Split
Technical  |  Non-technical (unknown→NT)
📌
TECHNICAL STUDENT DEVELOPER

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.

📌
NON-TECHNICAL STUDENT DEVELOPER

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.

TECHNICAL STUDENT DEVELOPER LANE
NON-TECHNICAL STUDENT DEVELOPER LANE
Tile ID 10970810
Filter · Entry segment
Segment B (already deployed) → skip M2, go to M3
Tile ID 10970830
Filter · Entry segment
Segment B (already deployed) → skip M2 (Build), go to M3
Tile ID 10970811
A/B Split · 50 / 50
M1 — Return to Dash → Workers AI template · subject-line test
Variant A · 50%
"See what students like you are shipping on Cloudflare"
Variant B · 50%
"Your account is still live — see what you can build next"
Winning metric: open rate → paths rejoin
Tile ID 10970831
A/B Split · 50 / 50
M1 — Return to Dash → Workers & Pages → Browse templates · subject-line test
Variant A · 50%
"Build anything, fast — no terminal required"
Variant B · 50%
"You don't need to be an engineer to ship this"
Winning metric: open rate → paths rejoin
Tile ID 10970812
Time delay · TBD days
Advance on: dashboard_opened
Tile ID 10970832
Time delay · TBD days
Advance on: select_worker_template
Tile ID 10970813
A/B Split · 50 / 50
M2 — First Worker deploy · subject-line test
Variant A · 50%
"Deploy it and drop the link in your portfolio"
Variant B · 50%
"Ship your first Worker — get a live URL in minutes"
Winning metric: click rate → paths rejoin
Tile ID 10970833
A/B Split · 50 / 50
M2 — Cloudflare Build → build.cloudflare.dev · subject-line test
Variant A · 50%
"Vibe-code your idea into a real app"
Variant B · 50%
"Build your idea — no code, no terminal, just describe it"
Winning metric: click rate → paths rejoin
Tile ID 10970814
Time delay · TBD days
Advance on: worker_deployed
Tile ID 10970834
Time delay · TBD days
Advance on: CTA click → Build
Tile ID 10970815
Email · M3 — Add a second product
"Make it more than a demo" — D1 / R2 (or try Workers AI)
Tile ID 10970835
A/B Split · 50 / 50
M3 — First Worker deploy · subject-line test
Variant A · 50%
"Get it live" — a real, shareable URL
Variant B · 50%
"Publish your build and share it with the world"
Winning metric: click rate → paths rejoin
Tile ID 10970816
Time delay · TBD days
Advance on: r2_uploaded
Tile ID 10970836
Time delay · TBD days
Advance on: worker_deployed
Tile ID 10970817
Email · M4 — Share
"Ship it loud" — X / README badge / Discord
Tile ID 10970837
Email · M4 — Share
"Take it further" — LinkedIn / CF for Students
Always-on
Exit Rules
Unsubscribe · hard bounce · spam · goal complete · 7-email cap reached
📌
HOW THE TIME DELAY TILE WORKS

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.

The Build

Core components mapped to this journey

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.

1 Entry Source

  • Type: List (dynamic), re-evaluated on a daily schedule. Dormancy is a state (lack of activity), not an event — so a list beats an event/API trigger here.
  • Two entry segments feed one journey: account_setup (set up account/zone, never returned) and worker_deployed (deployed one Worker, never returned).
  • Source list built from BI + ZoomInfo enrichment, synced to Iterable.

2 Conversion Goals

  • Primary goal: worker_deployed — first Worker deploy.
  • Secondary: r2_uploaded (expand / second-product attach).
  • Baseline KPIs: unique open rate, CTA click / click-to-open, conversion rate vs. a holdout control.

3 Audience & Eligibility

  • Entry rules: .edu + country = US + dormant = true (no login in 90d).
  • Entry limits: one active entry per user; 90-day re-entry guard so reactivated users aren't re-spammed.
  • Split immediately on persona; unknown → non-technical lane.

4 Workflow Tiles

  • Message: 4 milestone emails + up to 3 nudge follow-ups per lane (7 max).
  • Delay: 4-day waits between milestones, with behavioral skip.
  • Filter: persona split, entry-segment split, "did event fire?" Yes/No routing.
  • User Update: stamp persona, increment emails_sent, set milestone flags.
  • Integration: Journey Webhook to sync conversions to Amplitude / CM360.

5 Exit Rules

  • Always-on ejection on: unsubscribe, hard bounce, spam complaint.
  • Goal complete: all milestones reached → exit (no irrelevant follow-ups).
  • 7-email cap: hard exit once emails_sent = 7 (5 milestones + ≤2 nudges).

6 Testing & Publishing

  • Test Mode: run test profiles for each combo — Technical/Non-tech × Segment A/B × unknown — through every tile.
  • Verify skip logic fires on each event and the 7-cap holds.
  • Optionally scaffold with Journey Assist, then refine tiles by hand.

7 Profile Fields · DATA INPUT

  • What: attributes describing who the user is, stored on each Iterable profile. The entry list (#3) and persona/segment filters (#4) read these.
  • Fields: 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).
  • Removed: last_dash_login and dormant — replaced by activity_date (already exists in Iterable).
  • Source: student_classification ← Python script output → Iterable via server-side API key; entry_segment/activity_date ← Internal Data MCP / Joey's SQL.
  • Action before creating: check Iterable first to confirm fields don't already exist (Adam to verify).

8 Event Feed · DATA INPUT

  • What: real-time actions a user takes that get piped into Iterable. The delay tiles (#4) listen for these to advance the journey.
  • Product events (check Data Schema Management in Iterable first — may already exist): dashboard_opened · worker_deployed · select_worker_template (replaces NT M1 signal) · r2_uploaded (only R2 has a product event — no event for D1 or Workers AI). Send from warehouse via server-side API key.
  • Click events (Iterable-native, no setup needed — on by default): set a click listener on a specific link as the trigger (e.g. the Build CTA URL). Iterable tracks these automatically.
  • Share link: deep link — needs platform-by-platform research. Adam to help set up.
  • Source / owner: product events ← Internal Data MCP / warehouse SQL (Joey). API key must be server-side type (backend data movement, not browser).

9 Email Templates · CONTENT INPUT

  • What: the HTML templates the milestone copy lives in — what the Message tiles (#4) actually send.
  • Scope: 5 milestones × 2 personas (+ nudges) — built in Iterable's drag-and-drop editor or imported as coded HTML.
  • Style: follow the existing Cloudflare "What's new" email (orange bar, logo header, bulleted highlights, single CTA button, footer).

The Copy

Milestone email matrix

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.

Abbreviation key: Tech = Technical student developer (CS / engineering / IT) · NT = Non-technical student developer (business / founders / design) · M1–M4 = milestone messaging events (transactional emails we need to hit at each step of the journey)

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).

StepTechnical student developer laneNon-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.
Where to get the data: Product events (dashboard_opened, worker_deployed, r2_uploaded) come from the data warehouse via SQL — Joey/Adam help — pushed to Iterable through an API key. Click events (Build CTA, share links) are tracked by Iterable natively / UTM, no warehouse needed.

Nudge rule: each step waits 4 days; if the event doesn't fire, a nudge follow-up sends, then the journey advances to the next milestone. 4 milestones + 3 nudges = the hard 7-email cap. M4 (Share) is the final step, so it has no nudge.

The Copy — Full Drafts

Subject line, preheader & body for all 8 emails

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.

M1

Return to Dash

advance on: dashboard_opened
M2

Technical → First Worker deploy  ·  Non-technical → Cloudflare Build

Tech: advance on worker_deployed  ·  NT: advance on CTA click
M3

Technical → Add a second product  ·  Non-technical → First Worker deploy

Tech: advance on r2_uploaded  ·  NT: advance on worker_deployed
M4

Share the build

advance on: build_shared
Nudge follow-ups send after each 4-day delay if the student hasn't acted — one after M1, M2, and M3 (M4 is final, no nudge). They reuse the milestone body with a lighter, shorter alt subject (e.g. M1 → "Your dashboard's still warm"; M2 → "Your first deploy is 2 minutes away"). 4 milestones + 3 nudges = the 7-email cap.

The Logic

Trigger logic & timing between sends

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).

Behavioral skip: fast movers who fire the event jump straight to the next milestone — they're never nagged.

Lanes diverge at M2: technical deploys at M2; non-technical gets a Cloudflare Build step at M2 and deploys at M3.

Segment B (already deployed a Worker) enters past the first-deploy step, so they skip straight to the expand/share step.
Day 0
M1 — Return to Dash

Both lanes. Sends on entry.

Day TBD
Nudge — M1 follow-up

Only if no action during delay window.

TBDor on event
M2 — Tech: First Worker deploy · NT: Cloudflare Build

Sooner if the M1 event fires.

Day TBD
Nudge — M2 follow-up

Only if no action during delay window.

TBDor on event
M3 — Tech: Add a second product · NT: First Worker deploy

Sooner if the M2 event fires.

Day TBD
Nudge — M3 follow-up

Only if no action during delay window.

TBDor on event
M4 — Share

Both lanes → exit. (No nudge — final step.)

Measuring conversion

How first Worker deploy is tracked & advances the journey

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.

1
Deploy happens

Student deploys a Worker (GitHub push, wrangler deploy, or Build). The deploy is recorded in product data.

Workers / Build
2
Detect "first"

Internal Data MCP / Amplitude watches for the account's first deploy and dedupes (so only #1 counts).

Amplitude / BI
3
Push to Iterable

Fire the worker_deployed event + set first_deploy_at on the profile.

/api/events/track
4
Journey advances

The first-deploy delay tile (M2 technical / M3 non-technical) sees the event and advances the user immediately (skips the nudge).

Step 3 — the event you send to Iterable

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).

// POST https://api.iterable.com/api/events/track { "email": "jordan@utexas.edu", "eventName": "worker_deployed", "createdAt": 1719772800, "dataFields": { "worker_name": "my-first-app", "account_id": "a1b2c3" } }

Step 4 — how the tile reacts

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 step logic // M2 technical · M3 non-technical Send deploy email DELAY up to 4 days, listening… IF worker_deployed received advance (skip nudge) ELSE after 4 days send 1 nudge, wait 3d IF still none advance anyway Conversion Goal = worker_deployed // Iterable reports % of entrants who converted
Measure vs. trigger: the same worker_deployed event is set as the journey's Conversion Goal (so reporting shows first-deploy conversion %, your primary KPI) and is what the delay tile listens for to advance. Dedupe upstream — gate on first_deploy_at IS null so repeat deploys don't re-fire.

The Split

How we classify persona

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.

2Technical (sample)
4Non-technical (sample)
9Unknown → Non-tech
10Not students (excluded)

Counts from the proof-of-concept enrichment run 25_062926_PERSON_*.csv (25 contacts classified). Fields used: Job Title, Department, Persona, Classification_Reason.

Technical keywords

softwareengineerdevelopercomputer science dataITDevOpsMLbackend full-stackinformation systemsSWE

Non-technical keywords

founderbusinessmarketingproduct designcommunicationsfinanceliberal arts fine artsentrepreneur
Resolution order: ZoomInfo keyword hit → field-of-study/major keyword hit → unknown. Caveat: ZoomInfo coverage on current students is thin, so expect a sizable unknown bucket — worth A/B testing the unknown→non-technical default.

How it runs

Where the persona split actually happens

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

1
BI / Warehouse pull

Dormant .edu accounts (email + product signals: account created, Workers deployed, last login).

Internal Data MCP
2
ZoomInfo enrichment

Match each email to job title, department, and field-of-study signals.

ZoomInfo export
3
Keyword classifier

Score titles/majors against keyword lists → assign persona + entry_segment.

classify_students.py
4
Sync to Iterable

Write fields onto each profile via CSV import, Users API, or reverse-ETL (Hightouch/Census).

/api/users/bulkUpdate

Inside Iterable — at journey runtime

5
Entry list reads the fields

Dynamic list = .edu + country=US + dormant=true. Users already carry persona & entry_segment.

6
Filter tile · Persona

Criteria: persona = technical → Technical lane. Else → Non-technical lane (unknown defaults here).

7
Filter tile · Entry segment

Criteria: entry_segment = worker_deployed → skip the deploy step they've already done, go to the next milestone.

Step 4 — the payload you push to Iterable

One call per user (or batched via bulkUpdate). The dataFields are what the Filter tiles read.

// POST https://api.iterable.com/api/users/update { "email": "jordan@utexas.edu", "dataFields": { "persona": "technical", // from classifier "entry_segment": "worker_deployed", "field_of_study": "Computer Science", "dormant": true, "last_dash_login": "2026-03-10" } }

Step 6 — the Filter tile in Studio

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.

Filter tile criteria IF user.persona is "technical" Yes path (Technical lane) ELSE No path (Non-technical lane) // unknown / null also follows No path // = safe default to lower-friction lane
Timing rule: the classification job must run before the daily entry-list refresh, so 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.)