This reference documents the webhook payload structure sent by Content Guard Pro when security findings are detected.
Webhook Overview #
- Method: POST
- Content-Type: application/json
- Timeout: 30 seconds
- Retry Policy: Exponential backoff, 5 attempts
Headers #
| Header | Description |
| ——– | ————- |
Content-Type |
Always application/json |
X-CGP-Event |
Event type (e.g., critical_finding) |
X-CGP-Site |
Source site URL |
X-CGP-Signature |
HMAC-SHA256 signature (if secret configured) |
User-Agent |
Content-Guard-Pro/1.0 |
Signature Verification #
If a webhook secret is configured, verify authenticity:
X-CGP-Signature: sha256=5d8e5a9f3b2c1d4e...
The signature is computed as:
HMAC-SHA256(payload_body, webhook_secret)
PHP Verification Example #
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_CGP_SIGNATURE'] ?? '';$expected = 'sha256=' . hash_hmac('sha256', $payload, $your_secret);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
die('Invalid signature');
}
$data = json_decode($payload, true);
// Process webhook...
Event Types #
critical_finding #
Sent when a Critical severity finding is detected.
finding_detected #
Sent for any finding (when severity threshold includes lower severities).
test #
Sent when “Send Test Webhook” is clicked.
Payload Structure #
Full Payload Example #
{
"site": {
"url": "https://yoursite.com",
"name": "Your Site Name",
"blog_id": 1,
"wordpress_version": "6.4.2",
"plugin_version": "1.0.0"
},
"event": "critical_finding",
"finding": {
"id": 12345,
"fingerprint": "a1b2c3d4e5f6...",
"severity": "critical",
"confidence": 92,
"rule_id": "ext_script_non_allowlist",
"category": "external_resources",
"object": {
"type": "post",
"id": 678,
"field": "post_content",
"title": "My Blog Post",
"url": "https://yoursite.com/my-blog-post/",
"edit_url": "https://yoursite.com/wp-admin/post.php?post=678&action=edit"
},
"summary": "External script from suspicious-domain.example",
"matched_excerpt": "[script tag pointing to suspicious-domain.example]",
"domain": "suspicious-domain.example",
"url": "https://suspicious-domain.example/file.js",
"first_seen": "2025-01-15T10:30:00Z",
"last_seen": "2025-01-15T10:30:00Z",
"reputation": {
"google_safe_browsing": {
"threat_type": "MALWARE",
"checked_at": "2025-01-15T10:30:05Z"
},
"phishtank": {
"in_database": false
}
}
},
"actions": {
"review_url": "https://yoursite.com/wp-admin/admin.php?page=content-guard-pro-findings&finding=12345",
"quarantine_url": "https://yoursite.com/wp-admin/admin.php?page=content-guard-pro-findings&action=quarantine&finding=12345&_wpnonce=abc123"
},
"scan": {
"scan_id": 789,
"mode": "standard",
"type": "scheduled"
},
"timestamp": "2025-01-15T10:30:10Z"
}
Field Reference #
site Object #
| Field | Type | Description |
| ——- | —— | ————- |
url |
string | Site URL |
name |
string | Site title |
blog_id |
integer | Blog ID (for multisite) |
wordpress_version |
string | WordPress version |
plugin_version |
string | Content Guard Pro version |
finding Object #
| Field | Type | Description |
| ——- | —— | ————- |
id |
integer | Finding database ID |
fingerprint |
string | Unique finding identifier |
severity |
string | critical, suspicious, or review |
confidence |
integer | 0-100 confidence score |
rule_id |
string | Detection rule identifier |
category |
string | Finding category |
summary |
string | Human-readable description |
matched_excerpt |
string | Sanitized matched content |
domain |
string | Extracted domain (if applicable) |
url |
string | Full URL (if applicable) |
first_seen |
string | ISO 8601 first detection time |
last_seen |
string | ISO 8601 last detection time |
finding.object Object #
| Field | Type | Description |
| ——- | —— | ————- |
type |
string | post, postmeta, or option |
id |
integer | Object ID |
field |
string | Field name |
title |
string | Post title (for posts) |
url |
string | Permalink (for posts) |
edit_url |
string | Admin edit URL |
finding.reputation Object #
Contains results from reputation services (when available):
{
"google_safe_browsing": {
"threat_type": "MALWARE",
"platform_type": "ANY_PLATFORM",
"checked_at": "2025-01-15T10:30:05Z"
},
"phishtank": {
"in_database": true,
"verified": true,
"phish_id": 123456,
"checked_at": "2025-01-15T10:30:05Z"
}
}
actions Object #
| Field | Type | Description |
| ——- | —— | ————- |
review_url |
string | Direct link to review finding |
quarantine_url |
string | One-click quarantine URL (includes nonce) |
scan Object #
| Field | Type | Description |
| ——- | —— | ————- |
scan_id |
integer | Scan database ID |
mode |
string | quick or standard |
type |
string | manual, scheduled, or on_save |
Test Payload #
When “Send Test Webhook” is clicked:
{
"site": {
"url": "https://yoursite.com",
"name": "Your Site Name",
"blog_id": 1
},
"event": "test",
"message": "This is a test webhook from Content Guard Pro",
"timestamp": "2025-01-15T10:30:00Z"
}
Severity Values #
| Value | Meaning |
| ——- | ——— |
critical |
Immediate action required |
suspicious |
Investigation needed |
review |
Worth checking |
Category Values #
| Value | Description |
| ——- | ————- |
external_resources |
Scripts, iframes, embeds |
suspicious_urls |
Shorteners, tracking |
hidden_content |
CSS-hidden elements |
obfuscation |
Encoded/disguised code |
seo_spam |
Keyword spam |
javascript_injection |
XSS, event handlers |
code_injection |
PHP patterns |
cryptojacking |
Mining scripts |
redirects |
Unauthorized redirects |
link_analysis |
Anomalous patterns |
Responding to Webhooks #
Your endpoint should:
1. Respond quickly (within 30 seconds)
2. Return 2xx status for successful receipt
3. Process asynchronously if needed
Success Response #
HTTP/1.1 200 OK{"received": true}
Async Processing Pattern #
// Respond immediately
http_response_code(200);
echo json_encode(['received' => true]);// Flush output
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
// Process webhook
$data = json_decode(file_get_contents('php://input'), true);
// ... your processing logic
Retry Behavior #
If your endpoint fails:
| Attempt | Delay |
| ——— | ——- |
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 15 minutes |
| 5 | 1 hour |
After 5 failures, webhook is abandoned (logged in webhook history).