← back to Consulting Designerwallcoverings Com

docs/attribution-spec.md

136 lines

# Designer Wallcoverings — Attribution Spec

**Engagement:** DW consulting engagement (TK-19)
**Date:** 2026-07-26
**Status:** SPEC / PREP. The GA4 property already exists and is live (property `294178030`, measurement ID `G-53F1QBZSG0`, gtag collecting on the live store, Consent Mode v2 verified — commit 4bcdbe7), so the "create GA4 property" step is void. Still gated for Steve's approval: the Shopify discount codes and any live-theme changes (see `~/.claude/yolo-queue/pending-approval/`). No live theme is touched by this doc.
**Machine-readable companion:** [`data/attribution-scheme.json`](../data/attribution-scheme.json)
**Guiding decision:** DTD verdict **A — attribution before optimization**. You cannot optimize a channel mix you cannot measure; instrument first, then optimize.

---

## 1. The problem this solves

DW has **7 connected marketing channels** (Facebook, Instagram, TikTok, YouTube, Bluesky, Threads, LinkedIn) driving traffic to the flagship **LIVE Shopify store** (`designer-laboratory-sandbox.myshopify.com`, served at `www.designerwallcoverings.com`). The GA4 property **already exists** (property `294178030`, measurement ID **`G-53F1QBZSG0`**) and gtag is **installed and collecting** on the live store, so page-level and revenue data flow today. What's still missing for full channel attribution is a **consistent UTM stamp** on outbound links and the per-channel coupon path — without them, social conversions still collapse into "Direct / unknown."

Two attribution paths close the gap, and we run **both** because each fails where the other survives:

| Path | Mechanism | Survives | Fails when |
|------|-----------|----------|------------|
| **Click path (UTM)** | `utm_*` query params on every outbound link, read by GA4 | Description links, bio links, link cards | In-app browsers / dark social strip query strings |
| **Redemption path (coupon)** | Unique per-channel Shopify discount code | Any checkout, params stripped or not | Customer doesn't use a code |

Together they give a two-source triangulation: GA4 says "this session came from Instagram," Shopify says "this order redeemed `DW-IG-10`."

---

## 2. UTM taxonomy

Every link a DW channel emits to the store is stamped:

```
https://www.designerwallcoverings.com/{path}
    ?utm_source={source}
    &utm_medium={medium}
    &utm_campaign={campaign}
    &utm_content={content}
```

**Rules**
- **Lowercase** `source`/`medium`; **lowercase-kebab** for `campaign`/`content`. No spaces, no punctuation but hyphens. URL-encode everything.
- Always link to a canonical `www.designerwallcoverings.com/...` URL — never a raw `*.myshopify.com` URL — so GA4 hostname and cookies stay consistent.
- Reuse **one `utm_campaign` slug across channels** for a single campaign so GA4 can compare channels within it.

### `utm_source` — one canonical token per channel

`facebook` · `instagram` · `tiktok` · `youtube` · `bluesky` · `threads` · `linkedin`

(The short forms `fb`/`ig`/`yt` are **only** used inside coupon codes, never in URLs.)

### `utm_medium` — controlled vocabulary

| Value | Use |
|-------|-----|
| `social` | Organic feed / post |
| `social-bio` | Link-in-bio / profile link (IG, TikTok primary path) |
| `social-story` | Stories / Reels / Shorts |
| `paid-social` | Boosted / paid placement — files under **Paid Social** in GA4's default channel grouping, not Organic |
| `social-dm` | Direct-message / outreach links |

### `utm_campaign` — slug format

`<yyyy>-<qN>-<theme>` — e.g. `2026-q3-elitis-launch`, `2026-q3-staged-draft-drip`, or `always-on-brand` for evergreen.

### `utm_content` — creative / placement variant (optional, recommended)

`<format>-<descriptor>` — e.g. `reel-grasscloth-bedroom`, `carousel-thibaut-5`, `video-<slug>`. For multi-account channels (FB = 80 Pages, IG = 34 accounts) carry the account/page slug here for per-account attribution.

### `utm_term`

Reserved for paid keyword/audience on `paid-social` only. Omit for organic.

---

## 3. Per-channel table

