{
  "name": "Paid landing page and marketing feed failure modes",
  "description": "A reference taxonomy of the ways a paid landing page or a marketing data feed stops working, which HTTP status each one returns, and what a status-code uptime monitor reports while it is happening. Reference data, not measurements: no frequencies are claimed here. The one-day measurement lives in /destination-study.json with its own caveats.",
  "version": "1.0.0",
  "license": "https://creativecommons.org/licenses/by/4.0/",
  "creator": "VampireAI",
  "url": "https://vampireai.io/landing-page-monitoring",
  "documentation": "https://vampireai.io/how-it-works",
  "relatedDataset": "https://vampireai.io/destination-study.json",
  "destinationFailures": [
    {
      "id": "soft-404",
      "position": 1,
      "name": "The page says it is gone",
      "severity": "Sev-1",
      "stillReturns200": true,
      "what": "A CMS catch-all, a route with no match, a deleted product page. The visitor reads “page not found”; the server says 200 OK.",
      "whyOtherToolsMissIt": "The status code is fine, so status-code monitoring reports it healthy.",
      "uptimeMonitorReports": "200 OK. Healthy.",
      "example": "A migration drops a path prefix from the route table. /lp/spring-offer no longer matches anything, the router’s catch-all renders “this page could not be found”, and the framework never sets a status to go with it.",
      "remedy": "Restore the route, or repoint the campaign at the path that now exists. Then make the catch-all answer 404, so the next one is loud instead of silent.",
      "detector": "app.collect_destination_probes → soft_404_phrase",
      "canonical": "https://vampireai.io/landing-page-monitoring#soft-404"
    },
    {
      "id": "redirect",
      "position": 2,
      "name": "The click lands somewhere else",
      "severity": "Sev-1",
      "stillReturns200": true,
      "what": "A retired campaign URL that now bounces to a sign-in wall or the homepage. The visitor never sees the offer they clicked.",
      "whyOtherToolsMissIt": "The 200 belongs to wherever it ended up, not to your landing page.",
      "uptimeMonitorReports": "200 OK, for wherever it ended up.",
      "example": "A campaign URL for a retired promotion is 301’d to /login or to the homepage so it “still works”. Every paid click lands on a sign-in wall having never seen the offer it was sold.",
      "remedy": "Point the ad at a live URL. A retired offer should answer 410 Gone rather than redirect to something that looks alive — a redirect hides the failure from you and not from the visitor.",
      "detector": "app.collect_destination_probes → redirect_to",
      "canonical": "https://vampireai.io/landing-page-monitoring#redirect"
    },
    {
      "id": "thin",
      "position": 3,
      "name": "The page renders broken",
      "severity": "Sev-1",
      "stillReturns200": true,
      "what": "The page comes back a fraction of its usual size: a template that lost its body, a build that shipped an empty shell, a CDN that dropped the assets. It looks wrong to a human and fine to a machine.",
      "whyOtherToolsMissIt": "Every check that stops at the status code sees a healthy page.",
      "uptimeMonitorReports": "200 OK, and the right number of bytes to not care.",
      "example": "A deploy ships without its CSS bundle, or a CMS query times out and the template renders its shell. Fourteen kilobytes come back where a hundred and eighty normally do, and the page is a column of unstyled text.",
      "remedy": "Roll back. Compare each page against its own median over the previous week rather than a fixed byte threshold — “normal” is different for every destination and a global number will either miss this or cry wolf daily.",
      "detector": "app.detect_degraded_destinations",
      "canonical": "https://vampireai.io/landing-page-monitoring#thin"
    },
    {
      "id": "conversions",
      "position": 4,
      "name": "Conversions stopped while spend carried on",
      "severity": "Sev-1",
      "stillReturns200": true,
      "what": "The page is up, the page is whole, and nobody is converting. The funnel broke somewhere the page cannot show you.",
      "whyOtherToolsMissIt": "Nothing about the page is wrong, so no page check will ever fire.",
      "uptimeMonitorReports": "200 OK. Nothing about the page is wrong, and it is not.",
      "example": "The form’s endpoint starts returning 500, or a tag container is published with the conversion tag switched off. The page loads perfectly, reads perfectly, and records nothing.",
      "remedy": "Check the endpoint and the tag before you touch the page. This one is almost never a page problem, which is why a page check will never find it and why sending somebody to inspect the HTML wastes the hour that matters.",
      "detector": "app.detect_feed_silence",
      "canonical": "https://vampireai.io/landing-page-monitoring#conversions"
    },
    {
      "id": "mobile",
      "position": 5,
      "name": "Fine on your laptop, broken for the click",
      "severity": "Sev-2",
      "stillReturns200": true,
      "what": "A consent wall, an app-install interstitial or a geo-redirect that only fires for a phone — which is where most paid clicks come from.",
      "whyOtherToolsMissIt": "A desktop check never sees it, because it never asks as a phone.",
      "uptimeMonitorReports": "200 OK — it asked as a desktop browser and got the good page.",
      "example": "A consent banner whose accept button does not render below 400px, or an app-install interstitial that only fires for a mobile user agent. On a laptop the page is flawless — and most paid clicks are not on a laptop.",
      "remedy": "Fetch as a phone, which is the only way to see it at all. Then fix the breakpoint or exempt paid traffic from the interstitial; this is the one class where the desktop check is structurally blind rather than unlucky.",
      "detector": "app.probe_destinations (mobile user agent)",
      "canonical": "https://vampireai.io/landing-page-monitoring#mobile"
    },
    {
      "id": "blocked",
      "position": 6,
      "name": "The page is refusing visitors",
      "severity": "Sev-2",
      "stillReturns200": false,
      "what": "A 401, 403, 429 or 503 — a login wall left on by accident, a rate limiter, a WAF rule that started matching your own traffic.",
      "whyOtherToolsMissIt": "This is the one an uptime monitor does see — and files as down. The fix is a rule, not a rollback.",
      "uptimeMonitorReports": "DOWN. This is the one it sees — and it names the wrong problem.",
      "example": "A WAF rule tightened overnight starts challenging datacentre addresses, or a rate limiter begins counting the ad platform’s own crawler as abuse.",
      "remedy": "Allow-list the checker and the platform crawlers. This is a rule change, not a rollback — and that distinction is the whole value of catching it here, because an uptime monitor files it as downtime and sends somebody to revert a deploy that was never the cause.",
      "detector": "app.collect_destination_probes → blocked",
      "canonical": "https://vampireai.io/landing-page-monitoring#blocked"
    }
  ],
  "feedFailures": [
    {
      "position": 1,
      "name": "A number stopped arriving",
      "detector": "app.detect_feed_silence",
      "canonical": "https://vampireai.io/ppc-monitoring"
    },
    {
      "position": 2,
      "name": "A cost passed the line you set",
      "detector": "app.evaluate_guardrails",
      "canonical": "https://vampireai.io/ppc-monitoring"
    },
    {
      "position": 3,
      "name": "A target went off pace",
      "detector": "app.evaluate_goals",
      "canonical": "https://vampireai.io/ppc-monitoring"
    },
    {
      "position": 4,
      "name": "Two systems disagree about the same number",
      "detector": "app.detect_disagreements",
      "canonical": "https://vampireai.io/ppc-monitoring"
    }
  ],
  "notDetected": [
    {
      "name": "Response time",
      "why": "The probe cannot honestly time itself yet — it fires through a queue that batches its writes, so there is no per-request latency to report. It needs a prober that clocks its own fetch."
    },
    {
      "name": "Broken links on the page",
      "why": "Needs a crawl of every link on the destination, not just a fetch of the destination itself."
    },
    {
      "name": "Layout and visual regression",
      "why": "Needs the page rendered in a real browser and compared against a reference. The current probe reads the response, not the picture."
    },
    {
      "name": "Client-rendered not-found pages",
      "why": "The probe fetches the response and never runs your JavaScript, so a single-page app whose router paints \"not found\" in the browser looks healthy to it — the HTML off the wire really was fine. Conversions stopping still catches it, slower. A real browser fixes it properly."
    }
  ],
  "counts": {
    "destinationFailures": 6,
    "destinationFailuresReturning200": 5,
    "feedFailures": 4,
    "notDetected": 4
  }
}