MOUSA·BATARSEH Independent DTC ops review · PetSafe Brands

Sec. 03 — Campaign Runway · intake → QA → launch → readout

Prescription · this is what I would build; the worked SportDOG example below is hypothetical

Every 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 HUNT40 created, $ 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_promotion firing on the banner (checked in DebugView)GA4
  • Triple Whale campaign named to match utm_campaign exactlyTriple 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

Day 1 · monitoring

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.

Day 7 · early read

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.

Close · learnings

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.

Worked example hypothetical SportDOG fall promo Real evidence Klaviyo, Sanity, Bazaarvoice live on petsafe.com Rollout one brand first, then the family