Working with Webhooks

Webhooks are a powerful way to integrate your applications and services with Webflow, allowing you to receive real-time updates whenever specific events occur on your site. By setting up webhooks, you can automate workflows, trigger external processes, and synchronize data across different platforms without any manual intervention.

For instance, when a user submits a form on your Webflow site, a webhook can immediately notify your application, enabling you to take action instantly. This could involve sending an automated email response to the user, updating a lead record in your CRM system like HubSpot or Salesforce, or even triggering a custom workflow in an external service like Zapier.


Get Started with Webhooks

Ready to streamline your workflow? Follow the steps below to create your first webhook using the Webflow API.


Webhook Requests

When an event occurs, Webflow will send a POST request to a specified URL.

The webhook body will be a JSON resource object that relates to the event. The request headers will include:

  • A Content-Type header set to application/json
  • A x-webflow-timestamp with the time the webhook was sent
  • A x-webflow-signature header containing the request signature. Read on for information about validating request signatures.

Below is an example of webhook event data.

Payload
1{
2 "triggerType": "form_submission",
3 "payload": {
4 "name": "Contact Us",
5 "siteId": "65427cf400e02b306eaa049c",
6 "data": {
7 "First Name": "Zaphod",
8 "Last Name": "Beeblebrox",
9 "email": "zaphod@heartofgold.ai",
10 "Phone Number": 15550000000
11 },
12 "submittedAt": "2022-09-14T12:35:16.117Z",
13 "id": "6321ca84df3949bfc6752327",
14 "formId": "65429eadebe8a9f3a30f62d0"
15 }
16}

Creating a Webhook

In this tutorial, we’ll walk through creating a webhook to listen for new submissions to a contact form on a site. Whenever someone submits this form, Webflow will send a notification to the specified destination. Additionally, we’ll cover how to verify that the webhook requests you’re receiving are genuinely from Webflow, ensuring secure and reliable communication with your application.

Looking for a Simpler Setup?

If you’d prefer a way to set up webhooks without using the API, you can easily configure them through the Webflow dashboard. Please note, that webhooks created through the dashboard will not include the request headers needed to validate request signatures.

Prerequisites

  • A site token or bearer token from a Webflow Data Client App with the forms:read scope. If you plan on verifying requests, you must authenticate via an App.
  • A Webflow test site. You can use the Biznus template to quickly set up a site equipped with a contact form.
  • A service to accept an HTTPS request. While we’ll be using webhook.site in this tutorial, you’re free to choose any platform.

Webhook Retries

Your service should return a 200 response to show that the webhook was successfully captured. If the response status is anything else, the webhook will be retried up to three more times at which point the request will be considered failed and will no longer be retried.


Failure Conditions

Webflow considers the following scenarios as failure conditions:

  • Non-200 HTTP status code response: If we receive any response other than a valid HTTP 200 response, it is regarded as a failure.
  • Redirects: If the webhook encounters redirects while attempting to deliver the payload, it will be treated as a failure.
  • SSL Certificate Issues: If we cannot successfully negotiate or validate your server’s SSL certificate, it will be marked as a failure.
  • Timeouts: Webflow expects a swift response during webhook delivery. If there are prolonged delays in receiving a response from your server, it will be considered a failure.

Deactivation of Webhooks

If Webflow repeatedly encounters failure conditions while attempting to deliver a webhook payload, we will take the following action:

  • Deactivation: We will deactivate your webhook to prevent further delivery attempts.
  • Notification: You will be notified of the webhook deactivation via email.

To reactivate your webhook or if you have any questions regarding a deactivated webhook, please don’t hesitate to reach out to our support team.


Limits

Understanding the limits imposed by Webflow can help you design and manage your webhooks more efficiently:

CriteriaLimitation
Maximum webhooks for a specific trigger_type75
Maximum number of retry attempts after an unsuccessful webhook call3
Interval (in minutes) between each retry10

Event types

This is the full list of webhook events available in Webflow. For complete documentation of webhook events with payloads, please see the webhook events documentation.

