Tracking troubleshooting

Tracking audit

Runs an end-to-end, read-only tracking audit of a website or web app and scores it. Inventories every tag, pixel and unknown third-party host from browser network captures (HAR files) and published containers, tests consent with no choice, reject and accept, checks GA4 and ad platform events against the tracking plan, verifies conversions, values and counting, proves cross-domain and SPA continuity, finds browser plus server or Conversions API double counting, and traces click IDs and UTMs into the CRM. Returns a scored findings report and a prioritised fix list, using dependency-free scripts.

Free, no sign-up. Version 0.1.0, reviewed 6 November 2026.

How to use it

Install it, then ask your agent for the work in plain words. The agent loads the skill when the request matches. Requests like these:

  • Can you run a full tracking audit on example.com before we double our ad budget? Read-only, and give me a score and a list of what to fix first.

  • Here are HAR files from our site for no choice, reject and accept on the cookie banner, plus a HubSpot export. Audit our tags, consent and click ID capture.

  • Meta shows more purchases than we have orders and half our HubSpot leads have no gclid. Audit the whole setup, including the server-side container.

  • Our agency just handed over GA4, GTM, Google Ads and the Meta pixel for app.example.com. Audit everything end to end and tell me what to fix first.

In Claude Code you can also run the slash command /tracking-audit [site URL or journeys or HAR files].

What it needs from you

  • The site and the journeys that matter (lead form, signup, checkout), with every hostname they cross, including payment and app domains
  • The regions you serve and your consent decision for each: basic or advanced consent mode for Google tags, and which vendors sit in which banner category
  • Expected and retired destination IDs (GA4, Google Ads, Meta, TikTok, LinkedIn, Microsoft Ads, OpenAI Ads and others) , any server-side or native integrations, and first-party collection endpoints (server container paths, proxies, CNAME collectors), from martech-context/ if you have it
  • HAR files exported from browser DevTools for each consent scenario and journey, or someone who can export them; Chrome DevTools MCP cannot save a HAR, and Playwright MCP records one only when recordHar is set in its config
  • Read-only access or exports: GA4 and Google Ads read access or their MCP servers, Meta Events Manager or its MCP read tools, a GTM export, a CRM export of lead IDs, dates, sources and click IDs, and an order export
  • Approval for each live test (banner clicks, synthetic click IDs on landing pages, test leads, test signups, test orders, platform test events) and who cleans up the test records

What you get back

  • A scored report: overall score, grade and evidence coverage, health and coverage for ten domains, and any open privacy or bidding gates
  • A tag and pixel inventory showing what loads each vendor, its consent category and any unexpected or retired IDs
  • PASS, WARN or FAIL for 71 checks, each tied to evidence such as a HAR entry, script output, Tag Assistant record or platform read
  • A trace of click IDs and UTMs from the landing page through redirects and forms into CRM fields, with capture rates from the CRM export
  • A prioritised fix list a colleague can action, with owners, draft changes, tests and rollbacks, plus a list of what was not checked. Nothing is changed.

Install

Each block downloads the skill folder and unzips it where that agent looks for skills. Restart the agent afterwards.

Claude Code
mkdir -p ~/.claude/skills
curl -sL https://www.martechdevs.com/downloads/skills/tracking-audit.zip -o /tmp/tracking-audit.zip
unzip -oq /tmp/tracking-audit.zip -d ~/.claude/skills
mkdir -p ~/.claude/commands
curl -sL https://www.martechdevs.com/downloads/commands/tracking-audit.md -o ~/.claude/commands/tracking-audit.md
Codex
mkdir -p ~/.agents/skills
curl -sL https://www.martechdevs.com/downloads/skills/tracking-audit.zip -o /tmp/tracking-audit.zip
unzip -oq /tmp/tracking-audit.zip -d ~/.agents/skills
Cursor
mkdir -p ~/.cursor/skills
curl -sL https://www.martechdevs.com/downloads/skills/tracking-audit.zip -o /tmp/tracking-audit.zip
unzip -oq /tmp/tracking-audit.zip -d ~/.cursor/skills
Any agent, with the skills CLI
npx skills add https://www.martechdevs.com --skill tracking-audit

The skill

The full SKILL.md, as the agent reads it.

