Webhooks
Webhooks let S4E push real-time event notifications to your application. When an event occurs --- a scan completes, a new finding is discovered, or an action finishes --- S4E sends an HTTP POST request to your configured endpoint.
Creating a Webhook
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Descriptive name for the webhook. |
url |
string | Yes | HTTPS endpoint to receive events. |
events |
array | Yes | List of event types to subscribe to. |
secret |
string | No | HMAC-SHA256 secret for signature verification. |
headers |
object | No | Custom headers to include in requests. |
active |
boolean | No | Whether the webhook is active (default: true). |
Example
curl -X POST "https://api.s4e.io/api/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Security Dashboard",
"url": "https://dashboard.example.com/webhooks/s4e",
"events": ["finding.new", "scan.completed"],
"secret": "whsec_your_webhook_secret_here",
"active": true
}'
Response
{
"data": {
"id": "wh-001",
"name": "Security Dashboard",
"url": "https://dashboard.example.com/webhooks/s4e",
"events": ["finding.new", "scan.completed"],
"active": true,
"created_at": "2026-04-28T12:00:00Z"
}
}
Webhook Events
| Event | Description |
|---|---|
scan.created |
A new scan has been created. |
scan.completed |
A scan has finished. |
scan.failed |
A scan has failed. |
finding.new |
A new finding has been discovered. |
finding.status_changed |
A finding's status has been updated. |
action.triggered |
An action has been triggered. |
action.completed |
An action has finished executing. |
action.failed |
An action has failed. |
playbook.started |
A playbook execution has started. |
playbook.completed |
A playbook execution has finished. |
playbook.failed |
A playbook execution has failed. |
asset.created |
A new asset has been added. |
asset.verified |
An asset has been verified. |
Payload Format
Every webhook delivery follows this structure:
{
"id": "evt-8a3b9c2d",
"event": "finding.new",
"timestamp": "2026-04-28T12:05:00Z",
"data": {
"finding_id": "f-91827",
"title": "SQL Injection in /api/login",
"severity": "critical",
"cvss": 9.8,
"asset": {
"id": "a-1001",
"name": "api.example.com"
},
"scan_id": "sc-44021"
}
}
Webhook Security (HMAC-SHA256)
When you set a secret on a webhook, S4E signs every payload using HMAC-SHA256. The signature is sent in the X-S4E-Signature header.
Signature Format
Verifying Signatures --- Python
import hmac
import hashlib
def verify_signature(payload_body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
payload_body,
hashlib.sha256
).hexdigest()
received = signature_header.replace("sha256=", "")
return hmac.compare_digest(expected, received)
Verifying Signatures --- Node.js
const crypto = require("crypto");
function verifySignature(payloadBody, signatureHeader, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payloadBody)
.digest("hex");
const received = signatureHeader.replace("sha256=", "");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
}
Warning
Always verify webhook signatures before processing the payload. Without verification, an attacker could send forged events to your endpoint.
Retry Policy
If your endpoint returns a non-2xx status code or times out (30-second timeout), S4E retries delivery:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
After 4 failed attempts, the delivery is marked as failed. You can view failed deliveries and manually retry them in the webhook management UI.
Managing Webhooks
List Webhooks
Update Webhook
curl -X PUT "https://api.s4e.io/api/webhooks/wh-001" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": ["finding.new", "scan.completed", "action.completed"],
"active": true
}'
Delete Webhook
Test Webhook
Send a test event to verify your endpoint:
curl -X POST "https://api.s4e.io/api/webhooks/wh-001/test" \
-H "Authorization: Bearer YOUR_API_KEY"
This sends a webhook.test event with sample data. Your endpoint should return 200 OK.
Delivery Logs
View recent webhook delivery attempts:
curl -X GET "https://api.s4e.io/api/webhooks/wh-001/deliveries" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"data": [
{
"id": "del-001",
"event": "finding.new",
"status": "delivered",
"response_code": 200,
"duration_ms": 245,
"timestamp": "2026-04-28T12:05:01Z"
}
]
}
Best Practices
| Practice | Recommendation |
|---|---|
| Use HTTPS | Always use HTTPS endpoints for webhook delivery. |
| Verify signatures | Always validate the X-S4E-Signature header. |
| Respond quickly | Return 200 immediately, process asynchronously. |
| Handle duplicates | Use the id field to deduplicate events. |
| Monitor deliveries | Check delivery logs for failures regularly. |
Next Steps
- Event Streaming for real-time SSE-based events.
- SIEM Integration for forwarding events to SIEM platforms.