Sec. 03 — Campaign Runway · intake → QA → launch → readout
Prescription · this is what I would build; the worked SportDOG example below is hypotheticalEvery promo is a runway, not a launch.
The JD's operator-voice sentence is my favorite line in the whole post: "Shopify changes, campaigns, promotions, and site initiatives launch accurately and on time, with strong pre- and post-launch QA." That's the outcome I'd sign up to be measured on. Here's how I'd get there — a single template that scales across brands, one shared UTM taxonomy, one QA gate.
Intake template — one page, one form
Marketing, merch, and brand shouldn't have to know how the sausage is made. They fill out this form; I make it real.
# campaign-brief.md — filed in the ops backlog on submit brand: PetSafe | SportDOG | Invisible Fence | ScoopFree | ... campaign_name: SportDOG SD-425X Fall Hunt Promo offer: $40 off, code HUNT40, min $199 skus_in_scope: SD-425X, SD-1225X, SD-1825X (3 SKUs, 1 collection) go_live: 2026-10-01 06:00 America/New_York end: 2026-10-14 23:59 America/New_York channels: Klaviyo email, on-site banner, paid social, organic social success_metric: Incremental units on the 3 SKUs vs. prior-4-week baseline approver: CRM lead + brand owner (JD boundary rule)
UTM taxonomy — one page, hard rules
The single most common cause of "the numbers don't agree" is inconsistent UTMs. This is the rule sheet I'd publish and enforce.
# All lowercase. No spaces. No punctuation. Hyphens only. utm_source = the platform: klaviyo | meta | google | tiktok | pinterest | affiliate-{name} utm_medium = the channel type: email | sms | paid-social | paid-search | organic-social | display utm_campaign = {brand}-{offer-slug}-{yyyy-mm} e.g. sportdog-hunt40-2026-10 utm_content = the creative: {format}-{variant} e.g. hero-a | carousel-v2 | button-primary utm_term = paid only: keyword or audience id # Bonus: a 'sub_id' cookie captured at first-touch for later attribution reconciliation.
Why this matters for Tableau
utm_campaign is the join key across Klaviyo, Triple Whale, GA4, and Shopify. If it's inconsistent, every dashboard downstream lies quietly. The rule above is boring on purpose.
Pre-launch QA — the 20-line gate
Nothing goes live until every box is checked. I ran a version of this for United Textile Wholesale's Shopify migration; the shape is the same regardless of stack.
Pre-launch QA — SportDOG SD-425X Fall Hunt Promo (worked example)
- Discount code
HUNT40created, $ off, min $199, correct SKUs, correct dates (Shopify admin)Shopify - Collection page shows the 3 SKUs, correct order, correct pricing, no orphaned productsShopify
- Product page copy & images updated in Sanity, published to preview URL, brand-owner approval loggedSanity
- Homepage banner links to the collection with correct UTMs (per taxonomy sheet)Sanity + UTM
- Klaviyo send list segmented, subject line + preview text spellchecked, links carry correct UTMsKlaviyo
- Paid social creative delivered to media buyer with the exact UTM string, not a BitlyPaid
- GA4 event for
select_promotionfiring on the banner (checked in DebugView)GA4 - Triple Whale campaign named to match
utm_campaignexactlyTriple Whale - Tableau: baseline snapshot for the 3 SKUs (prior 4 weeks) exported and pinnedTableau
- Bazaarvoice widget still loading on the 3 PDPs; no missing reviews behind the discountBazaarvoice
- Rollback plan written: 3 steps, 5 minutes, doesn't require a developerOps
- All of the above logged in the campaign brief; brief timestamped and posted in the channelPM
Post-launch — day 1, day 7, close
15 minutes at go-live, then a check at hour 2 and hour 6
Look at three numbers: sessions to the collection, code applications, orders. If any is zero when it shouldn't be, walk the funnel with the UTM.
Half-page readout in the channel, in prose, not a dashboard link
What ran, what converted, one thing that surprised us, one thing to change in the second week. Written so a busy CRM lead reads it in 60 seconds.
One row in the promo library, one paragraph, versioned
Over a year, this library becomes the answer to "have we tried something like this before?" — which is the question that most often blocks a decision.
The pattern is portable
I've run the intake → QA → launch → close loop for seasonal apparel drops at United Textile Wholesale and for multi-location retail at Wild Bill's Tobacco. The names on the columns change; the shape doesn't.