push connector · cloudevents + hmac
Webhooks
Push Wytness anomaly alerts anywhere. CloudEvents 1.0 body. HMAC-SHA256 signature header. Retries with jitter. Public spec.
what it does
Where your alerts land
The generic webhook connector pushes Wytness anomaly alerts (severity, rule, agent ID, description) to any HTTPS endpoint you control — no raw event payloads, tool parameters, or PII. The envelope is CloudEvents 1.0. Every delivery is signed HMAC-SHA256 on a timestamp-prefixed body to defeat replay. A delivery that exhausts its retries is recorded as failed in the delivery log — never silently dropped.
setup
From nothing to first delivery
01
In the Wytness app, go to Integrations → Connectors → Add connection → Generic webhook and paste your receiver URL.
02
Choose an HMAC secret and enter it in the wizard; store the same value in your receiver's environment as WYTNESS_WEBHOOK_SECRET. Wytness keeps it encrypted and never displays it again.
03
Optionally narrow deliveries with severity or rule-name filters (e.g. only `high` and `critical` alerts).
04
On your server, verify the `Wytness-Signature` header on incoming POST requests before parsing the body. Reject if the timestamp is older than 5 minutes.
05
Click Test connection. A signed CloudEvent should hit your endpoint within seconds.
the payload
What arrives at your endpoint
One CloudEvents 1.0 envelope per anomaly. The signature covers the raw body bytes prefixed with the timestamp, so verify against exactly what you received — not a re-serialised parse.
// Headers on the POST:// Content-Type: application/cloudevents+json// Wytness-Signature: t=1716470621,v1=8af0c6c1c1b5e2c0e1d2f3a4b5c6d7e8f9a0b1c2d3e4f5061728394a5b6c7d8e// Wytness-Delivery: d7f3e2c1-9a4b-4d6e-8f1a-2b3c4d5e6f70// Wytness-Event: ai.wytness.anomaly.detected.v1//// Body:{ "specversion": "1.0", "type": "ai.wytness.anomaly.detected.v1", "source": "https://api.wytness.ai/orgs/7c2a91d5-4b3e-4f8a-9c1d-2e3f4a5b6c7d", "id": "9f2d1c34-8a7b-4e2d-b1a0-5c6d7e8f9a0b", "time": "2026-05-23T14:23:41Z", "datacontenttype": "application/json", "data": { "alert_id": "9f2d1c34-8a7b-4e2d-b1a0-5c6d7e8f9a0b", "agent_id": "payments-reconciler-v3", "rule_name": "off_hours_activity", "severity": "high", "description": "Tool invocation outside business hours window.", "detected_at": "2026-05-23T14:23:41.182Z", "url": "https://app.wytness.ai/anomalies?alert_id=9f2d1c34-8a7b-4e2d-b1a0-5c6d7e8f9a0b" }}prove it landed
Verify end to end
The same HMAC pattern works in any language with an HMAC-SHA256 primitive — this is the whole receiver-side contract:
# Drop into your webhook receiver. Reject any POST whose timestamp is# more than 5 minutes old or whose signature doesn't match. import hmac, hashlib, time WYTNESS_WEBHOOK_SECRET = "<the-secret-you-chose-in-the-wizard>" def verify(body: bytes, signature_header: str) -> bool: parts = dict(p.split("=", 1) for p in signature_header.split(",")) t = int(parts["t"]) if abs(time.time() - t) > 300: # 5-minute replay window return False signed = f"{t}.".encode() + body expected = hmac.new( WYTNESS_WEBHOOK_SECRET.encode(), signed, hashlib.sha256, ).hexdigest() return hmac.compare_digest(expected, parts["v1"])the boundary
What flows and what doesn't
Sends
CloudEvents 1.0 JSON envelope via HTTP POST
Headers
`Wytness-Signature: t=<unix>,v1=<hmac-sha256(t.body)>`, `Wytness-Delivery: <uuid>`, `Wytness-Event: <type>`
Retries
60s → 300s → 900s with ±25% jitter; 401/403/404/410/422 stop retries immediately
Reads
Nothing. One-way push from Wytness to your endpoint.
Network
One inbound HTTPS rule: the internet must reach your receiver. Wytness source IPs are not fixed — authenticate with the Wytness-Signature HMAC, not an IP allowlist.
stated plainly
Limitations
—
One event type today
ai.wytness.anomaly.detected.v1 is the only type the dispatcher sends — connectors forward anomaly alerts, not the raw event stream. The Wytness-Event header names the type so your receiver can branch when more arrive.
—
Public endpoints only
Private and internal network addresses are refused by design (SSRF protection). Your receiver needs a public HTTPS endpoint; if its certificate is from an internal CA, switch off Verify TLS certificate on the connection.
—
Replay tolerance is yours to enforce
Wytness signs a timestamped body; the 5-minute rejection window runs in your receiver. The Wytness-Delivery UUID is unique per delivery attempt — dedupe on it if you need exactly-once processing.
round trip
Smoke-test your receiver
Anything other than a fast 2xx is retried on the ladder above; the terminal statuses (401/403/404/410/422) fail the delivery immediately.
# Fire a test delivery from the wizard's Test connection button (or curl# with the secret you chose). Your receiver should respond 2xx quickly. $ curl -i https://your-receiver.example.com/wytness \ -H "Content-Type: application/cloudevents+json" \ -H "Wytness-Signature: t=$(date +%s),v1=<computed>" \ -H "Wytness-Delivery: $(uuidgen)" \ -H "Wytness-Event: ai.wytness.anomaly.detected.v1" \ -d @sample_event.jsonrelated
Keep reading
/docs/connectors
Connector setup guide
The full integrator contract — network requirements, delivery semantics, and every wizard field. Read the guide.
/connectors
All connectors
Sentinel, Splunk, ServiceNow, Slack, and the generic webhook. See the index.
tier
Tier availability
Business plan or higher.