Webhooks & Events
Subscribe to Safeguard events — findings, scans, remediations, attestations, audit — and drive your own automations.
Webhooks & Events
Safeguard publishes every meaningful state change as an event. Subscribe via webhook, stream via Kafka / Kinesis / Pub-Sub, or poll the events API. Every webhook payload is signed so you can verify the sender.
Event Catalog
Events follow the shape <domain>.<subject>.<verb>.
Findings
| Event | When |
|---|---|
findings.vulnerability.created | New vulnerability finding on any asset |
findings.vulnerability.severity_changed | EPSS / KEV / CVSS update changed severity bucket |
findings.vulnerability.reachability_changed | Reachability classification updated |
findings.vulnerability.suppressed | Exception granted or false-positive confirmed |
findings.vulnerability.resolved | Upgrade / patch / removal verified |
findings.secret.created | New secret detected |
findings.secret.verified | Secret confirmed live via issuer API |
findings.secret.revoked | Secret revocation completed |
findings.license.created | New license-policy violation |
findings.malware.created | Eagle flagged a malicious artifact |
Scans
| Event | When |
|---|---|
scan.started | Scan of any asset begins |
scan.completed | Scan completes; payload includes new findings summary |
scan.failed | Scan errored; payload includes failure reason |
Zero-Days
| Event | When |
|---|---|
zero_day.published | Safeguard publishes a new SG-YYYY-NNNNN advisory |
zero_day.affecting_tenant | A published zero-day affects an asset in your tenant |
zero_day.embargo_ended | CVE has been assigned and the advisory is now public |
Remediation
| Event | When |
|---|---|
remediation.plan_created | Griffin generated a plan |
remediation.pr_opened | Auto-fix PR opened in your repo |
remediation.pr_merged | Auto-fix PR merged upstream |
remediation.pr_closed | Auto-fix PR closed without merge |
remediation.self_heal.started | Self-heal container rebuild started |
remediation.self_heal.promoted | Self-heal image promoted to registry tag |
remediation.self_heal.rolled_back | Self-heal rollback triggered |
Attestations
| Event | When |
|---|---|
attestation.created | New attestation published |
attestation.verification_failed | Attestation verification failed (admission, CI, etc.) |
Workflows
| Event | When |
|---|---|
workflow.run.started | Workflow run began |
workflow.run.completed | Workflow run finished successfully |
workflow.run.failed | Workflow run failed |
Policy
| Event | When |
|---|---|
policy.violation | A BLOCK or WARN fired |
policy.exception_granted | An exception was approved |
policy.exception_expired | A time-boxed exception expired |
Asset & Vendor
| Event | When |
|---|---|
asset.discovered | New asset found (repo, image, model, workload, SBOM) |
asset.ungoverned | Asset flagged as missing owner / SBOM / governance |
vendor.sbom_received | New vendor SBOM uploaded |
vendor.risk_score_changed | TPRM risk score threshold crossed |
Audit
| Event | When |
|---|---|
audit.action | Any state-mutating action in the audit log |
Delivery Options
HTTP Webhook
safeguard webhooks create \
--url https://example.com/sg \
--events findings.vulnerability.created,findings.secret.verified \
--secret $(openssl rand -hex 32) \
--filter 'severity in ["critical","high"] and env == "production"'Payload:
{
"id": "evt_01J...ABC",
"type": "findings.vulnerability.created",
"created_at": "2026-04-23T12:34:56.789Z",
"data": { "finding": { ... }, "asset": { ... } },
"tenant": "acme",
"api_version": "2026-01-01"
}Every payload ships with headers:
X-Safeguard-Signature:v1,t=<unix>,s=<hex hmac sha-256>X-Safeguard-Event-Id: the event ID (idempotent).X-Safeguard-Delivery-Attempt: attempt count (1 for first delivery).X-Safeguard-Api-Version.
Streams
Push events directly into your stream:
- AWS Kinesis Data Streams / Firehose — direct integration.
- Azure Event Hubs — direct integration.
- GCP Pub/Sub — direct integration.
- Confluent Cloud / Apache Kafka — via webhook sink.
- NATS — via webhook sink.
- Redis Streams — via webhook sink.
SSE / WebSocket
For interactive dashboards:
const es = new EventSource("https://api.safeguard.sh/v1/events/stream", {
headers: { Authorization: `Bearer ${token}` }
});
es.addEventListener("findings.vulnerability.created", (e) => { ... });Polling
safeguard events list --after "2026-04-23T00:00:00Z" --type findings.*.createdFilters
Every subscription can be filtered server-side using a subset of the policy expression language:
severity in ["critical","high"]
AND env == "production"
AND NOT finding.kev == falseServer-side filters minimize traffic and reduce webhook noise.
Reliability
- At-least-once delivery. Consumers must handle duplicates by
X-Safeguard-Event-Id. - Exponential backoff retries — 11 attempts over 24 hours (1s, 5s, 30s, 1m, 5m, 15m, 1h, 3h, 6h, 12h, 24h).
- Dead-letter queue — undelivered events land in a DLQ you can replay from the UI or API.
- SLA — 99.9% delivery within 10 seconds for Pro, 99.99% for Enterprise.
Signature Verification
import hmac, hashlib, time
def verify(secret: bytes, header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",", 1)[1].split(","))
ts, sig = parts["t"], parts["s"]
# reject events older than 5 min (replay guard)
if abs(time.time() - int(ts)) > 300: return False
expected = hmac.new(secret, f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)import { createHmac, timingSafeEqual } from "crypto";
export function verify(secret: string, header: string, body: Buffer): boolean {
const [_, tsPart, sigPart] = header.split(",");
const ts = tsPart.slice(2);
const sig = sigPart.slice(2);
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${ts}.`).update(body).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}Idempotency
- Each event has a stable
id. - Consumers must deduplicate by ID; the guarantee is at-least-once, not exactly-once.
- Within a single event type, Safeguard guarantees monotonically increasing
created_atfor a given subject (finding, asset, workflow run, etc.).
Testing
Trigger synthetic events for testing without triggering actual state changes:
safeguard webhooks test --endpoint https://example.com/sg --type findings.vulnerability.createdThe test payload has a "test": true field so your consumer can ignore it in production pipelines.
Observability
The Webhooks page shows per-endpoint:
- Delivery attempts (success, retry, failed).
- Average latency.
- DLQ size.
- Last 1000 deliveries with request / response bodies (scrubbed of secrets).
API
safeguard webhooks list
safeguard webhooks create --url ... --events ...
safeguard webhooks update <id> --filter '...'
safeguard webhooks delete <id>
safeguard events list
safeguard events show <id>
safeguard events replay <id> # replays to all subscribed endpointsRelated
- API Reference — full REST API.
- SDKs — language SDKs with built-in signature verification.
- Workflows — Safeguard's own consumer of these events.