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:
parent
1e9dcfcfd1
commit
3325259af7
16 changed files with 98 additions and 19 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue