claude-plugin-wordpress-manager 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +30 -0
  3. package/agents/wp-content-strategist.md +30 -0
  4. package/agents/wp-monitoring-agent.md +50 -0
  5. package/docs/GUIDE.md +249 -15
  6. package/docs/plans/2026-02-28-wcop-strategic-assessment.md +220 -0
  7. package/hooks/hooks.json +9 -0
  8. package/package.json +6 -3
  9. package/servers/wp-rest-bridge/build/tools/index.d.ts +119 -0
  10. package/servers/wp-rest-bridge/build/tools/index.js +3 -0
  11. package/servers/wp-rest-bridge/build/tools/wc-webhooks.d.ts +129 -0
  12. package/servers/wp-rest-bridge/build/tools/wc-webhooks.js +142 -0
  13. package/servers/wp-rest-bridge/build/types.d.ts +12 -0
  14. package/skills/wordpress-router/references/decision-tree.md +7 -3
  15. package/skills/wp-content/SKILL.md +3 -0
  16. package/skills/wp-content-repurposing/SKILL.md +96 -0
  17. package/skills/wp-content-repurposing/references/content-atomization.md +117 -0
  18. package/skills/wp-content-repurposing/references/email-newsletter.md +136 -0
  19. package/skills/wp-content-repurposing/references/platform-specs.md +80 -0
  20. package/skills/wp-content-repurposing/references/social-formats.md +169 -0
  21. package/skills/wp-content-repurposing/scripts/repurposing_inspect.mjs +140 -0
  22. package/skills/wp-headless/SKILL.md +1 -0
  23. package/skills/wp-monitoring/SKILL.md +12 -2
  24. package/skills/wp-monitoring/references/fleet-monitoring.md +160 -0
  25. package/skills/wp-monitoring/scripts/monitoring_inspect.mjs +54 -1
  26. package/skills/wp-webhooks/SKILL.md +107 -0
  27. package/skills/wp-webhooks/references/integration-recipes.md +176 -0
  28. package/skills/wp-webhooks/references/payload-formats.md +134 -0
  29. package/skills/wp-webhooks/references/webhook-security.md +147 -0
  30. package/skills/wp-webhooks/references/woocommerce-webhooks.md +129 -0
  31. package/skills/wp-webhooks/references/wordpress-core-webhooks.md +162 -0
  32. package/skills/wp-webhooks/scripts/webhook_inspect.mjs +157 -0
  33. package/skills/wp-woocommerce/SKILL.md +1 -0
