Documentation menu

Vercel source

Turn Vercel deployment webhooks into EventSend events with no code — setup steps, the event mapping, and why two of them ship switched off.

A Vercel source turns deployments into events you can route anywhere. Paste one URL into your team's webhook settings and every deploy that starts, ships, fails or gets rolled back arrives as an event — no GitHub Action, no deploy hook script.

Paired with a GitHub source, one channel carries the whole story: the push, the merged pull request, and the deploy that followed.

Setup

Two things about Vercel's setup are worth knowing before you start. Account webhooks are a Pro or Enterprise team feature — they live in team settings, and Hobby accounts don't have them. And Vercel generates the secret, not you, showing it exactly once after the webhook is created. That makes setup two-phase, so create the source here first and leave the secret blank.

In EventSend, open Sources, create a source, choose Vercel, pick your events, and save with the signing secret empty. The source is created in an awaiting secret state and shows you its endpoint URL.

Then, in Vercel:

  1. Choose your team on the dashboard and go to Settings → Webhooks.
  2. Tick the deployment events you enabled in EventSend — anything else is safely ignored.
  3. Choose which projects the webhook covers (all of them, or a specific list).
  4. Paste this source's URL as the endpoint URL and click Create Webhook.
  5. The Webhook Created dialog shows the secret. This is the only time it is shown. Copy it, return to the source in EventSend, and paste it into the signing secret field.

Until you paste the secret in, deliveries are rejected with 401 — nothing is ever accepted unverified. Nothing is lost in the meantime, because Vercel retries (see below). If you do lose the secret, Vercel cannot show it again: delete the webhook and create a new one.

Vercel's own webhook documentation is at vercel.com/docs/webhooks.

What each Vercel event becomes

Vercel event Event name Level Icon On by default
deployment.created deploy-started default 🏗️ no
deployment.succeeded deploy-succeeded success 🚀 yes
deployment.promoted deploy-promoted success 📣 no
deployment.rollback deploy-rollback warning yes
deployment.error deploy-failed error 🔥 yes
deployment.canceled deploy-canceled default 🚫 yes

Two of these are deliberately switched off by default, because each one would meter an ordinary deploy a second time:

  • deployment.created fires on every deployment, alongside whichever terminal event follows. Turn it on if you want to see deploys begin rather than only land.
  • deployment.promoted fires when a deployment starts serving production traffic — which Vercel does automatically after a successful production build. succeeded means "it built"; promoted means "it's live". Most teams only need the first. Turn promoted on if the gap between the two matters to you, or if you promote manually.

Tick either one whenever you want it; they are on the checklist, just not preselected.

deployment.rollback fires when Vercel accepts an instant rollback request, which is why its message reads "Rollback requested" — traffic may still be moving at that moment, and Vercel sends no event when the rollback finishes, so don't wait for one. Its payload carries the deployment being replaced and the one being restored, but no project name, so the message stays short by necessity.

Vercel events that aren't in this table — project and environment-variable events, feature-flag events, firewall alerts — are ignored. EventSend answers 200, and nothing is recorded or metered.

Because the names are predictable, a topic pattern of deploy-* catches every deployment event, and a minimum level of error catches only failures. These are the same names the deploy notifications recipe uses, so an existing deploy routing table works unchanged. See routing rules.

Routing production separately from previews

Not supported today, and worth saying plainly. Every event carries its environment in the message and in the structured payload ("target": "production"), but routing rules match on event name and level only — they cannot yet test a payload field. So a rule that catches deploy-failed catches preview failures alongside production ones. If that noise is the problem, routing failures by level to a quieter destination is the practical workaround for now.

The payload

EventSend does not forward the raw Vercel object. Each event carries a small structured payload with just the useful fields:

{
"provider": "vercel",
"delivery_id": "AbCdEf123456",
"project": "my-app",
"target": "production",
"deployment_id": "dpl_abc123",
"url": "https://my-app-abc123.vercel.app",
"branch": "main",
"commit_sha": "a1b2c3d4e5f6",
"author": "jane",
"dashboard_url": "https://vercel.com/acme/my-app/dpl_abc123"
}

Fields Vercel didn't supply are omitted. target is production, staging, or preview — Vercel omits it entirely for preview deployments, which is how a preview is identified. url is the deployment's own address (Vercel sends it without a scheme; EventSend adds https://), while dashboard_url opens the deployment in Vercel. A rollback carries project_id, from_deployment_id and to_deployment_id instead of the deployment fields.

Git metadata — branch, commit_sha, author — comes from the deployment's metadata as GitHub populates it. Projects connected to GitLab or Bitbucket produce events without those three fields; everything else is identical.

Security and duplicates

Every request is verified against your signing secret before it is decoded, using the scheme Vercel documents: HMAC-SHA1 over the raw request body, sent as x-vercel-signature as a bare hex digest. The header is accepted only in exactly that form. A bad, malformed or missing signature returns 401 and increments the source's failure counter, which is visible on the source page.

Every delivery carries an event id, and Vercel reuses it when it retries. EventSend uses that id as the event's idempotency key, so a retry is recognized and answered 200 without producing a second event or a second notification.

One limit worth stating honestly: Vercel's scheme has no timestamp, so a captured request body stays verifiable indefinitely — there is no freshness check of the kind Stripe's timestamped scheme gives you. The exposure is limited, because the signature covers the whole body including the event id: a replayed delivery can't change that id without breaking its own signature, so while the original event is still in your history the replay dedups into it rather than creating a new one. That protection is bound to your plan's retention window, though — once the original event is pruned, nothing durable remains to recognize the replay by, and the same body would be accepted again.

When a delivery fails

Vercel retries a failed delivery with exponential backoff for up to about 24 hours, then gives up. That is shorter than Stripe's window and far better than GitHub's, which never retries at all.

That retry behaviour is exactly what makes the two-phase setup safe: deployments that happen while the source is still awaiting its secret are retried once you paste the secret in, so they still land.