EventDescription
form_submissionDetails about a form submission, including form name, site ID, data submitted, submission timestamp, and form ID.
site_publishDetails about a site publish event, including site ID, published timestamp, domains, and user who published the site.
page_createdInformation about a new page event, including site ID, page ID, page title, and creation timestamp.
page_metadata_updatedMetadata of a page is updated and published, including site ID, page ID, page title, and last updated timestamp.
page_deletedInformation about a deleted page, including site ID, page ID, page title, and deletion timestamp.
ecomm_new_orderInformation about a new order, including order ID, status, customer information, shipping details, and purchased items.
ecomm_order_changedDetails about an order that changed, including order ID, status, comments, customer information, and updated order details.
ecomm_inventory_changedInformation about the inventory item that changed, including item ID, quantity, and inventory type.
user_account_addedInformation about a new user account, including user ID, email verification status, account creation date, and status.
user_account_updatedDetails about an updated user account, including user ID, email verification status, last updated timestamp, and access groups.
user_account_deletedInformation about a deleted user account, including user ID, email verification status, and account creation date.
collection_item_createdDetails about a newly created collection item, including item ID, site ID, workspace ID, collection ID, creation date, and draft status.
collection_item_changedInformation about an updated collection item, including item ID, site ID, workspace ID, collection ID, last updated date, and item details.
collection_item_deletedDetails about a deleted collection item, including item ID, site ID, workspace ID, and collection ID.
collection_item_unpublishedInformation about an unpublished collection item, including item ID, site ID, workspace ID, and collection ID.

Validating Request Signatures

Webflow provides a method for your App to verify that requests are genuinely coming from the Webflow API by using signatures included in the request headers.

  • x-webflow-timestamp : The time the webhook was sent, represented in Unix epoch time format.
  • x-webflow-signature The request signature, formatted as a SHA-256 hash.
Only OAuth Apps carry these signatures.

Webhooks originating from a site dashboard or via a Site’s API Key won’t have request signatures. To benefit from this security feature, create your webhooks through an OAuth application.

To ensure the authenticity of a webhook request from Webflow, validate the request signature using the provided headers and your OAuth application’s client secret.

Steps to Validate the Request Signature

  1. Generate the HMAC Hash:
    • Retrieve the timestamp from the x-webflow-timestamp header.
    • Concatenate the timestamp and the request body with a colon (:) separator. The format should be:
      1timestamp + ":" + JSON.stringify(request_body)
    • Use your OAuth application’s client secret and the SHA-256 hashing algorithm to generate the HMAC hash. The client secret serves as the key for the HMAC process.
  2. Compare the Generated Hash with the Provided Signature:
    Compare the generated HMAC hash with the x-webflow-signature header from the request. A match confirms the request’s legitimacy; otherwise, it should be considered potentially tampered with or fraudulent.
  3. Verify the Timestamp:
    Check the x-webflow-timestamp header to ensure the request is recent. A request older than 5 minutes may indicate a replay attack. Calculate the request’s age as follows:
    1currentTime - Number(request_timestamp)
    If the difference exceeds 5 minutes (300,000 milliseconds), consider the request potentially compromised.

See below for examples that accept an incoming HTTPS request and validate the signature:

1const express = require('express');
2const crypto = require('crypto');
3const bodyParser = require('body-parser');
4
5const app = express();
6const PORT = 3000;
7const CLIENT_SECRET = 'your_client_secret'; // Replace with your Webflow OAuth application's client secret
8
9app.use(bodyParser.json()); // Parse JSON request bodies
10
11app.post('/webhook', (req, res) => \{
12 // Step 1: Extract headers and body from the request
13 const requestBody = JSON.stringify(req.body);
14 const timestamp = req.headers['x-webflow-timestamp'];
15 const providedSignature = req.headers['x-webflow-signature'];
16
17 // Step 2: Verify the signature
18 if (!verifyWebflowSignature(CLIENT_SECRET, timestamp, requestBody, providedSignature)) \{
19 return res.status(400).send('Invalid signature'); // Respond with a 400 Bad Request if verification fails
20 }
21
22 // Process the webhook request as necessary
23 console.log('Webhook verified and received:', req.body);
24 res.status(200).send('Webhook received');
25});
26
27function verifyWebflowSignature(clientSecret, timestamp, requestBody, providedSignature) \{
28 try \{
29 // Step 3: Convert the timestamp to an integer
30 const requestTimestamp = parseInt(timestamp, 10);
31
32 // Step 4: Generate the HMAC hash
33 const data = `$\{requestTimestamp}:$\{requestBody}`;
34 const hash = crypto.createHmac('sha256', clientSecret)
35 .update(data)
36 .digest('hex');
37
38 // Step 5: Compare the generated hash with the provided signature
39 if (!crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(providedSignature, 'hex'))) \{
40 throw new Error('Invalid signature');
41 }
42
43 // Step 6: Validate the timestamp (within 5 minutes)
44 const currentTime = Date.now();
45 if (currentTime - requestTimestamp > 300000) \{ // 5 minutes in milliseconds
46 throw new Error('Request is older than 5 minutes');
47 }
48
49 return true; // The request is valid
50
51 } catch (err) \{
52 console.error(`Error verifying signature: $\{err.message}`);
53 return false;
54 }
55}
56
57app.listen(PORT, () => \{
58 console.log(`Server is running on port $\{PORT}`);
59});