Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Skills for receiving and verifying webhooks from specific providers. Each includ
| Shopify | [`shopify-webhooks`](skills/shopify-webhooks/) | Verify Shopify HMAC signatures, handle order and product webhook events |
| Stripe | [`stripe-webhooks`](skills/stripe-webhooks/) | Verify Stripe webhook signatures, parse payment event payloads, handle checkout.session.completed events |
| Vercel | [`vercel-webhooks`](skills/vercel-webhooks/) | Verify Vercel webhook signatures (HMAC-SHA1), handle deployment and project events |
| Webflow | [`webflow-webhooks`](skills/webflow-webhooks/) | Verify Webflow webhook signatures (HMAC-SHA256), handle form submission, ecommerce, and CMS events |
| WooCommerce | [`woocommerce-webhooks`](skills/woocommerce-webhooks/) | Verify WooCommerce webhook signatures, handle order, product, and customer events |

### Webhook Handler Pattern Skills
Expand Down
15 changes: 15 additions & 0 deletions providers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,21 @@ providers:
- deployment.created
- deployment.succeeded

- name: webflow
displayName: Webflow
docs:
webhooks: https://developers.webflow.com/data/docs/working-with-webhooks
verification: https://developers.webflow.com/data/docs/working-with-webhooks#validating-request-signatures
notes: >
Website builder and CMS platform. Uses x-webflow-signature and x-webflow-timestamp headers
with HMAC-SHA256 (hex encoded). Signed content format is timestamp:body. Only webhooks
created via OAuth apps or API include signature headers; dashboard-created webhooks do not.
Common events: form_submission, site_publish, ecomm_new_order, collection_item_created.
testScenario:
events:
- form_submission
- ecomm_new_order

- name: woocommerce
displayName: WooCommerce
docs:
Expand Down
178 changes: 178 additions & 0 deletions skills/webflow-webhooks/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
---
name: webflow-webhooks
description: Receive and verify Webflow webhooks. Use when setting up Webflow webhook handlers, debugging signature verification, or handling Webflow events like form_submission, site_publish, ecomm_new_order, or collection item changes.
license: MIT
metadata:
author: hookdeck
version: "0.1.0"
repository: https://github.com/hookdeck/webhook-skills
---

# Webflow Webhooks

## When to Use This Skill

- How do I receive Webflow webhooks?
- How do I verify Webflow webhook signatures?
- How do I handle form_submission events from Webflow?
- How do I process Webflow ecommerce order events?
- Why is my Webflow webhook signature verification failing?
- Setting up Webflow CMS collection item webhooks

## Essential Code

### Signature Verification (Manual)

```javascript
const crypto = require('crypto');

function verifyWebflowSignature(rawBody, signature, timestamp, secret) {
// Check timestamp to prevent replay attacks (5 minute window - 300000 milliseconds)
const currentTime = Date.now();
if (Math.abs(currentTime - parseInt(timestamp)) > 300000) {
return false;
}

// Generate HMAC signature
const signedContent = `${timestamp}:${rawBody}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');

// Timing-safe comparison
try {
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
} catch {
return false; // Different lengths = invalid
}
}
```

### Processing Events

```javascript
app.post('/webhooks/webflow', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webflow-signature'];
const timestamp = req.headers['x-webflow-timestamp'];

if (!signature || !timestamp) {
return res.status(400).send('Missing required headers');
}

// Verify signature (use OAuth client secret or webhook-specific secret)
const isValid = verifyWebflowSignature(
req.body.toString(),
signature,
timestamp,
process.env.WEBFLOW_WEBHOOK_SECRET
);

if (!isValid) {
return res.status(400).send('Invalid signature');
}

// Parse the verified payload
const event = JSON.parse(req.body);

// Handle different event types
switch (event.triggerType) {
case 'form_submission':
console.log('New form submission:', event.payload.data);
break;
case 'ecomm_new_order':
console.log('New order:', event.payload);
break;
case 'collection_item_created':
console.log('New CMS item:', event.payload);
break;
// Add more event handlers as needed
}

// Always return 200 to acknowledge receipt
res.status(200).send('OK');
});
```

## Common Event Types

| Event | Triggered When | Use Case |
|-------|----------------|----------|
| `form_submission` | Form submitted on site | Contact forms, lead capture |
| `site_publish` | Site is published | Clear caches, trigger builds |
| `ecomm_new_order` | New ecommerce order | Order processing, inventory |
| `ecomm_order_changed` | Order status changes | Update fulfillment systems |
| `collection_item_created` | CMS item created | Content syndication |
| `collection_item_changed` | CMS item updated | Update external systems |
| `collection_item_deleted` | CMS item deleted | Remove from external systems |

## Environment Variables

```bash
# For webhooks created via OAuth App
WEBFLOW_WEBHOOK_SECRET=your_oauth_client_secret

# For webhooks created via API (after April 2025)
WEBFLOW_WEBHOOK_SECRET=whsec_xxxxx # Returned when creating webhook
```

## Local Development

For local webhook testing, install Hookdeck CLI:

```bash
# Install via npm
npm install -g hookdeck-cli

