fix(email): drop @TrackLink from per-subscriber CTAs (404 + collapse bug)

Listmonk @TrackLink registers ONE static URL per tracked link and points
every recipient's /link/<uuid> redirect at it. On per-subscriber hrefs
({{ lp_link }}, ?dot=, ?npi=, ?clia=) this is doubly broken:
 - the registered links.url was captured before the {{ lp_link }} token
   rendered, yielding /order/slug&utm_source=... (first &, no ?) -> 404
 - even when valid it collapses every carrier/provider onto the first
   subscriber's dot/npi/clia value

Real human clicks are already tracked via Umami campaign-click (bot
filtered), so Listmonk link tracking here is redundant and destructive.

Stripped @TrackLink from per-subscriber CTAs:
 - scripts/create_deficiency_source_campaigns.py (_cta, _dot_check_cta)
 - data/trucking_campaigns/{ucr,ifta}_*.html
 - data/hc_campaigns/*.html (10 templates)

Static CTAs (e.g. CRTC ?code= order link) keep @TrackLink (safe).
Live fix to the 10 broken registered links.url rows applied separately
(first & -> ?), backup in listmonk.pw_links_dkim_fix_bak_20260622.

Docs: new runbook incident section + corrected the disproven
'use @TrackLink on all CTAs' guidance in fmcsa/hc plans.
This commit is contained in:
justin 2026-06-22 17:01:39 -05:00
parent 1e9dcfcfd1
commit 3325259af7
16 changed files with 98 additions and 19 deletions

View file

@ -201,6 +201,65 @@ b.old_listmonk_sent_at FROM resend_dkim_backup_20260622 b WHERE c.dot_number =
b.dot_number;`. To resume normal warmup exclusion later, unset
`MAIN_EXCLUDE_OPERATORS` (reverts to Google+Microsoft+consumer-MX held to day 30).
### Incident: Jun 22 2026 — `@TrackLink` on per-subscriber CTAs = 404 + collapse
**Symptom.** The trucking "deficiency" CTA buttons (the primary order link and the
secondary DOT-check link) rendered as Listmonk tracking redirects
(`https://lists.performancewest.net/link/<uuid>/...`) that **404'd**. The redirect
target (registered in `links.url`) was `https://performancewest.net/order/boc3-filing&utm_source=...`
— note the `&` with **no `?`** — an invalid URL.
**Root cause.** Listmonk's `@TrackLink` marker registers **one static URL per
tracked link** and points every recipient's `/link/<uuid>` redirect at that single
row. This is fundamentally incompatible with a **per-subscriber** href such as
`{{ .Subscriber.Attribs.lp_link }}&utm_source=...`:
- The registered `links.url` was captured with the `{{ lp_link }}` token dropped,
yielding `/order/slug&utm_source=...` (first `&`, no `?`) → **404 for everyone**.
- Even if the URL had been valid, a static registration **collapses every carrier
onto the first subscriber's** `?dot=` (or `?npi=`/`?clia=`) value — wrong order
pre-fill for the entire blast.
By contrast, a **static** CTA (same URL for all recipients, e.g. the CRTC
`?code=...` order link) tracks correctly — keep `@TrackLink` there.
**Why removing tracking loses nothing.** Real human clicks are already attributed
via Umami's `campaign-click` event (bot-filtered by `pw-bot-filter.js`). Listmonk's
own click counters were already established as unreliable for this stream. So
Listmonk link tracking on per-subscriber CTAs is both redundant and destructive.
**Fix — live (already-sent + in-flight mail).** Rewrote the 10 broken registered
rows in place (replace the first `&` with `?`) so the baked `/link/<uuid>` redirects
resolve. Backup table `listmonk.pw_links_dkim_fix_bak_20260622` holds the old urls.
Verified the exact redirect that 404'd now returns 200 → lands on the (generic,
DOT-not-prefilled but fully functional) order page. To revert:
```sql
UPDATE links l SET url = b.url
FROM pw_links_dkim_fix_bak_20260622 b WHERE l.id = b.id; -- in the `listmonk` DB
```
**Fix — source (future builds, the real fix).** Stripped `@TrackLink` from every
**per-subscriber / per-provider** CTA so each row renders its own direct link (no
redirect, no collapse). Files changed:
- `scripts/create_deficiency_source_campaigns.py``_cta()` (lp_link order button)
and `_dot_check_cta()` (per-DOT tools link).
- `data/trucking_campaigns/{ucr_annual_reminder,ifta_quarterly_reminder}.html`
(per-carrier `lp_link`).
- `data/hc_campaigns/*.html` (10 templates, per-provider `?npi=`/`?clia=`).
`lp_link` already starts its query with `?dot=` (see `lp_link_with_coupon()`), so
`{{ lp_link }}&utm...` renders to a valid per-carrier URL once the redirect is gone.
**Healthcare note.** The HC Listmonk DB (`listmonk_hc`) had **0 registered links**
despite 13,425 sent — `@TrackLink` was not being stripped there at all, so the
literal `@TrackLink` shipped as harmless trailing text in `utm_campaign` and the
hrefs still 200'd (per-provider `?npi=` was present literally in the template, not
via lp_link). No live HC breakage; source templates cleaned anyway to remove the
collapse risk on the next send.
**Guardrail.** Never put `@TrackLink` on an href containing a `{{ .Subscriber... }}`
token. Per-subscriber links must render directly; rely on Umami `campaign-click`
for human-click attribution.
### Follow-up hardening — DONE (Jun 17-18 2026)
All discovered during the post-incident technical audit; each fix is codified.

View file

@ -137,7 +137,11 @@
- Same Listmonk infrastructure as FCC campaigns
- Warmup schedule (200/day → ramp up)
- Link to DOT compliance checker with `?dot={DOT#}&email={email}` pre-filled
- Use `@TrackLink` on all CTAs (learned from FCC campaign mistake)
- Use `@TrackLink` ONLY on **static** CTAs (same URL for all recipients). NEVER on
a per-subscriber href such as `{{ lp_link }}` / `?dot={DOT#}` — Listmonk registers
one static URL per tracked link and would 404 + collapse every carrier onto one
DOT (see runbook "Jun 22 2026 — @TrackLink on per-subscriber CTAs"). Per-subscriber
links render directly; human clicks are tracked via Umami `campaign-click`.
- Free compliance check as the CTA (not direct sell)
## Phase 5: Automation (Future)

View file

@ -7,8 +7,13 @@ click tracking, and de-risking unsubstantiated status claims.
1. **Removed all service prices** from the emails (price is now revealed on the
order page, after value is established). Catalog (`api/src/service-catalog.ts`)
remains the source of truth.
2. **Fixed click tracking** — appended `@TrackLink` + UTM to every conversion CTA
(root cause of clicks=0; Listmonk only registers links with that marker).
2. **Click tracking** — originally appended `@TrackLink` + UTM to every conversion
CTA. **SUPERSEDED (Jun 22 2026):** `@TrackLink` must NOT be used on per-provider
hrefs (`?npi=`/`?clia=`/`{{ lp_link }}`) — Listmonk registers one static URL per
tracked link, which 404s and collapses every provider onto one NPI. `@TrackLink`
removed from all HC templates; per-provider links render directly and human clicks
are tracked via Umami `campaign-click`. See runbook "Jun 22 2026 — @TrackLink on
per-subscriber CTAs."
3. **Reframed unsubstantiated per-record status assertions** to honest, hedged,
generally-true statements (defamation / FTC-deception risk).
4. This compliance review.
@ -84,8 +89,9 @@ These are factual compliance claims and must be **literally true**:
## HTML / deliverability QA — PASS
- All 10 templates render with **0 JS errors** headless, each has **exactly one
tracked `/order/...@TrackLink` CTA**, and **no price leaks** (only the $20,000
OIG penalty stat remains, intentionally).
per-provider `/order/...` CTA** (direct link, `@TrackLink` removed Jun 22 2026 —
see item 2), and **no price leaks** (only the $20,000 OIG penalty stat remains,
intentionally).
- External self-verify links (oig.hhs.gov, sam.gov, npiregistry, data.cms.gov) left
**untracked** on purpose (they're trust links, not conversions).