Audits one website or web app end to end and scores it: what every tag sends, under which consent state, with which IDs and values, into which platform and CRM field. The output is a scored report, an evidence ledger, a not-checked list and a prioritised fix list. The audit changes nothing, and a finding is a lead until a reproduction or a platform read confirms it.

When to use

Use this skill when:

  • Someone asks for a tracking, tag, analytics, pixel or conversion tracking audit, or a one-off tracking health check.
  • A redesign, replatform, CMP change, server-side migration or budget increase will rely on conversion data.
  • An agency or previous owner hands over tracking and nobody has a baseline.
  • Nobody trusts the numbers and no single incident explains why, or a scored baseline is needed to compare after fixes.

Use something else when:

  • One metric broke on a known date: tracking-break-map finds the first failing layer.
  • Only the GTM container needs reviewing: gtm-container-audit.
  • Consent across regions, partial choices and withdrawal: consent-debugging.
  • GA4 purchases disagree with orders order by order: purchase-reconciliation.
  • Unassigned or (not set) rows: ga4-unassigned-not-set-diagnosis.
  • Only one GA4 property's settings and data quality: ga4-audit.
  • The plan itself, or event conformance in Mixpanel, Amplitude, PostHog or Segment: tracking-plan-audit.
  • No tracking plan exists: tracking-plan-builder first, or audit against a derived plan.
  • Platforms differ by design (attribution, dates, view-through): platform-number-reconciliation (planned).
  • Recurring monitoring, alerts or an incident write-up: tracking-monitoring-and-incidents (planned); this skill is the one-off scored baseline.
  • Native mobile app SDK events: mobile-attribution-fundamentals.

If a named skill is not installed or is still planned, use the fallback for that domain in references/checks.md and mark the deeper check not covered.

Inputs

Read these first if they exist (assets/example-acme-stack.md shows the fields):

  • martech-context/stack.md: hosts, containers, expected and retired destination IDs, server and native routes, first-party collection endpoints, the offline upload route, CMP, owners.
  • martech-context/tracking-plan/: trigger, must-not-fire cases, dedup key, value rule and consent category per event. Pass rows whose release shipped; har-audit skips proposed rows.
  • martech-context/decisions.md: consent model per region (basic or advanced for Google tags and for Microsoft UET, vendor categories), value rules, first or latest touch policy, approved hashed matching.
  • martech-context/naming-rules.md and metrics.md: the UTM registry, conversion definitions, and agreed thresholds that replace this skill's defaults.

Then fill assets/audit-scope-template.md with the user.

  • Access levels: L1 public pages only; L2 read access (GA4 and Google Ads MCP servers, GTM export, Meta Ads MCP read tools, CRM and order exports); L3 approved test leads, signups and orders. The level reached caps coverage.
  • Exports carry IDs, dates, sources, click IDs, UTMs and amounts only. Ask for name, email and phone columns to be removed.
  • Never ask for passwords, tokens, API secrets or service account keys.

Procedure

1. Agree scope, level and approvals

  • Keep every audit file in the user's project under tracking-audit/<YYYY-MM-DD>/ (captures/, exports/, outputs/, checklist.csv, report.md, fix-list.md), never in the skill folder.
  • Fill the scope sheet before any capture. Banner clicks, synthetic click IDs on live landing pages, test leads, test signups, test orders and platform test events each need a named yes. Without one, mark the dependent checks NOT_CHECKED with "no approval".
  • Markers: gclid=TeSter-123 exactly; other click IDs and utm_campaign with the TeSter-123- prefix; test identities qa-audit-NN@example.com and +15555550100. Never a real click ID or customer record.
  • If the consent model per region is not documented, stop and get it from the privacy owner. Consent checks cannot pass or fail without it.

2. Write the expected state

  • From stack.md and the plan: expected and retired IDs, the route per event and destination, and each tier-1 conversion's source, trigger, dedup key and value rule.
  • No plan: derive one from the current setup, label it "derived, not approved" and cap EVT checks at WARN, because a derived plan turns today's bugs into expectations.
  • Absence of a browser tag is not absence of tracking. Ask which events go server-side, through a native integration or by offline import before judging coverage.

