{"id":2547,"date":"2026-02-23T12:16:14","date_gmt":"2026-02-23T12:16:14","guid":{"rendered":"https:\/\/cyberpanel.net\/KnowledgeBase\/webhooks-event-notifications\/"},"modified":"2026-02-23T12:17:16","modified_gmt":"2026-02-23T12:17:16","slug":"webhooks-event-notifications","status":"publish","type":"post","link":"https:\/\/cyberpanel.net\/KnowledgeBase\/webhooks-event-notifications\/","title":{"rendered":"Webhooks \u2014 Real-time Event Notifications"},"content":{"rendered":"<style>\n.kb-info-box { background: #e8f4fd; border-left: 4px solid #2196F3; padding: 16px 20px; margin: 20px 0; border-radius: 4px; }\n.kb-info-box.warning { background: #fff8e1; border-left-color: #ff9800; }\n.kb-info-box.success { background: #e8f5e9; border-left-color: #4caf50; }\n.kb-info-box.tip { background: #f3e5f5; border-left-color: #9c27b0; }\n.kb-info-box strong { display: block; margin-bottom: 4px; }\n.kb-table { width: 100%; border-collapse: collapse; margin: 20px 0; }\n.kb-table th, .kb-table td { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }\n.kb-table th { background: #f5f5f5; font-weight: 600; }\n.kb-table tr:nth-child(even) { background: #fafafa; }\n.kb-code { background: #1e1e1e; color: #d4d4d4; padding: 16px 20px; border-radius: 6px; overflow-x: auto; margin: 16px 0; font-size: 14px; line-height: 1.5; }\n.kb-code .comment { color: #6a9955; }\n.kb-code .keyword { color: #569cd6; }\n.kb-code .string { color: #ce9178; }\n<\/style>\n<p>Webhooks notify your application in real-time when email events occur &mdash; deliveries, bounces, opens, clicks, and more. Instead of polling the API, your server receives HTTP POST requests instantly.<\/p>\n<h2>Supported Events<\/h2>\n<table class=\"kb-table\">\n<tr>\n<th>Event<\/th>\n<th>Description<\/th>\n<th>When It Fires<\/th>\n<\/tr>\n<tr>\n<td><code>delivered<\/code><\/td>\n<td>Email delivered to recipient<\/td>\n<td>Recipient&#8217;s mail server accepted the email<\/td>\n<\/tr>\n<tr>\n<td><code>bounced<\/code><\/td>\n<td>Email bounced<\/td>\n<td>Recipient address invalid (hard) or mailbox full (soft)<\/td>\n<\/tr>\n<tr>\n<td><code>opened<\/code><\/td>\n<td>Email opened<\/td>\n<td>Recipient opened the email (tracking pixel loaded)<\/td>\n<\/tr>\n<tr>\n<td><code>clicked<\/code><\/td>\n<td>Link clicked<\/td>\n<td>Recipient clicked a tracked link<\/td>\n<\/tr>\n<tr>\n<td><code>complained<\/code><\/td>\n<td>Spam complaint<\/td>\n<td>Recipient marked the email as spam<\/td>\n<\/tr>\n<tr>\n<td><code>failed<\/code><\/td>\n<td>Delivery failed<\/td>\n<td>Email could not be sent after all retry attempts<\/td>\n<\/tr>\n<\/table>\n<h2>Setting Up Webhooks<\/h2>\n<ol>\n<li>Go to <a href=\"https:\/\/platform.cyberpersons.com\/email\/webhooks\/\" target=\"_blank\" rel=\"noopener\">https:\/\/platform.cyberpersons.com\/email\/webhooks\/<\/a><\/li>\n<li>Click <strong>Create Webhook<\/strong><\/li>\n<li>Enter your endpoint URL (must be HTTPS in production)<\/li>\n<li>Select which events to subscribe to<\/li>\n<li>Click <strong>Create<\/strong> &mdash; note the <strong>signing secret<\/strong> for verification<\/li>\n<\/ol>\n<h2>Webhook Payload<\/h2>\n<p>Each webhook sends a JSON POST request to your endpoint:<\/p>\n<pre class=\"kb-code\">{\n  <span class=\"string\">\"event\"<\/span>: <span class=\"string\">\"delivered\"<\/span>,\n  <span class=\"string\">\"timestamp\"<\/span>: <span class=\"string\">\"2026-02-23T10:30:02Z\"<\/span>,\n  <span class=\"string\">\"data\"<\/span>: {\n    <span class=\"string\">\"message_id\"<\/span>: <span class=\"string\">\"a1b2c3@cpmail.cyberpersons.com\"<\/span>,\n    <span class=\"string\">\"from\"<\/span>: <span class=\"string\">\"you@yourcompany.com\"<\/span>,\n    <span class=\"string\">\"to\"<\/span>: <span class=\"string\">\"recipient@example.com\"<\/span>,\n    <span class=\"string\">\"subject\"<\/span>: <span class=\"string\">\"Order Confirmation\"<\/span>,\n    <span class=\"string\">\"tags\"<\/span>: [<span class=\"string\">\"order\"<\/span>],\n    <span class=\"string\">\"metadata\"<\/span>: {\"order_id\": \"12345\"}\n  }\n}<\/pre>\n<p>For bounce events, additional fields are included:<\/p>\n<pre class=\"kb-code\">{\n  <span class=\"string\">\"event\"<\/span>: <span class=\"string\">\"bounced\"<\/span>,\n  <span class=\"string\">\"data\"<\/span>: {\n    <span class=\"string\">\"message_id\"<\/span>: <span class=\"string\">\"...\"<\/span>,\n    <span class=\"string\">\"bounce_type\"<\/span>: <span class=\"string\">\"hard\"<\/span>,  <span class=\"comment\">\/\/ \"hard\", \"soft\", or \"complaint\"<\/span>\n    <span class=\"string\">\"error_message\"<\/span>: <span class=\"string\">\"550 User not found\"<\/span>\n  }\n}<\/pre>\n<h2>Security: Verifying Webhook Signatures<\/h2>\n<p>Every webhook request includes an <code>X-Webhook-Signature<\/code> header with an HMAC-SHA256 signature. Always verify this to confirm the request came from CyberPanel.<\/p>\n<h3>Python<\/h3>\n<pre class=\"kb-code\"><span class=\"keyword\">import<\/span> hmac, hashlib\n\n<span class=\"keyword\">def<\/span> verify_signature(payload_body, signature, secret):\n    expected = hmac.new(\n        secret.encode(),\n        payload_body,\n        hashlib.sha256\n    ).hexdigest()\n    <span class=\"keyword\">return<\/span> hmac.compare_digest(expected, signature)<\/pre>\n<h3>Node.js<\/h3>\n<pre class=\"kb-code\"><span class=\"keyword\">const<\/span> crypto = require(<span class=\"string\">'crypto'<\/span>);\n\n<span class=\"keyword\">function<\/span> verifySignature(body, signature, secret) {\n  <span class=\"keyword\">const<\/span> expected = crypto\n    .createHmac(<span class=\"string\">'sha256'<\/span>, secret)\n    .update(body)\n    .digest(<span class=\"string\">'hex'<\/span>);\n  <span class=\"keyword\">return<\/span> crypto.timingSafeEqual(\n    Buffer.from(expected), Buffer.from(signature)\n  );\n}<\/pre>\n<h3>PHP<\/h3>\n<pre class=\"kb-code\"><span class=\"keyword\">function<\/span> verifySignature($body, $signature, $secret) {\n    $expected = hash_hmac(<span class=\"string\">'sha256'<\/span>, $body, $secret);\n    <span class=\"keyword\">return<\/span> hash_equals($expected, $signature);\n}<\/pre>\n<h2>Retry Policy<\/h2>\n<ul>\n<li>If your endpoint returns a non-2xx status, we retry with exponential backoff<\/li>\n<li>After <strong>10 consecutive failures<\/strong>, the webhook is automatically disabled<\/li>\n<li>You can re-enable disabled webhooks from the dashboard<\/li>\n<li>Use the <strong>Test<\/strong> button in the dashboard to send a test event to your endpoint<\/li>\n<\/ul>\n<h2>Related Guides<\/h2>\n<ul>\n<li><a href=\"https:\/\/cyberpanel.net\/KnowledgeBase\/email-service-documentation\/\">Documentation Hub<\/a><\/li>\n<li><a href=\"https:\/\/cyberpanel.net\/KnowledgeBase\/getting-started-email-delivery\/\">Getting Started with Email Delivery<\/a><\/li>\n<li><a href=\"https:\/\/cyberpanel.net\/KnowledgeBase\/sending-email-rest-api\/\">Sending Email via REST API<\/a><\/li>\n<li><a href=\"https:\/\/cyberpanel.net\/KnowledgeBase\/email-analytics-and-logs\/\">Email Analytics and Logs<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/cyberpanel.net\/KnowledgeBase\/email-service-documentation\/\">&larr; Back to Documentation Hub<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Webhooks notify your application in real-time when email events occur &mdash; deliveries, bounces, opens, clicks, and more. Instead of polling the API, your server receives HTTP POST requests instantly. Supported Events Event Description When It Fires delivered Email delivered to recipient Recipient&#8217;s mail server accepted the email bounced Email bounced Recipient address invalid (hard) or [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[108],"tags":[],"class_list":["post-2547","post","type-post","status-publish","format-standard","hentry","category-email-service"],"_links":{"self":[{"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/posts\/2547","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/comments?post=2547"}],"version-history":[{"count":3,"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/posts\/2547\/revisions"}],"predecessor-version":[{"id":2589,"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/posts\/2547\/revisions\/2589"}],"wp:attachment":[{"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/media?parent=2547"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/categories?post=2547"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cyberpanel.net\/KnowledgeBase\/wp-json\/wp\/v2\/tags?post=2547"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}