Webflow Webhooks
Receive, verify, and process Webflow webhook events for form submissions, CMS changes, ecommerce orders, site publishing, and more.
Quick Start Workflow
Prerequisite: You need a Webflow account with an active site. For signature verification, create webhooks via the API (not the dashboard) — see Setup.
- Create webhook: Register a webhook via the Webflow API for your desired event type
- Receive events: Set up an endpoint that accepts POST requests with raw body parsing
- Verify signatures: Validate
x-webflow-signature and x-webflow-timestamp headers
- Process events: Route events by
triggerType and handle each accordingly
- Acknowledge: Return
200 to confirm receipt (other statuses trigger retries)
Signature Verification
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
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');
}
const isValid = verifyWebflowSignature(
req.body.toString(),
signature,
timestamp,
process.env.WEBFLOW_WEBHOOK_SECRET
);
if (!isValid) {
return res.status(400).send('Invalid signature');
}
const event = JSON.parse(req.body);
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;
case 'collection_item_published':
console.log('Published CMS items:', event.payload.items);
break;
}
res.status(200).send('OK');
});
Event Types
Webflow supports 14 webhook event types across 6 categories: Forms, Site, Pages, Ecommerce, CMS, and Comments. See references/event-types.md for the complete reference with all payload schemas and examples.
| Category |
Events |
Required Scope |
| Forms |
form_submission |
forms:read |
| Site |
site_publish |
sites:read |
| Pages |
page_created, page_metadata_updated, page_deleted |
pages:read |
| Ecommerce |
ecomm_new_order, ecomm_order_changed, ecomm_inventory_changed |
ecommerce:read |
| CMS |
collection_item_created, collection_item_changed, collection_item_deleted, collection_item_unpublished, collection_item_published |
cms:read |
| Comments |
comment_created |
comments:read |
Environment Variables
# 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
Best Practices
- Always verify signatures: Use HMAC-SHA256 verification for webhooks created via OAuth or API — see Verification
- Use raw body for verification: Never verify against parsed JSON; configure your framework accordingly
- Validate timestamps: Enforce a 5-minute window (300000ms) to prevent replay attacks
- Return 200 quickly: Acknowledge receipt immediately; process events asynchronously for heavy workloads
- Handle retries gracefully: Webflow retries up to 3 times on failure (10-minute intervals) — implement idempotency
- Use HTTPS in production: Webhook endpoints must use HTTPS for security
Important Notes
- Never handle secrets in plain text. API tokens, OAuth client secrets, and webhook signing secrets must always be stored in environment variables or a secrets manager. Never ask the user for tokens or secrets directly, and never hard-code them in source files.
- 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
- 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, 10-minute intervals)
Reference Documentation
Each reference file includes YAML frontmatter with name, description, and tags for searchability. Use the search script available in scripts/search_references.py to quickly find relevant references by tag or keyword.
- references/event-types.md: Complete reference for all 14 event types with scopes, payload schemas, and examples
- references/webhook-api.md: REST API v2 endpoints for creating, listing, getting, and deleting webhooks
- references/overview.md: Webhook concepts, delivery behavior, limits, and security considerations
- references/setup.md: Dashboard and API configuration, OAuth, scopes, environment setup
- references/verification.md: HMAC-SHA256 signature verification, common gotchas, debugging
- references/faq.md: FAQ and troubleshooting for delivery issues, signature failures, and API errors
Searching References
# List all references with metadata
python scripts/search_references.py --list
# Search by tag (exact match)
python scripts/search_references.py --tag <tag>
# Search by keyword (across name, description, tags, and content)
python scripts/search_references.py --search <query>
Scripts
scripts/search_references.py: Search reference files by tag, keyword, or list all with metadata
1---2name: webflow-webhooks3description: 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.4license: MIT5---67# Webflow Webhooks89Receive, verify, and process Webflow webhook events for form submissions, CMS changes, ecommerce orders, site publishing, and more.1011## Quick Start Workflow1213> **Prerequisite:** You need a Webflow account with an active site. For signature verification, create webhooks via the API (not the dashboard) — see [Setup](references/setup.md).14151. **Create webhook**: Register a webhook via the Webflow API for your desired event type162. **Receive events**: Set up an endpoint that accepts POST requests with raw body parsing173. **Verify signatures**: Validate `x-webflow-signature` and `x-webflow-timestamp` headers184. **Process events**: Route events by `triggerType` and handle each accordingly195. **Acknowledge**: Return `200` to confirm receipt (other statuses trigger retries)2021### Signature Verification2223```javascript24const crypto = require('crypto');2526function verifyWebflowSignature(rawBody, signature, timestamp, secret) {27 // Check timestamp to prevent replay attacks (5 minute window - 300000 milliseconds)28 const currentTime = Date.now();29 if (Math.abs(currentTime - parseInt(timestamp)) > 300000) {30 return false;31 }3233 // Generate HMAC signature34 const signedContent = `${timestamp}:${rawBody}`;35 const expectedSignature = crypto36 .createHmac('sha256', secret)37 .update(signedContent)38 .digest('hex');3940 // Timing-safe comparison41 try {42 return crypto.timingSafeEqual(43 Buffer.from(signature),44 Buffer.from(expectedSignature)45 );46 } catch {47 return false; // Different lengths = invalid48 }49}50```5152### Processing Events5354```javascript55app.post('/webhooks/webflow', express.raw({ type: 'application/json' }), (req, res) => {56 const signature = req.headers['x-webflow-signature'];57 const timestamp = req.headers['x-webflow-timestamp'];5859 if (!signature || !timestamp) {60 return res.status(400).send('Missing required headers');61 }6263 const isValid = verifyWebflowSignature(64 req.body.toString(),65 signature,66 timestamp,67 process.env.WEBFLOW_WEBHOOK_SECRET68 );6970 if (!isValid) {71 return res.status(400).send('Invalid signature');72 }7374 const event = JSON.parse(req.body);7576 switch (event.triggerType) {77 case 'form_submission':78 console.log('New form submission:', event.payload.data);79 break;80 case 'ecomm_new_order':81 console.log('New order:', event.payload);82 break;83 case 'collection_item_created':84 console.log('New CMS item:', event.payload);85 break;86 case 'collection_item_published':87 console.log('Published CMS items:', event.payload.items);88 break;89 }9091 res.status(200).send('OK');92});93```9495## Event Types9697Webflow supports 14 webhook event types across 6 categories: Forms, Site, Pages, Ecommerce, CMS, and Comments. See **[references/event-types.md](references/event-types.md)** for the complete reference with all payload schemas and examples.9899| Category | Events | Required Scope |100|----------|--------|----------------|101| Forms | `form_submission` | `forms:read` |102| Site | `site_publish` | `sites:read` |103| Pages | `page_created`, `page_metadata_updated`, `page_deleted` | `pages:read` |104| Ecommerce | `ecomm_new_order`, `ecomm_order_changed`, `ecomm_inventory_changed` | `ecommerce:read` |105| CMS | `collection_item_created`, `collection_item_changed`, `collection_item_deleted`, `collection_item_unpublished`, `collection_item_published` | `cms:read` |106| Comments | `comment_created` | `comments:read` |107108## Environment Variables109110```bash111# For webhooks created via OAuth App112WEBFLOW_WEBHOOK_SECRET=your_oauth_client_secret113114# For webhooks created via API (after April 2025)115WEBFLOW_WEBHOOK_SECRET=whsec_xxxxx # Returned when creating webhook116```117118## Best Practices1191201. **Always verify signatures**: Use HMAC-SHA256 verification for webhooks created via OAuth or API — see [Verification](references/verification.md)1212. **Use raw body for verification**: Never verify against parsed JSON; configure your framework accordingly1223. **Validate timestamps**: Enforce a 5-minute window (300000ms) to prevent replay attacks1234. **Return 200 quickly**: Acknowledge receipt immediately; process events asynchronously for heavy workloads1245. **Handle retries gracefully**: Webflow retries up to 3 times on failure (10-minute intervals) — implement idempotency1256. **Use HTTPS in production**: Webhook endpoints must use HTTPS for security126127## Important Notes128129- **Never handle secrets in plain text.** API tokens, OAuth client secrets, and webhook signing secrets must always be stored in environment variables or a secrets manager. Never ask the user for tokens or secrets directly, and never hard-code them in source files.130- Webhooks created through the Webflow dashboard do NOT include signature headers131- Only webhooks created via OAuth apps or API include `x-webflow-signature` and `x-webflow-timestamp`132- Timestamp validation (5 minute window - 300000 milliseconds) is critical to prevent replay attacks133- Return 200 status to acknowledge receipt; other statuses trigger retries (up to 3 times, 10-minute intervals)134135## Reference Documentation136137Each reference file includes YAML frontmatter with `name`, `description`, and `tags` for searchability. Use the search script available in `scripts/search_references.py` to quickly find relevant references by tag or keyword.138139- **[references/event-types.md](references/event-types.md)**: Complete reference for all 14 event types with scopes, payload schemas, and examples140- **[references/webhook-api.md](references/webhook-api.md)**: REST API v2 endpoints for creating, listing, getting, and deleting webhooks141- **[references/overview.md](references/overview.md)**: Webhook concepts, delivery behavior, limits, and security considerations142- **[references/setup.md](references/setup.md)**: Dashboard and API configuration, OAuth, scopes, environment setup143- **[references/verification.md](references/verification.md)**: HMAC-SHA256 signature verification, common gotchas, debugging144- **[references/faq.md](references/faq.md)**: FAQ and troubleshooting for delivery issues, signature failures, and API errors145146### Searching References147148```bash149# List all references with metadata150python scripts/search_references.py --list151152# Search by tag (exact match)153python scripts/search_references.py --tag <tag>154155# Search by keyword (across name, description, tags, and content)156python scripts/search_references.py --search <query>157```158159## Scripts160161- **`scripts/search_references.py`**: Search reference files by tag, keyword, or list all with metadata