Relevant source files
The following files were used as context for generating this wiki page:
- apps/web/middleware.ts
- apps/web/lib/middleware/link.ts
- apps/web/lib/middleware/partners.ts
- apps/web/lib/middleware/app.ts
- apps/web/app/ee/api/partners/links/route.ts
- apps/web/app/ee/api/track/application/route.ts
- apps/web/lib/middleware/admin.ts
- apps/web/app/ee/api/embed/referrals/links/route.ts
- apps/web/lib/middleware/workspaces.ts
- apps/web/app/ee/api/cron/domains/update/route.ts
- apps/web/app/api/tokens/embed/referrals/route.ts
- apps/web/app/ee/api/admin/impersonate/route.ts
- apps/web/lib/middleware/embed.ts
- apps/web/lib/middleware/utils/crawl-bitly.ts
- apps/web/lib/middleware/utils/parse.ts
- apps/web/ui/program-marketplace/external/marketplace-external-router.tsx
- apps/web/lib/middleware/utils/partners-redirect.ts
- apps/web/app/app.dub.co/marketplace/...segments/page.tsx
- apps/web/lib/middleware/api.ts
- apps/web/app/api/domains/search-availability/route.ts
- apps/web/ui/program-marketplace/marketplace-router.tsx
- apps/web/app/ee/api/admin/domains/search-availability/route.ts
- packages/utils/src/constants/middleware.ts
- apps/web/lib/middleware/utils/app-redirect.ts
- apps/web/app/app.dub.co/onboarding/onboarding/steps/program/page.tsx
- apps/web/app/wellknown/domain/file/route.ts
- apps/web/lib/middleware/create-link.ts
- apps/web/lib/middleware/new-link.ts
- apps/web/app/ee/api/embed/referrals/token/route.ts
- apps/web/lib/middleware/utils/is-top-level-settings-redirect.ts
Overview
Request routing in Dub is powered by a central edge middleware layer that intercepts incoming HTTP requests across all domains and subdomains, parses hostnames and paths, and delegates execution to specialized middleware handlers. This routing architecture solves multi-tenant separation, short link resolution, partner program routing, and administrative security enforcement at the edge before requests reach application routes. Key design decisions include executing lightweight path parsing and telemetry logging via Axiom prior to domain-specific branching, leveraging Redis caching and edge lookups for fast link redirection, and applying workspace context and authentication checks for platform apps. It interacts closely with edge caching layers, database lookups, and authentication guard utilities to ensure low-latency routing and secure access control across all core services.
Sources: apps/web/middleware.ts:34-88, apps/web/lib/middleware/utils/parse.ts:5-54
Central Dispatch and Request Parsing
Middleware Entry and Request Matching
The root middleware acts as the primary public entry point for incoming HTTP traffic, operating on the Node.js runtime environment defined in apps/web/middleware.ts. Its routing scope is governed by a Next.js matcher configuration that intercepts all requests while explicitly excluding application programming interface endpoints, Next.js internal assets, third-party proxies, and standard metadata files such as favicons, sitemaps, robots declarations, and web manifests.
export const config = {
runtime: "nodejs",
matcher: [
/*
* Match all paths except for:
* 1. /api/ routes
* 2. /_next/ (Next.js internals)
* 3. /_proxy/ (proxies for third-party services)
* 4. Metadata files: favicon.ico, sitemap.xml, robots.txt, manifest.webmanifest
*/
"/((?!api/|_next/|_proxy/|favicon.ico|sitemap.xml|robots.txt|manifest.webmanifest).*)",
],
};Sources: apps/web/middleware.ts:20-32
Upon receiving a request, the middleware immediately invokes telemetry collection through Axiom by transforming the request and dispatching asynchronous log flushes via ev.waitUntil() before evaluating branch logic.
// Axiom logging
logger.info(...transformMiddlewareRequest(req));
ev.waitUntil(logger.flush());Sources: apps/web/middleware.ts:37-40
Hostname Normalization and Parsing Utility
The parse utility function in apps/web/lib/middleware/utils/parse.ts extracts and normalizes core routing properties from the incoming NextRequest. It retrieves the host header, strips any leading www. prefix, converts the domain string to lowercase, and checks for domain-level mappings defined in DOMAIN_REDIRECTS.
export const parse = (req: NextRequest) => {
let domain = req.headers.get("host") as string;
// path is the path of the URL (e.g. dub.sh/stats/github -> /stats/github)
let path = req.nextUrl.pathname;
// remove www. from domain and convert to lowercase
domain = domain.replace(/^www./, "").toLowerCase();
if (DOMAIN_REDIRECTS[domain]) {
domain = DOMAIN_REDIRECTS[domain];
}Sources: apps/web/lib/middleware/utils/parse.ts:1-15
Note
End-to-end test request detection inspects local development hostnames (dub.localhost:8888) as well as Vercel preview environments where the x-e2e-redirect-test header is set to true with an application hostname. When matched, case-sensitive tests override the domain to dub-internal-test.com, while general preview requests resolve to the standard SHORT_DOMAIN.
Sources: apps/web/lib/middleware/utils/parse.ts:17-34
Path components and search parameters are subsequently compiled into a structured routing context object. The primary link key and multi-level subpaths (fullKey) are decoded using decodeURIComponent to accommodate internationalized character sets such as Hebrew and Korean.
// fullPath is the full URL path (along with search params)
const searchParams = req.nextUrl.searchParams.toString();
const searchParamsObj = Object.fromEntries(req.nextUrl.searchParams);
const searchParamsString = searchParams.length > 0 ? `?${searchParams}` : "";
const fullPath = `${path}${searchParamsString}`;
// Here, we are using decodeURIComponent to handle foreign languages like Hebrew and Korean
const key = decodeURIComponent(path.split("/")[1]); // key is the first part of the path (e.g. dub.sh/stats/github -> stats)
const fullKey = decodeURIComponent(path.slice(1)); // fullKey is the full path without the first slash (to account for multi-level subpaths, e.g. d.to/github/repo -> github/repo)
return {
domain,
path,
fullPath,
key,
fullKey,
shortLink: `https://${domain}/${fullKey}`,
searchParamsObj,
searchParamsString,
};
};Sources: apps/web/lib/middleware/utils/parse.ts:35-54
Dispatch Execution Flow
Once parsing concludes in apps/web/middleware.ts, the request is routed through a prioritized sequence of domain checks and path conditions before delegating to specialized handlers.
Sources: apps/web/middleware.ts:34-88
Application and Workspace Subdomain Routing
App Middleware Request Validation
Requests arriving at application domains pass through AppMiddleware in apps/web/lib/middleware/app.ts. The middleware first checks if the path matches /embed, immediately delegating to EmbedMiddleware. Otherwise, it identifies the authenticated user via getUserViaToken(req). If no user is present and the requested path is not public—such as /marketplace, paths starting with /share/, /deeplink/, /unsubscribe/, or /auth/reset-password/—the request redirects to /login with a ?next= query parameter preserving the original destination.
const isPublicPath = (path: string) =>
["/marketplace"].some((p) => path === p) ||
[
"/marketplace/",
"/share/",
"/deeplink/",
"/unsubscribe/",
"/auth/reset-password/",
].some((p) => path.startsWith(p));Sources: apps/web/lib/middleware/app.ts:16-24
For authenticated users accessing non-public paths, visiting /new invokes NewLinkMiddleware. Users within their initial onboarding window—evaluated via ONBOARDING_WINDOW_SECONDS and cached in onboardingStepCache—undergo onboarding flow validation unless they visit /onboarding, /account, or /workspaces, or possess a default workspace or pending workspace invites.
Note
If an onboarding user already possesses a default workspace, the workspace onboarding step is automatically bypassed and advanced to link.
Sources: apps/web/lib/middleware/app.ts:65-91
Workspace Resolution and Path Mapping
When standard users request root or core application paths, WorkspacesMiddleware resolves their active context. It inspects query parameters for a validated next redirect path using isValidInternalRedirect. If the user has a default workspace retrieved via getDefaultWorkspace(user), the middleware rewrites top-level application views and settings routes into workspace-scoped paths.
| Target Path Pattern | Route Condition | Resolved Workspace Destination |
|---|---|---|
| Root & Auth | ["/", "/login", "/register"].includes(path) | /[defaultWorkspace][product] (derived via getWorkspaceProduct) |
| Top-Level Settings | isTopLevelSettingsRedirect(path) | /[defaultWorkspace]/settings/[path] |
| Standard Pages | /links, /analytics, /events, /settings, etc. | /[defaultWorkspace]/[path] |
Sources: apps/web/lib/middleware/workspaces.ts:24-46
Warning
Top-level settings redirects are strictly governed by isTopLevelSettingsRedirect, matching /domains, /integrations, /webhooks, and their respective child subpaths.
Sources: apps/web/lib/middleware/utils/is-top-level-settings-redirect.ts:1-8
If no default workspace exists, WorkspacesMiddleware queries prismaEdge.projectInvite for matching user email records. If a pending project invite exists, the user redirects to /[projectSlug]/invite; otherwise, they route to /onboarding/workspace.
const projectInvite = await prismaEdge.projectInvite.findFirst({
where: {
email: user.email,
},
select: {
project: {
select: {
slug: true,
},
},
},
});
if (projectInvite) {
return NextResponse.redirect(
new URL(`/${projectInvite.project.slug}/invite`, req.url),
);
}
return NextResponse.redirect(new URL("/onboarding/workspace", req.url));Sources: apps/web/lib/middleware/workspaces.ts:48-70
Legacy Path Redirection
The appRedirect utility normalizes legacy URLs, account paths, and restructured partner program routes before rendering. Except for excluded paths like /workspaces, legacy endpoints map to their updated counterparts.
const EXCLUDED_PAGES = ["/workspaces"];
const APP_REDIRECTS = {
"/account": "/account/settings",
"/referrals": "/account/settings/referrals",
"/onboarding": "/onboarding/welcome",
"/welcome": "/onboarding/welcome",
"/campaigns": "/program/campaigns",
"/messages": "/program/messages",
"/fraud": "/program/risks",
"/risks": "/program/risks",
};Sources: apps/web/lib/middleware/utils/app-redirect.ts:1-15
Additional regex patterns handle dynamic workspace product paths, upgrading billing routes (/upgrade to /settings/billing/upgrade), tracking guides, and migrating pluralized program routes (/programs/* to /program/*). Unmatched requests that bypass these rules fall through to a rewrite against app.dub.co.
Sources: apps/web/lib/middleware/app.ts:128-130, apps/web/lib/middleware/utils/app-redirect.ts:37-125
Partner Portal and Marketplace Routing
Partner Portal Authentication and Redirection Middleware
The partner portal middleware (PartnersMiddleware) manages request flow for partner domains, enforcing authentication on restricted paths such as /programs, /marketplace, /onboarding, /settings, /profile, /messages, /payouts, /account, /invite, and /rewind. When unauthenticated users request these paths, the middleware intercepts them. If the path begins with /programs/, it extracts the program slug to redirect to /[programSlug]/login; otherwise, it redirects to /login with an encoded ?next= parameter containing the original path.
const AUTHENTICATED_PATHS = [
"/programs",
"/marketplace",
"/onboarding",
"/settings",
"/profile",
"/messages",
"/payouts",
"/account",
"/invite",
"/rewind",
];Sources: apps/web/lib/middleware/partners.ts:12-23
For authenticated users, the middleware verifies whether a default partner ID exists via getDefaultPartnerId(user). If no partner ID is found, and the path is not a partner invite, onboarding, or account route, the user redirects to /onboarding. Furthermore, standard redirects map deprecated or legacy partner paths to their current equivalents.
| Legacy / Source Path Pattern | Target Destination |
|---|---|
/settings | /profile |
/settings/payouts | /payouts |
/settings/notifications | /profile/notifications |
/account/settings/notifications | /profile/notifications |
/profile/sites | /profile |
/rewind | /rewind/2025 |
/onboarding/online-presence | /onboarding/platforms |
/onboarding/verify | /onboarding/payouts |
Sources: apps/web/lib/middleware/utils/partners-redirect.ts:1-10
Warning
When handling ?next= query parameters for authenticated users, the middleware validates destinations using isValidInternalRedirect to prevent open redirect vulnerabilities, explicitly omitting /onboarding paths from automatic immediate redirection to ensure prerequisite setup steps are completed.
Sources: apps/web/lib/middleware/partners.ts:91-102
Program Slug Aliasing and Marketplace Segments
The partners utility routing layer handles structural transformations for partner programs and marketplace categories. Specifically, it maps legacy program identifiers to their current canonical slugs, normalizing references during redirection.
| Program Alias | Canonical Slug |
|---|---|
florafauna | flora |
ship-30 | dwp |
supercutai | supercut |
teller-perps | teller |
hundred-health | hundred |
gitroom-short | postiz |
getviktor-com | viktor |
getviktor | viktor |
heynavii | navii |
galaxyai | magica |
voice-os | voiceos |
speechify-inc | speechifyai |
ggms-labs-ltd-ai | mira |
missioncontrolhq | squad-so |
nozomio | folk |
base | coinbasewallet |
Sources: apps/web/lib/middleware/utils/partners-redirect.ts:36-53
Marketplace paths under app/app.dub.co/marketplace/[[...segments]]/page.tsx parse segment parameters to render either internal workspace views via MarketplaceRouter or external views via MarketplaceExternalRouter. The external router processes segments to determine layout rendering based on path depth and prefix matching.
Sources: apps/web/ui/program-marketplace/external/marketplace-external-router.tsx:7-37
Link Redirect and Crawler Delegation
Short Link Interception and Resolution
Incoming short link requests are intercepted at the edge within LinkMiddleware (detailed further in Link Dispatch), where the request's hostname and path are parsed. The system handles case sensitivity by checking isCaseSensitiveDomain(domain), converting keys to lowercase if the domain is case-insensitive, and performing Punycode encoding via punyEncode. Inspect mode, triggered by a trailing + character on the key, is stripped for lookup and later routes the request to an inspection view if the link lacks password protection. Requests with unsupported keys, such as .php paths prone to high bot traffic, receive an immediate 302 redirect to the root domain accompanied by X-Robots-Tag: googlebot: noindex and a dub-no-track parameter.
if (isUnsupportedKey(key)) {
return NextResponse.redirect(new URL("/?dub-no-track=1", req.url), {
headers: {
...DUB_HEADERS,
"X-Robots-Tag": "googlebot: noindex",
},
status: 302,
});
}Sources: apps/web/lib/middleware/link.ts:77-87
When a link key evaluates to an empty string, it is normalized to _root to service root domain links. The middleware first queries Redis via linkCache.get({ domain, key }) (documented in Caching & Rate Limiting). If the cache lookup misses, it falls back to PlanetScale using getLinkViaEdge({ domain, key }) (see Database Architecture). If the link is still missing and the domain is specifically buff.ly, the router delegates control to the Bitly migration handler. Otherwise, it issues a rewrite to /[domain]/notfound with static page caching headers configured for a s-maxage of 86400 seconds.
if (!cachedLink) {
let linkData = await getLinkViaEdge({
domain,
key,
});
if (!linkData) {
if (domain === "buff.ly") {
return await crawlBitly(req, ev);
}
return NextResponse.rewrite(new URL(`/${domain}/notfound`, req.url), {
headers: {
...DUB_HEADERS,
...STATIC_PAGES_CACHE_HEADERS,
},
});
}Sources: apps/web/lib/middleware/link.ts:100-117
Note
During Redis failover conditions, linkCache.get flags redisFailOver as true. The middleware automatically bypasses click tracking and click ID cookie lookups to protect downstream response latencies from timing out.
Sources: apps/web/lib/middleware/link.ts:89-99, apps/web/lib/middleware/link.ts:193-211
Bitly Migration Hooks and Crawler Delegation
When short link resolution encounters an unknown key on the buff.ly domain, crawlBitly executes a programmatic crawl against the Bitly API to fetch the backing long URL on-demand. Before querying Bitly, the handler validates that the key does not contain restricted Bitly characters matching the regular expression invalidBitlyKeyRegex.
// bitly doesn't support the following characters: ` ~ , . < > ; ‘ : " / \ [ ] ^ { } ( ) = + ! * @ & $ £ ? % # |
// @see: https://support.bitly.com/hc/en-us/articles/360030780892-What-characters-are-supported-when-customizing-links
const invalidBitlyKeyRegex = /[`~,.<>;':"/\\[\]^{}()=+!*@&$£?%#|]/;
if (key && !invalidBitlyKeyRegex.test(key)) {
const link = await fetchBitlyLink({ domain, key });Sources: apps/web/lib/middleware/utils/crawl-bitly.ts:23-28
If a valid Bitly link object is returned, the long URL is sanitized using getUrlFromStringIfValid. The middleware then asynchronously provisions a new link record inside Prisma tied to a designated Buffer workspace, user, and folder, while concurrently recording the link and publishing a usage event via Upstash Redis streams. Finally, it redirects the client to the resolved long URL with a 302 status. If the Bitly API encounters a rate limit or returns no record, the utility defaults to a 302 redirection toward https://buffer.com with static CDN caching headers.
| Constant Name | Identifier / Value | Purpose |
|---|---|---|
BUFFER_WORKSPACE_ID | cm05wnnpo000711ztj05wwdbu | Target workspace assignment for crawled Bitly links |
BUFFER_USER_ID | cm05wnd49000411ztg2xbup0i | Creator user association for on-demand links |
BUFFER_FOLDER_ID | fold_1JNQBVZV8P0NA0YGB11W2HHSQ | Default folder placement for migrated links |
BUFFER_BITLY_API_KEY | process.env.BUFFER_BITLY_API_KEY | Bearer token credential for Bitly v4 API requests |
Sources: apps/web/lib/middleware/utils/crawl-bitly.ts:15-18, apps/web/lib/middleware/utils/crawl-bitly.ts:45-50
Well-Known Path Routing
Domain configuration files required by native applications and deep-linking services are resolved dynamically via the well-known route handler at app/wellknown/[domain]/[file]/route.ts. The route accepts requests parameterized by a domain slug and a file name, validating the requested file against a whitelist of supported files.
if (!supportedWellKnownFiles.includes(file)) {
return NextResponse.json({ error: "File not supported" }, { status: 400 });
}Sources: apps/web/app/wellknown/domain/file/route.ts:16-18
The handler queries PlanetScale for the domain's corresponding appleAppSiteAssociation and assetLinks fields. Depending on the requested file identifier, it constructs a JSON payload containing either Apple app site association data with default empty app links structure or Android asset links configuration, returning the response with public Vercel CDN caching headers.
let response: WellKnownConfig[SupportedWellKnownFiles];
switch (file) {
case "apple-app-site-association":
response =
(appleAppSiteAssociation as WellKnownConfig["apple-app-site-association"]) || {
applinks: {
apps: [],
details: [],
},
};
break;
case "assetlinks.json":
response = (assetLinks as WellKnownConfig["assetlinks.json"]) || [];
break;
}Sources: apps/web/app/wellknown/domain/file/route.ts:34-48
Administrative Path Access Control
Middleware-Level Gatekeeping and Session Verification
The AdminMiddleware function guards internal moderation and tools in Admin Dashboard by intercepting incoming requests, parsing path parameters, and validating session tokens via getUserViaToken (integrated with Authentication). If no authenticated user session exists and the target path differs from /login, the request receives a 302 redirect toward /login. The file apps/web/app/ee/api/admin/impersonate/route.ts provides user and workspace lookup capabilities for the admin portal, documented in Admin Dashboard.
export async function AdminMiddleware(req: NextRequest) {
const { path } = parse(req);
const user = await getUserViaToken(req);
if (!user && path !== "/login") {
return NextResponse.redirect(new URL("/login", req.url));Sources: apps/web/lib/middleware/admin.ts:8-14
When an authenticated user session is present, the middleware queries prismaEdge.projectUsers for a matching record where the user ID corresponds to the session and the project ID equals DUB_WORKSPACE_ID. If the user is absent from the designated administrative workspace, the handler passes the request through via NextResponse.next(), which surfaces a 404 error page.
} else if (user) {
const isAdminUser = await prismaEdge.projectUsers.findUnique({
where: {
userId_projectId: {
userId: user.id,
projectId: DUB_WORKSPACE_ID,
},
},
});
if (!isAdminUser) {
return NextResponse.next(); // throw 404 pageSources: apps/web/lib/middleware/admin.ts:15-26
Caution
Returning NextResponse.next() for non-workspace users intentionally triggers a 404 Not Found response rather than a 403 Forbidden error, preventing information disclosure regarding the existence of administrative endpoints.
Sources: apps/web/lib/middleware/admin.ts:25-26
Route-Level Authorization Guards
For workspace-verified administrators, further route-level validation applies. If the requested path is /login or fails the canAccessAdminPath guard check, the middleware redirects the client to the root URL /.
} else if (
path === "/login" ||
!canAccessAdminPath({ userId: user.id, pathname: path })
) {
return NextResponse.redirect(new URL("/", req.url));
}
}Sources: apps/web/lib/middleware/admin.ts:27-33
If all authorization checks pass, the middleware routes the request internally by rewriting the destination URL to the internal administrative subdomain admin.dub.co, preserving or normalizing the trailing path segments.
return NextResponse.rewrite(
new URL(`/admin.dub.co${path === "/" ? "" : path}`, req.url),
);Sources: apps/web/lib/middleware/admin.ts:35-37
Embedded Components and Quick-Action Handlers
Standalone Link Creators and Widget Embeds
Requests targeting embedded widgets, quick-action link creators, and referral portals are processed through distinct middleware and API routes. The EmbedMiddleware function parses incoming requests and inspects their path or search parameters. If a request begins with /embed/support-chat or includes a token search parameter, it rewrites the destination to the primary application domain at /app.dub.co. Requests lacking these valid parameters are redirected to the root URL /.
export function EmbedMiddleware(req: NextRequest) {
const { path, searchParamsObj, fullPath } = parse(req);
if (path.startsWith("/embed/support-chat")) {
return NextResponse.rewrite(new URL(`/app.dub.co${fullPath}`, req.url));
}
if (searchParamsObj.token) {
return NextResponse.rewrite(new URL(`/app.dub.co${fullPath}`, req.url));
}
// TODO: Show token expiry page
return NextResponse.redirect(new URL("/", req.url));Sources: apps/web/lib/middleware/embed.ts:4-16
Shortcuts that instantiate link generation from external contexts rely on the CreateLinkMiddleware. This utility extracts the domain and full path from the request, decodes the target URL string, and redirects the client to the /new endpoint on the application domain with link and domain query parameters attached.
export function CreateLinkMiddleware(req: NextRequest) {
const { domain, fullPath } = parse(req);
const url = getUrlFromString(fullPath.slice(1));
const redirectURL = new URL(`${APP_DOMAIN}/new`);
redirectURL.searchParams.append("link", url);
redirectURL.searchParams.append("domain", domain);
return NextResponse.redirect(redirectURL.toString());Sources: apps/web/lib/middleware/create-link.ts:5-14
Once a user interacts with the link creation shortcut, NewLinkMiddleware resolves the user's default workspace using getDefaultWorkspace. If a workspace exists, it redirects the client to the workspace's link management page with query arguments passing the link parameters forward; otherwise, it directs unassigned users to the workspace creation flow.
export async function NewLinkMiddleware(req: NextRequest, user: UserProps) {
const { fullPath } = parse(req);
const defaultWorkspace = await getDefaultWorkspace(user);
const searchParams = new URL(fullPath, APP_DOMAIN).searchParams;
if (defaultWorkspace) {
return NextResponse.redirect(
new URL(
`/${defaultWorkspace}/links?newLink=${searchParams.get("link") || true}${searchParams.has("domain") ? `&newLinkDomain=${searchParams.get("domain")}` : ""}`,
req.url,
),
);
} else {
return NextResponse.redirect(
new URL(`/workspaces?newWorkspace=true`, req.url),
);
}Sources: apps/web/lib/middleware/new-link.ts:7-25
Note
NewLinkMiddleware validates the presence of an active user workspace before constructing redirect URLs, ensuring users without a designated default workspace are forced through the /workspaces?newWorkspace=true onboarding path.
Sources: apps/web/lib/middleware/new-link.ts:14-25
Referral Widget API Routes
Referral program embeds interact with authenticated backend handlers to retrieve and provision tracking links. The endpoint at /api/embed/referrals/links uses withReferralsEmbedToken to govern both read and write operations.
| HTTP Method | Route Handler | Authentication / Guard | Action & Validation |
|---|---|---|---|
GET | /api/embed/referrals/links | withReferralsEmbedToken | Parses and returns existing partner links via ReferralsEmbedLinkSchema. |
POST | /api/embed/referrals/links | withReferralsEmbedToken | Verifies active enrollment status, checks link count limits against group.maxPartnerLinks, and processes new links. |
GET | /api/embed/referrals/token | withReferralsEmbedToken | Returns the validated active embed token object. |
POST | /api/tokens/embed/referrals | withWorkspace (Required: links.write, advanced/enterprise) | Creates or retrieves program enrollment and provisions a new referrals embed token. |
Sources: apps/web/app/ee/api/embed/referrals/links/route.ts:18-158, apps/web/app/ee/api/embed/referrals/token/route.ts:4-7, apps/web/app/api/tokens/embed/referrals/route.ts:15-121
When creating partner links via the embed referral POST handler, the system ensures the partner's enrollment status is included in ACTIVE_ENROLLMENT_STATUSES and that the requested URL satisfies group constraints via validatePartnerLinkUrl.
if (!ACTIVE_ENROLLMENT_STATUSES.includes(programEnrollment.status)) {
throw new DubApiError({
code: "forbidden",
message:
"You cannot create links in this program because your enrollment is not active.",
});
}
const { url, key } = createPartnerLinkSchema
.pick({ url: true, key: true })
.parse(await parseRequestBody(req));
if (!program.domain || !program.url) {
throw new DubApiError({
code: "bad_request",
message:
"This program needs a domain and URL set before creating a link.",
});
}
if (links.length >= group.maxPartnerLinks) {
throw new DubApiError({
code: "bad_request",
message: `You have reached the limit of ${group.maxPartnerLinks} program links.`,
});
}
validatePartnerLinkUrl({ group, url });
Sources: apps/web/app/ee/api/embed/referrals/links/route.ts:28-56
Related
On This Page
OverviewCentral Dispatch and Request ParsingMiddleware Entry and Request MatchingHostname Normalization and Parsing UtilityDispatch Execution FlowApplication and Workspace Subdomain RoutingApp Middleware Request ValidationWorkspace Resolution and Path MappingLegacy Path RedirectionPartner Portal and Marketplace RoutingPartner Portal Authentication and Redirection MiddlewareProgram Slug Aliasing and Marketplace SegmentsLink Redirect and Crawler DelegationShort Link Interception and ResolutionBitly Migration Hooks and Crawler DelegationWell-Known Path RoutingAdministrative Path Access ControlMiddleware-Level Gatekeeping and Session VerificationRoute-Level Authorization GuardsEmbedded Components and Quick-Action HandlersStandalone Link Creators and Widget EmbedsReferral Widget API RoutesRelated