---
title: "Domain Management"
description: "Domain Management serves as the foundational infrastructure layer in Dub for connecting, registering, provisioning, and verifying custom link domains. This subsystem bridges the gap between workspa..."
last_updated: "2026-10-12T07:00:10.151461+00:00"
canonical_url: "https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/link-attribution/domain-management"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [apps/web/app/api/domains/[domain]/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/route.ts)
- [apps/web/app/api/domains/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/route.ts)
- [apps/web/app/api/domains/[domain]/verify/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/verify/route.ts)
- [apps/web/lib/dynadot/register-domain.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/dynadot/register-domain.ts)
- [apps/web/app/api/domains/[domain]/domain-connect/apply/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/domain-connect/apply/route.ts)
- [apps/web/ui/domains/domain-card.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/domains/domain-card.tsx)
- [apps/web/app/api/domains/[domain]/forward-instructions/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/forward-instructions/route.ts)
- [apps/web/app/(ee)/api/email-domains/[domain]/verify/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/email-domains/%5Bdomain%5D/verify/route.ts)
- [apps/web/app/(ee)/admin.dub.co/(dashboard)/domains/page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/admin.dub.co/(dashboard)/domains/page.tsx)
- [apps/web/lib/api/domains/claim-dot-link-domain.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/claim-dot-link-domain.ts)
- [apps/web/app/[domain]/page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/%5Bdomain%5D/page.tsx)
- [apps/web/app/(ee)/api/domains/register/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/domains/register/route.ts)
- [apps/web/app/api/domains/[domain]/validate/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/validate/route.ts)
- [apps/web/app/(ee)/api/admin/domains/register-premium/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/admin/domains/register-premium/route.ts)
- [apps/web/lib/api/domains/finalize-premium-domain-registration.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/finalize-premium-domain-registration.ts)
- [apps/web/lib/api/domains/get-domain-response.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/get-domain-response.ts)
- [apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/settings/domains/page-client.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/(ee)/settings/domains/page-client.tsx)
- [apps/web/lib/domain-connect/constants.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/domain-connect/constants.ts)
- [apps/web/lib/api/domains/verify-domain.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/verify-domain.ts)
- [apps/web/app/(ee)/api/email-domains/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/email-domains/route.ts)
- [apps/web/app/(ee)/api/cron/domains/verify/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/domains/verify/route.ts)
- [apps/web/scripts/customers/annature/import-domains.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/scripts/customers/annature/import-domains.ts)
- [apps/web/app/(ee)/api/admin/domains/renew/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/admin/domains/renew/route.ts)
- [apps/web/ui/domains/domain-configuration.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/domains/domain-configuration.tsx)
- [apps/web/lib/dynadot/client.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/dynadot/client.ts)
- [apps/web/app/app.dub.co/(onboarding)/onboarding/(steps)/domain/custom/page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/(onboarding)/onboarding/(steps)/domain/custom/page.tsx)
- [apps/web/ui/partners/program-link-configuration.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/partners/program-link-configuration.tsx)
- [packages/utils/src/functions/domains.ts](https://github.com/blade47/dub/blob/packages/utils/src/functions/domains.ts)
- [apps/web/lib/api/domains/get-domain-search-availability.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/get-domain-search-availability.ts)
- [apps/web/lib/api/domains/initiate-premium-domain-registration.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/initiate-premium-domain-registration.ts)
</details>

## Overview

Domain Management serves as the foundational infrastructure layer in Dub for connecting, registering, provisioning, and verifying custom link domains. This subsystem bridges the gap between workspace project management, edge routing providers like Vercel, automated DNS configuration standards via Domain Connect, and external domain registrars like Dynadot. By abstracting the complexities of DNS validation, SSL certificate provisioning, and domain lifecycle maintenance, Dub enables marketing teams and link attribution platforms to operate entirely under their own branded domains.

Sources: [apps/web/app/api/domains/route.ts:96-233](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/route.ts#L96-L233)

The architecture addresses several core engineering challenges: tracking domain verification states across distributed edge networks, handling automated DNS setup with strict origin and signature security validations, and managing automated or premium domain registration invoices integrated with Stripe billing and Dynadot registrar APIs. Interfacing tightly with route handlers, background cron jobs, and database models, Domain Management ensures that every incoming custom URL resolves accurately while maintaining strict multi-tenant boundaries and plan-based limits.

Sources: [apps/web/app/api/domains/domain/verify/route.ts:16-139](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/verify/route.ts#L16-L139)

```mermaid
flowchart TD
    A["User submits custom domain"] --> B["Validate domain format & availability"]
    B --> C["Register domain on Vercel API"]
    C --> D["Create Prisma domain record"]
    D --> E["Verify DNS configuration & SSL"]
    E --> F{"Domain verified?"}
    F -->|Yes| G["Active & Ready for Link Dispatch"]
    F -->|No| H["Trigger Domain Connect / DNS Instructions"]
```

Sources: [apps/web/app/api/domains/route.ts:96-233](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/route.ts#L96-L233), [apps/web/app/api/domains/domain/verify/route.ts:16-139](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/verify/route.ts#L16-L139)

## Domain Validation and Registration Pipeline

When a workspace attempts to add or claim a custom domain, the request passes through rigorous validation layers to check syntax, prevent reserved subdomain collisions, and detect existing active sites or domain conflicts. 

Sources: [apps/web/app/api/domains/route.ts:96-233](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/route.ts#L96-L233)

The validation sequence begins by checking string validity through regular expressions, filtering out forbidden structures such as domains starting with `www.` or conflicting with reserved `.dub.link` subdomains. For standard custom domains, the system performs both an active HTTP `HEAD` check (with a 3-second timeout) and a DNS lookup (`dns.resolve`) to verify whether a site is already actively configured. If a site responds or records exist, the API returns a status flag indicating potential conflicts.

Sources: [apps/web/app/api/domains/domain/validate/route.ts:8-48](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/validate/route.ts#L8-L48)

```mermaid
sequenceDiagram
    participant Client
    participant API as /api/domains
    participant Vercel as Vercel API
    participant DB as PostgreSQL (Prisma)

    Client->>API: POST /api/domains (slug)
    API->>API: Validate format, plan limits, & active sites
    API->>Vercel: addDomainToVercel(slug)
    Vercel-->>API: Response (or domain_already_in_use)
    API->>DB: prisma.domain.create()
    DB-->>API: Domain record created
    API-->>Client: Return serialized domain object
```

Sources: [apps/web/app/api/domains/route.ts:96-233](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/route.ts#L96-L233), [apps/web/app/api/domains/domain/validate/route.ts:8-48](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/validate/route.ts#L8-L48)

For workspaces claiming free or premium `.link` domains, the registration flow interacts directly with Dynadot via the `registerDomain` client, records expiration timestamps, provisions a default root link (`_root`), and queues Vercel domain and nameserver configuration tasks.

Sources: [apps/web/lib/api/domains/claim-dot-link-domain.ts:15-163](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/claim-dot-link-domain.ts#L15-L163), [apps/web/lib/dynadot/register-domain.ts:24-64](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/dynadot/register-domain.ts#L24-L64)

## DNS Verification and Vercel Integration

Domain verification relies on querying Vercel's domain and configuration APIs to evaluate whether records point correctly to Dub's target endpoints (`76.76.21.21` for A records, `cname.dub.co` for CNAME records).

Sources: [apps/web/lib/domain-connect/constants.ts:1-7](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/domain-connect/constants.ts#L1-L7)

The verification endpoint (`GET /api/domains/[domain]/verify`) evaluates verification status by executing parallel requests to `getDomainResponse(domain)` and `getConfigResponse(domain)`. If Vercel reports the domain as unverified, the system invokes `verifyDomainWithRetry(domain)`, which executes up to 3 retry attempts separated by a configurable delay (`2500ms`).

Sources: [apps/web/app/api/domains/domain/verify/route.ts:16-139](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/verify/route.ts#L16-L139), [apps/web/lib/api/domains/verify-domain.ts:16-41](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/verify-domain.ts#L16-L41)

> [!NOTE]
> During domain verification, if Vercel configuration responses indicate conflicting records or misconfigurations, the system updates the Prisma domain record to `verified: false` and sets `lastChecked` to the current timestamp.

Sources: [apps/web/app/api/domains/domain/verify/route.ts:70-85](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/verify/route.ts#L70-L85)

```typescript
export const verifyDomainWithRetry = async (
  domain: string,
  {
    attempts = 3,
    delayMs = 2500,
  }: { attempts?: number; delayMs?: number } = {},
) => {
  let last: any = null;
  for (let i = 0; i < attempts; i++) {
    if (i > 0) {
      await sleep(delayMs);
    }
    try {
      last = await verifyDomain(domain);
    } catch (error) {
      if (i === attempts - 1) {
        throw error;
      }
      continue;
    }
    if (last?.verified) {
      return last;
    }
  }
  return last;
};
```
Sources: [apps/web/lib/api/domains/verify-domain.ts:16-41](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/verify-domain.ts#L16-L41)

## Domain Connect Automated DNS Setup

To simplify DNS configuration for end-users, Dub implements Domain Connect support, allowing automated configuration via supported DNS providers such as Cloudflare and Vercel.

Sources: [apps/web/app/api/domains/domain/domain-connect/apply/route.ts:24-143](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/domain-connect/apply/route.ts#L24-L143)

When a user triggers auto-configuration via `POST /api/domains/[domain]/domain-connect/apply`, the system discovers the apex domain's Domain Connect service descriptor via `discoverDomainConnect(apex)`. It validates that the provider URL satisfies allowed synchronization origins (`isAllowedSyncUXOrigin`) and constructs a cryptographically signed apply URL using RSA private keys (`DOMAIN_CONNECT_PRIVATE_KEY`).

Sources: [apps/web/app/api/domains/domain/domain-connect/apply/route.ts:24-143](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/domain-connect/apply/route.ts#L24-L143)

| Constant / Parameter | Value / Description |
| :--- | :--- |
| `DUB_CUSTOM_DOMAIN_A_RECORD` | `76.76.21.21` (Target IP for apex domains) |
| `DUB_CUSTOM_DOMAIN_CNAME` | `cname.dub.co` (Target host for subdomains) |
| `DOMAIN_CONNECT_KEY_HOST` | `_dck1` (TXT record key host for Domain Connect) |
| `DEFAULT_DC_SERVICE_APEX` | `links-apex` (Service ID for apex configuration) |
| `DEFAULT_DC_SERVICE_SUBDOMAIN` | `links-subdomain` (Service ID for subdomain configuration) |

Sources: [apps/web/lib/domain-connect/constants.ts:1-7](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/domain-connect/constants.ts#L1-L7)

## Dynadot Registrar Operations and Renewals

For domains provisioned or registered through Dub's partnership with Dynadot, backend routines manage domain purchases, availability searches, and annual renewals.

Sources: [apps/web/lib/dynadot/client.ts:14-73](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/dynadot/client.ts#L14-L73)

The `DynadotClient` extends `HttpBaseClient` to communicate with Dynadot's XML/JSON API (`https://api.dynadot.com/api3.json`), authenticating via query parameters (`key: DYNADOT_API_KEY`). Premium domain registrations are initiated by creating Stripe payment intents and recording pending invoices. Once payment succeeds, background workers finalize registrations and synchronize expiration dates.

Sources: [apps/web/lib/dynadot/client.ts:14-73](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/dynadot/client.ts#L14-L73), [apps/web/lib/api/domains/initiate-premium-domain-registration.ts:10-164](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/domains/initiate-premium-domain-registration.ts#L10-L164)

```typescript
class DynadotClient extends HttpBaseClient {
  protected readonly vendor = "Dynadot";
  protected readonly baseUrl =
    process.env.DYNADOT_BASE_URL || "https://api.dynadot.com/api3.json";
  protected readonly redactedQueryParams = ["key"];

  protected buildAuthHeaders() {
    return {};
  }

  protected buildAuthQuery() {
    return {
      key: assertEnv("DYNADOT_API_KEY"),
    };
  }
...
```
Sources: [apps/web/lib/dynadot/client.ts:14-30](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/dynadot/client.ts#L14-L30)

## API Endpoints and Routing Reference

The Domain Management subsystem exposes several [REST API](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/administration-and-api/rest-api) routes for managing workspace domains, verifying configurations, and triggering automated setups.

Sources: [apps/web/app/api/domains/route.ts:23-233](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/route.ts#L23-L233)

| Endpoint | Method | Description |
| :--- | :--- | :--- |
| `/api/domains` | `GET` | Retrieves all domains associated with the authenticated workspace. |
| `/api/domains` | `POST` | Adds a new custom domain or claims a `.dub.link` subdomain. |
| `/api/domains/[domain]` | `GET` | Retrieves details and metadata for a specific workspace domain. |
| `/api/domains/[domain]` | `PATCH` | Updates domain configuration (logos, asset links, auto-renewal options). |
| `/api/domains/[domain]/verify` | `GET` | Checks Vercel domain verification status and discovers Domain Connect support. |
| `/api/domains/[domain]/domain-connect/apply` | `POST` | Generates a signed Domain Connect URL for automated DNS setup. |
| `/api/domains/[domain]/forward-instructions` | `POST` | Emails DNS setup instructions to a specified recipient. |
| `/api/domains/[domain]/validate` | `GET` | Validates domain syntax, availability, and active site conflicts. |

Sources: [apps/web/app/api/domains/route.ts:23-233](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/route.ts#L23-L233), [apps/web/app/api/domains/domain/route.ts:28-208](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/route.ts#L28-L208), [apps/web/app/api/domains/domain/verify/route.ts:16-139](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/verify/route.ts#L16-L139), [apps/web/app/api/domains/domain/domain-connect/apply/route.ts:24-143](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/domain-connect/apply/route.ts#L24-L143)

## Error Handling and Invariant Enforcement

To protect multi-tenant boundaries and prevent unauthorized domain squatting, the subsystem enforces strict invariant checks across database transactions and API boundaries.

Sources: [apps/web/app/api/domains/domain/route.ts:118-125](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/route.ts#L118-L125)

> [!CAUTION]
> When updating a domain slug via `PATCH /api/domains/[domain]`, if the domain is Dub-provisioned (`registeredDomain` exists), the update is immediately rejected with a forbidden error to prevent orphaned registrar records.

Sources: [apps/web/app/api/domains/domain/route.ts:118-125](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/domains/%5Bdomain%5D/route.ts#L118-L125)

> [!WARNING]
> Cron verification routines (`GET /api/cron/domains/verify`) automatically track unverified domains. Domains failing verification past grace periods trigger reminder notifications or eventual deletion from the database.

Sources: [apps/web/app/ee/api/cron/domains/verify/route.ts:9-14](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/domains/verify/route.ts#L9-L14)

## Related

- [Request Routing](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/core-architecture/request-routing)
- [Link Dispatch](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/link-attribution/link-dispatch)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/llms.txt) for all pages in this wiki.
