Security findings that don't reach engineers in time aren't security findings — they're future incidents. This tutorial shows you how to wire PentestCheck's alert system directly into the tools your team already uses.
Webhook Architecture Overview
PentestCheck fires webhooks on three trigger types:
| Trigger | Description |
|---|---|
finding.created | New vulnerability found during scan |
scan.completed | Full scan finished with summary |
asset.discovered | New internet-facing asset found by EASM |
asset.changed | Existing asset changed (new port, cert expiry, etc.) |
score.threshold | Threat Score crossed a configured threshold |
Each webhook delivers a JSON payload to your configured endpoint. You can configure multiple endpoints with different severity filters.
Payload Format
{
"event": "finding.created",
"timestamp": "2026-04-09T14:23:11Z",
"organization": "acme-corp",
"payload": {
"id": "find_7g3kx9",
"severity": "CRITICAL",
"cvss": 9.1,
"category": "injection",
"owasp": "A03",
"title": "SQL Injection — /api/v1/search (parameter: q)",
"asset": {
"hostname": "app.acmecorp.com",
"ip": "104.18.22.45",
"port": 443
},
"remediation": "Use parameterized queries. Do not interpolate user input directly into SQL strings.",
"evidence_url": "https://app.pentestcheck.com/findings/find_7g3kx9",
"scan_id": "scan_p4r7k1"
}
}
Step 1: Configure Your Endpoint
In PentestCheck dashboard → Settings → Webhooks → Add Endpoint.
Configure:
- URL: Your endpoint URL
- Secret: Used for HMAC-SHA256 signature verification (see below)
- Events: Select which trigger types to receive
- Severity filter: Minimum severity to trigger (e.g., HIGH+ only)
- Target filter: Optionally limit to specific targets
Step 2: Slack Integration
Option A: Direct Slack Incoming Webhook (simplest)
Create a Slack Incoming Webhook in your workspace and set it as the PentestCheck endpoint URL directly. Slack accepts raw JSON but ignores fields it doesn't understand.
For a formatted Slack message, set up a lightweight transform function (e.g., Cloudflare Worker, AWS Lambda, Vercel Edge Function):
// transform.js — Cloudflare Worker
export default {
async fetch(request) {
const payload = await request.json();
// Verify HMAC signature
const signature = request.headers.get('X-PentestCheck-Signature');
if (!verifySignature(await request.text(), signature, SECRET)) {
return new Response('Unauthorized', { status: 401 });
}
const { payload: finding } = payload;
const severityEmoji = {
CRITICAL: '🔴', HIGH: '🟠', MEDIUM: '🟡', LOW: '🟢', INFO: '⚪'
}[finding.severity] || '⚪';
const slackPayload = {
text: `${severityEmoji} *${finding.severity}* finding on ${finding.asset.hostname}`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `${severityEmoji} *${finding.title}*\n*Asset:* ${finding.asset.hostname}\n*CVSS:* ${finding.cvss} | *OWASP:* ${finding.owasp}`
}
},
{
type: 'actions',
elements: [{
type: 'button',
text: { type: 'plain_text', text: 'View Finding' },
url: finding.evidence_url,
style: finding.severity === 'CRITICAL' ? 'danger' : 'primary'
}]
}
]
};
await fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(slackPayload)
});
return new Response('OK');
}
};
Step 3: Verify Webhook Signatures
Every PentestCheck webhook includes an X-PentestCheck-Signature header for payload verification:
X-PentestCheck-Signature: sha256=a8f3b2c...
Verify it before processing:
import hmac, hashlib
def verify_signature(payload_bytes: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
payload_bytes,
hashlib.sha256
).hexdigest()
received = signature.removeprefix('sha256=')
return hmac.compare_digest(expected, received)
Never process webhook payloads without verifying the signature. An attacker who knows your endpoint URL could inject false findings.
Step 4: GitHub Actions Integration
For CI/CD blocking — fail the pipeline if a critical finding is discovered:
# .github/workflows/security-scan.yml
name: Security Scan
on:
push:
branches: [main, staging]
pull_request:
jobs:
pentest-scan:
runs-on: ubuntu-latest
steps:
- name: Trigger PentestCheck Scan
id: scan
run: |
SCAN_ID=$(curl -s -X POST \
-H "Authorization: Bearer ${{ secrets.PENTESTCHECK_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"target": "${{ vars.STAGING_URL }}", "mode": "deep"}' \
https://api.pentestcheck.com/v1/scans | jq -r '.scan_id')
echo "scan_id=$SCAN_ID" >> $GITHUB_OUTPUT
- name: Wait for Scan Completion
run: |
while true; do
STATUS=$(curl -s \
-H "Authorization: Bearer ${{ secrets.PENTESTCHECK_API_KEY }}" \
https://api.pentestcheck.com/v1/scans/${{ steps.scan.outputs.scan_id }} \
| jq -r '.status')
[ "$STATUS" = "completed" ] && break
sleep 30
done
- name: Check Results
run: |
CRITICAL=$(curl -s \
-H "Authorization: Bearer ${{ secrets.PENTESTCHECK_API_KEY }}" \
"https://api.pentestcheck.com/v1/scans/${{ steps.scan.outputs.scan_id }}/findings?severity=CRITICAL,HIGH" \
| jq '.count')
if [ "$CRITICAL" -gt "0" ]; then
echo "::error::$CRITICAL critical/high security findings. Blocking deployment."
exit 1
fi
Step 5: Severity-Based Routing
Different severities should reach different teams:
| Severity | Destination | Response Time |
|---|---|---|
| CRITICAL | #security-critical + PagerDuty | Immediate |
| HIGH | #security-alerts + Jira auto-ticket | < 1 hour |
| MEDIUM | #security-weekly digest | Next sprint |
| LOW | Jira backlog | Next quarter |
Configure separate webhook endpoints with different severity filters for each destination.
Avoiding Alert Fatigue
The most common webhook implementation failure: routing every finding to the same channel at the same priority. Within a week, the channel is muted.
Rules for a sustainable alert configuration:
- CRITICAL/HIGH go to a channel that your team actually monitors and treats as urgent
- MEDIUM findings go to a weekly digest, not individual alerts
asset.discoveredevents go to a separate channel (infrastructure awareness, not security response)- Set up a 30-day review of suppressed findings to prevent systematic blind spots
A security alert system that engineers ignore provides less value than no system at all.
PentestCheck's webhook system supports Slack, Discord, Telegram, Microsoft Teams, and custom HTTPS endpoints. Configure alerts in under 2 minutes from your dashboard.