# Or via Homebrew
brew install hookdeck/hookdeck/hookdeck
```

Then start the tunnel:

```bash
hookdeck listen 3000 --path /webhooks/webflow
```

No account required. Provides local tunnel + web UI for inspecting requests.

## Resources

- [What Are Webflow Webhooks](references/overview.md) - Event types and payload structure
- [Setting Up Webflow Webhooks](references/setup.md) - Dashboard configuration and API setup
- [Signature Verification Details](references/verification.md) - In-depth verification guide
- [Express Example](examples/express/) - Node.js implementation with tests
- [Next.js Example](examples/nextjs/) - App Router implementation
- [FastAPI Example](examples/fastapi/) - Python implementation

## Important Notes

- Webhooks created through the Webflow dashboard do NOT include signature headers
- Only webhooks created via OAuth apps or API include `x-webflow-signature` and `x-webflow-timestamp`
- Always use raw body for signature verification, not parsed JSON
- Timestamp validation (5 minute window - 300000 milliseconds) is critical to prevent replay attacks
- Return 200 status to acknowledge receipt; other statuses trigger retries (up to 3 times)

## Recommended: webhook-handler-patterns

This skill pairs well with webhook-handler-patterns for production-ready implementations:

- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md) — Request flow and middleware order
- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md) — Handling duplicate events
- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md) — Graceful failure and recovery
- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md) — Handling failed processing

## Related Skills

- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe webhook handling with similar HMAC-SHA256 verification
- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify webhook implementation
- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub webhook handling
- [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) - Production patterns for all webhooks
- [hookdeck-event-gateway](https://github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway) - Webhook infrastructure and reliability

Sources:
- [Working with Webhooks – Webflow Docs](https://developers.webflow.com/data/docs/working-with-webhooks)
- [Webhook Signatures Changelog](https://developers.webflow.com/data/changelog/webhook-signatures)
10 changes: 10 additions & 0 deletions skills/webflow-webhooks/examples/express/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# For webhooks created via OAuth App
# Use your OAuth app's client secret
WEBFLOW_WEBHOOK_SECRET=your_oauth_client_secret

# For webhooks created via API (after April 2025)
# Use the webhook-specific secret returned when creating the webhook
# Format: whsec_xxxxxxxxxxxxx

# Server port (optional)
PORT=3000
99 changes: 99 additions & 0 deletions skills/webflow-webhooks/examples/express/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Webflow Webhooks - Express Example

Minimal example of receiving Webflow webhooks with signature verification using Express.

## Prerequisites

- Node.js 18+
- Webflow account with webhook configured
- Webhook signing secret (from OAuth app or API-created webhook)

## Setup

1. Install dependencies:
```bash
npm install
```

2. Copy environment variables:
```bash
cp .env.example .env
```

3. Add your Webflow webhook signing secret to `.env`:
- For OAuth app webhooks: Use your OAuth client secret
- For API-created webhooks: Use the `secret` field from the creation response

## Run

```bash
# Production
npm start

# Development (with auto-reload)
npm run dev
```

Server runs on http://localhost:3000

Webhook endpoint: `POST http://localhost:3000/webhooks/webflow`

## Test Locally

Use Hookdeck CLI to create a public URL for your local server:

```bash
# Install Hookdeck CLI
npm install -g hookdeck-cli

# Create tunnel
hookdeck listen 3000 --path /webhooks/webflow
```

1. Copy the Hookdeck URL (e.g., `https://events.hookdeck.com/e/src_xxxxx`)
2. Add this URL as your webhook endpoint in Webflow
3. Trigger test events in Webflow
4. View requests in the Hookdeck dashboard

## Test Suite

Run the test suite:

```bash
npm test
```

Tests verify:
- Signature verification with valid signatures
- Rejection of invalid signatures
- Timestamp validation (5-minute window)
- Proper error handling
- Event type handling

## Project Structure

```
├── src/
│ └── index.js # Express server and webhook handler
├── test/
│ └── webhook.test.js # Test suite
├── .env.example # Environment variables template
├── package.json # Dependencies
└── README.md # This file
```

## Common Issues

### Signature Verification Fails
- Ensure you're using the correct secret (OAuth client secret vs webhook-specific secret)
- Webhook must be created via API or OAuth app (not dashboard) for signatures
- Check that the raw body is being used (not parsed JSON)

### Missing Headers
- Dashboard-created webhooks don't include signature headers
- Recreate the webhook via API or OAuth app

### Webhook Not Received
- Verify the endpoint URL is correct
- Check Webflow webhook logs for delivery attempts
- Ensure your server returns 200 status for successful receipt
23 changes: 23 additions & 0 deletions skills/webflow-webhooks/examples/express/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "webflow-webhooks-express",
"version": "1.0.0",
"description": "Express webhook handler for Webflow",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"test": "jest"
},
"keywords": ["webflow", "webhooks", "express"],
"author": "",
"license": "MIT",
"dependencies": {
"express": "^5.2.1",
"dotenv": "^16.3.1"
},
"devDependencies": {
"jest": "^29.7.0",
"supertest": "^6.3.4",
"nodemon": "^3.1.0"
}
}
Loading