Webhooks.
Outbound: a generic webhook connector with a deterministic HMAC signature over the payload. Inbound: provider signatures verified fail-closed with a five-minute timestamp window.
Outbound events
Configure a Generic webhook under Integrations → Ticketing with a URL, an optional shared secret, an optional signature header name (default X-Signature), a signature prefix (default sha256=) and any static extra headers. The connector posts finding events as JSON.
POST https://hooks.example.com/securityvault
Content-Type: application/json
User-Agent: ESOS-Webhook/1.0
X-Timestamp: 2026-09-13T09:12:41.118Z
X-Signature: sha256=7c4a…d3f1
{
"event": "esos.finding",
"timestamp": "2026-09-13T09:12:41.118Z",
"payload": { "finding_id": "…", "severity": "high", "title": "…", "entity": { … } }
}Verifying the signature
The signature is HMAC-SHA256 over the canonical JSON of the payload object only — keys sorted, no whitespace — so identical payloads always produce identical signatures and receivers can de-duplicate replays. The wall-clock timestamp travels in X-Timestamp for staleness checks.
# python
import hmac, hashlib, json
payload = json.dumps(body["payload"], sort_keys=True, separators=(",", ":")).encode()
expected = "sha256=" + hmac.new(secret, payload, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers["X-Signature"])X-Timestamp is older than your tolerance.Inbound webhooks
Provider webhooks that reach SecurityVault — GitHub, GitLab, Bitbucket, Azure DevOps, Slack, Microsoft Teams and ticketing systems — are verified before any payload is trusted: provider-specific HMAC schemes, constant-time comparison, and a ±5-minute timestamp window where the provider supplies one. Verification never raises; a malformed input is a reject. Ticketing webhooks additionally carry a per-organisation, per-provider key with X-SV-Body-Signature and an X-SV-Nonce replay guard, and fail closed when signing is required.
Delivery and retries
Outbound notifications go through a retrying dispatcher with an outbox and a dead-letter queue, so a receiver outage delays delivery rather than losing it. Non-2xx responses are retried with exponential backoff; the delivery record — attempted, delivered, failed — is visible on the integration.