Webhooks
Set up webhook destinations to receive c/side security alerts via HTTP POST requests with HMAC signature verification.
How webhooks work
Webhooks let you receive c/side notifications as HTTP POST requests to a URL you specify. When a trigger fires, c/side sends a JSON payload to your webhook endpoint containing details about the event.
You can configure webhooks in three formats:
- JSON - standard JSON payload for custom integrations
- Slack - pre-formatted for Slack channels
- Discord - pre-formatted for Discord channels
Adding a webhook destination
To add a webhook as a destination in a notification config:
- Open the dashboard and navigate to Team Settings > Notifications
- Create a new notification config or edit an existing one
- Under Send To, click Add destination and select Webhook
- In the Configuration panel, fill in:
- Endpoint URL: the URL where you want to receive webhook notifications (e.g.,
https://example.com/cside/notify) - Secret: a secret key used to sign webhook requests (used for HMAC verification)
- Format: choose JSON, Slack, or Discord
- Endpoint URL: the URL where you want to receive webhook notifications (e.g.,
- Click Save or Save & Test
The webhook payload
Every webhook request contains a structured JSON payload with the following fields:
event"alert.created"
sent_atstring
dataAlertCreatedObject
domain_idstring
destination_idstring
Events
Below are the event IDs and their payloads sent with webhooks.
Event IDs
| ID | Name | data Object |
|---|---|---|
| alert.created | Alert Created | Alert Object |
Event objects
type"URL"|"HASH"|"IP"|"DOMAIN"|"HOSTNAME"
domainstring
targetstring
action"alert"
HMAC verification
Every webhook request includes an x-cside-signature header containing an HMAC SHA256 hash of the request body, signed with the secret you provided when creating the webhook destination.
You should always verify this signature to confirm the request came from c/side and prevent replay attacks.
How to verify the signature
If you are using JavaScript, use our JavaScript package for built-in verification.
For other languages, compute the HMAC SHA256 hash of the raw request body using your webhook secret, then compare it to the x-cside-signature header value.
async function verifyHmac(body: string, signature: string, secret: string) {
const encoder = new TextEncoder();
const encodedBody = encoder.encode(body);
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signatureBuffer = await crypto.subtle.sign("HMAC", key, encodedBody);
const finalSig = Array.from(new Uint8Array(signatureBuffer))
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
return signature.toLowerCase() === finalSig;
}fn verify_hmac(body: &str, signature: &str, secret: &str) -> Result<(), &'static str> {
let mut mac = HmacSha256::new_from_slice(secret.as_bytes()).map_err(|_| "Invalid secret")?;
mac.update(body.as_bytes());
let result = mac.finalize();
let code_bytes = result.into_bytes();
let final_sig = hex::encode(code_bytes);
if signature.to_lowercase() == final_sig {
Ok(())
} else {
Err("Invalid signature")
}
}
// Add to Cargo.toml:
// [dependencies]
// hmac = "0.12"
// sha2 = "0.10"
// hex = "0.4"package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strings"
)
func verifyHmac(body, signature, secret string) bool {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(body))
finalSig := hex.EncodeToString(h.Sum(nil))
return strings.ToLower(signature) == finalSig
}defmodule HmacVerifier do
def verify_hmac(body, signature, secret) do
final_sig =
:crypto.mac(:hmac, :sha256, secret, body)
|> Base.encode16(case: :lower)
String.downcase(signature) == final_sig
end
endimport hmac
import hashlib
import secrets
def verify_hmac(body: str, signature: str, secret: str) -> bool:
final_sig = hmac.new(
secret.encode('utf-8'),
body.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature.lower() == final_sigThanks for your feedback!