Getting Started
Core Architecture
Link Engine
Analytics & Attribution
Partners & Affiliates
Third-Party Integrations
Identity & Security
Automation & Messaging
Developer Tools
The following files were used as context for generating this wiki page:
Dub is the modern link attribution platform engineered for short links, real-time conversion tracking, and scalable affiliate programs. This system overview explores the underlying architecture and developer toolchains powering Dub, encompassing its monorepo layout, OpenAPI-driven REST interface, custom domain infrastructure, onboarding pathways, enterprise partner programs, and extensible third-party ecosystem integrations.
Sources: apps/web/lib/openapi/index.ts:28-32
Dub is structured as a robust monorepo organizing distinct application runtimes, shared component libraries, utility modules, and template rendering engines. The codebase divides responsibilities across specialized packages and Next.js applications, governing hostnames, environment bindings, SDK integrations, and transactional messaging.
The infrastructure relies on explicit environment variables and runtime checks to partition API routing, partner portals, and core application domains across production, staging, and local development environments.
Note
Preview environments utilize dynamic Vercel URL patterns for app hostnames, whereas preview API and partner domains rely explicitly on their respective staging subdomains (api-staging.dub.co and partners-staging.dub.co).
The user interface layer (packages/ui) exports consolidated navigation taxonomies, layout wrappers, and design primitives utilized across web views. This includes product feature arrays, legal document lists, and official SDK metadata.
export const SDKS = [
{ icon: Typescript, href: "/sdks/typescript", title: "Typescript" },
{ icon: Python, href: "/sdks/python", title: "Python" },
{ icon: Go, href: "/sdks/go", title: "Go" },
{ icon: Ruby, href: "/sdks/ruby", title: "Ruby" },
{ icon: Php, href: "/sdks/php", title: "PHP" },
];Sources: packages/ui/src/content.ts:81-115
The platform UI also maintains strict categorization for product solutions, secondary resource directories, and social handles.
Sources: packages/ui/src/content.ts:44-79, packages/ui/src/content.ts:117-140, packages/ui/src/content.ts:206-234
The email package (packages/email) builds responsive transactional notifications utilizing @react-email/components and Tailwind CSS. The WelcomeEmail workflow handles customer onboarding messages by conditionally parsing workspace slugs, logos, and onboarding step links.
export default function WelcomeEmail({
email = "panic@thedis.co",
workspace,
unsubscribeUrl,
}: WelcomeEmailProps) {
const workspaceUrl = workspace
? `https://app.dub.co/${workspace?.slug}`
: "https://app.dub.co";
// Renders container, branding, workspace card, and getting started checklist
}The Dub platform exposes its programmatic interface via an OpenAPI-driven REST specification constructed using zod-openapi within apps/web/lib/openapi/index.ts. The OpenAPI document is configured with version 0.0.1, metadata identifying the API as the Dub API, and contact/license references.
export const document = createDocument({
openapi: "3.0.3",
info: {
title: "Dub API",
description:
"Dub is the modern link attribution platform for short links, conversion tracking, and affiliate programs.",
version: "0.0.1",
contact: {
name: "Dub Support",
email: "support@dub.co",
url: "https://dub.co/support",
},
license: {
name: "AGPL-3.0 license",
url: "https://github.com/dubinc/dub/blob/main/LICENSE.md",
},
},
servers: [
{
url: "https://api.dub.co",
description: "Production API",
},
],
// Paths and components configuration...
});Sources: apps/web/lib/openapi/index.ts:26-48
The specification aggregates routing modules across core domain endpoints and registers standard schema components and security mechanisms.
Sources: apps/web/lib/openapi/index.ts:67-88
The API paths integrated into the document cover all platform subsystems, combining link management, analytics, tracking, customer attribution, partners, payout systems, and embed tokens.
Sources: apps/web/lib/openapi/index.ts:50-66
Note
The OpenAPI document utilizes Speakeasy annotations (x-speakeasy-example) within security scheme definitions to automatically generate strongly-typed public SDKs across multiple languages.
Sources: apps/web/lib/openapi/index.ts:77-84
Internal web applications interact with the public API using the official dub Node.js client package. For instance, customer record retrieval initializes a client instance and queries customer entities filtering by external database identifiers.
import { Dub } from "dub";
export const dub = new Dub();
// fetch Dub customer using their external ID (ID in our database)
export const getDubCustomer = async (userId: string) => {
try {
const { result: customers } = await dub.customers.list({
externalId: userId,
includeExpandedFields: true,
});
return customers.length > 0 ? customers[0] : null;
} catch (error) {
console.error(error);
return null;
}
};Sources: apps/web/lib/dub.ts:1-18
Management operations can also be executed via the official command-line interface package (packages/cli), which is built on top of the commander library. Process signal handlers (SIGINT and SIGTERM) ensure clean exits.
#!/usr/bin/env node
import { config } from "@/commands/config";
import { domains } from "@/commands/domains";
import { login } from "@/commands/login";
import { shorten } from "@/commands/shorten";
import { getPackageInfo } from "@/utils/get-package-info";
import { Command } from "commander";
import { links } from "./commands/links";
process.on("SIGINT", () => process.exit(0));
process.on("SIGTERM", () => process.exit(0));
async function main() {
const packageInfo = await getPackageInfo();
const program = new Command()
.name("dub")
.description("A CLI for shortening links with the Dub API.")
.version(
packageInfo.version || "1.0.0",
"-v, --version",
"display the version number",
);
program
.addCommand(login)
.addCommand(config)
.addCommand(domains)
.addCommand(shorten)
.addCommand(links);
program.parse();
}
main();Sources: packages/cli/src/index.ts:1-36
The CLI configuration and error contracts are typed via dedicated TypeScript interfaces governing local credential persistence and structured API error responses.
Sources: packages/cli/src/types/index.ts:1-14
Link infrastructure and deep linking form the core routing mechanisms that translate short URLs, custom domains, and mobile intents into precise destinations. The platform combines dynamic domain welcome pages, feature placeholders, and intelligent deep link preview resolution to handle both web navigation and mobile app deep linking.
Sources: apps/web/ui/placeholders/features-section.tsx:15-120, apps/web/app/domain/page.tsx:1-86, apps/web/app/app.dub.co/deeplink/deeplink/domain/...key/page.tsx:45-255
When visitors access a custom domain root, the application renders a specialized welcome page (CustomDomainPage) configured with custom metadata derived from the domain name parameter. The page caches responses indefinitely via revalidate = false and serves static parameters via generateStaticParams().
export const revalidate = false; // cache indefinitely
export async function generateMetadata(props: {
params: Promise<{ domain: string }>;
}) {
const params = await props.params;
const title = `${params.domain.toUpperCase()} - A Dub Custom Domain`;
const description = `${params.domain.toUpperCase()} is a custom domain on Dub - the modern link attribution platform for short links, conversion tracking, and affiliate programs.`;
return constructMetadata({
title,
description,
});
}
export function generateStaticParams() {
return [];
}Sources: apps/web/app/domain/page.tsx:11-28
The custom domain layout embeds a feature showcase section (FeaturesSection) that dynamically formats feature cards with marketing utilities. Each card generates tracking-enabled hyperlinks via createHref utilizing UTM parameters (utm_source: "Custom Domain", utm_medium: "Welcome Page").
Note
The FeaturesSection client component extracts the active domain using Next.js useParams() and wraps feature descriptions in a Markdown renderer that intercepts anchor tags to open them in external browser contexts.
Sources: apps/web/ui/placeholders/features-section.tsx:15-20, apps/web/ui/placeholders/features-section.tsx:161-176
The deep linking subsystem (DeepLinkPreviewPage) processes incoming requests across arbitrary domains and optional key parameters (...key), orchestrating mobile platform detection and database validation.
export default async function DeepLinkPreviewPage(props: {
params: Promise<{ domain: string; key?: string[] }>;
}) {
const params = await props.params;
const domain = params.domain;
const key = params.key ? decodeURIComponent(params.key.join("/")) : "_root";
// Detect language from Accept-Language header
const headersList = await headers();
const acceptLanguage = headersList.get("accept-language");
const language = getLanguage(acceptLanguage);
const t = getTranslations(language);
const ua = userAgent({ headers: headersList });
const platform: "ios" | "android" =
ua.os?.name === "Android" ? "android" : "ios";
// Encode the key for case-sensitive domains before querying
const encodedKey = encodeKeyIfCaseSensitive({
domain,
key,
});
let link = await prisma.link.findUnique({
where: {
domain_key: {
domain,
key: encodedKey,
},
},
select: {
domain: true,
key: true,
shortLink: true,
url: true,
ios: true,
android: true,
shortDomain: {
select: {
appleAppSiteAssociation: true,
assetLinks: true,
deepviewData: true,
},
},
},
});
// if the link doesn't exist, we redirect to the root domain link
if (!link) {
redirect(`https://${domain}`);
}
...Once the database record is retrieved, platform-specific association checks determine whether to render the deep link preview UI or perform an immediate redirection.
const { appleAppSiteAssociation, assetLinks, deepviewData } =
link.shortDomain;
// if the domain isn't set up for deep linking on the user's platform, skip
// the preview and forward to the platform-specific URL (or the canonical URL)
if (platform === "android") {
if (!assetLinks || !deepviewData) {
redirect(link.android ?? link.url);
}
} else {
if (!appleAppSiteAssociation || !deepviewData) {
redirect(link.ios ?? link.url);
}
}
const deepViewData = parseDeepViewData(deepviewData);
// decode the link if the domain is case sensitive
link = decodeLinkIfCaseSensitive(link);
// This should never happen
if (!link) {
redirect(`https://${domain}`);
}Warning
If a short link record cannot be found in the database during deep link resolution, the handler immediately terminates execution and issues an HTTP redirect back to the root domain (https://${domain}).
Account registration and workspace setup begin through the authentication marketing registration page (RegisterPage), which wraps RegisterPageClient inside an AuthLayout configured with showTerms="app".
Once registered, users enter the multi-step onboarding journey governed by the layout component in apps/web/app/app.dub.co/onboarding/onboarding/steps/layout.tsx. This layout renders an absolute background featuring a 60px-cell grid (Grid) and an AuroraGradient, centered with a Wordmark pointing to https://dub.co/home, and a mobile SignedInHint component.
The onboarding flow progresses through discrete steps:
/welcome): Invokes TrackSignup and renders AccountTypeSelector inside StepPage with test ID testIds.onboarding.stepWelcome and a maximum width of 640px.
Sources: apps/web/app/app.dub.co/onboarding/onboarding/steps/welcome/page.tsx:6-20/products): Renders ProductSelector within StepPage (testIds.onboarding.stepProducts), asking users what they want to do with Dub across an unconstrained width (max-w-none).
Sources: apps/web/app/app.dub.co/onboarding/onboarding/steps/products/page.tsx:5-16/plan): Dynamically adjusts its title and description based on the active product retrieved via useOnboardingProduct(). It provisions plan choices via PlanSelector and offers an enterprise link, a free plan button, and a product-specific pricing comparison link.
Sources: apps/web/app/app.dub.co/onboarding/onboarding/steps/plan/page.tsx:13-78/success): Concludes onboarding by rendering workspace settings shortcuts, an optional Slack support invitation flow via SlackSupportInviteModal, and navigational links to team management, the help center, documentation, and support chat.
Sources: apps/web/app/app.dub.co/onboarding/onboarding/steps/success/page-client.tsx:273-382Note
During the onboarding plan step (/plan), if the selected product is set to "links", the UI renders a LaterButton that allows users to defer plan selection and jump straight to the "success" step with the free tier.
Feature availability across subscription tiers is defined in PRICING_PLAN_COMPARE_FEATURES. The structure organizes limits, boolean checks, and text renderers across categories such as Links, Partners, Analytics, Domains, API, Workspace, and Support.
Enterprise partner program management and embeddable client systems provide infrastructure for scaling referral networks. The Dub platform coordinates partner analytics, creation flows, and browser-side embedding across distributed web properties.
Sources: apps/web/app/app.dub.co/dashboard/slug/ee/program/page.tsx:5-20, apps/web/app/ee/app.dub.co/embed/referrals/quickstart.tsx:22-209
The embed client initializes global browser hooks by attaching a Dub interface to the window object when executed in browser environments. This system powers embedded partner portals such as quickstart links, resource management, and payout connections.
import { init } from "./core";
import { DubEmbed } from "./types";
declare global {
interface Window {
Dub: DubEmbed;
}
}
if (typeof window !== "undefined") {
window.Dub = (window.Dub || {}) as DubEmbed;
window.Dub.init = init;
}The referral quickstart component structures partner onboarding flows into discrete actionable blocks including link sharing, resource downloads, and payout configurations.
Note
The Payout configuration step dynamically checks TREMENDOUS_SUPPORTED_COUNTRIES against the partner's registered country profile to determine if internal payout settings should render or if redirection to external partners is required.
Third-party application integration, CRM connectors, and embedded marketplace layouts extend Dub's core link management capabilities into broader enterprise ecosystems. The platform coordinates integrations through dedicated packages, client configuration parameters, and layout structures.
Sources: packages/hubspot-app/hsproject.json:1-5, packages/stripe-app/src/utils/constants.ts:1-6, apps/web/app/app.dub.co/marketplace/layout.tsx:1-19
Dub packages integrations with external platforms such as HubSpot and Stripe, providing standardized identifiers and environment properties for application exchange. The HubSpot project configuration establishes project names and platform execution versions, while Stripe application constants define client identifiers and host endpoints.
{
"name": "Dub",
"srcDir": "src",
"platformVersion": "2025.2"
}The external marketplace layout renders responsive integration directories with custom grid lines, header components, and integrated footers. Navigation resources organize documentation, company profiles, and updates into categorized resource columns.
const COLUMNS = [
{
heading: "Help and Support",
titles: ["Docs", "Help Center", "Contact"],
},
{
heading: "Company",
titles: ["About", "Careers", "Dub Brand"],
},
{
heading: "Updates",
titles: ["Blog", "Changelog"],
},
];Sources: apps/web/app/app.dub.co/marketplace/layout.tsx:1-34, packages/ui/src/nav/content/resources-content.tsx:10-23
Tip
The marketplace external layout includes MarketplaceExternalGridLines which draws fixed vertical border dividers constrained to a maximum container width of max-w-screen-xl with gradient masks starting at 96px.