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:
Embeddable Referral Widgets provide a portable interface that allows host applications to integrate partner program dashboards directly into their user settings or interface via secure iframe encapsulation. The system addresses the challenge of securely exposing affiliate marketing tools, link tracking stats, and reward management outside the primary platform without sacrificing authentication or data integrity. Key architectural decisions include server-side token generation backed by Upstash Redis persistence, client SDK DOM injection with automated iframe sizing via postMessage synchronization, and streamlined React wrapper integration. Adjacent components such as middleware routing, enrollment verification, and centralized API endpoints coordinate to hydrate partner context and deliver seamless navigation across reward management and payout configurations.
Sources: packages/embeds/core/src/core.ts:5-103, packages/embeds/react/src/embed.tsx:1-40, apps/web/app/api/tokens/embed/referrals/route.ts:16-122, apps/web/lib/embed/referrals/token-class.ts:13-33
The client SDK architecture coordinates core DOM manipulation logic and React wrapper integration to mount referral widgets securely inside host applications. The initialization sequence bridges vanilla TypeScript modules with React component lifecycles using reference hooks and dynamic element creation.
When a host application mounts an embed, initialization executes a strict call sequence: DubEmbed component renders → DubEmbedInner fires useEffect → init() instantiates DubEmbed → renderEmbed() creates or reuses the DOM container → createIframe() builds the iframe element with URL parameters.
Warning
If token is omitted during initialization, renderEmbed logs an error to the console and immediately returns null, preventing the container and iframe from mounting.
Sources: packages/embeds/core/src/core.ts:36-39
The core SDK accepts explicit configuration options and listens for specific incoming message events from the hosted iframe via window.addEventListener.
The React integration layer wraps the core imperative SDK inside declarative components. The DubEmbed component uses memo and delegates to DubEmbedInner, which assigns a unique identifier via useId(), sets up a useRef<HTMLDivElement>, and runs an useEffect hook that triggers init() and cleanup via destroy().
export const DubEmbed = memo(
({ token, data, options, ...rest }: DubEmbedProps) => (
<DubEmbedInner options={{ ...options, token, data }} {...rest} />
),
);Note
The useEffect dependency array serializes options using JSON.stringify(options) alongside the React useId() value to safely re-initialize the embed when configuration properties change without incurring reference equality bugs.
Sources: packages/embeds/react/src/embed.tsx:37-37
The token generation and authentication subsystem governs how referral embed tokens are created, persisted in Upstash Redis, validated by server-side middleware, and used to authorize requests against program enrollments. The lifecycle begins when an authenticated client requests a token through either the workspace-scoped API (POST /api/tokens/embed/referrals), user-level onboarding routes (GET /api/user/referrals-token), or specialized authentication wrappers (withReferralsEmbedToken).
Sources: apps/web/app/api/tokens/embed/referrals/route.ts:16-122, apps/web/app/api/user/referrals-token/route.ts:11-74, apps/web/lib/embed/referrals/auth.ts:33-134
The ReferralsEmbedToken class handles the creation and retrieval of public tokens. Tokens are generated using a unique identifier prefixed with EMBED_PUBLIC_TOKEN_PREFIX and persisted inside Upstash Redis with a strict time-to-live (EMBED_PUBLIC_TOKEN_EXPIRY).
class ReferralsEmbedToken {
async create(props: ReferralsEmbedTokenProps) {
const publicToken = createId({
prefix: EMBED_PUBLIC_TOKEN_PREFIX,
});
await redis.set(publicToken, JSON.stringify(props), {
ex: EMBED_PUBLIC_TOKEN_EXPIRY,
nx: true,
});
return {
publicToken,
expires: new Date(Date.now() + EMBED_PUBLIC_TOKEN_EXPIRY * 1000),
};
}
async get(token: string) {
return await redis.get<ReferralsEmbedTokenProps>(token);
}
}Note
The nx: true option in redis.set ensures that token creation fails if a key collision occurs, guaranteeing uniqueness across generated embed tokens.
Sources: apps/web/lib/embed/referrals/token-class.ts:19-22
The withReferralsEmbedToken wrapper secures API routes by extracting the bearer token from the Authorization header, validating it against Redis, enforcing rate limits, and fetching the associated program enrollment from the database.
Embed routes and token parameters are governed by EmbedMiddleware. Incoming requests are inspected for query parameters or support paths, and rewritten to internal application endpoints or redirected accordingly.
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));
}
return NextResponse.redirect(new URL("/", req.url));
}Sources: apps/web/lib/embed/referrals/token-class.ts:19-31, apps/web/lib/embed/referrals/auth.ts:48-82
The dynamic resizing and cross-window messaging subsystem synchronizes iframe dimensions between the embedded widget application and the host page. It relies on a bidirectional window.postMessage protocol combined with a ResizeObserver running inside the iframe document. When content changes inside the widget, height updates are dispatched to the parent window, which resizes the host container element accordingly.
Sources: packages/embeds/core/src/core.ts:68-90, apps/web/app/ee/app.dub.co/embed/referrals/dynamic-height-messenger.tsx:5-27
When the DubEmbed core initializes an embed, it constructs an iframe URL appending parameters such as token, theme, themeOptions, and sets dynamicHeight: "true" to signal support for dynamic resizing scripts.
const createIframe = (
iframeUrl: string,
token: string,
options: Pick<DubEmbedOptions, "theme" | "themeOptions">,
): HTMLIFrameElement => {
const iframe = document.createElement("iframe");
const params = new URLSearchParams({
token,
...(options.theme ? { theme: options.theme } : {}),
...(options.themeOptions
? { themeOptions: JSON.stringify(options.themeOptions) }
: {}),
// Allows the iframe content to set overflow values and send height messages without affecting older embed scripts
dynamicHeight: "true",
});
iframe.src = `${iframeUrl}?${params.toString()}`;
iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "none";
iframe.setAttribute("credentialssupport", "");
iframe.setAttribute("allow", "clipboard-write");
return iframe;
};Inside the referral widget and support chat applications, DynamicHeightMessenger and SupportChatDynamicHeightMessenger lock the document body overflow to hidden and instantiate a ResizeObserver monitoring document.body. Every time the body dimensions shift, update() calculates document.body.scrollHeight and transmits a PAGE_HEIGHT message via parent.postMessage.
export function DynamicHeightMessenger() {
useEffect(() => {
document.body.style.overflow = "hidden";
const update = () => {
const height = document.body.scrollHeight;
parent.postMessage(
{
originator: "Dub",
event: "PAGE_HEIGHT",
data: { height },
},
"*",
);
};
update();
const resizeObserver = new ResizeObserver(update);
resizeObserver.observe(document.body);
return () => {
resizeObserver.disconnect();
};
}, []);
return false;
}Note
Both referral embeddings and support chat implementations use identical messenger mechanics, ensuring uniform behavior across distinct embedded components. Sources: apps/web/app/app.dub.co/embed/support-chat/dynamic-height-messenger.tsx:5-28
The host page registers a message event listener on window inside DubEmbed.renderEmbed(). When an incoming message with event: "PAGE_HEIGHT" arrives, it targets the container element by DUB_CONTAINER_ID and updates its CSS height property to match the reported scroll height.
// Listen the message from the iframe
window.addEventListener("message", (e) => {
const { data, event } = e.data as IframeMessage;
console.debug("[Dub] Iframe message", data);
switch (event) {
case "ERROR":
onError?.(
new EmbedError({
code: data?.code ?? "",
message: data?.message ?? "",
}),
);
break;
case "PAGE_HEIGHT": {
const container = document.getElementById(DUB_CONTAINER_ID);
if (container) container.style.height = `${data.height}px`;
break;
}
}
});The referral embed server-side entry point processes incoming requests inside Next.js Server Components, extracts query search parameters including token, themeOptions, and dynamicHeight, and hydrates the page client context. When the ReferralsEmbedPage component loads, it wraps execution in a Suspense boundary utilizing EmbedInlineLoading as a fallback, while ReferralsEmbedRSC coordinates data fetching via getReferralsEmbedData(token) before mounting ReferralsEmbedPageClient.
The getReferralsEmbedData asynchronous function validates the incoming token using referralsEmbedToken.get(token). If either programId or partnerId is missing, it triggers a notFound() response. It subsequently calls getProgramEnrollmentOrThrow to load the partner enrollment record with nested relations including partner platforms, program metadata, links with associated link rewards, click rewards, lead rewards, sale rewards, referral rewards, custom rewards, discounts, and partner groups.
export const getReferralsEmbedData = async (token: string) => {
const { programId, partnerId } = (await referralsEmbedToken.get(token)) ?? {};
if (!programId || !partnerId) {
notFound();
}
const programEnrollment = await getProgramEnrollmentOrThrow({
partnerId,
programId,
include: {
partner: {
select: {
id: true,
name: true,
email: true,
username: true,
country: true,
tremendousEmail: true,
defaultPayoutMethod: true,
platforms: {
select: {
type: true,
identifier: true,
verifiedAt: true,
},
},
},
},
program: {
select: {
id: true,
name: true,
slug: true,
domain: true,
defaultGroupId: true,
minPayoutAmount: true,
termsUrl: true,
embedData: true,
resources: true,
},
},
links: {
include: {
linkReward: {
include: {
clickReward: true,
leadReward: true,
saleReward: true,
discount: true,
},
},
},
},
partnerGroup: true,
clickReward: true,
leadReward: true,
saleReward: true,
referralReward: true,
customReward: true,
discount: true,
programPartnerTags: {
select: {
partnerTagId: true,
},
},
},
});
if (!programEnrollment || !programEnrollment.partnerGroup) {
notFound();
}
...Warning
If a partner attempts to load an embed widget for a program where their enrollment is missing or inactive, getProgramEnrollmentOrThrow and subsequent null checks will throw or trigger a 404 error, preventing unauthorized rendering.
Sources: apps/web/app/ee/app.dub.co/embed/referrals/get-referrals-embed-data.ts:17-19, apps/web/app/ee/app.dub.co/embed/referrals/get-referrals-embed-data.ts:83-85
Concurrently with partner bounty checks via getBountiesForPartner, getReferralsEmbedData queries the database for Commission records grouped by status, filtering for records with earnings: { gt: 0 } for the specific programId and partnerId. It aggregates partner link stats using aggregatePartnerLinksStats(links).
const { totalClicks, totalLeads, totalConversions } =
aggregatePartnerLinksStats(links);
const [commissions, bounties] = await Promise.all([
prisma.commission.groupBy({
by: ["status"],
_sum: {
earnings: true,
},
_count: {
id: true,
},
where: {
earnings: {
gt: 0,
},
programId,
partnerId,
},
}),
getBountiesForPartner(programEnrollment),
]);Once data is returned from the server component, ReferralsEmbedPageClient parses resource and embed schemas, evaluates whether the partner has active embed access via ACTIVE_ENROLLMENT_STATUSES, and renders either an unapproved fallback view or the full dashboard wrapped inside ReferralsEmbedDataProvider.
export const ReferralsReferralsEmbedToken = () => {
const token = useEmbedToken();
const { error } = useSWR<{ token: number }>(
"/api/embed/referrals/token",
(url) =>
fetcher(url, {
headers: {
Authorization: `Bearer ${token}`,
},
}),
{
revalidateOnFocus: true,
dedupingInterval: 30000,
keepPreviousData: true,
},
);
// Inform the parent if there's an error (Eg: token is expired)
useEffect(() => {
if (error) {
window.parent.postMessage(
{
originator: "Dub",
event: "ERROR",
data: error.info,
},
"*",
);
}
}, [error]);
return null;
};Referral management and payout UI within embedded widgets enables partners to generate affiliate links, view activity metrics, explore bounties, select payout methods, and view program FAQs. The host dashboard integrates these views by mounting client entrypoints like ReferralsPageClient, which verifies public embed tokens via SWR or renders empty states when tokens are missing.
Sources: apps/web/app/app.dub.co/dashboard/account/settings/referrals/page-client.tsx:11-49
The link management UI toggles between the links list view (ReferralsEmbedLinksList) and the creation/update form (ReferralsEmbedCreateUpdateLink). Partners can manage their custom referral URLs using construct utilities.
Sources: apps/web/app/ee/app.dub.co/embed/referrals/links.tsx:6-31, apps/web/app/ee/app.dub.co/embed/referrals/quickstart.tsx:1
The ReferralsEmbedActivity component renders aggregate performance metrics for clicks, leads, and conversions. When statistics are non-zero, it fetches composite analytics via SWR with a 1-year annual interval and composites timeseries data.
Sources: apps/web/app/ee/app.dub.co/embed/referrals/activity.tsx:10-40
const analyticsSearchParams = new URLSearchParams({
event: "composite",
groupBy: "timeseries",
interval: "1y",
saleType: "new",
});
const { data: analytics } = useSWR<AnalyticsTimeseries[]>(
!isEmpty &&
`/api/embed/referrals/analytics?${analyticsSearchParams.toString()}`,
(url) =>
fetcher(url, {
headers: {
Authorization: `Bearer ${token}`,
},
}),
{
keepPreviousData: true,
dedupingInterval: 60000,
},
);Partners configure payout destinations through ReferralsEmbedSettings, which supports two primary payout methods: Tremendous gift cards and direct cash registrations.
Sources: apps/web/app/ee/app.dub.co/embed/referrals/settings.tsx:506-546
Sources: apps/web/app/ee/app.dub.co/embed/referrals/settings.tsx:6-7, apps/web/app/ee/app.dub.co/embed/referrals/settings.tsx:246-277, apps/web/app/ee/app.dub.co/embed/referrals/settings.tsx:509-543
Warning
Payout methods are mutually exclusive and permanent. Once a partner connects a default payout method (partner.defaultPayoutMethod), any other payout option is disabled with a tooltip explaining that multiple methods cannot be combined, and existing methods cannot be changed through the widget interface.
Sources: apps/web/app/ee/app.dub.co/embed/referrals/settings.tsx:355-360, apps/web/app/ee/app.dub.co/embed/referrals/settings.tsx:521-523
The referrals UI also surfaces competitive program incentives via ReferralsEmbedBounties, allowing partners to view individual bounties, track completion periods, and inspect program details. Program FAQs render dynamic reward commission calculations using constructRewardAmount alongside embedded schemas.
Sources: apps/web/app/ee/app.dub.co/embed/referrals/bounties/index.tsx:16-102, apps/web/app/ee/app.dub.co/embed/referrals/faq.tsx:14-26