{"id":1812,"date":"2026-07-21T17:00:00","date_gmt":"2026-07-21T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1812"},"modified":"2026-03-07T03:37:24","modified_gmt":"2026-03-07T08:37:24","slug":"analytics-alerting","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/analytics-alerting\/","title":{"rendered":"Analytics Alerts for Deep Link Anomalies"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">A broken AASA file can silently kill your deep link performance for hours before anyone notices. By the time a marketer reports &quot;our email links are not working,&quot; thousands of users have already been sent to fallback pages. Alerting catches these problems in minutes instead of hours.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers how to set up analytics alerts for deep link anomalies. For real-time dashboards, see <a href=\"https:\/\/tolinku.com\/blog\/real-time-analytics-dashboards\/\">real-time analytics dashboards for deep links<\/a>. For webhook integrations, see <a href=\"https:\/\/tolinku.com\/blog\/webhooks-integrations-deep-linking\/\">webhooks and integrations for deep linking<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/screenshot-analytics-1772819420927.png\" alt=\"Tolinku analytics dashboard showing click metrics and conversion funnel\">\n<em>The analytics dashboard with date range selector, filters, charts, and breakdowns.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What to Alert On<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Critical Alerts (Immediate Action)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">These indicate something is broken:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Alert<\/th>\n<th>Condition<\/th>\n<th>Severity<\/th>\n<th>Typical Cause<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Open rate crash<\/td>\n<td>Open rate drops below 40% for 1 hour<\/td>\n<td>Critical<\/td>\n<td>AASA\/assetlinks.json down or misconfigured<\/td>\n<\/tr>\n<tr>\n<td>Error rate spike<\/td>\n<td>Error rate exceeds 5% for 15 minutes<\/td>\n<td>Critical<\/td>\n<td>Server error, broken route<\/td>\n<\/tr>\n<tr>\n<td>Zero traffic<\/td>\n<td>No clicks for 30+ minutes during business hours<\/td>\n<td>Critical<\/td>\n<td>DNS issue, server down<\/td>\n<\/tr>\n<tr>\n<td>Fallback rate spike<\/td>\n<td>Fallback rate exceeds 60% for existing users<\/td>\n<td>Critical<\/td>\n<td>Universal Links \/ App Links broken<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Warning Alerts (Investigate)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">These may indicate a problem developing:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Alert<\/th>\n<th>Condition<\/th>\n<th>Severity<\/th>\n<th>Typical Cause<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Conversion drop<\/td>\n<td>Conversion rate drops 30%+ vs 7-day average<\/td>\n<td>Warning<\/td>\n<td>Landing page issue, offer expired<\/td>\n<\/tr>\n<tr>\n<td>Latency increase<\/td>\n<td>Average latency exceeds 500ms for 30 minutes<\/td>\n<td>Warning<\/td>\n<td>Server performance degradation<\/td>\n<\/tr>\n<tr>\n<td>Platform divergence<\/td>\n<td>iOS and Android open rates diverge by 20%+<\/td>\n<td>Warning<\/td>\n<td>Platform-specific configuration issue<\/td>\n<\/tr>\n<tr>\n<td>Campaign underperformance<\/td>\n<td>Campaign CTR below 50% of baseline<\/td>\n<td>Warning<\/td>\n<td>Poor targeting, creative fatigue<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Informational Alerts (FYI)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Track notable events:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Alert<\/th>\n<th>Condition<\/th>\n<th>Severity<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Traffic spike<\/td>\n<td>Clicks exceed 200% of normal for the hour<\/td>\n<td>Info<\/td>\n<\/tr>\n<tr>\n<td>New country detected<\/td>\n<td>Significant traffic from a country not in top 20<\/td>\n<td>Info<\/td>\n<\/tr>\n<tr>\n<td>Budget threshold<\/td>\n<td>Campaign spend reaches 80% of budget<\/td>\n<td>Info<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Alert Architecture<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Components<\/h3>\n\n\n\n<pre><code>Data Pipeline \u2192 Aggregation \u2192 Rule Engine \u2192 Notification Router\n                                               \u251c\u2192 Slack\n                                               \u251c\u2192 Email\n                                               \u251c\u2192 PagerDuty\n                                               \u2514\u2192 Webhook\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Implementation<\/h3>\n\n\n\n<pre><code class=\"language-typescript\">interface AlertRule {\n  id: string;\n  name: string;\n  metric: string;\n  condition: {\n    type: &#39;threshold&#39; | &#39;percentage_change&#39; | &#39;anomaly&#39;;\n    operator: &#39;above&#39; | &#39;below&#39;;\n    value: number;\n    window: string;       \/\/ &quot;15m&quot;, &quot;1h&quot;, &quot;24h&quot;\n    baseline?: string;    \/\/ &quot;7d_average&quot;, &quot;previous_period&quot;\n  };\n  severity: &#39;critical&#39; | &#39;warning&#39; | &#39;info&#39;;\n  notifications: {\n    channels: string[];   \/\/ Slack channels, email addresses\n    cooldown: string;     \/\/ &quot;15m&quot;, &quot;1h&quot; - minimum time between alerts\n  };\n  filters?: {\n    platform?: string;\n    route?: string;\n    campaign?: string;\n  };\n}\n\nconst alertRules: AlertRule[] = [\n  {\n    id: &#39;open-rate-crash&#39;,\n    name: &#39;Deep Link Open Rate Crash&#39;,\n    metric: &#39;open_rate&#39;,\n    condition: {\n      type: &#39;threshold&#39;,\n      operator: &#39;below&#39;,\n      value: 40,\n      window: &#39;1h&#39;\n    },\n    severity: &#39;critical&#39;,\n    notifications: {\n      channels: [&#39;#deep-links-critical&#39;, &#39;oncall@company.com&#39;],\n      cooldown: &#39;15m&#39;\n    }\n  },\n  {\n    id: &#39;conversion-drop&#39;,\n    name: &#39;Conversion Rate Drop&#39;,\n    metric: &#39;conversion_rate&#39;,\n    condition: {\n      type: &#39;percentage_change&#39;,\n      operator: &#39;below&#39;,\n      value: -30,\n      window: &#39;4h&#39;,\n      baseline: &#39;7d_average&#39;\n    },\n    severity: &#39;warning&#39;,\n    notifications: {\n      channels: [&#39;#deep-links-alerts&#39;],\n      cooldown: &#39;1h&#39;\n    }\n  }\n];\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Evaluation Loop<\/h3>\n\n\n\n<pre><code class=\"language-typescript\">async function evaluateAlerts(rules: AlertRule[]) {\n  for (const rule of rules) {\n    \/\/ Check cooldown\n    if (isInCooldown(rule.id)) continue;\n\n    \/\/ Get current metric value\n    const currentValue = await getMetricValue(\n      rule.metric,\n      rule.condition.window,\n      rule.filters\n    );\n\n    \/\/ Evaluate condition\n    let triggered = false;\n\n    switch (rule.condition.type) {\n      case &#39;threshold&#39;:\n        triggered = rule.condition.operator === &#39;below&#39;\n          ? currentValue &lt; rule.condition.value\n          : currentValue &gt; rule.condition.value;\n        break;\n\n      case &#39;percentage_change&#39;:\n        const baseline = await getBaselineValue(\n          rule.metric,\n          rule.condition.baseline || &#39;7d_average&#39;,\n          rule.filters\n        );\n        const change = ((currentValue - baseline) \/ baseline) * 100;\n        triggered = rule.condition.operator === &#39;below&#39;\n          ? change &lt; rule.condition.value\n          : change &gt; rule.condition.value;\n        break;\n\n      case &#39;anomaly&#39;:\n        triggered = await isAnomaly(rule.metric, currentValue, rule.condition.window);\n        break;\n    }\n\n    if (triggered) {\n      await sendAlert(rule, currentValue);\n      setCooldown(rule.id, rule.notifications.cooldown);\n    }\n  }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Anomaly Detection<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Statistical Methods<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Simple threshold alerts miss gradual degradation. Anomaly detection catches changes that are unusual relative to historical patterns.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Moving Average with Standard Deviation<\/strong><\/p>\n\n\n\n<pre><code class=\"language-typescript\">function detectAnomaly(\n  current: number,\n  history: number[],\n  sensitivity: number = 2 \/\/ standard deviations\n): { isAnomaly: boolean; zScore: number; expected: number } {\n  const mean = history.reduce((sum, v) =&gt; sum + v, 0) \/ history.length;\n  const variance = history.reduce((sum, v) =&gt; sum + Math.pow(v - mean, 2), 0) \/ history.length;\n  const stdDev = Math.sqrt(variance);\n\n  const zScore = stdDev &gt; 0 ? (current - mean) \/ stdDev : 0;\n\n  return {\n    isAnomaly: Math.abs(zScore) &gt; sensitivity,\n    zScore,\n    expected: mean\n  };\n}\n\n\/\/ Usage\nconst last7Days = await getHourlyValues(&#39;open_rate&#39;, 7);\nconst currentHour = await getCurrentHourValue(&#39;open_rate&#39;);\nconst result = detectAnomaly(currentHour, last7Days);\n\nif (result.isAnomaly) {\n  console.log(`Anomaly detected: open rate is ${currentHour}%, expected ~${result.expected}%`);\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Seasonal Adjustment<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Deep link traffic follows daily and weekly patterns. Compare the current hour to the same hour on previous days:<\/p>\n\n\n\n<pre><code class=\"language-sql\">SELECT\n  AVG(open_rate) AS expected_open_rate,\n  STDDEV(open_rate) AS stddev_open_rate\nFROM hourly_metrics\nWHERE EXTRACT(HOUR FROM timestamp) = EXTRACT(HOUR FROM NOW())\n  AND EXTRACT(DOW FROM timestamp) = EXTRACT(DOW FROM NOW())\n  AND timestamp &gt;= NOW() - INTERVAL &#39;28 days&#39;\n  AND timestamp &lt; NOW() - INTERVAL &#39;1 day&#39;;\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This compares Tuesday 2pm to the last 4 Tuesdays at 2pm, accounting for both daily and weekly seasonality.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Notification Channels<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Slack Notifications<\/h3>\n\n\n\n<pre><code class=\"language-typescript\">async function sendSlackAlert(\n  webhookUrl: string,\n  rule: AlertRule,\n  currentValue: number\n) {\n  const severityEmoji = {\n    critical: &#39;:rotating_light:&#39;,\n    warning: &#39;:warning:&#39;,\n    info: &#39;:information_source:&#39;\n  };\n\n  const message = {\n    blocks: [\n      {\n        type: &#39;header&#39;,\n        text: {\n          type: &#39;plain_text&#39;,\n          text: `${severityEmoji[rule.severity]} ${rule.name}`\n        }\n      },\n      {\n        type: &#39;section&#39;,\n        fields: [\n          { type: &#39;mrkdwn&#39;, text: `*Metric:* ${rule.metric}` },\n          { type: &#39;mrkdwn&#39;, text: `*Current:* ${currentValue}` },\n          { type: &#39;mrkdwn&#39;, text: `*Threshold:* ${rule.condition.operator} ${rule.condition.value}` },\n          { type: &#39;mrkdwn&#39;, text: `*Window:* ${rule.condition.window}` }\n        ]\n      },\n      {\n        type: &#39;actions&#39;,\n        elements: [\n          {\n            type: &#39;button&#39;,\n            text: { type: &#39;plain_text&#39;, text: &#39;View Dashboard&#39; },\n            url: &#39;https:\/\/app.tolinku.com\/analytics&#39;\n          }\n        ]\n      }\n    ]\n  };\n\n  await fetch(webhookUrl, {\n    method: &#39;POST&#39;,\n    headers: { &#39;Content-Type&#39;: &#39;application\/json&#39; },\n    body: JSON.stringify(message)\n  });\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">PagerDuty Integration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For critical alerts that require immediate response:<\/p>\n\n\n\n<pre><code class=\"language-typescript\">async function sendPagerDutyAlert(\n  routingKey: string,\n  rule: AlertRule,\n  currentValue: number\n) {\n  await fetch(&#39;https:\/\/events.pagerduty.com\/v2\/enqueue&#39;, {\n    method: &#39;POST&#39;,\n    headers: { &#39;Content-Type&#39;: &#39;application\/json&#39; },\n    body: JSON.stringify({\n      routing_key: routingKey,\n      event_action: &#39;trigger&#39;,\n      payload: {\n        summary: `${rule.name}: ${rule.metric} is ${currentValue} (threshold: ${rule.condition.value})`,\n        severity: rule.severity === &#39;critical&#39; ? &#39;critical&#39; : &#39;warning&#39;,\n        source: &#39;deep-link-analytics&#39;,\n        component: &#39;deep-links&#39;,\n        custom_details: {\n          metric: rule.metric,\n          current_value: currentValue,\n          threshold: rule.condition.value,\n          window: rule.condition.window\n        }\n      }\n    })\n  });\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Alert Best Practices<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Avoid Alert Fatigue<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Too many alerts cause people to ignore all of them:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Problem<\/th>\n<th>Solution<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Too many low-priority alerts<\/td>\n<td>Batch informational alerts into a daily digest<\/td>\n<\/tr>\n<tr>\n<td>Flapping (alert triggers and resolves repeatedly)<\/td>\n<td>Add hysteresis: trigger at 40%, resolve at 50%<\/td>\n<\/tr>\n<tr>\n<td>Duplicate alerts for the same issue<\/td>\n<td>Group related alerts, suppress duplicates within cooldown<\/td>\n<\/tr>\n<tr>\n<td>Alerts during maintenance<\/td>\n<td>Implement maintenance windows that suppress alerts<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Runbook for Each Alert<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Every alert should link to a runbook explaining what to do:<\/p>\n\n\n\n<pre><code class=\"language-markdown\">## Alert: Deep Link Open Rate Crash\n\n**What it means:** The percentage of deep link clicks that successfully\nopen the app has dropped below 40%.\n\n**Common causes:**\n1. AASA file (iOS) is returning a non-200 status or incorrect content-type\n2. assetlinks.json (Android) verification is failing\n3. App was removed from an app store\n4. CDN is caching a stale or error response for verification files\n\n**Investigation steps:**\n1. Check AASA: `curl -I https:\/\/yourdomain.com\/.well-known\/apple-app-site-association`\n2. Check assetlinks: `curl -I https:\/\/yourdomain.com\/.well-known\/assetlinks.json`\n3. Check platform breakdown: Is it iOS-only, Android-only, or both?\n4. Check if the issue started at a specific time (deployment, DNS change?)\n\n**Resolution:**\n- Fix the verification file and ensure it returns 200 with correct headers\n- Purge CDN cache for the verification file\n- Verify with Apple&#39;s and Google&#39;s validation tools\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku for Alert Configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/webhooks\">Tolinku&#39;s webhooks<\/a> can push deep link events to your alert infrastructure. Combined with <a href=\"https:\/\/tolinku.com\/features\/analytics\">Tolinku&#39;s analytics<\/a>, you can monitor open rates, fallback rates, and error rates from the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/analytics\/\">Tolinku dashboard<\/a> and set up external alerting via <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/webhooks\/\">webhook integrations<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For real-time monitoring, see <a href=\"https:\/\/tolinku.com\/blog\/real-time-analytics-dashboards\/\">real-time analytics dashboards for deep links<\/a>. For analytics fundamentals, see <a href=\"https:\/\/tolinku.com\/blog\/deep-link-analytics-measuring-what-matters\/\">deep link analytics: measuring what matters<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Set up alerts for deep link performance anomalies. Detect traffic spikes, conversion drops, and broken links before they impact campaigns.<\/p>\n","protected":false},"author":2,"featured_media":1811,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Analytics Alerts for Deep Link Anomalies","rank_math_description":"Set up alerts for deep link performance anomalies. Detect traffic spikes, conversion drops, and broken links before they impact campaigns.","rank_math_focus_keyword":"analytics alerting","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-analytics-alerting.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-analytics-alerting.png","footnotes":""},"categories":[14],"tags":[551,37,552,165,20,293,274,61],"class_list":["post-1812","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-analytics","tag-alerting","tag-analytics","tag-anomaly-detection","tag-automation","tag-deep-linking","tag-devops","tag-monitoring","tag-webhooks"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1812","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=1812"}],"version-history":[{"count":2,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1812\/revisions"}],"predecessor-version":[{"id":2423,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1812\/revisions\/2423"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1811"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1812"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1812"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1812"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}