Webhooks
Push real-time events from AEO Optima to your own systems — snapshots, alerts, visibility changes, audits, reports, and billing events.
Overview
Webhooks let you receive HTTP POST notifications whenever something interesting happens in AEO Optima. Instead of polling the API to check for new snapshots or alerts, you register an endpoint and the platform pushes events to it as they occur.
Use webhooks to:
- Pipe alerts into Slack, Microsoft Teams, or PagerDuty
- Trigger your CI/CD pipeline when a content optimization is published
- Sync visibility scores into your data warehouse in real time
- Notify a chatbot or internal tool when a hallucination correction is approved
- Chain into IndexNow to push fresh URLs to crawlers automatically
Plan gate: Webhooks are available on Pro-SME (5 endpoints), Enterprise-Brand (20 endpoints), and Enterprise-Agency (50 endpoints) plans.
Supported events
| Event | Fires when |
|---|---|
snapshot.completed | A snapshot finishes capturing and analysis is done |
alert.triggered | An Alert Rule condition matches |
visibility.changed | A project's visibility score changes by more than the configured threshold |
geo_audit.completed | A GEO Audit finishes |
report.generated | A Report is generated in the app, through the reports API, or on a schedule (a report generated through the MCP generate_report tool sends no event) |
report.shared | A report is shared via a shareable link |
subscription.changed | A billing subscription changes (upgrade, downgrade, cancellation, renewal) |
goal.created | A new visibility goal is created |
goal.at_risk | A goal's pace status changes to "at risk" — projected to miss its target |
insight.generated | An intelligence engine generates a new insight |
action.verified | A completed action's impact measurement is finished (positive, negative, or no change) |
Registering a webhook
- Open Settings → Webhooks from the sidebar
- Click Add endpoint
- Enter:
- URL — the HTTPS endpoint that will receive POSTs (must be public and reachable)
- Events — pick which events to subscribe to (you can select multiple)
- Description — a label for your own reference (e.g., "Slack alerts")
- Click Save
- The platform generates a signing secret — copy it now, you'll need it to verify incoming requests
Payload format
Every webhook delivery has the same envelope:
The data field varies by event type. For example, an alert.triggered event includes the rule name, the metric that crossed the threshold, the current and previous values, and a deep link back into the platform.
Report events
report.generated carries the report's identity and its headline:
| Field | Meaning |
|---|---|
projectId, reportId, historyId | Which report, and its row in the report library |
reportType, format, days | What was generated (frequency and recipientCount instead of format and days for a scheduled report) |
aiBrandScore, letterGrade | The AI Brand Score and its grade — null when the window has no captures, or when one of the measures the score blends could not be loaded |
insufficientData | true when the score is null because nothing was captured in the window |
scoreNotLoaded | true when the score is null because one of its inputs could not be loaded (added 2026-09-24; absent from earlier deliveries). Never read a null score as zero |
goals, insights | Goal counts (activeCount, achievedCount, atRiskCount) and new-insight counts (totalNew, criticalCount). Every report.generated event carries both since 2026-09-24 (before, only a report the API generated itself did). Each is null when the report has none, when the report type leaves it out, or when its read could not be loaded; read null as not reported, never as zero |
A schedule's own Webhook delivery channel posts report.delivered with the same aiBrandScore, letterGrade, insufficientData and scoreNotLoaded fields, plus projectName, reportType, frequency, totalSnapshots, reportUrl and historyId. A report that could not be generated at all sends neither event.
Verifying signatures
Every webhook request includes an X-AEO-Signature header of the form sha256=<hex> plus a companion X-AEO-Timestamp header (Unix seconds). The signature is an HMAC-SHA256 computed with your endpoint's signing secret over the string timestamp + "." + rawBody — not the body alone. Always verify this signature before trusting the payload — without verification, anyone who knows your URL can forge events.
Verification in pseudocode:
Use a constant-time comparison to avoid timing attacks. Most HMAC libraries provide this — for example, hmac.compare_digest in Python or crypto.timingSafeEqual in Node.js. You may also reject deliveries whose X-AEO-Timestamp is too old, to limit replay.
Each delivery also carries X-AEO-Event (the event type) and X-AEO-Delivery (a unique delivery id) — use the latter to dedupe idempotently.
Delivery, retries, and timeouts
- Timeout: 10 seconds. If your endpoint doesn't respond in time, the delivery is marked failed.
- Retries: 3 attempts with backoff of 60s, 10min, then 1hr. After the third failure the delivery is dropped.
- Circuit breaker: If your endpoint fails 5 deliveries in a row, the platform automatically disables it and alerts you. Re-enable from the webhook settings page once you've fixed the issue.
The webhook settings page shows recent deliveries with status, timestamp, and the response your endpoint returned — useful for debugging.
Security
- Webhooks must use HTTPS. HTTP endpoints are rejected.
- Always verify the
X-AEO-Signatureheader (see above) - Rotate the signing secret periodically — the settings page has a Rotate secret button. After rotation, deliveries use the new secret immediately, so deploy the new secret to your endpoint first.
- Don't include the signing secret in URLs, query strings, or logs
Best practices
- Respond fast. Acknowledge the delivery (return 2xx) within a couple seconds, then process the payload asynchronously. If you do heavy work synchronously, you'll hit the 10-second timeout.
- Be idempotent. Each event has a unique
id. If you receive the sameidtwice (from a retry), don't process it twice. - Log raw deliveries. Save the raw payload and signature header. When debugging, you'll want to replay the exact bytes the platform sent.
- Subscribe to only the events you need. A webhook firing on every snapshot completion can be hundreds of POSTs per day on a busy project — only subscribe to high-volume events if you actually need them.
- Use the test button. The settings page has a "Send test event" button that delivers a synthetic payload to your endpoint. Use it to verify your endpoint and signature verification before relying on real events.
Related docs
- API Keys — alternative if you'd rather poll the API instead of receive pushes
- Alert Rules — most webhook subscribers care about this event
- IndexNow — chain webhooks into URL re-indexing