| Channel | `utm_source` | Default `utm_medium` | Coupon code | Notes |
|---------|-------------|----------------------|-------------|-------|
| Facebook (80 Pages) | `facebook` | `social` | `DW-FB-10` | Per-Page: carry page slug in `utm_content`. |
| Instagram (34 accts) | `instagram` | `social-bio` | `DW-IG-10` | Feed posts unclickable → bio link is primary; in-app browser strips params, coupon is the reliable fallback. |
| TikTok | `tiktok` | `social-bio` | `DW-TT-SAMPLE` | Aggressive param stripping → coupon essential. |
| YouTube | `youtube` | `social` | `DW-YT-15` | Description + pinned-comment links carry full UTM; Shorts → `social-story`. |
| Bluesky | `bluesky` | `social` | `DW-BS-10` | Organic only, no paid surface. Low volume but still measurable vs "direct." |
| Threads | `threads` | `social` | `DW-TH-10` | Meta property; keep source distinct from FB/IG so Meta channels separate in GA4. |
| LinkedIn | `linkedin` | `social` | `DW-LI-TRADE` | B2B / to-the-trade lean → TRADE incentive instead of consumer % off. |

**Microsite fleet (~50 sister sites):** when they link into the flagship, stamp `utm_source=<microsite-slug>&utm_medium=referral-microsite`. Same convention, out of the per-channel table.

---

## 4. Coupon scheme

**Format:** `DW-<CHANNELCODE>-<OFFER>`

- **Channel codes:** FB · IG · TT · YT · BS · TH · LI
- **Offer tokens:** `SAMPLE` (memo sample), `10` / `15` (% off), `TRADE` (design-pro incentive)
- **One code per (channel × offer)** so redemptions bucket cleanly by channel.
- Keep offer economics **identical across channels within a campaign** so channel comparison isn't confounded.
- Codes are Shopify discount codes — **GATED**, drafted for approval, never created by this prep pass.
- Report from **Shopify Admin → Discounts** (redemptions per code) cross-referenced with GA4's item-level `coupon` param on `purchase`.

---

## 5. GA4 events & key events

**Automatic (enhanced measurement):** `page_view`, `scroll`, outbound `click`, `view_search_results`, `video_*`, `file_download`.

**Commerce events the Shopify theme must send** (via gtag / dataLayer): `view_item`, `add_to_cart`, `begin_checkout`, `add_shipping_info`, `add_payment_info`, `purchase`.

**DW custom events:** `request_sample`, `sample_added_to_cart`, `trade_account_signup`, `email_signup`, `quote_request`.

**Mark as Key Events (conversions):** `purchase`, `request_sample`, `begin_checkout`, `trade_account_signup`, `email_signup`.

**Coupon capture:** on `purchase`, pass the redeemed discount code in the `coupon` field so GA4 attributes revenue to the channel by coupon, matching §4.

> Event configuration is part of the **gated** GA4 setup (memo a). This section is the spec that setup implements.

---

## 6. Reporting views

1. **GA4 → Reports → Acquisition → Traffic acquisition**, grouped by *Session source / medium* — validates the taxonomy is landing correctly.
2. **GA4 → Explore → Free form:** *Session source/medium* × *Key events + Purchase revenue* — the core social-to-revenue table across all 7 channels.
3. **Shopify Admin → Discounts:** redemptions per `DW-<CHANNEL>-<OFFER>` — redemption-path cross-check.
4. **Campaign roll-up:** filter by `utm_campaign` to compare channels head-to-head within one campaign.

---

## 7. What's gated (not done here)

This spec is prep. The following are drafted to `~/.claude/yolo-queue/pending-approval/` and require Steve's go:

- **`attribution-ga4-property.md`** — ~~create the GA4 property + web data stream~~ **VOID / already done.** The GA4 property (`294178030`) and web data stream already exist and the live measurement ID is **`G-53F1QBZSG0`** — gtag is installed and collecting on the live store with Consent Mode v2 verified (commit 4bcdbe7). No property needs to be created; creating one would be a forbidden duplicate. Remaining GA4-side work is only marking the commerce/custom events as key events in GA4 Admin.
- **`attribution-gtag-install.md`** — already satisfied on the live store: gtag for `G-53F1QBZSG0` is installed and collecting on the live Shopify theme (Consent Mode v2 verified, commit 4bcdbe7). `snippets/gtag.html` is a reconciled local reference copy only — do not re-install it over the live theme.
- **Shopify discount codes** — creating the seven `DW-*` codes is a live Shopify write and would be drafted separately when Steve wants the coupon path armed.

Local rebuild + commit only. **No deploy** — deploy of the portal is noted in the GA4 memo.