Skip to content
Docs

Multi-tenant Reference

Use the Custom Domain block and Add Custom Domain action to build domain-management flows with the Vercel API.

In a multi-tenant platform, one Vercel project serves many tenants. Add each tenant hostname as a project domain so it follows the project's current production deployment.

The examples use teamId to scope requests to a team. You can use slug instead to identify the team by its slug.

Use the project domain APIs in this order:

  1. Call addProjectDomain to add the tenant domain to the project.
  2. If the response returns verified: false, give the tenant one of the returned verification challenges.
  3. After the tenant configures the DNS record, call verifyProjectDomain.
  4. Call getProjectDomain or getProjectDomains to check the domain status.
  5. Call removeProjectDomain to detach the domain from the project.

Use vercel.projects.addProjectDomain to assign a tenant domain to the shared Vercel project. See the Add a Domain to a Project API.

SDK:

add-domain.ts
import { Vercel } from '@vercel/sdk';
 
const vercel = new Vercel({
  bearerToken: '<YOUR_BEARER_TOKEN_HERE>',
});
 
async function run() {
  const result = await vercel.projects.addProjectDomain({
    idOrName: 'your_project_id_or_name_here',
    teamId: 'your_team_id_here',
    requestBody: {
      name: 'your_domain_here',
    },
  });
 
  console.log(result);
}
 
run();

After the tenant completes a DNS verification challenge, use vercel.projects.verifyProjectDomain to verify the domain. See the Verify Project Domain API.

SDK:

verify-domain.ts
import { Vercel } from '@vercel/sdk';
 
const vercel = new Vercel({
  bearerToken: '<YOUR_BEARER_TOKEN_HERE>',
});
 
async function run() {
  const result = await vercel.projects.verifyProjectDomain({
    idOrName: 'your_project_id_or_name_here',
    domain: 'your_domain_here',
    teamId: 'your_team_id_here',
  });
 
  console.log(result);
}
 
run();

Use vercel.projects.getProjectDomain to retrieve one project domain and its verification status. See the Get a Project Domain API.

SDK:

get-project-domain.ts
import { Vercel } from '@vercel/sdk';
 
const vercel = new Vercel({
  bearerToken: '<YOUR_BEARER_TOKEN_HERE>',
});
 
async function run() {
  const result = await vercel.projects.getProjectDomain({
    idOrName: 'your_project_id_or_name_here',
    domain: 'your_domain_here',
    teamId: 'your_team_id_here',
  });
 
  console.log(result);
}
 
run();

Use vercel.projects.getProjectDomains to retrieve the domains assigned to a project. See the Retrieve Project Domains API.

SDK:

list-domains.ts
import { Vercel } from '@vercel/sdk';
 
const vercel = new Vercel({
  bearerToken: '<YOUR_BEARER_TOKEN_HERE>',
});
 
async function run() {
  const result = await vercel.projects.getProjectDomains({
    idOrName: 'your_project_id_or_name_here',
    limit: 20,
    teamId: 'your_team_id_here',
  });
 
  console.log(result);
}
 
run();

Use vercel.projects.removeProjectDomain to detach a domain from a project. This operation does not remove account-level domain ownership. See the Remove a Domain from a Project API.

SDK:

remove-domain.ts
import { Vercel } from '@vercel/sdk';
 
const vercel = new Vercel({
  bearerToken: '<YOUR_BEARER_TOKEN_HERE>',
});
 
async function run() {
  const result = await vercel.projects.removeProjectDomain({
    idOrName: 'your_project_id_or_name_here',
    domain: 'your_domain_here',
    teamId: 'your_team_id_here',
  });
 
  console.log(result);
}
 
run();

Project domain APIs cover the standard multi-tenant flow. Use another API when you need different ownership or routing behavior:

APIUse it when
Domains APIYou need to manage account-level domain ownership separately from a project. You do not need this step before adding a project domain.
Aliases APIThe domain should remain pinned to one deployment instead of following the project's current production deployment.
CodeDescriptionSolution
domain_already_in_useDomain is used by another projectVerify domain ownership with TXT record
invalid_domainDomain format is invalidCheck domain spelling and format
forbiddenInsufficient permissionsCheck API token permissions
rate_limit_exceededToo many requestsWait and retry with exponential backoff

