---
title: "Receive Vercel Webhooks Locally | WebhookRelay"
meta:
  "og:description": "Test Vercel webhooks locally and run your handler on localhost. Inspect the real deployment payload, forward to your endpoint, and verify the x-vercel-signature."
  "og:title": "Receive Vercel Webhooks Locally"
  description: "Test Vercel webhooks locally and run your handler on localhost. Inspect the real deployment payload, forward to your endpoint, and verify the x-vercel-signature."
---

![Stripes](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/images/stripes.svg)

# **Receive Vercel Webhooks Locally**

Test Vercel webhooks locally and run your handler on localhost. Inspect the real deployment payload, forward to your endpoint, and verify the x-vercel-signature.

![Receive Vercel Webhooks Locally (Test Vercel Webhooks on localhost)](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/images/blog/heroes/localhost.jpg)

You are building a Vercel integration — a Slack notifier for failed builds, a changelog updater, an internal dashboard that tracks deploys — and you need to see your handler react to a real `deployment.succeeded` or `deployment.error` event. The problem is immediate: Vercel will only POST to a **public URL**, and your handler is running on `localhost:8080`. Vercel has no way to reach it.

The usual workarounds are painful. Deploying your handler for every code change is slow and ironic. Pasting a sample payload from the docs into curl gives you a _guess_ at the real request, not the real headers and body Vercel actually sends. What you want is to **receive Vercel webhooks locally** — real events, hitting your local handler, with a URL that does not change every time you restart.

This guide shows how to do exactly that.

## [Why receiving Vercel webhooks locally is tricky](#why-receiving-vercel-webhooks-locally-is-tricky)

A webhook is just an HTTP request Vercel sends to a URL when something happens — a deployment starts, succeeds, errors, or is canceled; a project is created. Vercel sits on the public internet; your dev machine usually does not. It is behind a router, a corporate firewall, or both, with no public IP and no inbound ports open.