@@ -0,0 +1,176 @@
1
+ # Integration Recipes
2
+
3
+ ## Overview
4
+
5
+ Common webhook integration patterns for connecting WordPress to external services. Each recipe shows the webhook configuration and expected behavior.
6
+
7
+ ## Zapier Integration
8
+
9
+ **Setup:**
10
+ 1. Create a Zap with "Webhooks by Zapier" as trigger (Catch Hook)
11
+ 2. Copy the Zapier webhook URL
12
+ 3. Create webhook in WordPress:
13
+ ```
14
+ Tool: wc_create_webhook (for WooCommerce events)
15
+ name: "Zapier - New Orders"
16
+ topic: "order.created"
17
+ delivery_url: "https://hooks.zapier.com/hooks/catch/123456/abcdef/"
18
+ secret: "zapier-secret"
19
+ ```
20
+ 4. Test by creating a test order
21
+ 5. Configure Zapier actions (email, Slack, Google Sheets, etc.)
22
+
23
+ **Common Zaps:**
24
+ | Trigger | Action | Use Case |
25
+ |---------|--------|----------|
26
+ | New order | Send email | Order confirmation to team |
27
+ | New order | Add row to Google Sheet | Order tracking spreadsheet |
28
+ | New product | Post to Slack | Team notification |
29
+ | New customer | Add to Mailchimp | Email list growth |
30
+
31
+ ## Make (Integromat) Integration
32
+
33
+ **Setup:**
34
+ 1. Create a scenario with "Webhooks" module (Custom webhook)
35
+ 2. Copy the Make webhook URL
36
+ 3. Create webhook:
37
+ ```
38
+ delivery_url: "https://hook.eu1.make.com/abc123def456"
39
+ ```
40
+ 4. Run the scenario once to register the webhook structure
41
+ 5. Map fields and configure subsequent modules
42
+
43
+ **Common Scenarios:**
44
+ - Order placed → Create invoice in QuickBooks
45
+ - Product updated → Sync to external catalog
46
+ - Customer created → Add to CRM (HubSpot, Pipedrive)
47
+
48
+ ## n8n Integration
49
+
50
+ **Setup (self-hosted n8n):**
51
+ 1. Create a workflow with "Webhook" trigger node
52
+ 2. Set to POST method, configure path
53
+ 3. Copy the production webhook URL
54
+ 4. Create webhook:
55
+ ```
56
+ delivery_url: "https://n8n.example.com/webhook/wordpress-events"
57
+ ```
58
+
59
+ **Common Workflows:**
60
+ - Content published → SEO check → Notify team
61
+ - Order → Inventory check → Fulfillment API
62
+ - Customer feedback → Sentiment analysis → Support ticket
63
+
64
+ ## Slack Notifications
65
+
66
+ **Setup:**
67
+ 1. Create a Slack Incoming Webhook in Slack App settings
68
+ 2. Use the mu-plugin approach (WordPress core webhooks) with custom formatting:
69
+
70
+ ```php
71
+ add_action('transition_post_status', function($new, $old, $post) {
72
+ if ($new === 'publish' && $old !== 'publish') {
73
+ $slack_url = defined('SLACK_WEBHOOK_URL') ? SLACK_WEBHOOK_URL : '';
74
+ if (!$slack_url) return;
75
+
76
+ wp_remote_post($slack_url, [
77
+ 'headers' => ['Content-Type' => 'application/json'],
78
+ 'body' => wp_json_encode([
79
+ 'text' => sprintf(
80
+ "New %s published: *%s*\n<%s|Read it here>",
81
+ $post->post_type,
82
+ $post->post_title,
83
+ get_permalink($post)
84
+ ),
85
+ ]),
86
+ ]);
87
+ }
88
+ }, 10, 3);
89
+ ```
90
+
91
+ ## Email Service Integration
92
+
93
+ ### Mailchimp / SendGrid
94
+
95
+ **Trigger:** New subscriber or customer event
96
+ **Method:** WooCommerce webhook → Zapier/Make → Mailchimp API
97
+
98
+ Or direct with mu-plugin:
99
+ ```php
100
+ add_action('user_register', function($user_id) {
101
+ $user = get_userdata($user_id);
102
+ // POST to Mailchimp API to add subscriber
103
+ wp_remote_post('https://usX.api.mailchimp.com/3.0/lists/LIST_ID/members', [
104
+ 'headers' => [
105
+ 'Authorization' => 'Bearer ' . MAILCHIMP_API_KEY,
106
+ 'Content-Type' => 'application/json',
107
+ ],
108
+ 'body' => wp_json_encode([
109
+ 'email_address' => $user->user_email,
110
+ 'status' => 'subscribed',
111
+ ]),
112
+ ]);
113
+ });
114
+ ```
115
+
116
+ ## CDN Purge
117
+
118
+ ### Cloudflare
119
+
120
+ **Trigger:** Content updated
121
+ **Method:** mu-plugin that purges Cloudflare cache on publish:
122
+
123
+ ```php
124
+ add_action('transition_post_status', function($new, $old, $post) {
125
+ if ($new === 'publish') {
126
+ wp_remote_post(
127
+ 'https://api.cloudflare.com/client/v4/zones/' . CF_ZONE_ID . '/purge_cache',
128
+ [
129
+ 'headers' => [
130
+ 'Authorization' => 'Bearer ' . CF_API_TOKEN,
131
+ 'Content-Type' => 'application/json',
132
+ ],
133
+ 'body' => wp_json_encode([
134
+ 'files' => [get_permalink($post)],
135
+ ]),
136
+ ]
137
+ );
138
+ }
139
+ }, 10, 3);
140
+ ```
141
+
142
+ ## Search Index Update
143
+
144
+ ### Algolia / Meilisearch
145
+
146
+ **Trigger:** Content published or updated
147
+ **Method:** WooCommerce webhook or mu-plugin → search API
148
+
149
+ ```php
150
+ add_action('save_post', function($post_id, $post) {
151
+ if ($post->post_status !== 'publish') return;
152
+
153
+ wp_remote_post(ALGOLIA_API_URL . '/indexes/posts', [
154
+ 'headers' => [
155
+ 'X-Algolia-API-Key' => ALGOLIA_ADMIN_KEY,
156
+ 'X-Algolia-Application-Id' => ALGOLIA_APP_ID,
157
+ 'Content-Type' => 'application/json',
158
+ ],
159
+ 'body' => wp_json_encode([
160
+ 'objectID' => $post_id,
161
+ 'title' => $post->post_title,
162
+ 'content' => wp_strip_all_tags($post->post_content),
163
+ 'url' => get_permalink($post),
164
+ ]),
165
+ ]);
166
+ }, 10, 2);
167
+ ```
168
+
169
+ ## Best Practices
170
+
171
+ - Always use HTTPS for delivery URLs
172
+ - Set a webhook secret and verify signatures on the receiving end
173
+ - Keep webhook timeout under 5 seconds to avoid blocking WordPress
174
+ - Use async processing on the receiving end for heavy operations
175
+ - Monitor webhook delivery logs for failures
176
+ - Group related events when possible to reduce webhook volume
@@ -0,0 +1,134 @@
1
+ # Webhook Payload Formats
2
+
3
+ ## Standard WordPress Webhook Payload
4
+
5
+ When using the mu-plugin approach, payloads follow this structure:
6
+
7
+ ```json
8
+ {
9
+ "event": "post.published",
10
+ "timestamp": "2026-02-28T14:30:00+00:00",
11
+ "site_url": "https://example.com",
12
+ "data": {
13
+ "id": 123,
14
+ "title": "Post Title",
15
+ "slug": "post-title",
16
+ "type": "post",
17
+ "url": "https://example.com/post-title/"
18
+ }
19
+ }
20
+ ```
21
+
22
+ ### Event Types
23
+
24
+ | Event | Data Fields |
25
+ |-------|-------------|
26
+ | `post.published` | id, title, slug, type, url |
27
+ | `post.updated` | id, title, slug, type, url |
28
+ | `term.updated` | id, name, slug, taxonomy |
29
+ | `user.created` | id, username, email, role |
30
+ | `menu.updated` | menu_id |
31
+
32
+ ## WooCommerce Webhook Payloads
33
+
34
+ WooCommerce sends the full resource object as the payload.
35
+
36
+ ### Order Created Payload (truncated)
37
+
38
+ ```json
39
+ {
40
+ "id": 456,
41
+ "status": "processing",
42
+ "currency": "EUR",
43
+ "total": "29.99",
44
+ "customer_id": 12,
45
+ "billing": {
46
+ "first_name": "Mario",
47
+ "last_name": "Rossi",
48
+ "email": "mario@example.com",
49
+ "phone": "+39 123 456 7890",
50
+ "address_1": "Via Roma 1",
51
+ "city": "Palermo",
52
+ "state": "PA",
53
+ "postcode": "90100",
54
+ "country": "IT"
55
+ },
56
+ "line_items": [
57
+ {
58
+ "id": 1,
59
+ "name": "Cactus Water - Dolce",
60
+ "product_id": 78,
61
+ "quantity": 6,
62
+ "total": "29.99"
63
+ }
64
+ ],
65
+ "date_created": "2026-02-28T14:30:00",
66
+ "payment_method": "stripe",
67
+ "payment_method_title": "Credit Card"
68
+ }
69
+ ```
70
+
71
+ ### Product Updated Payload (truncated)
72
+
73
+ ```json
74
+ {
75
+ "id": 78,
76
+ "name": "Cactus Water - Dolce",
77
+ "slug": "cactus-water-dolce",
78
+ "type": "simple",
79
+ "status": "publish",
80
+ "price": "4.99",
81
+ "regular_price": "5.99",
82
+ "sale_price": "4.99",
83
+ "stock_quantity": 150,
84
+ "stock_status": "instock",
85
+ "categories": [
86
+ { "id": 3, "name": "Beverages", "slug": "beverages" }
87
+ ]
88
+ }
89
+ ```
90
+
91
+ ## Custom Payload Formatting
92
+
93
+ ### Minimal Payload (reduce bandwidth)
94
+
95
+ If the receiving service only needs specific fields, format a custom payload in the mu-plugin:
96
+
97
+ ```php
98
+ send_webhook('order.created', [
99
+ 'order_id' => $order->get_id(),
100
+ 'total' => $order->get_total(),
101
+ 'email' => $order->get_billing_email(),
102
+ 'items' => count($order->get_items()),
103
+ ]);
104
+ ```
105
+
106
+ ### Enriched Payload (add computed fields)
107
+
108
+ ```php
109
+ send_webhook('post.published', [
110
+ 'id' => $post->ID,
111
+ 'title' => $post->post_title,
112
+ 'url' => get_permalink($post),
113
+ 'word_count' => str_word_count(wp_strip_all_tags($post->post_content)),
114
+ 'categories' => wp_get_post_categories($post->ID, ['fields' => 'names']),
115
+ 'author' => get_the_author_meta('display_name', $post->post_author),
116
+ ]);
117
+ ```
118
+
119
+ ## Content-Type Headers
120
+
121
+ | Format | Content-Type | When to Use |
122
+ |--------|-------------|-------------|
123
+ | JSON | `application/json` | Default, most integrations |
124
+ | Form-encoded | `application/x-www-form-urlencoded` | Legacy systems |
125
+ | XML | `application/xml` | SOAP integrations |
126
+
127
+ WooCommerce always sends `application/json`. The mu-plugin approach defaults to JSON but can be customized.
128
+
129
+ ## Payload Size Considerations
130
+
131
+ - **WooCommerce**: Full resource payloads can be large (5-50 KB for orders with many items)
132
+ - **Custom payloads**: Keep under 1 MB for reliability
133
+ - **Truncation strategy**: For large content fields, send an ID + URL instead of the full body
134
+ - **Batch events**: If multiple events fire in quick succession, consider debouncing on the receiving end
@@ -0,0 +1,147 @@
1
+ # Webhook Security
2
+
3
+ ## Overview
4
+
5
+ Webhook security ensures that only legitimate notifications are processed by the receiving endpoint. Without verification, an attacker could send fake payloads to trigger unauthorized actions.
6
+
7
+ ## Shared Secret Authentication (HMAC-SHA256)
8
+
9
+ The standard approach: sender and receiver share a secret key. The sender computes an HMAC hash of the payload and includes it in a header. The receiver recomputes the hash and compares.
10
+
11
+ ### Sending (WordPress side)
12
+
13
+ ```php
14
+ $payload = wp_json_encode($data);
15
+ $secret = WEBHOOK_SECRET;
16
+ $signature = hash_hmac('sha256', $payload, $secret);
17
+
18
+ wp_remote_post($url, [
19
+ 'headers' => [
20
+ 'Content-Type' => 'application/json',
21
+ 'X-WP-Webhook-Signature' => $signature,
22
+ ],
23
+ 'body' => $payload,
24
+ ]);
25
+ ```
26
+
27
+ ### Receiving (endpoint side)
28
+
29
+ ```javascript
30
+ const crypto = require('crypto');
31
+
32
+ function verifyWebhookSignature(rawBody, signature, secret) {
33
+ const expected = crypto
34
+ .createHmac('sha256', secret)
35
+ .update(rawBody, 'utf8')
36
+ .digest('hex');
37
+ return crypto.timingSafeEqual(
38
+ Buffer.from(signature),
39
+ Buffer.from(expected)
40
+ );
41
+ }
42
+
43
+ // In your webhook handler:
44
+ app.post('/webhook', (req, res) => {
45
+ const signature = req.headers['x-wp-webhook-signature'];
46
+ if (!verifyWebhookSignature(req.rawBody, signature, SECRET)) {
47
+ return res.status(401).json({ error: 'Invalid signature' });
48
+ }
49
+ // Process the webhook...
50
+ res.status(200).json({ received: true });
51
+ });
52
+ ```
53
+
54
+ ## WooCommerce Signature Verification
55
+
56
+ WooCommerce uses base64-encoded HMAC-SHA256 in the `X-WC-Webhook-Signature` header:
57
+
58
+ ```javascript
59
+ function verifyWooCommerceWebhook(rawBody, signature, secret) {
60
+ const expected = crypto
61
+ .createHmac('sha256', secret)
62
+ .update(rawBody, 'utf8')
63
+ .digest('base64');
64
+ return crypto.timingSafeEqual(
65
+ Buffer.from(signature),
66
+ Buffer.from(expected)
67
+ );
68
+ }
69
+ ```
70
+
71
+ **Important:** Always use `crypto.timingSafeEqual()` to prevent timing attacks.
72
+
73
+ ## HTTPS Enforcement
74
+
75
+ - **Always use HTTPS** for delivery URLs — never HTTP in production
76
+ - WooCommerce webhooks require HTTPS by default (configurable)
77
+ - Self-signed certificates will cause delivery failures
78
+ - Use Let's Encrypt for free, auto-renewing SSL
79
+
80
+ ## IP Allowlisting
81
+
82
+ If the receiving endpoint supports it, restrict incoming webhooks to known IPs:
83
+
84
+ | Source | IP Ranges |
85
+ |--------|-----------|
86
+ | Your WordPress host | Check with your hosting provider |
87
+ | Zapier | Published at zapier.com/help/account/data-management/ip-addresses |
88
+ | Make | Published in Make documentation |
89
+
90
+ **Note:** IP allowlisting is a defense-in-depth measure, not a replacement for signature verification.
91
+
92
+ ## Rate Limiting
93
+
94
+ ### Sender Side (WordPress)
95
+ - Debounce rapid-fire events (e.g., bulk post updates)
96
+ - Use `wp_schedule_single_event()` for non-urgent webhooks
97
+ - Limit to 1 webhook per resource per 5 seconds
98
+
99
+ ### Receiver Side
100
+ - Accept webhooks immediately (200 OK) and process asynchronously
101
+ - Queue incoming webhooks for processing
102
+ - Reject if rate exceeds threshold (429 Too Many Requests)
103
+
104
+ ## Timeout Configuration
105
+
106
+ - **WordPress default timeout**: 5 seconds (`wp_remote_post` timeout parameter)
107
+ - **WooCommerce default**: 5 seconds
108
+ - **Recommendation**: Keep timeouts at 5 seconds or less
109
+ - If the receiver needs more time, respond 200 immediately and process in background
110
+
111
+ ## Retry Behavior
112
+
113
+ ### WooCommerce Built-in Retries
114
+ - Retries up to 5 times on failure
115
+ - Exponential backoff between retries
116
+ - After 5 failures: webhook set to `disabled`
117
+ - Re-enable manually via `wc_update_webhook` with `status: "active"`
118
+
119
+ ### Custom Retry (mu-plugin)
120
+ ```php
121
+ function send_webhook_with_retry($url, $payload, $max_retries = 3) {
122
+ for ($i = 0; $i < $max_retries; $i++) {
123
+ $response = wp_remote_post($url, [
124
+ 'timeout' => 5,
125
+ 'headers' => ['Content-Type' => 'application/json'],
126
+ 'body' => wp_json_encode($payload),
127
+ ]);
128
+ if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
129
+ return true;
130
+ }
131
+ sleep(pow(2, $i)); // Exponential backoff: 1s, 2s, 4s
132
+ }
133
+ error_log("Webhook delivery failed after {$max_retries} retries: {$url}");
134
+ return false;
135
+ }
136
+ ```
137
+
138
+ ## Security Checklist
139
+
140
+ - [ ] HTTPS enforced on all delivery URLs
141
+ - [ ] Shared secret configured and verified
142
+ - [ ] Signature verification uses timing-safe comparison
143
+ - [ ] Payload validated (expected structure and types)
144
+ - [ ] Rate limiting in place on receiver
145
+ - [ ] Timeout set to 5 seconds or less
146
+ - [ ] Failed webhooks logged for monitoring
147
+ - [ ] Secrets stored securely (environment variables, not code)
@@ -0,0 +1,129 @@
1
+ # WooCommerce Webhooks
2
+
3
+ ## Overview
4
+
5
+ WooCommerce provides a built-in webhook system via the `wc/v3/webhooks` REST API. Webhooks send JSON payloads to a delivery URL when specific store events occur. This is the preferred approach for WooCommerce event notifications — no custom code needed.
6
+
7
+ ## MCP Tools
8
+
9
+ The WP REST Bridge provides 4 tools for managing WooCommerce webhooks:
10
+
11
+ | Tool | Method | Endpoint | Purpose |
12
+ |------|--------|----------|---------|
13
+ | `wc_list_webhooks` | GET | `webhooks` | List all webhooks, optionally filter by status |
14
+ | `wc_create_webhook` | POST | `webhooks` | Create a new webhook with topic and delivery URL |
15
+ | `wc_update_webhook` | PUT | `webhooks/{id}` | Update webhook status, URL, or topic |
16
+ | `wc_delete_webhook` | DELETE | `webhooks/{id}` | Delete a webhook (safety hook requires confirmation) |
17
+
18
+ ## Available Topics
19
+
20
+ ### Order Topics
21
+ | Topic | Trigger |
22
+ |-------|---------|
23
+ | `order.created` | New order placed |
24
+ | `order.updated` | Order details changed |
25
+ | `order.deleted` | Order deleted |
26
+ | `order.restored` | Order restored from trash |
27
+
28
+ ### Product Topics
29
+ | Topic | Trigger |
30
+ |-------|---------|
31
+ | `product.created` | New product published |
32
+ | `product.updated` | Product details changed |
33
+ | `product.deleted` | Product deleted |
34
+ | `product.restored` | Product restored from trash |
35
+
36
+ ### Customer Topics
37
+ | Topic | Trigger |
38
+ |-------|---------|
39
+ | `customer.created` | New customer registered |
40
+ | `customer.updated` | Customer profile changed |
41
+ | `customer.deleted` | Customer deleted |
42
+
43
+ ### Coupon Topics
44
+ | Topic | Trigger |
45
+ |-------|---------|
46
+ | `coupon.created` | New coupon created |
47
+ | `coupon.updated` | Coupon details changed |
48
+ | `coupon.deleted` | Coupon deleted |
49
+
50
+ ### Action Topic
51
+ | Topic | Trigger |
52
+ |-------|---------|
53
+ | `action.woocommerce_*` | Any WooCommerce action hook |
54
+
55
+ ## Usage Examples
56
+
57
+ ### Create a webhook for new orders
58
+
59
+ ```
60
+ Tool: wc_create_webhook
61
+ Params:
62
+ name: "New Order Notification"
63
+ topic: "order.created"
64
+ delivery_url: "https://hooks.zapier.com/hooks/catch/123/abc"
65
+ secret: "my-webhook-secret-key"
66
+ status: "active"
67
+ ```
68
+
69
+ ### List active webhooks
70
+
71
+ ```
72
+ Tool: wc_list_webhooks
73
+ Params:
74
+ status: "active"
75
+ ```
76
+
77
+ ### Pause a webhook
78
+
79
+ ```
80
+ Tool: wc_update_webhook
81
+ Params:
82
+ id: 42
83
+ status: "paused"
84
+ ```
85
+
86
+ ## Webhook Status
87
+
88
+ | Status | Description |
89
+ |--------|-------------|
90
+ | `active` | Webhook is delivering payloads |
91
+ | `paused` | Webhook exists but is not delivering |
92
+ | `disabled` | Webhook was disabled (usually after delivery failures) |
93
+
94
+ ## Delivery Behavior
95
+
96
+ - **Timeout**: 5 seconds (configurable via `woocommerce_webhook_deliver_async`)
97
+ - **Retries**: WooCommerce retries failed deliveries up to 5 times with exponential backoff
98
+ - **Failure threshold**: After 5 consecutive failures, webhook is automatically set to `disabled`
99
+ - **Logs**: Delivery logs available in WooCommerce > Status > Logs
100
+
101
+ ## Webhook Headers
102
+
103
+ WooCommerce sends these headers with every delivery:
104
+
105
+ ```
106
+ X-WC-Webhook-Source: https://your-site.com/
107
+ X-WC-Webhook-Topic: order.created
108
+ X-WC-Webhook-Resource: order
109
+ X-WC-Webhook-Event: created
110
+ X-WC-Webhook-Signature: <HMAC-SHA256 hash>
111
+ X-WC-Webhook-ID: 42
112
+ X-WC-Webhook-Delivery-ID: <uuid>
113
+ ```
114
+
115
+ ## Signature Verification
116
+
117
+ The receiving endpoint should verify the `X-WC-Webhook-Signature` header:
118
+
119
+ ```javascript
120
+ const crypto = require('crypto');
121
+
122
+ function verifyWebhook(payload, signature, secret) {
123
+ const hash = crypto
124
+ .createHmac('sha256', secret)
125
+ .update(payload, 'utf8')
126
+ .digest('base64');
127
+ return hash === signature;
128
+ }
129
+ ```