# Webhooks

Get notified when runs complete, websites verify or plans change.

Add an HTTPS endpoint in Settings → Webhooks and choose events: serp.run.completed, serp.run.failed, website.verified, billing.subscription.updated, usage.quota.reached, serp.rank.changed (a tracked position moved), serp.failure_rate.high (over 50% of a website’s runs failed in 24 hours), plan.limit.reached (a website used all its search terms or clicks per day), competitor.rank.changed (a tracked competitor moved 3+ places, or entered or left the top 10 or top 30 for one of your terms), exchange.installed (your Partners block was found and the website joined the partner network), exchange.link_missing (your block was missing or hidden on two checks in a row, so your links on partner sites are paused), exchange.partner_added (a partner was added to your block, or your link was added to a partner's), audit.completed (an SEO audit of one of your or your competitor pages finished, with score, previous score and fixed/new issue counts), audit.score_dropped (one of your pages' audit score fell 5 points or more), uptime.monitor.down (an uptime monitor confirmed an outage and an incident opened), uptime.monitor.up (the monitor recovered, with the outage length), backlinks.domain.new and backlinks.domain.lost (referring domains gained or lost in a monthly backlinks update).

## Verifying signatures

Each request has a Serpfull-Signature header: t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.body" using your signing secret>. Reject requests older than 5 minutes.

```bash
const [t, v1] = header.split(',').map(p => p.split('=')[1])
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
const ok = timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
```

## Retries

Respond with any 2xx within 10 seconds. Anything else, or a timeout, is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours; after 8 attempts the delivery is marked failed. Respond 410 Gone to stop retries for that event. Settings → Webhooks shows recent deliveries, lets you retry a failed one and send a test.ping.

## Idempotency

Deliveries are at-least-once. Each request carries Serpfull-Event-Id (also the id field in the body); store it and ignore repeats. Serpfull-Delivery-Attempt tells you which attempt this is.

## Endpoint rules

Endpoints must be public HTTPS URLs on port 443. Private, loopback and link-local addresses are refused, and redirects aren't followed.
