> ## 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](/integrations/slack). |
| **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.
</Note>

## 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 rank stopped linking to the site. | min domain rank, default 20 |
| `new_backlink` | New backlink | A domain at or above this rank 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

<Steps>
  <Step title="Turn the rule on">
    A webhook only fires for a rule that is on. Email and Slack can stay off.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

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.