3. Capture browser evidence

  • Per page type in scope: no-choice, reject and accept captures in a fresh context, cache disabled, log preserved. Consent, SPA and cross-domain cases run three times, one throttled. Journeys start from the landing routes the ads use, built from the final URL and tracking template with markers; never request an ad click URL.
  • har-audit reads only HAR files exported from DevTools or Playwright recordHar, never one built with invented or copied start times. Without an export, apply the checks by hand from saved request lists, label them "script not run" and mark time-dependent checks NOT_CHECKED (references/capture-protocol.md).
  • The agent's isolated browser (Playwright MCP with --isolated, or Chrome DevTools MCP new_page with a new isolatedContext per run; never the user's profile) drives scenarios, reads cookie names from request headers and saves complete request lists (Playwright MCP: static: true).
  • Log every file in the evidence ledger, named after its ID.

4. Run the scripts

One har-audit run per scenario. Options and destination keys are in each script's header:

S="$SKILL_DIR/scripts"; A=tracking-audit/2026-10-28   # SKILL_DIR: this skill's folder
node "$S/har-audit.mjs" "$A/captures/E-01_no-choice_home_run1.har.json" --scenario no-choice --mode advanced --uet-mode basic \
  --expect-ids G-XXXXXXXXXX,AW-123456789 --own-domains example.com --cmp-hosts cdn.consent.example.org > "$A/outputs/E-01.txt"
node "$S/har-audit.mjs" "$A/captures/E-06_journey_run1.har.json" --scenario journey --own-domains example.com \
  --markers gclid=TeSter-123 --pii qa-audit-01@example.com --server-routes meta:Purchase \
  --plan-events "$A/plan-events.csv" --order-value 99.00 --order-currency USD > "$A/outputs/E-06.txt"
node "$S/crm-attribution-coverage.mjs" "$A/exports/leads.csv" --created "Create date" \
  --source "Original Traffic Source" --paid-search "Paid search" --gclid gclid > "$A/outputs/E-17.txt"
