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:
- Call
addProjectDomainto add the tenant domain to the project. - If the response returns
verified: false, give the tenant one of the returnedverificationchallenges. - After the tenant configures the DNS record, call
verifyProjectDomain. - Call
getProjectDomainorgetProjectDomainsto check the domain status. - Call
removeProjectDomainto 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:
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:
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:
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:
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:
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:
| API | Use it when |
|---|---|
| Domains API | You need to manage account-level domain ownership separately from a project. You do not need this step before adding a project domain. |
| Aliases API | The domain should remain pinned to one deployment instead of following the project's current production deployment. |
| Code | Description | Solution |
|---|---|---|
domain_already_in_use | Domain is used by another project | Verify domain ownership with TXT record |
invalid_domain | Domain format is invalid | Check domain spelling and format |
forbidden | Insufficient permissions | Check API token permissions |
rate_limit_exceeded | Too many requests | Wait 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.comandns2.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
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.
Was this helpful?