Webhooks can be used to automatically publish the website when you have finished building your Vev project. We can trigger a webhook, which is essentially an HTTP POST request containing the relevant data needed to publish your Vev project. This webhook is sent to a webhook receiver, which could be another application or service that is responsible for handling the publishing process. The webhook receiver would then process the webhook data, which could include the HTML, CSS, and JavaScript files needed to publish the website. The receiver would then use this data to publish the website to your desired location, such as a web server or a content delivery network (CDN).
Once the webhook is set up, the publishing process can be automated, reducing the time and effort required to publish the website manually.
Our webhook integration is straightforward and requires the user's service to respond with a 2xx code; any other response codes will be considered an error. The requests have a 20-second timeout.
β Adding a Webhook trigger to your account
In the dashboard, click the User tab on the bottom left corner
Select Hosting
Click on Add domain
Enter your domain or sub-domain (e.g www.mywebsite.com)
Select Advanced hosting option
Choose Webhook trigger from the list
Within Access options, toggle if you want to share your domain across your team. It'll be off by default making it only available for you
Click on Add domain
βοΈ Setting up a Webhook trigger
You'll need to supply a number of parameters to properly connect your Webhook trigger, some of them are optional depending on how your server is set up.
Have a look at the chart below to understand how each parameter works.
Parameter | Required | Description |
Webhook URL | Yes | The address where Vev will send the contents when you publish from Vev. If you do not have a server/endpoint available, you can use a tunneling service such as Ngrok to send contents to your local computer. |
Page content to send in addition to body | No | Send full page: Check off if you want Vev to generate the full page (<html /> and all). Without this setting on, only the HTML to render the content will be sent.
Send plugin settings: Add the generated data from the Hosting Plugins of your project and attach them as a JSON attribute. |
Ping | No | Check off to grant Vev permission to send pings |
Unpublish | No | Check off to grant Vev permission to unpublish the content |
Adding a Web Path
You can add a Path to your domain at the core level, which means it will be used as a base path in all projects to which the domain is added. Follow the steps below to learn how to do it.
In the dashboard, click the User tab on the bottom left corner
Select Hosting
Select your domain
Enter your path in the Web path field
Payload settings (closed beta)
If you encounter publishing issues regarding payload too large
, then Content as links
might be the right solution for you. Selecting the Content as links option will send a list of HTML files, instead of having the code included in the payload's pages
field. This requires the files to be downloaded from the receiving server which requires some more code, but it also allows for much larger projects and smaller payload sizes. URLs to the files required are placed in the field htmlFiles
in the form of an array of strings, and items in pages
will no longer have the page
field.
Please contact our support team, if you want to use this feature.
Signature
The token is set by you and can be anything; it is used to verify that the content is sent from Vev. To see or generate a Secret for your webhook, go to the Hosting overview and click on Generate token. This string of characters is used in the Webhook server code.
When publishing to a Webhook all requests from Vev is signed with a header X-Vev-Signature for clients to verify that Vev is the actual sender. In order to verify the signature from the request, you need the secret set by you in Vev and a crypto library to create HMAC digests.
X-Vev-Signature value consists of an algorithm to generate a hash and the signature itself, e.g 'sha512=super-long-hashed-value'. For now, we only support sha512 as an algorithm to create a signature. If you use the payload and secret with sha512, you get the same value as the signature.
Endpoint security
It is used to communicate with users' endpoints in the correct way. We support non-secured endpoints, basic, and token-based. OpenID Connect (OIDC) is not supported yet.
Tip: Go to our roadmap and submit your request if you'd like OpenID endpoints to be supported.
Adding a Favicon
Follow the steps below to learn how to add a Favicon to your domain.
In the dashboard, click the User tab on the top right corner
Select Hosting
Select your domain
Scroll down to the Favicon section
Drag and drop your file or click to upload it from your computer
π Publishing to a Webhook trigger
Once your Webhook trigger has been properly set you'll need to add it to your project so that you can publish to it. Read the steps below to learn how to do it.
Open the project you want to publish
Click the Publish button on the top right corner
Select Add publish destination from the publish dialog
Choose your domain from the dropdown list
Add a path to your domain or leave it blank if you want to publish to its root (the path will only be added at a project level)
Hit Add publish destination
Open the publish dialog
Hit Publish project
Events
All requests are sent as type events, every event contains meta information for the event, and every event has a payload. We currently support two types of events: Ping and Publish. Ping is used for testing the webhook integration with your backend. Publish event contains all necessary HTML, CSS, and JavaScript to display the project.
WebhookRequest
export interface WebhookRequest {
id: string;
hosting: string;
event: WebhookEvent;
payload: Json;
}
PublishWebhookPayload
export interface PublishWebhookPayload {
projectId: string;
version: number;
dir: string;
pages: WebhookPage[];
workspace?: IWorkspace;
team?: FSTeam;
css: ExternalFile[];
js: ExternalFile[];
other: ExternalFile[];
pluginsData?: any;
}
PingPayload
interface PingPayload {
message: string;
html: string;
css: ExternalFile[];
js: ExternalFile[];
}
PublishPayload
interface PublishPayload {
projectId: string;
version: number;
dir: string;
pages: WebhookPage[];
workspace?: IWorkspace;
team?: FSTeam;
css: ExternalFile[];
js: ExternalFile[];
other: ExternalFile[];
pluginsData?: any;
}
interface WebhookPage {
html: string;
key: string;
title: string;
path: string;
cover?: string;
image?: { thumb: string; location: string };
index?: boolean;
settings?: any;
}
interface ExternalFile {
url: string;
contentType?: string;
cacheControl?: string;
}
π Examples
Vev JSON request
Download an example of the JSON which is sent from Vev here.
Implementation Examples
These examples are for NodeJS and PHP, but you can use any language you like (.NET, Ruby) as long as you can run a web server that can receive a POST request and parse it as JSON.
NodeJS
Create a folder called vev-webhook-server
Create the file
index.js
as per belowRun
npm install express body-parser crypto
Run
node index.js
Note: NodeJS (server-side JavaScript, requires NodeJS & Node Package Manager to run).
index.js (server code):
/**
* Vev Examples - Webhook Server
*
* A NodeJS + Express example server for receiving
* PUBLISH events from Vev securely.
*
* [Read more here](https://help.vev.design/hosting/custom/webhook)
*/
const express = require("express");
const fs = require("fs");
const crypto = require("crypto");
const bodyParser = require("body-parser");
const path = require("path");
// This should not be stored in plain text
const VEV_SECRET = "...";
const VEV_IP = "34.72.55.20";
const app = express();
// Add this to get IP information (no required, just an additional layer of security)
app.set("trust proxy", true);
// Since the large full websites can be sent over the web
const LargeJSONBodyParser = bodyParser.json({
limit: "150mb"
});
app.use(express.static(path.join(__dirname, "..", "public")));
// Creating a post route to handle the the webhook call
// Adding json body parser midleware
// Also Adding vev signature validator (you find the implementation at the bottom)
app.post(
"/webhook-receiver",
LargeJSONBodyParser,
VevSignatureMiddleware,
async (req, res) => {
const { payload, event } = req.body;
console.log("event: ", event);
console.log("payload: ", payload);
if (event === "PUBLISH") {
await Promise.all(payload.pages.map(StoreVevPage));
} else if (event === "PING") {
console.log("Webhook test ping recieved from: " + req.ip);
}
res.send({ message: "I received your webhook!!" });
}
);
async function StoreVevPage(page) {
// Putting the files in the public dir
// if page is index page then set the path to be public/index.html
let pagePath = path.join("./public", page.index ? "index.html" : page.path);
// Page path can have file extntion
if (!path.extname(pagePath)) pagePath = path.join(pagePath, "index.html");
// Creating the dir to put the file in
// Remeber that in vev the page path can be nested (example dir1/dir2/index.html)
// So important to create dir recursive
const dir = path.dirname(pagePath);
try {
fs.promises.access(dir);
} catch (e) {
await fs.promises.mkdir(dir, { recursive: true });
}
//Writing the file to the page path
await fs.promises.writeFile(pagePath, page.html);
}
function VevSignatureMiddleware(req, res, next) {
const signatureHeader = req.get("X-Vev-Signature");
if (!signatureHeader) return next("Missing signature");
// For extra secuirty you can validate that the IP is the vev IP address
if (req.ips.indexOf(VEV_IP) === -1)
return next("Invlid IP request (not from vev)");
const [algorithm, signature] = signatureHeader.split("=");
if (!algorithm || !signature) return next("Invalid signature");
// Validating signature of request
// Done by comparing the signature with HMAC (hash-based message authentication code)) using the secret key for hasing
const hmac = crypto.createHmac(algorithm, VEV_SECRET);
const generatedSignature = hmac
.update(JSON.stringify(req.body))
.digest("hex");
// Comparing the request signature with the generated
// This will validate that the sender knows the secret
// and that the content was not tampered with and that the
if (generatedSignature !== signature) {
console.error("Webhook trigered with invalid signature");
return next("Invliad signature");
}
next();
}
app.listen(8001, () => console.log(`Example app listening on port 8001!`));
Note: You will still need to set the Webhook URL in your webhook settings to a URL that points to where you are running your webhook server.
PHP
<?php
/**
* Vev Examples - Webhook endpoint
*
* PHP example script for receiving PUBLISH events from Vev securely.
*
* [Read more here](https://help.vev.design/hosting/custom/webhook)
*/
const VEV_SECRET = '85806154-43ae-4427-8f27-479af8239b2e';
if (!isset($_SERVER['X-Vev-Signature'])) return 'Missing signature';
$signatureHeader = $_SERVER['X-Vev-Signature'];
if (!$signatureHeader) return 'Missing signature';
list($algorithm, $signature) = explode('=', $signatureHeader);
if (!$algorithm || !$signature) return 'Invalid signature';
if (!in_array($algorithm, hash_hmac_algos())) return 'Invalid algorithm';
$requestBody = json_decode(file_get_contents('php://input'));
// Validating signature of request
// Done by comparing the signature with HMAC (hash-based message authentication code)) using the secret key for hasing
$generatedSignature = hash_hmac($algorithm, json_encode($requestBody), VEV_SECRET);
// Comparing the request signature with the generated
// This will validate that the sender knows the secret and that the content was not tampered with
if ($generatedSignature !== $signature) return 'Invalid signature';
$ip = $_SERVER['REMOTE_ADDR'];
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
if (!empty($_SERVER['HTTP_CLIENT_IP'])) $ip = $_SERVER['HTTP_CLIENT_IP'];
function storeVevPage ($page) {
// Putting the files in the public dir
// if page is index page then set the path to be public/index.html
$pagePath = './public/' . ($page['index'] ? 'index.html' : $page['path']);
if (!str_contains($pagePath, '.')) $pagePath .= '/index.html';
$directory = substr($pagePath, 0, strrpos($pagePath, '/') - 1);
if (!file_exists($directory)) mkdir($directory, 0777, true);
// Writing the file to the page path
file_put_contents($pagePath, $page['html']);
}
$payload = $requestBody['payload'];
$event = $requestBody['event'];
echo $payload;
echo $event;
switch ($event) {
case 'PUBLISH':
foreach ($payload['pages'] as $page) {
storeVevPage($page);
}
break;
case 'PING':
echo "Webhook test ping received from {$ip}";
break;
}
echo 'I received your webhook';
π Unpublishing a project
To unpublish your project from your Webhook:
In the dashboard, click the User tab on the bottom left corner
Click "Hosting"
Select your domain
Click the "Projects" tab on the left bar
If you have published projects, click the three dots next to the project you want to unpublish and click "Unpublish".
The project will no longer appear on the list.