node "$S/score-audit.mjs" "$A/checklist.csv"
  • Never mix scenarios in one run: the scenario decides the consent rules. Exit 1 means at least one FAIL (score-audit: an open S1 or S2 FAIL); 2 means bad input, such as a HAR without real request times.
  • Pass --mode and --uet-mode from the scope sheet on every consent run (--uet-mode advanced only where UET is documented to fire denied before a choice). Pass --granted-defaults only where the documented model grants signals by default, --form-hosts for embedded schedulers or forms, --collector-hosts for the scope sheet's first-party collection endpoints, and --choice-at when the capture line's choice time is more than 2 seconds from the recorded click.
  • Run crm-attribution-coverage once per click ID column (in HubSpot the hidden-field property and HubSpot's own Google ad click ID) and judge the one the offline import reads.
  • If Node is unavailable, apply references/checks.md by hand and label the report "script not run".

5. Read platform settings and reports (L2)

  • Exact reads for GA4, Google Ads (landing pages and tracking templates included), Meta, BigQuery and exports, plus the completed-day rule: references/platform-reads.md.
  • The GA4 MCP server cannot read data retention, enhanced measurement or data redaction (the Admin API can); data filters, unwanted referrals and configured domains need a UI read or an owner's screenshot.

6. Judge each domain

Rules for all 71 checks: references/checks.md. The calls that need judgement:

  • Consent in opt-in regions: any non-essential vendor event request or cookie before a choice or after reject is a FAIL (S1); a library loaded with no event request, or an unclassified host receiving data, is a WARN until classified. Google tags follow the documented mode: basic sends nothing before a grant; advanced sends only denied-state requests (gcs G100) and writes no _ga, _gcl or FPID cookies. Advanced UET may send only asc=D beacons.
  • A conversion fires on the committed outcome, once. One on a submit click, form start, checkout view, failed payment or the click that opens an embed is a FAIL (S2), and reload, back, double click or revisit must send nothing new unless the destination deduplicates on the same ID.
  • GA4 deduplicates web-stream purchases on transaction_id, and all empty IDs together; a missing or empty ID is a FAIL. One primary Google Ads action per outcome across website tag, GA4 import, offline import and integrations.
  • Dual routes: Meta pairs browser and server copies on event name plus eventID/event_id within 48 hours; TikTok and OpenAI Ads need the same event_id on both; LinkedIn counts only the Insight Tag copy when IDs match. GA4 has no general dedup beyond purchases.
  • Cross-domain passes only with an unchanged client ID in 3 of 3 fresh-click runs (_gl expires after two minutes). In SPAs, send_page_view: false does not stop history page views; the observed count must equal the fixture's.
  • Click IDs are case sensitive and must reach the CRM field exactly. CRM control segment: coverage at least 95 percent PASS, 80 to 95 WARN, under 80 FAIL, over consented records where the decision forbids capture without consent; malformed values or gclids with no upper-case letter above 1 percent FAIL.
  • The test email or phone raw, encoded, base64 or lower-cased in any tag, own-host collector or third-party request is a FAIL (S1); a body the script cannot read is a WARN until the vendor's tool shows no test value. Its SHA-256 belongs only in approved matching fields after a grant.
  • Numbers that differ by design are not defects until aligned definitions still disagree. Browser protections, blockers and refused consent are limits to report, never defects to route around.

7. Confirm the findings that matter

  • Every FAIL and every S1 or S2 WARN gets one reproduction in a fresh context, with its evidence ID. A FAIL that does not reproduce becomes a WARN marked unconfirmed. A platform-read finding is confirmed by a second read or the owner's UI screenshot.
  • Negative conversion tests (validation error, declined test card, reload, double click) run only with approval, on staging or a test payment method, with markers. List every record created.
  • If real personal data appears in a vendor request, stop, restrict the evidence and tell the user the same day, with a draft note for the privacy owner. Never send it yourself.

8. Score, report and hand off

  • Copy assets/audit-checklist.csv and give each check one status (PASS, WARN, FAIL, NOT_CHECKED, NA): the worst result across every capture, run, region, route and read that tested it. INFO lines are evidence, not statuses. Add evidence IDs, owner, impact and effort; NOT_CHECKED and NA need a reason, and a reproduced finding gets "confirmed" in notes.
  • Run score-audit: severity weights 8, 4, 2, 1 with WARN at half; coverage under 60 percent withholds the score; any open S1 FAIL makes the verdict "Fail: privacy" with grade F; any open S2 FAIL caps the grade at D. Model: references/scoring.md.
  • Write the report from assets/audit-report-template.md and the fix list from assets/fix-list-template.md, in score-audit's fix order.
  • Propose updates to stack.md, decisions.md and tracking-plan/. Write nothing until the user approves.

Common failure modes

Mechanisms and the tempting wrong fix for each: references/failure-modes.md.

  • Snippet outside the CMP, or a pixel that collects by default. Detect: event requests in the no-choice capture, loaded by page source or carrying no denied state (CON-02).
  • Unknown collector. Detect: an unclassified host or own-host candidate receiving data (INV-06, CON-02).
  • Session replay recording personal data. Detect: base64 hit or unreadable-body WARN (PII-01).
  • Browser pixel without the server's event ID. Detect: Meta Purchase without eid while Conversions API runs (SST-02).
  • GET forms leaking personal data. Detect: the test email in page URLs and in every tag's page location (PII-01, PII-02).
  • Hidden-field script lowercases click IDs. Detect: case changed in the form POST; the custom CRM property lowercased while HubSpot's own Google ad click ID is not (CID-04 to CID-06).
  • Scheduler in a third-party frame. Detect: an embed URL without markers and no conversion from the vendor's success message (CID-04, CNV-01).
  • Server route ignoring consent. Detect: server logs show vendor requests for a visitor who rejected (CON-07).

Worked example

Acme Analytics (fictional B2B SaaS, UK opt-in, advanced consent mode): www.example.com, app.example.com (SPA), hosted checkout on pay.example.net, server container on sgtm.example.com; inputs in assets/example-acme-*.

score-audit: 71 checks, 37 PASS, 13 WARN, 16 FAIL, 4 NOT_CHECKED, 1 NA; score 59.5; coverage 94.4 percent; verdict Fail: privacy, grade F.

  • P1: a theme-coded LinkedIn tag fires before a choice and after reject (CON-02, CON-03); a GET demo form puts the test email in the URL, so five vendors receive it (PII-01, PII-02).
  • P2: two primary purchase actions (CNV-06); Meta Purchase without eventID while the server sends event_id (SST-02); VAT inside the value, 118.8 where the net order is 99.00 (CNV-04).
  • P3: the /pricing redirect drops every marker (CID-01); since 2026-09-15 the form script lowercases the gclid property the offline import reads (CID-04 to CID-06, CID-08); the payment return starts a referral session (XDS-03); two SPA page view owners send 8 views where 5 are expected (XDS-05).

Commands, exact output, finding handling, fix items, regression fixtures: references/worked-example.md.

Acceptance checks

  • Scope: the scope sheet names level, hosts, journeys, regions with consent model, expected and retired IDs and approvals. Pass: no capture predates it.
  • Expected state: written from stack.md and the plan, or labelled derived. Pass: every tier-1 conversion has a source, trigger, dedup key and value rule, or an open question for its owner.
  • Captures: every page type in scope has no-choice, reject and accept HAR exports from a fresh isolated context; race-sensitive cases ran three times. Pass: the evidence ledger lists each file with scenario, UTC time and run number.
  • Scripts: har-audit ran on every capture, crm-attribution-coverage on each click ID column at L2, score-audit on the checklist, or the report says "script not run". Pass: outputs are cited by evidence ID, and each unclassified host and own-host candidate is classified or open with an owner.
  • Statuses: all 71 checks carry one status; every NOT_CHECKED and NA has a reason. Pass: score-audit reports no validation errors.
  • Evidence: every FAIL and every S1 or S2 WARN cites evidence and a reproduction or second read, or is labelled unconfirmed. Pass: no confirmed finding without evidence.
  • Score: the score-audit output appears unchanged in the report. Pass: verdict, grade, score and coverage match it.
  • Fix list: each item has check IDs, owner, draft change, test with approval, rollback and expected report movement. Pass: a colleague could apply it without asking a question.
  • Limits and gaps: the report has "Not checked" and "Expected limits" sections. Pass: neither says "none" without a reason.
  • Safety: no write call was made and every test record is listed with its cleanup owner. Pass: searching the report, ledger, fix list and any shared excerpt for "@", "%40" and "%2540" outside example domains, non-marker click IDs, "Bearer", "token" and "key=" finds only test values. Raw captures stay with the project; their personal-data result comes from har-audit PII-01 and PII-02.

Boundaries

  • Read-only. No GTM publish or workspace edit, no GA4, Google Ads, Meta or CRM setting change, no conversion upload, no Measurement Protocol or Conversions API send, no data filter activation. Every fix leaves as a draft for its owner.
  • Live side effects only with the approval in the scope sheet. Meta events sent with a test event code still reach ads measurement, so block ad conversion hosts in the test browser when ads are not under test.
  • The audit tests against the privacy owner's documented consent decision. It does not choose the consent model, decide legal basis or certify compliance, and it never deletes data.
  • Never use the user's personal browser profile or credentials, or sign in to Tag Assistant or a vendor tool. Never recommend circumventing browser protections, blockers or a refusal (no parameter renaming, fingerprinting or consent bypass).
  • Official tools, read-only:
    • Google Analytics MCP server: analytics.readonly credentials, started without --allow-write.
    • Google Ads MCP server: only search, get_resource_metadata and list_accessible_customers, with labels: false and assets: false in its tools_config.yaml (its mutate tools are on by default) and a Read-only user.
    • Meta Ads MCP server: read tools only (ads_get_*, ads_pixel_event_read, ads_pixel_parameter_read), never the create, update or delete tools.
    • Tag Manager has no Google MCP server: use an export or the tagmanager.readonly API, or Stape's GTM MCP server with a Read-permission identity and read actions only.
    • BigQuery MCP server: execute_sql_readonly, with execute_sql and cancel_job denied by policy; Cloud Logging under a log reader role for server routes.
  • Other hand-offs: utm-convention-and-registry. Planned: cross-domain-and-spa-qa, server-side-tracking-debug, offline-conversion-import.

Live docs

Platform settings, limits and menu paths change. Before stating any parameter name, limit, quota or UI path:

  • Fetch the current page from the vendor docs MCP server or llms.txt listed in sources.yaml, or open the canonical URL.
  • If the live page disagrees with this skill, follow the live page and tell the user what changed.
  • Cite the URL you used.

Most used here: GA4 limits, transaction ID, cross-domain, SPA and Admin API pages; Google's consent guide; the Tag Manager release notes; Google Ads offline import help (2998031); Meta, TikTok and OpenAI Ads dedup; Microsoft's UET consent page; HubSpot default properties; WebKit tracking prevention. Checked 2026-11-01 to 2026-11-06.

Files in this skill

Want it set up for you? See implementation plans or book a free call.