Safeguard Docs

Webhooks & Events

Subscribe to Safeguard events — findings, scans, remediations, attestations, audit — and drive your own automations.

The /v1 REST endpoints on this page are not yet available — every path under https://api.safeguard.sh/v1/ returns 404 today. They describe the intended shape only. See API Reference for what is supported now.

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

EventWhen
findings.vulnerability.createdNew vulnerability finding on any asset
findings.vulnerability.severity_changedEPSS / KEV / CVSS update changed severity bucket
findings.vulnerability.reachability_changedReachability classification updated
findings.vulnerability.suppressedException granted or false-positive confirmed
findings.vulnerability.resolvedUpgrade / patch / removal verified
findings.secret.createdNew secret detected
findings.secret.verifiedSecret confirmed live via issuer API
findings.secret.revokedSecret revocation completed
findings.license.createdNew license-policy violation
findings.malware.createdEagle flagged a malicious artifact

Scans

EventWhen
scan.startedScan of any asset begins
scan.completedScan completes; payload includes new findings summary
scan.failedScan errored; payload includes failure reason

Zero-Days

EventWhen
zero_day.publishedSafeguard publishes a new SG-YYYY-NNNNN advisory
zero_day.affecting_tenantA published zero-day affects an asset in your tenant
zero_day.embargo_endedCVE has been assigned and the advisory is now public

Remediation

EventWhen
remediation.plan_createdGriffin generated a plan
remediation.pr_openedAuto-fix PR opened in your repo
remediation.pr_mergedAuto-fix PR merged upstream
remediation.pr_closedAuto-fix PR closed without merge
remediation.self_heal.startedSelf-heal container rebuild started
remediation.self_heal.promotedSelf-heal image promoted to registry tag
remediation.self_heal.rolled_backSelf-heal rollback triggered

Attestations

EventWhen
attestation.createdNew attestation published
attestation.verification_failedAttestation verification failed (admission, CI, etc.)

Workflows

EventWhen
workflow.run.startedWorkflow run began
workflow.run.completedWorkflow run finished successfully
workflow.run.failedWorkflow run failed

Policy

EventWhen
policy.violationA BLOCK or WARN fired
policy.exception_grantedAn exception was approved
policy.exception_expiredA time-boxed exception expired

Asset & Vendor

EventWhen
asset.discoveredNew asset found (repo, image, model, workload, SBOM)
asset.ungovernedAsset flagged as missing owner / SBOM / governance
vendor.sbom_receivedNew vendor SBOM uploaded
vendor.risk_score_changedTPRM risk score threshold crossed

Audit

EventWhen
audit.actionAny 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:

The event stream is not yet available. The snippet below shows the intended shape so you can plan an integration; it will not connect today. Use webhooks or polling for anything you are building now.

The browser EventSource constructor takes EventSourceInit as its second argument, and its only member is withCredentialscustom headers are not part of the API and are silently ignored. Authenticate with a short-lived session cookie instead:

// Browser: the cookie is sent because withCredentials is set.
// Do not put the token in the query string — it lands in access logs.
const es = new EventSource("https://api.safeguard.sh/v1/events/stream", {
  withCredentials: true
});
es.addEventListener("findings.vulnerability.created", (e) => { ... });

Server-side, where you can set headers, use an EventSource implementation that supports them (for example eventsource on Node):

import { EventSource } from "eventsource";

const es = new EventSource("https://api.safeguard.sh/v1/events/stream", {
  fetch: (url, init) =>
    fetch(url, { ...init, headers: { ...init.headers, Authorization: `Bearer ${token}` } })
});

Polling

safeguard events list --after "2026-04-23T00:00:00Z" --type findings.*.created

Filters

Every subscription can be filtered server-side using a subset of the policy expression language:

severity in ["critical","high"]
AND env == "production"
AND finding.kev == true

Server-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_at for 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.created

The 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 endpoints
  • API Reference — full REST API.
  • SDKs — language SDKs with built-in signature verification.
  • Workflows — Safeguard's own consumer of these events.

On this page