
Seasonal Campaign Automation
- 65 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Automate seasonal marketing for Black Friday, holidays, and shopping events with templated workflows, countdown sequences, and year-round planning.
About
Automates seasonal campaigns for events like Black Friday and holidays using templated workflows, countdown sequences, and a year-round calendar. A developer uses it to plan and run recurring seasonal promotions.
- Templated workflows and countdown sequences for peak events
- Year-round seasonal planning calendar
Seasonal Campaign Automation by the numbers
- 65 all-time installs (skills.sh)
- Ranked #525 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 seasonal-campaign-automationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 65 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Automate seasonal marketing for Black Friday, holidays, and shopping events with templated workflows, countdown sequences, and year-round planning.
Files
Seasonal Campaign Automation
Overview
Seasonal peaks — Black Friday/Cyber Monday, holiday gifting, Valentine's Day, Back-to-School — can account for 30–50% of annual ecommerce revenue for many categories. Automating seasonal campaigns means building reusable email flows and discount templates that activate on calendar triggers rather than requiring manual setup each time. Klaviyo's scheduled sends and Shopify's automated discounts handle most of this without custom code.
When to Use This Skill
- When planning Black Friday/Cyber Monday campaigns and needing a structured playbook
- When seasonal campaigns are built from scratch every year with no reusable templates
- When wanting to automate countdown email sequences for a sale event
- When needing to schedule promotional pricing changes in sync with email and ad campaigns
Core Instructions
Step 1: Define the seasonal campaign calendar
Plan your full campaign year in a single document. The most important ecommerce events by revenue impact:
| Event | Timing | Lead Time Needed |
|---|---|---|
| Black Friday / Cyber Monday | 4th Friday of November | 8 weeks |
| Holiday gifting (Christmas) | Dec 1–20 | 6 weeks |
| Valentine's Day | Feb 14 | 4 weeks |
| Mother's Day | 2nd Sunday of May | 4 weeks |
| Back-to-School | August | 6 weeks |
| Summer Sale | July | 4 weeks |
Build email templates and discount codes for each event in September (for BFCM) or 8 weeks in advance for other events.
Step 2: Build your BFCM email sequence
---
Shopify with Klaviyo
1. Go to Klaviyo → Campaigns → Create Campaign and create a folder called "BFCM 2026" 2. Build the following scheduled campaigns (all as one-time sends):
| Campaign Name | Send Date | Segment | Subject |
|---|---|---|---|
| BFCM Teaser | T-21 | All subscribers | "Something big is coming…" |
| BFCM Preview | T-14 | All subscribers | "Early access preview — save the date" |
| VIP Early Access | T-7 | VIP segment (loyalty Gold+) | "25% off — yours 24h early" |
| Countdown (3 days) | T-3 | All subscribers | "3 days until Black Friday" |
| Launch Eve | T-1 | All subscribers | "Tomorrow: our biggest sale of the year" |
| Launch Day | T-0 (8am) | All subscribers | "25% off everything — Black Friday is live" |
| Cyber Monday | T+3 | Non-purchasers from above | "Last chance: Cyber Monday sale ends tonight" |
3. Build each email template now, save as a reusable Klaviyo template — next year you update the discount value and dates only 4. Add a countdown timer image to launch day and Cyber Monday emails using Klaviyo's native countdown timer block (available under Content → Dynamic Content)
Segment non-purchasers for Cyber Monday: In Klaviyo, create a segment: "Has received BFCM Launch campaign AND has NOT placed an order since [T-0 date]" — send Cyber Monday email only to this group
---
WooCommerce with Klaviyo (or Mailchimp)
1. Connect Klaviyo to WooCommerce using the Klaviyo plugin or the Klaviyo for WooCommerce extension 2. Build the same campaign sequence as above in Klaviyo — the campaign structure is platform-agnostic 3. For customers using Mailchimp: build a Customer Journey map with scheduled email nodes at the same offsets
Alternative (simpler): Use AutomateWoo workflows triggered by dates — set up date-based triggers for each campaign step.
---
BigCommerce with Klaviyo
1. Klaviyo syncs all BigCommerce order history automatically 2. Follow the same Klaviyo campaign setup as the Shopify section above 3. For the VIP segment: use Klaviyo's Total Customer Value > $500 predicate to identify high-value customers
---
Step 3: Schedule promotional pricing
---
Shopify
1. Go to Shopify Admin → Discounts → Create Discount → Amount off products 2. Configure:
- Discount type: Percentage (e.g., 25%)
- Applies to: All products (or specific collections)
- Active dates: Set start = Black Friday 12:00am, End = Cyber Monday 11:59pm
- Shopify automatically activates and deactivates the discount at the set times
3. For automatic discount (applied at checkout without code): go to Shopify → Discounts → Create Automatic Discount 4. For a sitewide sale with original/sale price displayed on product pages: use Shopify → Products → Bulk Edit to set compare_at_price = original price and price = sale price; then revert after the sale
Important: If using a discount code approach (recommended for BFCM), announce the code in every email and across social — friction from requiring a code reduces conversion; automatic discounts convert better.
---
WooCommerce
1. Go to WooCommerce → Marketing → Coupons → Add Coupon 2. Configure:
- Coupon type: Percentage discount
- Coupon amount: 25
- Usage restriction: No minimum order (for BFCM)
- Usage limits: Leave blank (unlimited)
- Expiry date: Cyber Monday 11:59pm
3. For automatic pricing on all products: use WooCommerce → Products → Bulk Edit to set sale price, then bulk-remove it after the event
---
BigCommerce
1. Go to BigCommerce → Marketing → Promotions → Create Promotion 2. Configure the percentage off, start date, and end date 3. BigCommerce applies and removes promotions automatically based on the schedule
---
Step 4: Set up SMS for seasonal events
Send 1–2 SMS per event maximum. More than 2 SMS in a 4-day period causes significant unsubscribe spikes.
In Klaviyo SMS: 1. Create an SMS campaign alongside the email campaigns 2. Best send times: Launch day at 9am (first SMS), Cyber Monday morning (second SMS if not purchased) 3. Subject for launch day: "BFCM is live — 25% off everything: [store link] — Reply STOP to opt out"
In Postscript (Shopify): 1. Go to Postscript → Campaigns → Create Campaign 2. Set the audience to "All subscribed" minus "Has purchased in last 3 days" 3. Schedule for launch day at 9am local time
Step 5: Warm up your email domain before BFCM
Sending a sudden volume spike on Black Friday damages your domain reputation. Start 4 weeks before:
1. In Klaviyo, increase sends by 10% per week starting in October 2. Go to Klaviyo → Account → Sending Infrastructure and check your sending domain health 3. Suppress unengaged contacts (no opens in 90+ days) before BFCM — sending to cold contacts tanks deliverability
Step 6: Measure seasonal lift
| Metric | Target | Where to Find |
|---|---|---|
| Revenue vs. prior year (YoY growth) | 15–30% YoY | Shopify Analytics → Compare dates |
| Email revenue during event | 30–40% of event revenue | Klaviyo → Campaign analytics |
| Email list unsubscribe rate (BFCM week) | < 0.5% per campaign | Klaviyo campaign analytics |
| Discount code redemption rate | > 25% of emails clicked | Shopify → Analytics → Discounts |
Best Practices
- Build templates in September, send in November — last-minute BFCM campaigns have poor creative and poor deliverability; email templates must be ready 8 weeks out
- Cap at 4–5 emails total across BFCM week — sending 7+ emails causes significant unsubscribe spikes; 4 is the comfortable maximum per customer
- Give VIPs 24-hour early access — loyalty Gold and Platinum customers get the same discount 24 hours before the public launch; this is the most cost-effective way to make your best customers feel valued
- Use automatic discounts for conversion rate — removing the friction of entering a coupon code typically increases conversion rate 10–15% compared to code-based discounts
- Schedule the pricing rollback before launch — setting the end date in Shopify's discount scheduler before BFCM starts prevents the most common post-sale mistake (forgetting to turn off the discount)
Common Pitfalls
| Problem | Solution |
|---|---|
| Discount still active after Cyber Monday | Always set an end date/time in the discount or promotion settings before launching |
| BFCM email going to spam | Suppress unengaged contacts 2 weeks before BFCM; use a confirmed warm sending domain |
| VIP early access email sent to entire list | Build the VIP segment in Klaviyo before scheduling the campaign; verify segment size before sending |
| Countdown timer shows wrong timezone | Use Klaviyo's native countdown timer which adapts to the recipient's timezone automatically |
| Email sequence sending to already-purchased customers | Add a flow filter to all post-launch emails: "Has NOT placed an order since [launch date] → continue" |
Related Skills
- @email-marketing-automation
- @product-launch-campaigns
- @sms-marketing
- @meta-ads-integration
- @loyalty-program-optimization
{
"context": "Tests whether the agent correctly implements the SeasonalCampaign and SeasonalEmailStep TypeScript interfaces with correct field names and value types, applies the correct audience segmentation strategy (VIPs get early access, lapsed customers get re-engagement framing, prospects get maximum discount), caps email volume appropriately, uses a sitewide single discount code, and plans the campaign timeline sufficiently in advance.",
"type": "weighted_checklist",
"checklist": [
{
"name": "SeasonalCampaign interface shape",
"max_score": 8,
"description": "The campaign object includes all required fields: id, name, type, startDate, endDate, discountType, discountValue, segments, channels, cadence, status"
},
{
"name": "discountType enum value",
"max_score": 8,
"description": "discountType is set to 'percent_off' (not a custom string like 'percentage' or 'pct')"
},
{
"name": "status field value",
"max_score": 5,
"description": "status is set to one of: 'planned', 'active', 'completed', or 'paused' — not a freeform string"
},
{
"name": "campaign type field value",
"max_score": 5,
"description": "type field is set to one of: 'sale', 'launch', 'gifting', or 'awareness'"
},
{
"name": "SeasonalEmailStep interface shape",
"max_score": 8,
"description": "Each cadence step includes: offsetDays (number), template (string), subject (string), incentive (boolean); segment is optional"
},
{
"name": "offsetDays convention",
"max_score": 8,
"description": "Steps before the sale start use negative offsetDays values; steps during or after use zero or positive values"
},
{
"name": "VIP early access step",
"max_score": 10,
"description": "At least one cadence step targets a VIP or loyalty segment with early access (incentive: true and a VIP-specific segment override)"
},
{
"name": "Lapsed customer re-engagement framing",
"max_score": 8,
"description": "planning-notes.md or campaign config acknowledges lapsed customers as a distinct segment with re-engagement framing (not the same messaging as general subscribers)"
},
{
"name": "Email volume cap",
"max_score": 10,
"description": "The cadence contains no more than 4 email steps that send to the general audience during the sale period (i.e., from day -1 through day +4 of the sale)"
},
{
"name": "Single sitewide discount code strategy",
"max_score": 8,
"description": "planning-notes.md or code comments specify using a single sitewide discount code (not unique per-customer codes)"
},
{
"name": "Multi-channel definition",
"max_score": 5,
"description": "channels array includes at least two of: 'email', 'sms', 'push', 'paid-social' (uses the canonical string literals from the interface)"
},
{
"name": "Campaign template lead time",
"max_score": 10,
"description": "planning-notes.md mentions building/finalising campaign templates at least 6-8 weeks before the sale date (i.e., in December for a February sale), not in the week before"
},
{
"name": "Domain warmup plan",
"max_score": 7,
"description": "planning-notes.md includes a domain warm-up recommendation: increasing send volume gradually (e.g., 10% per week) starting at least 4 weeks before the sale"
}
]
}
Valentine's Day Campaign Setup
Problem/Feature Description
Botanica & Bloom, an online florist and gifting brand, runs Valentine's Day as their biggest revenue event of the year — often accounting for 40% of Q1 sales. For the past three years the marketing team has built the campaign from scratch each February: writing briefs, manually scheduling emails, and negotiating discount levels at the last minute. Last year, a key email went out a day late because a team member was ill, and a promotional code continued to work for two weeks after the sale ended because no one remembered to deactivate it.
The CTO has asked for a TypeScript-based campaign configuration system that can be reused each year. You have been brought in to implement the initial campaign definition and email sequence for Valentine's Day 2027 (the sale runs February 12–16, 2027). The company wants a 20% sitewide discount, a multi-step email warm-up starting three weeks out, and targeting rules that differentiate between their VIP loyalty members, lapsed customers who haven't ordered in 12 months, and general subscribers.
Output Specification
Produce a TypeScript file named campaign.ts that contains:
- The type/interface definitions needed to represent the campaign and its email steps
- A fully populated Valentine's Day 2027 campaign object using those types
- The email cadence should include at least 5 steps spanning from 3 weeks before to the last day of the sale
- A brief
planning-notes.mdexplaining key decisions around audience segmentation strategy and campaign timeline
The TypeScript file should be self-contained (no external imports needed beyond type definitions) and should compile without errors if types are used correctly.
{
"context": "Tests whether the agent implements a per-day contact frequency cap of 2 emails, monitors complaint rate against a 0.08% threshold, builds a server-side countdown timer with customer timezone support and no-cache headers, handles the expired countdown case correctly, and measures campaign performance using year-over-year comparison and pull-forward incrementality analysis.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Daily frequency cap: 2 emails",
"max_score": 10,
"description": "email-safety.ts enforces a maximum of 2 emails per customer per day (not 3, not 1, specifically 2)"
},
{
"name": "Complaint rate threshold",
"max_score": 10,
"description": "email-safety.ts or a related monitoring function checks the complaint rate against a 0.08% threshold and pauses or flags sending when this threshold is exceeded"
},
{
"name": "Complaint rate: numeric precision",
"max_score": 8,
"description": "The complaint rate threshold is 0.08% (i.e., 0.0008 as a decimal) — not 0.8% or 8%"
},
{
"name": "Countdown timer: server-side rendering",
"max_score": 8,
"description": "countdown-timer.ts calculates the remaining time on the server at request time — does NOT rely on client-side JavaScript or a fixed pre-rendered image"
},
{
"name": "Countdown timer: timezone from request",
"max_score": 10,
"description": "The countdown timer handler reads the timezone from the request query parameters (or headers) — does NOT hardcode a timezone like 'America/New_York'"
},
{
"name": "Countdown timer: no-cache headers",
"max_score": 8,
"description": "The countdown timer response includes Cache-Control: 'no-cache, no-store' (or equivalent) so each email open fetches a fresh countdown image"
},
{
"name": "Expired countdown redirect",
"max_score": 8,
"description": "When the sale has ended (diff <= 0), the countdown handler redirects to a static 'sale ended' image rather than showing a negative or zero countdown"
},
{
"name": "YoY comparison",
"max_score": 10,
"description": "campaign-report.ts compares current campaign revenue against the same date range in the prior year (year-over-year) — not a week-over-week or month-over-month comparison"
},
{
"name": "Incrementality vs pull-forward",
"max_score": 10,
"description": "analytics-notes.md or campaign-report.ts addresses whether revenue is truly incremental vs. demand pulled forward from a subsequent period (e.g., comparing December revenue in sale years vs. non-sale years)"
},
{
"name": "Parallel DB queries",
"max_score": 8,
"description": "campaign-report.ts fetches current revenue, prior year revenue, and email metrics in parallel (Promise.all or equivalent) rather than sequentially awaiting each"
},
{
"name": "Email metrics in report",
"max_score": 10,
"description": "campaign-report.ts returns at least: revenue, yoyGrowth percentage, email open rate or click rate, and revenue per email sent"
}
]
}
BFCM Campaign Infrastructure: Safety Controls and Analytics
Problem/Feature Description
Northfield Outdoors ran their Black Friday / Cyber Monday campaign last year and encountered three problems. During the sale, their email sender reputation tanked because they sent too many emails too quickly and their complaint rate spiked — ISPs started routing their messages to spam. They also discovered after the fact that some customers received both a win-back campaign and the general BFCM broadcast on the same day, which drove unsubscribes. Finally, their post-sale reporting only showed total revenue for the campaign week — the board questioned whether the sale cannibalized December sales or generated truly new revenue, and the marketing team had no way to answer.
You have been asked to address all three issues by building the safety and analytics layer for their email platform. Specifically: implement the logic that determines whether it is safe to send an email to a given customer on a given day, implement the API endpoint for the countdown timer image that is embedded in sale emails, and design the post-campaign performance report. All work should be in TypeScript.
Output Specification
Produce the following files:
email-safety.ts— Logic that checks whether a customer can receive an email today (contact frequency enforcement) and monitors sender healthcountdown-timer.ts— The Express route handler forGET /api/countdown-timerthat generates a countdown image for use in emailscampaign-report.ts— The function that calculates and returns post-campaign performance metricsanalytics-notes.md— A short explanation of how you chose to measure whether the campaign generated incremental revenue vs. pulled forward demand from a later period
Each file should include inline comments explaining the key safety/correctness decisions.
{
"context": "Tests whether the agent correctly implements promotional pricing with original price preservation, uses a database-level scheduled job (not in-memory queue) for pricing rollback so it survives server restarts, applies the correct discount calculation logic for each discount type, adds expiresAt to discount codes, and uses a cron-based deactivation strategy.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Original price stored",
"max_score": 10,
"description": "pricing.ts creates a priceOverrides (or equivalent) record that stores the originalPrice before applying the sale price — not just updating the product price in place"
},
{
"name": "priceOverrides schema fields",
"max_score": 8,
"description": "The override record includes: productId, campaignId, originalPrice, salePrice, and date range fields (activeFrom/activeTo or equivalent)"
},
{
"name": "Database-level rollback job",
"max_score": 14,
"description": "The rollback is scheduled via a database-level mechanism (e.g., a jobs/scheduled_tasks table, pg_cron, database-backed queue) — NOT an in-memory setTimeout or in-process scheduler that would be lost on server restart"
},
{
"name": "Rollback scheduled before launch",
"max_score": 8,
"description": "The rollback job is scheduled during pricing activation (at campaign start), not manually triggered after the sale ends"
},
{
"name": "percent_off calculation",
"max_score": 8,
"description": "applyDiscount for 'percent_off' computes: price * (1 - value/100), rounded to 2 decimal places"
},
{
"name": "fixed_amount calculation",
"max_score": 8,
"description": "applyDiscount for 'fixed_amount' uses Math.max(0, price - value) to prevent negative prices, rounded to 2 decimal places"
},
{
"name": "free_shipping handling",
"max_score": 8,
"description": "applyDiscount for 'free_shipping' returns the original price unchanged (free shipping is handled at order level, not by changing product price)"
},
{
"name": "expiresAt on discount codes",
"max_score": 10,
"description": "discount-codes.md specifies that discount codes must include an expiresAt (or equivalent expiry/end date) field set to the campaign end date"
},
{
"name": "Cron deactivation strategy",
"max_score": 10,
"description": "discount-codes.md describes using a cron job (or scheduled task) to automatically deactivate or expire codes at the end date — not relying on manual deactivation"
},
{
"name": "2 decimal precision",
"max_score": 8,
"description": "Discount calculations use parseFloat(...toFixed(2)) or equivalent to ensure prices are stored with exactly 2 decimal places"
},
{
"name": "Rollback job ID",
"max_score": 8,
"description": "The pricing rollback job uses a deterministic job ID tied to the campaign (e.g., 'pricing-rollback-{campaignId}') to prevent duplicate rollback jobs"
}
]
}
Summer Sale Pricing Engine
Problem/Feature Description
Coastal Surf Co. runs a major Summer Sale every July — 15% off all surfboards and accessories for one week. Last year's sale was a commercial success, but it created two operational headaches. First, when the sale ended, three products were left at sale price for an additional 11 days because the engineer who was supposed to revert prices was on vacation and the task fell through the cracks. Second, a 15% discount code that was created for the sale was still being redeemed in September because it had no expiry — this went unnoticed until a finance review.
You have been asked to build the pricing automation module in TypeScript. It should: activate sale pricing across all eligible products at the start of the campaign, persist enough information to restore original prices afterwards, and ensure the price reversion happens automatically at campaign end without any manual intervention. You also need to implement the discount calculation logic for at least three discount types. Finally, document how discount codes created for the sale should be configured to prevent the September problem from happening again.
Output Specification
Produce the following files:
pricing.ts— TypeScript implementation of the pricing activation function, discount calculation function, and the rollback scheduling approachdiscount-codes.md— A short document describing the required fields and configuration for sale discount codes, including how expiry is handled and how codes are deactivated after the sale
The code in pricing.ts should clearly show: how original prices are preserved, how the rollback is scheduled, and the discount calculation for percent_off, fixed_amount, and free_shipping types. Add inline comments where the approach differs from the naive implementation.
{
"name": "finsi/seasonal-campaign-automation",
"version": "0.1.0",
"summary": "Automate seasonal marketing campaigns for Black Friday, holidays, and shopping events with templated workflows, countdown sequences, and year-round planning",
"skills": {
"seasonal-campaign-automation": {
"path": "SKILL.md"
}
}
}