From a8174a40ae26c95457e2890d95296fc96f8e648f Mon Sep 17 00:00:00 2001 From: Telemetry Tracker Bot Date: Tue, 20 Oct 2026 10:49:54 +0200 Subject: [PATCH 1/2] feat(marketing): add missing-event alerts (business heartbeats) landing page --- .../components/marketing/guides/GuideCta.tsx | 4 +- apps/dashboard/app/docs/alerts/page.tsx | 5 +- .../features/missing-event-alerts/page.tsx | 294 ++++++++++++++++++ apps/dashboard/app/llms.txt/route.ts | 1 + apps/dashboard/lib/marketing-guides.test.ts | 1 + apps/dashboard/lib/public-seo-paths.test.ts | 1 + apps/dashboard/lib/public-seo-paths.ts | 4 +- 7 files changed, 306 insertions(+), 4 deletions(-) create mode 100644 apps/dashboard/app/features/missing-event-alerts/page.tsx diff --git a/apps/dashboard/app/components/marketing/guides/GuideCta.tsx b/apps/dashboard/app/components/marketing/guides/GuideCta.tsx index 9a8fb828..74f1c805 100644 --- a/apps/dashboard/app/components/marketing/guides/GuideCta.tsx +++ b/apps/dashboard/app/components/marketing/guides/GuideCta.tsx @@ -6,11 +6,13 @@ export function GuideCta({ body = "No credit card. Create a project, install the SDK, and the first error shows up in Issues.", secondaryHref = "/docs/hosted-cloud", secondaryLabel = "Hosted cloud guide", + primaryLabel = "Start free", }: { heading?: string; body?: string; secondaryHref?: string; secondaryLabel?: string; + primaryLabel?: string; }) { return (
@@ -22,7 +24,7 @@ export function GuideCta({ pendingLabel="Loading…" className="inline-flex items-center gap-1.5 rounded-full bg-primary px-5 py-2.5 text-sm font-medium text-primary-foreground transition-transform hover:scale-[1.02]" > - Start free + {primaryLabel} ERROR_RATE between error ingests -

Business heartbeats

+

Business heartbeats

Some failures throw nothing: a verification email is never sent, a webhook returns 200 but skips the work, a nightly job never runs. Track an event when the flow succeeds, then alert - when that event has not arrived for a number of hours. + when that event has not arrived for a number of hours. Examples and limits are on{" "} + missing-event alerts.

{`trackEvent("signup_verified");          // user confirmed their email
 trackEvent("stripe_webhook_processed"); // webhook handler finished the work
diff --git a/apps/dashboard/app/features/missing-event-alerts/page.tsx b/apps/dashboard/app/features/missing-event-alerts/page.tsx
new file mode 100644
index 00000000..f7ef27f2
--- /dev/null
+++ b/apps/dashboard/app/features/missing-event-alerts/page.tsx
@@ -0,0 +1,294 @@
+import Link from "next/link";
+import { CodeBlock } from "@/app/components/docs/CodeBlock";
+import { GuideCta } from "@/app/components/marketing/guides/GuideCta";
+import { GuideJsonLd } from "@/app/components/marketing/guides/GuideJsonLd";
+import { GuideRelatedLinks } from "@/app/components/marketing/guides/GuideRelatedLinks";
+import { MarketingGuideLayout } from "@/app/components/marketing/guides/MarketingGuideLayout";
+import {
+  breadcrumbJsonLd,
+  faqPageJsonLd,
+  marketingGuideMetadata,
+  webPageJsonLd,
+} from "@/lib/marketing-guide-metadata";
+import { marketingSiteOrigin } from "@/lib/marketing-json-ld";
+
+const PATH = "/features/missing-event-alerts";
+const TITLE = "Alert When Events Stop Arriving: Missing-Event Alerts";
+const H1 = "Get alerted when an event you expect stops arriving";
+const DESCRIPTION =
+  "Get an email when an expected event, like signup_verified or a processed webhook, hasn't arrived for 1–24 hours. Built on the trackEvent calls you already send.";
+
+const FAQ = [
+  {
+    question: "What is a business heartbeat?",
+    answer:
+      "A custom event your app sends when a key flow succeeds. We alert you when none arrive in the window you choose.",
+  },
+  {
+    question: "Does this monitor my payments or signups automatically?",
+    answer: "No. You send the event from your code. We only see the events you track.",
+  },
+  {
+    question: "How quickly will I hear about it?",
+    answer:
+      "After the window passes, on the next scheduled evaluation (by default every 5 minutes). It is not real-time.",
+  },
+  {
+    question: "Can I limit it to production?",
+    answer: "Yes. Each rule has an optional environment filter.",
+  },
+  {
+    question: "Do I get a recovery alert?",
+    answer: "No. The alerts stop when the events come back.",
+  },
+  {
+    question: "How is this different from cron check-ins?",
+    answer:
+      "Check-ins are for scheduled jobs, with ping URLs and deadlines. Heartbeats are for events your app flows send.",
+  },
+] as const;
+
+const TRACK = `import { trackEvent } from "@telemetry-tracker/node"; // or @telemetry-tracker/next, @telemetry-tracker/react-native
+
+// Call it after the flow actually succeeded
+trackEvent("signup_verified");`;
+
+const RULE_JSON = `{ "type": "NO_EVENTS", "eventName": "signup_verified",
+  "windowMinutes": 360, "environment": "production" }`;
+
+const linkClass = "text-brand hover:underline";
+
+export function generateMetadata() {
+  return marketingGuideMetadata({
+    title: TITLE,
+    description: DESCRIPTION,
+    path: PATH,
+    omitBrandSuffix: true,
+  });
+}
+
+export default function MissingEventAlertsPage() {
+  const origin = marketingSiteOrigin();
+
+  return (
+    <>
+      
+      
+            Error tracking only sees code that throws. Some failures throw nothing: the verification
+            email never goes out, a webhook returns 200 but skips the work, a nightly job never
+            runs. We call the fix business heartbeats: send an event when the flow succeeds, and
+            we'll email you when it goes missing.
+          
+        }
+      >
+        

How it works

+
    +
  1. + Call trackEvent("signup_verified") after the flow succeeds, with + the{" "} + + Node + + ,{" "} + + Next.js + {" "} + or{" "} + + React Native + {" "} + SDK. +
  2. +
  3. + In Alerts → Custom rules, choose No events (heartbeat). Enter the exact + event name, a window in hours (1–24) and, optionally, an environment. +
  4. +
  5. + When the event goes missing, the alert names it (for example{" "} + no "signup_verified" events in the last 6 hours in production) and + links to the Events page filtered to that event. +
  6. +
+ +

+ The same rule through the alert rules API is a NO_EVENTS condition with an{" "} + eventName: +

+ +

+ Full setup is in the{" "} + + business heartbeats setup + {" "} + docs. Every event goes through the{" "} + + SDK + {" "} + you already use for errors, so the alert sits next to your issues. +

+ +

What to monitor

+

Signup verification

+

+ Send signup_verified when a user confirms their email. A heartbeat can tell + you when those stop arriving, for example because verification emails stopped going out. + A small app can go hours without a signup, so start with a 6–12 hour window. +

+

Payment webhook processed

+

+ Send stripe_webhook_processed after your handler did the work, not when the + request arrives. A handler that returns 200 and skips the event then shows up as missing. + Pick a window a few times your normal gap between webhooks. +

+

Nightly job finished

+

+ Send daily_report_finished as the last line of the job. The window can be at + most 24 hours, which leaves little slack for a daily job. For job deadlines, consider{" "} + + check-ins + {" "} + instead. +

+ +

Choosing a window

+
    +
  • Make it comfortably longer than the normal gap between those events.
  • +
  • Check the Events page first to see how often the event really arrives.
  • +
  • + At low traffic, quiet nights and weekends cause false alerts if the window is too short. +
  • +
  • The dashboard defaults to 6 hours, with a cooldown equal to the window.
  • +
+ +

Limits

+

We'd rather you know these up front:

+
    +
  • Each condition watches one exact event name.
  • +
  • The window is at most 24 hours.
  • +
  • + It is not real-time. A scheduled evaluator checks rules, by default every 5 minutes, so + you hear about it after your window plus up to one check. +
  • +
  • + There is no recovery notification. While the event stays missing, the rule fires again + every cooldown, and it stops when events come back. +
  • +
  • There is no learned baseline or anomaly detection. You choose the window.
  • +
  • + You instrument and send the event yourself. Nothing is monitored that you don't + track, so we can't guarantee that your signups or payments are covered. +
  • +
  • + If you{" "} + + self-host + + , you need to run the alert-rules evaluator cron. +
  • +
+ +

Heartbeats vs check-ins

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Business heartbeatCheck-ins
SignalCustom event via the SDKRequest to a secret ping URL
Best forIrregular, user-driven flowsCron and time-zone schedules, runtime deadlines
Configured asAn alert ruleA check-in monitor
RecoveryNo recovery notificationDown and recovery states
+
+

+ See{" "} + + cron and background-job check-ins + {" "} + for scheduled jobs. +

+ +

FAQ

+ {FAQ.map((item) => ( +
+

{item.question}

+

{item.answer}

+
+ ))} + + +

+ Plans are on{" "} + + pricing + + . Questions? Write to{" "} + + info@telemetry-tracker.com + + . +

+ +
+ + ); +} diff --git a/apps/dashboard/app/llms.txt/route.ts b/apps/dashboard/app/llms.txt/route.ts index e6f9945d..0394894f 100644 --- a/apps/dashboard/app/llms.txt/route.ts +++ b/apps/dashboard/app/llms.txt/route.ts @@ -43,6 +43,7 @@ export function GET() { - ${origin}/docs/hosted-cloud - ${origin}/error-tracking/react-native/expo-source-maps - ${origin}/sentry-alternative +- ${origin}/features/missing-event-alerts ## Not for model training diff --git a/apps/dashboard/lib/marketing-guides.test.ts b/apps/dashboard/lib/marketing-guides.test.ts index 15773854..044f68bf 100644 --- a/apps/dashboard/lib/marketing-guides.test.ts +++ b/apps/dashboard/lib/marketing-guides.test.ts @@ -17,6 +17,7 @@ const pageFiles: Record<(typeof MARKETING_GUIDE_PATHS)[number], string> = { "/error-tracking/react-native": "error-tracking/react-native/page.tsx", "/error-tracking/react-native/expo-source-maps": "error-tracking/react-native/expo-source-maps/page.tsx", + "/features/missing-event-alerts": "features/missing-event-alerts/page.tsx", }; describe("marketing guide pages", () => { diff --git a/apps/dashboard/lib/public-seo-paths.test.ts b/apps/dashboard/lib/public-seo-paths.test.ts index 76ab3ace..eb89a655 100644 --- a/apps/dashboard/lib/public-seo-paths.test.ts +++ b/apps/dashboard/lib/public-seo-paths.test.ts @@ -24,6 +24,7 @@ describe("public SEO paths", () => { expect(PUBLIC_SEO_PATHS).toContain( "/error-tracking/nextjs/server-components-render-error" ); + expect(PUBLIC_SEO_PATHS).toContain("/features/missing-event-alerts"); expect(PUBLIC_SEO_PATHS).not.toContain("/error-tracking/nextjs/source-maps"); }); diff --git a/apps/dashboard/lib/public-seo-paths.ts b/apps/dashboard/lib/public-seo-paths.ts index 159eb06e..3a061c1a 100644 --- a/apps/dashboard/lib/public-seo-paths.ts +++ b/apps/dashboard/lib/public-seo-paths.ts @@ -36,6 +36,7 @@ export const PUBLIC_SEO_PATHS = [ "/error-tracking/nodejs", "/error-tracking/react-native", "/error-tracking/react-native/expo-source-maps", + "/features/missing-event-alerts", ] as const; /** Affiliate program pages: indexable, but only listed when NEXT_PUBLIC_AFFILIATES_ENABLED=true. */ @@ -59,10 +60,11 @@ export const MARKETING_GUIDE_PATHS = [ "/error-tracking/nodejs", "/error-tracking/react-native", "/error-tracking/react-native/expo-source-maps", + "/features/missing-event-alerts", ] as const; /** Content date for sitemap lastmod. Bump when public pages change. */ -export const SITEMAP_LAST_MODIFIED = new Date("2026-10-19T00:00:00.000Z"); +export const SITEMAP_LAST_MODIFIED = new Date("2026-10-20T00:00:00.000Z"); export function sitemapPriority(path: PublicSeoPath): number { if (path === "") return 1; From 683f9ececab989433019c485679c997f766e4b4d Mon Sep 17 00:00:00 2001 From: Telemetry Tracker Bot Date: Tue, 20 Oct 2026 10:51:12 +0200 Subject: [PATCH 2/2] feat(marketing): list verified heartbeat alert channels, trim setup duplication --- .../features/missing-event-alerts/page.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/dashboard/app/features/missing-event-alerts/page.tsx b/apps/dashboard/app/features/missing-event-alerts/page.tsx index f7ef27f2..2f4a3bad 100644 --- a/apps/dashboard/app/features/missing-event-alerts/page.tsx +++ b/apps/dashboard/app/features/missing-event-alerts/page.tsx @@ -53,9 +53,6 @@ const TRACK = `import { trackEvent } from "@telemetry-tracker/node"; // or @tele // Call it after the flow actually succeeded trackEvent("signup_verified");`; -const RULE_JSON = `{ "type": "NO_EVENTS", "eventName": "signup_verified", - "windowMinutes": 360, "environment": "production" }`; - const linkClass = "text-brand hover:underline"; export function generateMetadata() { @@ -114,8 +111,8 @@ export default function MissingEventAlertsPage() { SDK.
  • - In Alerts → Custom rules, choose No events (heartbeat). Enter the exact - event name, a window in hours (1–24) and, optionally, an environment. + Add a No events (heartbeat) alert rule for that exact event name, with + a window of 1–24 hours and an optional environment.
  • When the event goes missing, the alert names it (for example{" "} @@ -125,12 +122,12 @@ export default function MissingEventAlertsPage() {

    - The same rule through the alert rules API is a NO_EVENTS condition with an{" "} - eventName: + We email you by default. The same alert also shows up in your in-app notifications, and + you can send it to HTTPS webhooks, Slack, Discord, Microsoft Teams or Telegram channels + you have set up for the project.

    -

    - Full setup is in the{" "} + Step-by-step setup, including the API version of the rule, is in the{" "} business heartbeats setup {" "} @@ -180,11 +177,11 @@ export default function MissingEventAlertsPage() {

  • Each condition watches one exact event name.
  • The window is at most 24 hours.
  • - It is not real-time. A scheduled evaluator checks rules, by default every 5 minutes, so + It is not real-time. Rules are checked about every 5 minutes, so you hear about it after your window plus up to one check.
  • - There is no recovery notification. While the event stays missing, the rule fires again + Recovery notifications aren't supported yet. While the event stays missing, the rule fires again every cooldown, and it stops when events come back.
  • There is no learned baseline or anomaly detection. You choose the window.