Vercel Deploy Button
One-click deploy flow. Users click a button, Vercel clones a Git repo into their GitHub/GitLab/Bitbucket, deploys it, and optionally redirects back to your app with metadata about the created project.
The button
Markdown:
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FOWNER%2FREPO)
HTML:
<a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FOWNER%2FREPO">
<img src="https://vercel.com/button" alt="Deploy with Vercel"/>
</a>
What the button does
- User clicks the link
- Vercel shows the Project creation flow
- User picks their GitHub/GitLab/Bitbucket account
- Vercel clones the source repo into the user's account as a new repo
- User fills in env vars (if required), picks project/repo name
- Vercel builds and deploys
- If
redirect-urlis set, user is redirected to your app with callback params
URL parameters — complete reference
Base URL: https://vercel.com/new/clone
All parameter values must be URI-encoded.
Source parameters
repository-url(string, required) — the Git repo to clone. Can include a subdirectory path (e.g..../tree/main/examples/hello-world). The user cannot change this; it is fixed by the link author.project-name(string) — default Vercel project name. User can change it. Not guaranteed to stick if a project with that name already exists.repository-name(string) — default name for the new Git repo created in the user's account. No spaces.stores(string) — JSON array of store configs to auto-provision during deployment. Supports Vercel Blob stores and Marketplace integration stores. See stores section below.
Environment variable parameters
env(comma-separated strings) — list of required env var keys. User must fill in values before deploying. You cannot pass values via this param (URL is in browser history, insecure).envDefaults(URI-encoded JSON object) — non-sensitive default values for env vars. Keys must also appear inenv. Pre-populates the form. Never use for secrets, API keys, tokens, or passwords.envDescription(string) — short description shown next to the env var inputs. Only displayed ifenvis set.envLink(string) — URL to docs explaining what values to enter. Only displayed ifenvis set. Point to specific docs, not top-level.
Callback parameters
redirect-url(string) — URL to send the user to after successful deployment. Vercel appends callback query params to this URL. See callback params below.developer-id(string) — Vercel Integration Client ID. Shows your logo and name on the redirect UI. Requiresredirect-url. The Integration's website field must match the redirect URL.external-id(string) — your own arbitrary ID passed through the flow. Relayed to the redirect URL of each required Integration. Requiresintegration-ids.production-deploy-hook(string) — name of a Deploy Hook to create. When set alongsideredirect-url, the callback includesproduction-deploy-hook-urlyou can use to trigger redeployments (useful for headless CMS content changes).
Integration parameters
integration-ids(comma-separated strings) — Vercel Integration IDs the user must install before deploying. Max 3. Find IDs in the Integrations Developer Console.skippable-integrations(number) — if present, integrations become optional (user picks one or skips). They should all serve the same purpose (e.g. competing error trackers).
Demo card parameters
All four are required for the demo card to appear:
demo-title(string) — title of the example deploymentdemo-description(string) — description textdemo-url(string) — link to a live exampledemo-image(string) — screenshot URL
Stores
The stores param accepts a JSON array. Two store types:
Blob store:
[{ "type": "blob", "access": "private" }]
Properties: type (required, "blob"), access ("public" or "private"), envVarPrefix (custom prefix for env vars like MYBLOG_BLOB_READ_WRITE_TOKEN).
Integration store:
[{
"type": "integration",
"integrationSlug": "neon",
"productSlug": "postgres",
"protocol": "storage"
}]
Properties: type (required, "integration"), integrationSlug, productSlug (both required), protocol, envVarPrefix, allowConnectExistingProduct (boolean).
Encode with encodeURIComponent(JSON.stringify([...])) before putting in URL.
Callback parameters returned by Vercel
When redirect-url is set, Vercel redirects the user after deploy and appends these query params:
project-dashboard-url— Vercel dashboard URL for the created projectproject-name— name of the created projectdeployment-dashboard-url— Vercel dashboard URL for the deploymentdeployment-url— live deployment URL (the*.vercel.appdomain)repository-url— the new Git repo URL in the user's accountproduction-deploy-hook-url— Deploy Hook URL (only ifproduction-deploy-hookwas set)
Vercel system environment variables
Vercel auto-populates these at both build time and runtime (must be enabled in project settings). These are available in the deployed app without you setting them.
Git-related vars (the important ones for association):
VERCEL_GIT_PROVIDER—github,gitlab, orbitbucketVERCEL_GIT_REPO_SLUG— repo name, e.g.my-docsVERCEL_GIT_REPO_OWNER— account/org that owns the repo, e.g.acme-corpVERCEL_GIT_REPO_ID— numeric GitHub repo ID, e.g.117716146VERCEL_GIT_COMMIT_REF— branch nameVERCEL_GIT_COMMIT_SHA— commit hashVERCEL_GIT_COMMIT_AUTHOR_LOGIN— GitHub username of the committer
Deployment-related vars:
VERCEL— always1when system env vars are enabledVERCEL_ENV—production,preview, ordevelopmentVERCEL_URL— deployment domain without protocol (e.g.my-site.vercel.app)VERCEL_PROJECT_PRODUCTION_URL— shortest production custom domain, or*.vercel.appVERCEL_DEPLOYMENT_ID— unique deployment IDVERCEL_PROJECT_ID— unique project IDVERCEL_REGION— runtime region (e.g.cdg1)
Associating a deployed repo with your backend resource
This is the key pattern for SaaS products that deploy a template for users and need to link that deployment back to a user/org in their own database.
The problem
User clicks your deploy button, Vercel creates a repo + deployment in the user's account. You need to know: which user deployed it, which repo was created, and which resource (org, project, workspace) in your system it belongs to.
The solution: pre-filled env var + redirect callback
Step 1: Generate a per-user deploy URL on your dashboard.
Your backend knows the user's org ID. Generate the deploy button URL
with envDefaults pre-filling it:
const orgId = currentUser.orgId
const deployUrl = new URL('https://vercel.com/new/clone')
deployUrl.searchParams.set('repository-url', 'https://github.com/yourco/template')
deployUrl.searchParams.set('env', 'YOUR_ORG_ID')
deployUrl.searchParams.set('envDefaults', JSON.stringify({ YOUR_ORG_ID: orgId }))
deployUrl.searchParams.set('envDescription', 'Your org ID (pre-filled, do not change)')
deployUrl.searchParams.set('redirect-url', 'https://yourapp.com/deploy/callback')
deployUrl.searchParams.set('project-name', 'my-docs')
deployUrl.searchParams.set('production-deploy-hook', 'content-update')
The user sees YOUR_ORG_ID pre-filled with their actual org ID. They
can technically change it, but the UX discourages it.
Step 2: Handle the redirect callback.
After the user deploys, Vercel redirects to your callback URL with query params:
// GET https://yourapp.com/deploy/callback?repository-url=...&deployment-url=...&project-name=...
app.get('/deploy/callback', async (req) => {
const repoUrl = req.query['repository-url'] // https://github.com/user/my-docs
const deploymentUrl = req.query['deployment-url'] // https://my-docs-xyz.vercel.app
const projectName = req.query['project-name']
const deployHookUrl = req.query['production-deploy-hook-url']
// Parse GitHub owner/repo from the repo URL
const match = repoUrl.match(/github\.com\/([^/]+)\/([^/]+)/)
const [, owner, repo] = match
// Create your internal project record
await db.insert(projects).values({
userId: currentUser.id, // from session cookie
githubOwner: owner,
githubRepo: repo,
vercelDeploymentUrl: deploymentUrl,
vercelDeployHookUrl: deployHookUrl,
})
return redirect('/dashboard/projects')
})
Step 3: At runtime, the deployed app reads both your env var and Vercel's system vars.
The template app can phone home on first request or during build:
// In the deployed template app (runs on Vercel)
const orgId = process.env.YOUR_ORG_ID
const repoOwner = process.env.VERCEL_GIT_REPO_OWNER
const repoSlug = process.env.VERCEL_GIT_REPO_SLUG
const repoId = process.env.VERCEL_GIT_REPO_ID
const gitProvider = process.env.VERCEL_GIT_PROVIDER
// Phone home to associate this deployment
await fetch('https://yourapp.com/api/deployments/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orgId,
repoOwner,
repoSlug,
repoId,
gitProvider,
deploymentUrl: process.env.VERCEL_URL,
projectId: process.env.VERCEL_PROJECT_ID,
}),
})
This gives you two independent association paths:
- Redirect callback (immediate, from the user's browser) — gives you
repository-urlanddeployment-urlbefore the app even boots - Runtime phone-home (from the deployed app) — confirms the deployment is live and sends
VERCEL_GIT_REPO_OWNER,VERCEL_GIT_REPO_ID, and your pre-filledYOUR_ORG_ID
Use the redirect callback as the primary signal. The runtime phone-home is a backup confirmation.
Triggering redeployments from your backend
If you set production-deploy-hook, the callback includes
production-deploy-hook-url. Use it to trigger redeployments when
content changes (e.g. headless CMS, config update):
// Redeploy the user's project
await fetch(project.vercelDeployHookUrl, { method: 'POST' })
Limitations
- No inline files. Source must be a real public Git repo. You cannot pass file contents in the URL.
- No branch selection. Always clones the default branch of the source repo.
envDefaultsare visible in the URL. Never put secrets there. The URL is saved in browser history.envvalues cannot be passed in the URL. User must type them in.repository-urlis fixed. The user cannot choose a different source repo.project-nameis a suggestion. If it already exists, Vercel asks the user to pick a new name.- No callback on failure.
redirect-urlonly fires on successful deployment.
Resources
- Deploy Button overview: https://vercel.com/docs/deploy-button
- Source parameters: https://vercel.com/docs/deploy-button/source
- Callback parameters: https://vercel.com/docs/deploy-button/callback
- Environment variables: https://vercel.com/docs/deploy-button/environment-variables
- Integrations: https://vercel.com/docs/deploy-button/integrations
- Demo card: https://vercel.com/docs/deploy-button/demo
- System environment variables: https://vercel.com/docs/environment-variables/system-environment-variables
- Deploy Hooks: https://vercel.com/docs/deploy-hooks
- Vercel Integrations Console: https://vercel.com/dashboard/integrations/console