Problem: Domain not resolving after adding to project.

Solution:

  • DNS changes take 24 to 48 hours to propagate globally
  • Use WhatsMyDNS to check propagation
  • Verify nameservers are set correctly
  • Clear your local DNS cache: sudo dscacheutil -flushcache (macOS)

Problem: Domain verification failing with TXT record added.

Solution:

  • Wait 5 to 10 minutes after adding TXT record
  • Verify TXT record is set correctly: dig TXT _vercel.tenant1.com
  • Ensure no trailing dots in TXT value
  • Check for duplicate TXT records
  • Try verification again via SDK

Problem: Subdomains not routing to your application.

Solution:

  • Verify nameservers point to ns1.vercel-dns.com and ns2.vercel-dns.com
  • Confirm wildcard domain (*.yourapp.com) is added to project
  • Wait for DNS propagation (up to 48 hours)
  • Check wildcard certificate status in project settings
  • Ensure apex domain is also added to project

Problem: Domain shows "Certificate Error" in browser.

Solution:

  • Complete domain verification first
  • Wait 5 to 10 minutes for certificate issuance
  • Check domain status in Vercel dashboard
  • Ensure no CAA records blocking Let's Encrypt
  • Verify domain is not on SSL blacklist

Problem: Preview deployment URLs not working with custom domains.

Solution:

  • Preview URLs with custom domains require Enterprise plan
  • Use subdomain-based preview URLs: branch-name---project.vercel.app
  • Contact sales to upgrade for multi-tenant preview URLs
  • Keep branch names under 63 characters (DNS label limit)

Problem: Same content served on multiple domains.

Solution:

  • Set canonical URLs pointing to primary domain
  • Redirect subdomain to custom domain (or vice versa)
  • Use consistent domain in sitemaps
  • Configure 301 redirects in Next.js Proxy
app/layout.tsx
// app/layout.tsx
export async function generateMetadata() {
  return {
    alternates: {
      canonical: 'https://primary-domain.com',
    },
  };
}

Multi-Tenant: Single codebase serving multiple tenants with their own domains. All tenants share the same deployment.

Multi-Project: Multiple projects, each with unique code and isolated deployments. Each tenant has their own Vercel project.

Use Multi-Tenant when all tenants need the same functionality but different content. Use Multi-Project when tenants need custom code.

  • Hobby: 50 domains
  • Pro: Unlimited (soft limit: 100,000)
  • Enterprise: Unlimited (soft limit: 1,000,000)

Soft limits can be increased by contacting support.

Upgrade to the Pro plan for unlimited custom domains. View pricing.

Multi-tenant preview URLs let you test changes for specific tenants before deploying to production. They follow the format: tenant1---project-git-branch.vercel.app.

This feature is Enterprise only. Contact your sales representative to enable it.

Multi-tenant applications are priced based on:

  • Team plan: Hobby, Pro, or Enterprise
  • Usage: Bandwidth, function invocations, build minutes
  • Domains: No additional cost for domains (within plan limits)

See pricing documentation for details.

All Vercel applications include:

  • Firewall: DDoS protection and rate limiting
  • WAF: Web Application Firewall
  • SSL certificates: Automatic HTTPS for all domains
  • CDN: Global content delivery network with low latency
  • Vercel Dashboard: View domain status and SSL certificates
  • API: Query domain status programmatically
  • Webhooks: Get notified of domain events (Enterprise)
  • Logs: View domain resolution and errors

DNS changes take 24 to 48 hours to propagate. Use WhatsMyDNS to monitor propagation across global nameservers.

SSL certificates require domain verification. Add the TXT record provided by Vercel, wait 5 to 10 minutes, then trigger verification via the SDK or dashboard.

Set canonical URLs to indicate the primary domain for each page. This prevents duplicate content issues.

Last updated September 3, 2026

Was this helpful?

supported.