> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trysight.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# SEO Alerts

> Per-site rules for rank, backlink, audit, AI-search and traffic alerts, delivered by email, Slack or a webhook you control.

## Overview

SEO alerts tell you when something changes for a site: a tracked keyword drops, a strong domain stops linking to you, a site audit finds new errors, Google's AI answers stop citing you, or traffic moves far outside its normal range. Every alert lands in the **SEO Suite → Alerts** feed. Rules decide which alerts are also sent out, where, and how often.

Rules are set per site under **Settings → Site → SEO Alerts**. The **Rules** button on the Alerts page opens the same screen.

## How rules work

Every rule starts **off**. Turn a rule on, then choose where its alerts go.

| Column | What it does |
| - | - |
| **On** | Whether alerts of this kind are delivered. Alerts of a rule that is off are still detected and listed in the feed; they are just never sent anywhere. |
| **Threshold** | How big a change must be before it counts, for the kinds that have one: places lost or gained, a minimum domain rank, or a z-score. |
| **Email** | Send to the team's owners and admins. |
| **Slack** | Post to the Slack channel linked to the site. See [Slack](https://docs.trysight.ai/integrations/slack.md). |
| **Delivery** | **Instantly**, as soon as the alert is detected, or bundled into a **daily** or **weekly digest**. Applies to email and Slack. |
| **Webhook URL** | POST every alert of this kind to your own endpoint the moment it is detected, whatever the delivery setting. |
| **Send test** | Sends a sample alert through the rule's channels: an email to you only, the Slack channel, and the webhook. Nothing is added to the feed. |

Changes save as you make them.

> [!NOTE]
>
> Alerts are part of the SEO module, so they need a plan that includes it. Rank, backlink and audit alerts also need those checks running for the site.

## Alert kinds

The `kind` column is the value a webhook receives.

| `kind` | Name | Fires when | Setting |
| - | - | - | - |
| `rank_drop` | Rank drop | A tracked keyword lost at least this many places since the last check. | places, default 5 |
| `rank_gain` | Rank gain | A tracked keyword gained at least this many places since the last check. | places, default 5 |
| `top3_lost` | Lost a top 3 spot | A keyword that ranked in the top 3 no longer does. | none |
| `page1_lost` | Fell off page one | A keyword that ranked in the top 10 now ranks below it or not at all. | none |
| `backlinks_lost` | Backlinks lost | Referring pages from domains at or above this rating stopped linking to the site. | min domain rank, default 20 |
| `new_backlink` | New backlink | A domain at or above this rating started linking to the site in the last 7 days. | min domain rank, default 30 |
| `audit_errors` | Site audit errors | A site audit found more page-level errors than the last one. | none |
| `ai_mode_lost` | Google AI Mode citation lost | Google AI Mode cited the site for a prompt last time and not this time. | none |
| `ai_overview_lost` | AI Overview citation lost | A Google AI Overview stopped citing the site for a tracked keyword. | none |
| `url_flip` | Ranking URL changed | Google started ranking a different page of the site for a tracked keyword. | none |
| `not_indexed` | Page not indexed | A page that should be in Google is reported as not indexed. | none |
| `decay` | Content decay | Pages losing clicks over 28 days against the 28 before. | none |
| `cannibalization` | Cannibalization | Two or more of the site's pages draw impressions for the same query. | none |
| `traffic_anomaly` | Traffic anomaly | Daily Search Console clicks or impressions far outside the last 28 days. | z-score, default 3 |
| `citations_anomaly` | AI citations anomaly | Daily AI mentions or citations far outside the last 28 days. | z-score, default 3 |

## Webhooks

A webhook turns an alert into an HTTP request to a URL you control, so you can route it into your own tooling: a ticket, a chat message, a dashboard, a spreadsheet, an automation.

### Setting it up

**1. Turn the rule on**

A webhook only fires for a rule that is on. Email and Slack can stay off.

**2. Enter the URL**

Paste an `https://` URL on a public host into the rule's **Webhook URL** field. Plain `http://` is not accepted, and private or local addresses (`localhost`, `10.x`, `192.168.x`, and so on) are rejected. The field saves when you leave it.

**3. Send a test**

Click **Send test** on the row. Your endpoint receives the payload below with `"test": true`, and the app shows the HTTP status your endpoint answered with.

Each rule has its own URL, so different kinds can go to different places. The same URL on several rules is fine; use the `kind` field to tell them apart.

### When it fires

As soon as the alert is detected, one request per alert, even when the rule's email and Slack delivery is a daily or weekly digest. Webhooks are never batched.

### The request

```http theme={null}
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Sight-Alerts/1.0
X-Sight-Event: seo.alert
```

```json theme={null}
{
  "event": "seo.alert",
  "site": {
    "id": "LdIpYezSDEEZnME-M6NWe",
    "name": "Usehenry",
    "host": "usehenry.ai"
  },
  "kind": "rank_drop",
  "severity": "warning",
  "title": "“ai sales assistant” dropped #4 → #11",
  "message": "Checked daily against the previous check. Open Keywords to see the full move.",
  "url": "https://app.trysight.ai/keywords?site=LdIpYezSDEEZnME-M6NWe",
  "occurredAt": "2026-10-12T14:05:31.000Z"
}
```

| Field | Type | Meaning |
| - | - | - |
| `event` | `"seo.alert"` | Always this value. Check it, and the `X-Sight-Event` header, before doing anything. |
| `site.id` | string | The site's id in Sight. Stable for the life of the site. |
| `site.name` | string | The site's name, falling back to its host. |
| `site.host` | string | The site's hostname. |
| `kind` | string | One of the kinds in the table above. New kinds may be added; ignore ones you do not know. |
| `severity` | `"info"`, `"warning"` or `"error"` | How urgent the alert is. Gains and new backlinks are `info`; most losses are `warning`. |
| `title` | string | A one-line summary, the same line the feed shows. |
| `message` | string | The detail under the title. |
| `url` | string or `null` | A link into Sight to the page the alert is about, already scoped to the site. |
| `occurredAt` | ISO 8601 string | When the alert was detected. |
| `test` | `true`, only on test sends | Present only for **Send test**. Real alerts omit the field. |

Titles and messages are written for people and may change wording; branch on `kind` and `severity`, not on text.

### Responding, retries and timeouts

* Answer with any **2xx** status within **10 seconds**. The body is ignored.
* Redirects are not followed; point the URL at the final endpoint.
* A non-2xx answer or a timeout is retried **three more times** with increasing delays. After that the alert is still in the feed, but the webhook is not attempted again.
* A URL that stops passing the checks above (no longer `https`, or resolving to a private host) is not retried.

### Security and idempotency

Requests are not signed. Treat the URL as a secret: use a long random path or a token in the query string, and reject requests whose `X-Sight-Event` header is not `seo.alert`.

There is no delivery id in the payload. If a retry reaches you after a slow but successful first attempt, you may see the same alert twice; dedupe on `site.id`, `kind`, `title` and `occurredAt`.

### Example receiver

A minimal Next.js route handler:

```ts theme={null}
export async function POST(req: Request) {
  if (req.headers.get("x-sight-event") !== "seo.alert") {
    return new Response("ignored", { status: 400 });
  }
  const alert = await req.json();
  if (alert.event !== "seo.alert") return new Response("ignored", { status: 400 });
  if (alert.test) return new Response("ok");

  await notifyTeam(`${alert.site.name}: ${alert.title}`, alert.url);
  return new Response("ok");
}
```

Keep the handler fast: queue slow work and answer first.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.