So you need something in the middle: a public endpoint Vercel can hit, that relays each request down to your laptop without you opening a single firewall port. That is what [Webhook Relay](https://my.webhookrelay.com/register) does — and unlike a random tunnel URL, the endpoint is **stable**, so you configure Vercel once and never touch it again.

(One note before you start: webhooks are a **Pro and Enterprise** feature on Vercel, configured at the team level. Make sure your team is on a plan that includes them.)

## [Step 1: Inspect the real payload with Webhook Bin](#step-1-inspect-the-real-payload-with-webhook-bin)

Before you write any handler code, find out what Vercel actually sends. Open the free [Webhook Bin](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/webhook-bin/) — no signup — and you get an instant public URL.

1. Copy the Webhook Bin URL.
2. In the Vercel Dashboard, select your team scope and go to **Settings → Webhooks**.
3. Click **Create Webhook**, paste the URL as the endpoint, and select the events you care about (for example **Deployment Created**, **Deployment Succeeded**, **Deployment Error**).
4. Save the webhook.

Now trigger a real action — push to a connected repo or redeploy from the dashboard. Vercel POSTs the event to your endpoint. Inspect the captured request in Webhook Bin: the full JSON body (with the event `type`, `id`, `payload`, and details about the deployment, project, and team), and every header, including the **`x-vercel-signature`** signature.

Now you know the exact shape of the data before writing a line of code. For more on this approach, see [How to test webhooks](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/blog/how-to-test-webhooks/) and [What is a webhook](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/blog/what-is-webhook/).

## [Step 2: Forward the events to localhost with the relay agent](#step-2-forward-the-events-to-localhost-with-the-relay-agent)

Once you know the payload, route those same events into your local handler. [Sign up for Webhook Relay](https://my.webhookrelay.com/register), install the `relay` agent (CLI or Docker), and create a bucket — say `vercel`. The bucket gives you a stable public input endpoint.

Start forwarding to your local server:

```
relay forward --bucket vercel http://localhost:8080/webhook
```

The agent opens an **outbound** connection to Webhook Relay and streams every incoming request down to `http://localhost:8080/webhook`. Because the connection is outbound, there are **no firewall ports to open and no public IP needed** — this works from your laptop, behind a corporate proxy, or inside a Kubernetes cluster. Running in Docker? The same command works in the official `webhookrelay/webhookrelayd` image. Full details are in the [localhost forwarding docs](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/docs/webhooks/internal/localhost/).

Now update the Vercel webhook's endpoint to your Webhook Relay URL (or just create it there from the start). Push a change and watch the `deployment.created` and `deployment.succeeded` events arrive on `localhost`.

## [Vercel-specific configuration and quirks](#vercel-specific-configuration-and-quirks)

A few Vercel details worth knowing:

- **Where to add it:** select your **team scope**, then **Settings → Webhooks**. Webhooks live at the account/team level (or per Integration), not per individual project from inside a project's settings.
- **Events:** common ones include `deployment.created`, `deployment.succeeded`, `deployment.error`, and `deployment.canceled`, plus project events like `project.created`. Some project-scoped events are only available when the webhook covers **all team projects**. Branch on the `type` field in your handler.
- **JSON payload:** the body is JSON, posted to your endpoint as a single `POST` per event.
- **The secret is shown once:** when you create a webhook, Vercel displays a **secret key once** and never again. Copy it immediately — you need it to verify signatures (see below).
- **Integration webhooks:** if you are building a Vercel Integration rather than a team webhook, you verify with your **Integration (Client) Secret** instead of a per-webhook secret.
- **Limits:** Vercel allows up to 20 custom webhooks per team, which is plenty for a dev endpoint plus production.

## [Step 3: Verify the Vercel webhook signature](#step-3-verify-the-vercel-webhook-signature)

Vercel signs every request. It computes an **HMAC-SHA1** of the **raw request body** using your webhook secret and sends the digest, **hex-encoded**, in the **`x-vercel-signature`** header. Your handler should recompute the same HMAC over the raw body and compare in constant time before trusting the payload. In Node.js that is essentially:

```
const expected = crypto
  .createHmac('sha1', secret)
  .update(rawBody)            // the raw bytes, before JSON parsing
  .digest('hex');
// constant-time compare expected with req.headers['x-vercel-signature']
```

The single most common mistake is computing the HMAC over a re-serialized body after your framework has already parsed the JSON — the bytes differ and verification fails. Capture the **raw** body. To sanity-check your implementation, paste a captured body, your secret, and the received signature into the free [HMAC signature verifier](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/hmac-verification/). For language-specific code and the rest of the pitfalls — raw-body handling, timing-safe comparison — read [Verify a webhook signature](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/blog/verify-webhook-signature/).

## [Replay and iterate](#replay-and-iterate)

This is where local development gets fast:

- **Replay from Webhook Relay** — past requests are stored on your bucket, so you can resend a captured deployment event without triggering a new build on Vercel.
- **Iterate on your handler** by editing code and replaying the same delivery until it branches on `type`, parses the deployment details, and verifies the SHA1 signature correctly. No new commits, no pushes, no deploys just to test a code path.
- **No wasted builds** — you do not have to redeploy your project repeatedly just to generate a fresh `deployment.succeeded` event; one captured event replays as many times as you need.

Because the Webhook Relay endpoint is stable, you can stop and restart the agent, reboot your machine, or come back next week — the Vercel webhook configuration never needs to change.

## [Get started](#get-started)

1. Inspect the real payload in the free [Webhook Bin](https://webhookrelay.com/blog/receive-vercel-webhooks-locally/webhook-bin/) — no signup needed.
2. [Create a Webhook Relay account](https://my.webhookrelay.com/register), install the agent, and run `relay forward --bucket vercel http://localhost:8080/webhook`.
3. Point your Vercel team webhook at the stable endpoint, trigger a deployment, and watch it hit `localhost`.

You will be testing real Vercel deployment events against your local handler in a few minutes — no extra deploys, no open firewall ports, and a URL you configure exactly once.