
Email Marketing Automation
- 79 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Build triggered email flows for welcome series, post-purchase nurture, win-back, and browse abandonment to drive repeat revenue.
About
Sets up the four core lifecycle email flows via Klaviyo or AutomateWoo pre-built templates. A developer uses it for baseline lifecycle coverage, recovering browse-abandonment revenue, or re-engaging lapsed customers.
- Per-platform tool recommendation table
- Covers welcome, post-purchase, win-back, and browse-abandonment flows
Email Marketing Automation by the numbers
- 79 all-time installs (skills.sh)
- Ranked #491 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill email-marketing-automationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 79 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Build triggered email flows for welcome series, post-purchase nurture, win-back, and browse abandonment to drive repeat revenue.
Files
Email Marketing Automation
Overview
Triggered email flows automatically send personalized messages based on customer behavior and lifecycle stage, achieving 3–5x higher open rates than broadcast campaigns. Klaviyo (for Shopify/BigCommerce) and AutomateWoo or Klaviyo plugin (for WooCommerce) provide pre-built flow templates for the four critical flows: welcome series, post-purchase nurture, win-back campaigns, and browse abandonment. Most merchants can set these up without writing any code.
When to Use This Skill
- When setting up a new store and needing baseline lifecycle email coverage
- When replacing manual one-off campaigns with behavior-triggered sequences
- When recovering revenue from customers who browsed but did not purchase
- When re-engaging lapsed customers who have not ordered in 60–180 days
- When personalizing post-purchase communication to reduce returns and increase LTV
Core Instructions
Step 1: Choose the right tool
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Klaviyo | Pre-built flow templates, deep Shopify integration, automatic event tracking (Viewed Product, Added to Cart, Checkout Started, Placed Order), predictive analytics |
| WooCommerce | AutomateWoo ($99/yr) or Klaviyo (WooCommerce plugin) | AutomateWoo has native WooCommerce triggers and hooks; Klaviyo plugin syncs WooCommerce events |
| BigCommerce | Klaviyo or Omnisend | Both have native BigCommerce integrations and pre-built lifecycle flow templates |
| Custom / Headless | Klaviyo (via Track API) or SendGrid | Send events to Klaviyo's API; build flows in their visual editor |
Step 2: Set up the four core flows
---
Shopify with Klaviyo
Install Klaviyo from the Shopify App Store. Klaviyo automatically tracks Shopify events — no additional setup required for event capture.
Flow 1: Welcome Series
1. Go to Klaviyo → Flows → Create Flow → Welcome Series (use the pre-built template) 2. The default template sends 3 emails:
- Email 1 (immediate): Welcome + brand story + 10% discount for first purchase
- Email 2 (day 2): Bestsellers or curated collection
- Email 3 (day 7): Brand mission / social proof
3. Customize the email copy and design using Klaviyo's drag-and-drop editor 4. The flow triggers automatically when someone subscribes via your Shopify popups, checkout opt-in, or newsletter form 5. Add a conditional split on "Has placed an order" to skip the discount email for customers who already purchased after subscribing
Flow 2: Post-Purchase Series
1. Go to Klaviyo → Flows → Create Flow → Post-Purchase (use template) 2. Default 4-step structure:
- Email 1 (immediate): Order confirmation + product care tips
- Email 2 (day 2): Shipping notification (Klaviyo connects to Shopify's fulfillment events)
- Email 3 (day 7): Delivery check-in + cross-sell recommendations
- Email 4 (day 21): Review request (connects to Klaviyo's review integration or Judge.me)
3. Add a filter: "Has NOT placed order since starting this flow" → skip the review request if they ordered again (they are already happy)
Flow 3: Browse Abandonment
1. Go to Klaviyo → Flows → Create Flow → Browse Abandonment (use template) 2. Trigger: Viewed Product metric 3. Wait 30 minutes → Check: "Has NOT started checkout since flow start" → send email 4. Email content: Use Klaviyo's Dynamic Product Block — it automatically shows the exact product the customer was viewing with live price and image 5. Wait 24 hours → Send follow-up with "Others also liked" product recommendations
Flow 4: Win-Back / At-Risk
1. Go to Klaviyo → Flows → Create Flow → Win Back (use template) 2. Trigger: Segment membership → "Predicted Churn Risk: High" (Klaviyo calculates this automatically) 3. Default 3-step structure:
- Email 1 (day 0): "We miss you" + personalized product recommendations based on past purchases
- Email 2 (day 7): Social proof + new arrivals
- Email 3 (day 14): Final offer with 10–15% discount + expiry date
4. Add a flow filter: "Has not placed an order" — this ensures the flow stops immediately when the customer converts
---
WooCommerce with AutomateWoo
1. Go to AutomateWoo → Workflows → Add Workflow
Welcome Series:
- Trigger: Customer → Created (new customer account) or Subscribe → Newsletter (with your email plugin)
- Add 3 email actions with timing delays: immediate, +2 days, +7 days
- Use AutomateWoo's variable system to insert first name, recommended products, etc.
Post-Purchase Series:
- Trigger: Order → Status Changed to Completed
- Action 1 (immediate): Send order confirmation email
- Action 2 (+7 days): Send review request (AutomateWoo integrates with YITH Reviews and WooCommerce Reviews Pro)
- Add Rule: "Order was placed by this customer" and "Customer has not created a review for this order"
Browse Abandonment:
- Requires: AutomateWoo Pro + the Browse Abandonment add-on
- Trigger: Browse Abandonment → Wait 30 minutes → Send email with viewed product
Win-Back:
- Trigger: Customer → Win Back (built-in AutomateWoo trigger)
- Set "No order in the last" to 60 days
- Create 3 workflows for escalating interventions at 60, 90, and 120 days
---
BigCommerce
1. Install Klaviyo from the BigCommerce App Marketplace 2. Klaviyo syncs all BigCommerce order and browse events automatically 3. Follow the same Klaviyo flow setup as the Shopify instructions above 4. BigCommerce does not have an equivalent to Shopify's native abandoned checkout emails — Klaviyo handles this entirely
---
Custom / Headless
Send behavioral events to Klaviyo's API, then build flows in Klaviyo's visual editor:
// Track key events via Klaviyo's Events API
async function trackKlaviyoEvent(email: string, eventName: string, properties: object) {
await fetch('https://a.klaviyo.com/api/events/', {
method: 'POST',
headers: {
'Authorization': `Klaviyo-API-Key ${process.env.KLAVIYO_PRIVATE_KEY}`,
'Content-Type': 'application/json',
'revision': '2024-10-15',
},
body: JSON.stringify({
data: {
type: 'event',
attributes: {
metric: { data: { type: 'metric', attributes: { name: eventName } } },
profile: { data: { type: 'profile', attributes: { email } } },
properties,
time: new Date().toISOString(),
},
},
}),
});
}
// Usage:
await trackKlaviyoEvent(customer.email, 'Viewed Product', {
ProductName: product.name, ProductID: product.id,
ImageURL: product.imageUrl, URL: product.url, Price: product.price,
});
await trackKlaviyoEvent(customer.email, 'Placed Order', {
$value: order.subtotal, OrderId: order.id,
Items: order.lineItems.map(i => ({ ProductName: i.name, ItemPrice: i.price })),
});Once events are flowing into Klaviyo, build the flows in Klaviyo's visual editor using the same instructions as the Shopify section.
Step 3: Configure suppression and frequency rules
In Klaviyo:
- Enable Smart Sending on all flows: go to each flow, click the email step, and toggle "Smart Sending" on — this prevents sending more than 1 email per 16-hour window to any contact
- Set a daily message limit under Account Settings → Email Sending Limits
- Add "Unsubscribed from email" as a Flow Filter on every flow — contacts unsubscribed from Klaviyo will not receive flow emails regardless, but this makes the logic explicit
In AutomateWoo:
- Go to AutomateWoo → Settings → Email and configure sending limits
- Add "Customer has unsubscribed" as a Rule on every workflow
Step 4: Measure flow performance
| Flow | Key Metric | Target |
|---|---|---|
| Welcome Series | Revenue per recipient | $1–$5 for first 30 days |
| Post-Purchase | Review submission rate (email 4) | 5–15% |
| Browse Abandonment | Recovered revenue per trigger | $2–$8 |
| Win-Back | Re-activation rate | 5–10% of at-risk contacts |
In Klaviyo: go to Flows → [Flow name] → Analytics to see revenue, open rate, and click rate per email step.
Best Practices
- Use Klaviyo's pre-built flow templates — they incorporate years of industry learning; customize the copy and design, not the flow structure
- Cancel competing flows — in Klaviyo, use flow filters: "Has placed an order since starting flow → exit flow"; this prevents win-back emails reaching customers who just bought
- Personalize with dynamic product blocks — Klaviyo's product recommendation blocks automatically show the right products based on browsing and purchase history
- Respect quiet hours — Klaviyo's Smart Sending respects timezone-based sending windows; enable it on all flows
- A/B test subject lines — Klaviyo's built-in A/B test sends to 20% of recipients and picks the winner automatically; use it on every flow's first email
Common Pitfalls
| Problem | Solution |
|---|---|
| Win-back emails sent after customer just placed an order | Add flow filter: "Has NOT placed order since starting flow" — Klaviyo exits the contact from the flow immediately |
| Browse abandonment fires for anonymous visitors | Gate the Viewed Product event on having a known email (logged-in user or cookie-captured email); Klaviyo only tracks identified profiles |
| Duplicate emails sent when Shopify webhooks fire twice | Klaviyo's flow deduplication prevents this automatically — each profile enters a flow only once per trigger period |
| High unsubscribe rate on win-back step 3 | 120-day lapsed customers are cold; lead with value (new arrivals, social proof) before the discount |
| Email renders broken on Outlook | Use Klaviyo's email templates — they are pre-tested across clients; avoid custom HTML in Outlook without Litmus testing |
Related Skills
- @cart-abandonment-recovery
- @sms-marketing
- @email-list-segmentation
- @customer-retention-engine
- @push-notifications
{
"context": "Tests whether the agent correctly implements browse abandonment with debounce logic and either the custom BullMQ flow or the Klaviyo Track API integration, including correct event properties, payload structure, and the anonymous visitor gate.",
"type": "weighted_checklist",
"checklist": [
{
"name": "30-minute trigger delay",
"max_score": 10,
"description": "The browse abandonment first email is scheduled with a 30-minute delay (1800000ms or 30*60000) from the product view event"
},
{
"name": "Debounce: remove existing job",
"max_score": 12,
"description": "Before scheduling a new browse-abandon job for a customer, any previously queued browse-abandon step-0 job for that customer is removed/cancelled"
},
{
"name": "Browse abandon 2 steps",
"max_score": 6,
"description": "The browse abandonment flow schedules exactly 2 email steps"
},
{
"name": "Step 1 delay is 25 hours",
"max_score": 10,
"description": "The second browse-abandon email (step 1) is scheduled 25 hours after the product view (90000000ms or 25*3600000), NOT 24 hours"
},
{
"name": "Step 1 includes withReviews",
"max_score": 8,
"description": "The step 1 (second) browse abandonment email payload includes withReviews: true"
},
{
"name": "4 related products",
"max_score": 8,
"description": "The browse abandonment payload fetches or requests exactly 4 related products (getRelatedProducts or equivalent called with count=4)"
},
{
"name": "Anonymous visitor gate",
"max_score": 8,
"description": "The browse abandonment trigger is gated on a known/authenticated customer ID — the code checks that the customer is identified (logged in or has captured email) before scheduling, or documents this requirement"
},
{
"name": "Klaviyo package import",
"max_score": 8,
"description": "If Klaviyo integration is implemented, imports from 'klaviyo-api' (ApiClient, EventsApi, EventCreateQueryV2) — does NOT use a generic fetch/axios call or unofficial SDK"
},
{
"name": "Klaviyo auth setup",
"max_score": 7,
"description": "If Klaviyo is used, the API key is set via ApiClient.instance.authentications['Klaviyo-API-Key'].apiKey"
},
{
"name": "Klaviyo Viewed Product properties",
"max_score": 10,
"description": "If Klaviyo is used, the 'Viewed Product' event includes all six properties: ProductName, ProductID, Categories, ImageURL, URL, Price"
},
{
"name": "Cancel browse-abandon on cart",
"max_score": 13,
"description": "An onCartItemAdded (or equivalent) function removes all pending browse-abandon jobs for the customer when they add to cart"
}
]
}
Browse Abandonment Email System
Problem/Feature Description
Verdant Supply Co. sells premium outdoor gear online. Their analytics show that roughly 40% of their logged-in customers view a product detail page and then leave without adding anything to their cart. The marketing team wants to follow up those browsing sessions with targeted emails that reference the specific product the customer looked at, along with similar items they might like.
The tricky part is the timing: customers often browse multiple products in a single session, and the team does not want to send a separate email for every product viewed — they want one follow-up per browsing session, triggered by the last product the customer viewed. If the customer comes back and adds something to their cart before the email fires, the email should not send at all.
The system should work with either their custom BullMQ queue (already set up as emailQueue from ./email-queue) or, if they later switch to Klaviyo, via Klaviyo's native flow triggering. Implement the browsing event handler and, as an alternative path, a Klaviyo-compatible integration function.
Note: This feature should only activate for customers who are signed in — do not trigger it for anonymous sessions.
Output Specification
Produce the following TypeScript source files:
src/flows/browse-abandon.ts— the main browse abandonment handler using the BullMQ approach. ExportonProductViewed(customerId, productId)andonCartItemAdded(customerId).src/integrations/klaviyo.ts— the Klaviyo Track API integration. ExporttrackKlaviyoEvent(email, eventName, properties)and atrackProductViewed(customer, product)function that fires the appropriate Klaviyo event for browse abandonment.
Stub any database calls with TODO comments. Types can be defined inline.
{
"context": "Tests whether the agent correctly models the email event schema and queue infrastructure for a triggered email system. Covers the core EmailEvent interface shape, BullMQ queue setup, the scheduleEmail helper, and worker configuration with deduplication and cleanup guards.",
"type": "weighted_checklist",
"checklist": [
{
"name": "EmailEvent type union",
"max_score": 8,
"description": "The EmailEvent type/interface includes a 'type' field with exactly the union 'welcome' | 'post_purchase' | 'win_back' | 'browse_abandon'"
},
{
"name": "EmailEvent required fields",
"max_score": 8,
"description": "The EmailEvent type/interface contains all six required fields: customerId, email, firstName, payload, scheduledAt, flowStep"
},
{
"name": "payload typed as Record",
"max_score": 6,
"description": "The payload field is typed as Record<string, unknown> (or equivalent generic map type) rather than a specific object shape"
},
{
"name": "flowStep is 0-indexed",
"max_score": 5,
"description": "A comment or documentation note indicates flowStep is 0-indexed"
},
{
"name": "BullMQ or SQS queue",
"max_score": 10,
"description": "Uses BullMQ Queue (or SQS or equivalent job queue library) to schedule delayed email sends — does NOT just use setTimeout or setInterval"
},
{
"name": "Deterministic jobId pattern",
"max_score": 12,
"description": "Jobs are added with a jobId following the pattern `${type}-${customerId}-step${flowStep}` (or equivalent interpolation of type, customerId, and step index)"
},
{
"name": "removeOnComplete set",
"max_score": 7,
"description": "Queue add options include removeOnComplete: true"
},
{
"name": "delay parameter used",
"max_score": 8,
"description": "Queue add options include a delay parameter (in milliseconds) passed from the scheduleEmail function"
},
{
"name": "Unsubscribe check in worker",
"max_score": 10,
"description": "The worker or send function checks opt-out/unsubscribe status before sending each email (NOT only at trigger time)"
},
{
"name": "Daily send cap guard",
"max_score": 10,
"description": "The worker implements a daily send-count limit per customer (max 2 or 3 emails per customer per day)"
},
{
"name": "Redis connection config",
"max_score": 8,
"description": "The queue and worker are configured with a Redis connection using process.env.REDIS_HOST"
},
{
"name": "Worker processes queue",
"max_score": 8,
"description": "A Worker instance is created that processes the same named queue as the Queue instance (matching queue name string)"
}
]
}
Email Automation Core Infrastructure
Problem/Feature Description
Florette, a mid-sized DTC skincare brand, is migrating away from its ESP's built-in automation builder into custom code so the engineering team has full control over timing, suppression logic, and analytics. The platform lead wants a typed TypeScript module that models all triggered email flows through a single, uniform event structure and a queue-based scheduler. The goal is to have one consistent "shape" that every flow (welcome series, post-purchase, win-back, browse abandonment) sends through — making it easy to add new flows or change ESP providers in the future.
The existing codebase already handles order management and customer records, but has no email automation infrastructure at all. The team wants to start from scratch with best practices around deduplication (preventing duplicate sends if a webhook fires twice), daily frequency caps (customers should not be overwhelmed), and a clean worker pattern that enforces suppression checks centrally.
Output Specification
Produce the following TypeScript source files:
src/email-types.ts— defines the EmailEvent type/interface and any supporting typessrc/email-queue.ts— sets up the queue, exports ascheduleEmailfunction, and creates the BullMQ worker that processes and sends emails (include a stubrenderTemplateandgetSubjectfunction — they don't need real implementations)src/email-worker.tsOR integrate the worker intoemail-queue.tsif preferred
The files should be production-ready TypeScript stubs: correct types, proper imports, and documented logic — but external calls (DB, ESP API) can be stubbed with TODO comments.
Do not use setTimeout or setInterval for scheduling — use a proper job queue.
{
"context": "Tests whether the agent implements the four email flow trigger functions with correct step counts, timing delays, template names, and cancellation logic when a customer converts. Covers the welcome series, post-purchase nurture, and win-back campaign specifications.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Welcome series 3 steps",
"max_score": 6,
"description": "The welcome flow schedules exactly 3 email steps (not 2, not 4)"
},
{
"name": "Welcome step delays",
"max_score": 8,
"description": "Welcome step delays are: step 0 = 0ms (immediate), step 1 = 2 days (172800000ms or 2*86400000), step 2 = 7 days (604800000ms or 7*86400000)"
},
{
"name": "Welcome trigger event",
"max_score": 5,
"description": "The welcome flow is documented or triggered on customer.created or newsletter signup event"
},
{
"name": "Post-purchase 4 steps",
"max_score": 6,
"description": "The post-purchase flow schedules exactly 4 email steps"
},
{
"name": "Post-purchase step delays",
"max_score": 8,
"description": "Post-purchase delays are: step 0 = 0ms, step 1 = 2 days, step 2 = 7 days, step 3 = 21 days (1814400000ms or 21*86400000)"
},
{
"name": "Post-purchase template names",
"max_score": 7,
"description": "Post-purchase steps reference the four templates: 'order-confirmation', 'shipping-update', 'delivery-check-in', 'review-request' (in any form: string constants, enum values, or comments)"
},
{
"name": "Cancel win-back on purchase",
"max_score": 10,
"description": "The post-purchase trigger function (or order.paid handler) cancels/removes pending win-back jobs for the customer before scheduling new emails"
},
{
"name": "Win-back 3 thresholds",
"max_score": 8,
"description": "The win-back cron targets customers at exactly 3 thresholds: 60, 90, and 120 days since last order"
},
{
"name": "Escalating win-back discounts",
"max_score": 10,
"description": "Win-back discount amounts escalate: 10% at 60 days, 15% at 90 days, 20% at 120 days"
},
{
"name": "Win-back expiry",
"max_score": 6,
"description": "Win-back discount payload includes expiresInDays: 7"
},
{
"name": "Win-back cron schedule",
"max_score": 8,
"description": "The win-back cron is scheduled at 8:00 AM daily (cron expression '0 8 * * *' or equivalent comment/annotation)"
},
{
"name": "Skip already-queued win-back",
"max_score": 8,
"description": "Before enqueuing a win-back job, the code checks whether a job with that ID already exists and skips if it does"
},
{
"name": "Cancel browse-abandon on cart add",
"max_score": 10,
"description": "An onCartItemAdded handler (or equivalent) removes pending browse-abandon jobs for the customer when they add to cart"
}
]
}
Lifecycle Email Flow Triggers
Problem/Feature Description
Harbour & Lane is a home goods e-commerce company that has just finished building a BullMQ-based email queue infrastructure. Now they need to implement the actual trigger functions that feed events into that queue. Their CRM team has outlined four lifecycle moments they care about: new customer signup, order completion, customers who haven't bought in a while, and customers who viewed products without purchasing.
The engineering lead wants each trigger function to schedule the right number of email steps at the right time intervals — no more, no less. She is particularly concerned about two failure modes they have seen with competitors: (1) customers receiving a "we miss you" email the day after placing an order because the win-back job wasn't cancelled, and (2) customers receiving a flood of emails because a duplicate webhook fired the same trigger twice. The implementation must handle both cases correctly.
The company runs at international scale, so the win-back campaign needs to run on a schedule that catches up with lapsed customers systematically.
Output Specification
Produce the following TypeScript source files. Assume a scheduleEmail(event, delayMs) helper and an emailQueue BullMQ Queue instance are already available (import them from ./email-queue).
src/flows/welcome.ts— exportstriggerWelcomeSeries(customer)src/flows/post-purchase.ts— exportstriggerPostPurchaseFlow(order)src/flows/win-back.ts— exportsenqueueWinBackCandidates()with the nightly cron commentsrc/flows/cancellation.ts— exports handlers for the conversion and browsing events that cancel competing flows
Types can be stubbed inline (e.g., interface Customer { id: string; email: string; firstName: string; welcomeCode: string; }). Database calls can be stubs with TODO comments. Focus on the scheduling logic and timing.
{
"name": "finsi/email-marketing-automation",
"version": "0.1.0",
"summary": "Triggered email flows — welcome, post-purchase, win-back, browse abandonment",
"skills": {
"email-marketing-automation": {
"path": "SKILL.md"
}
}
}