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:
- Choose your team on the dashboard and go to Settings → Webhooks.
- Tick the deployment events you enabled in EventSend — anything else is safely ignored.
- Choose which projects the webhook covers (all of them, or a specific list).
- Paste this source's URL as the endpoint URL and click Create Webhook.
- 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.createdfires on every deployment, alongside whichever terminal event follows. Turn it on if you want to see deploys begin rather than only land.deployment.promotedfires when a deployment starts serving production traffic — which Vercel does automatically after a successful production build.succeededmeans "it built";promotedmeans "it's live". Most teams only need the first. Turnpromotedon 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.