Receive real-time HTTP notifications when AttackerView finds something.
When a scan completes, detects new issues, fixes, regressions, or new subdomains, AttackerView sends a signed JSON POST request to your webhook URL. You configure one URL per account in Settings.
Webhooks fire for all scan types: manual scans, API-triggered scans, retests, and Watchtower monitoring. If you paste a Slack incoming webhook URL, payloads are automatically formatted as Slack Block Kit messages.
findings.changedFires when a scan creates, fixes, or regresses findings on a domain. Only sent when there are actual changes.
scan.completedFires when a scan finishes. Includes a finding summary by severity. Useful for async CI/CD pipelines: trigger a scan via API, then listen for completion on your webhook instead of polling.
subdomain.discoveredFires when Watchtower discovery finds new subdomains or related hosts for a monitored domain.
pentest.completedFires when an AI pentest session finishes, and only if this account already has webhook delivery. Includes finding count, verified count, and credit cost. Buying a one-off pentest does not grant a webhook subscription.
{
"eventType": "findings.changed",
"timestamp": "2026-02-24T09:15:00.000Z",
"domain": {
"hostname": "example.com",
"id": "domain-uuid"
},
"scanId": "scan-uuid",
"scanType": "sentinel",
"permalink": "https://attackerview.com/app/d/example.com?section=issues",
"changes": {
"new": [
{
"checkId": "security-headers-hsts",
"title": "Missing HSTS header",
"severity": "medium",
"category": "Security Headers",
"evidence": "No Strict-Transport-Security header found",
"permalink": "https://attackerview.com/app/d/example.com?section=issues&checkId=security-headers-hsts"
}
],
"regressed": [
{
"checkId": "tls-cert-expiring",
"title": "TLS certificate expiring soon",
"severity": "high",
"category": "TLS",
"evidence": "Certificate expires in 6 days",
"permalink": "...",
"regressionCount": 2
}
],
"fixed": []
}
}scanType is one of: manual, retest, sentinel, patrol, recon.
Regressed findings include regressionCount (how many times the issue has been fixed then reappeared).
{
"eventType": "scan.completed",
"timestamp": "2026-02-27T12:00:00.000Z",
"domain": {
"hostname": "staging.example.com",
"id": "domain-uuid"
},
"scan": {
"id": "scan-uuid",
"status": "completed",
"triggeredBy": "api",
"findingSummary": {
"total": 4,
"critical": 0,
"high": 1,
"medium": 2,
"low": 1,
"info": 0
}
},
"permalink": "https://attackerview.com/app/d/staging.example.com"
}triggeredBy is one of: manual, api, watchtower.
The findingSummary counts failing checks by severity at completion time.
{
"eventType": "subdomain.discovered",
"timestamp": "2026-02-24T09:15:00.000Z",
"domain": {
"hostname": "example.com",
"id": "domain-uuid"
},
"permalink": "https://attackerview.com/app/d/example.com?section=overview",
"subdomains": [
{
"hostname": "staging.example.com",
"discoveryMethod": "certificate_transparency"
},
{
"hostname": "api.example.com",
"discoveryMethod": "dns_expansion"
}
]
}{
"eventType": "pentest.completed",
"timestamp": "2026-03-01T14:30:00.000Z",
"pentest": {
"id": "session-uuid",
"hostname": "app.example.com",
"status": "completed",
"findingCount": 3,
"verifiedCount": 2,
"creditCost": 12
},
"permalink": "https://attackerview.com/app/d/app.example.com"
}status is one of: completed, failed. findingCount is the total number of findings discovered. verifiedCount is how many were deterministically verified. creditCost is the number of credits consumed.
Every webhook request includes an X-AV-Signature-256 header containing an HMAC-SHA256 signature of the request body, using the signing key you received when setting up the webhook.
The header format is:
Example verification in Node.js:
import { createHmac, timingSafeEqual } from "node:crypto";
function verifySignature(body, signature, secret) {
const expected = "sha256=" +
createHmac("sha256", secret).update(body).digest("hex");
return timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your handler:
const sig = req.headers["x-av-signature-256"];
const raw = await req.text();
if (!verifySignature(raw, sig, process.env.AV_WEBHOOK_SECRET)) {
return new Response("Invalid signature", { status: 401 });
}
const payload = JSON.parse(raw);Example in Python:
import hmac, hashlib
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)If your endpoint returns a non-2xx status code or the request times out (10 seconds), AttackerView retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | ~5 minutes |
| 2nd retry | ~30 minutes |
| 3rd retry | ~2 hours |
| 4th retry | ~5 hours |
| After 5 failures | Delivery marked as failed |
A small random jitter (0-60 seconds) is added to each retry delay. You can see the delivery status and retry count in Settings under the delivery log.
If your webhook URL is a Slack incoming webhook (hooks.slack.com), payloads are automatically formatted as Slack Block Kit messages with severity-colored sidebar, emoji prefixes, and a "View all changes" button. No extra configuration needed.
Content-Type: application/json.