---
title: "Program Marketplace"
description: "The Program Marketplace serves as Dub's centralized directory for discovering, exploring, and applying to network affiliate and referral programs. It connects prospective affiliates with top SaaS p..."
last_updated: "2026-10-12T07:00:10.092093+00:00"
canonical_url: "https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/partner-programs/program-marketplace"
---

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

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

- [apps/web/app/ee/api/cron/network/calculate-program-similarities/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/route.ts)
- [packages/ui/src/nav/content/program-marketplace.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/packages/ui/src/nav/content/program-marketplace.tsx)
- [apps/web/lib/api/network/calculate-partner-ranking.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/network/calculate-partner-ranking.ts)
- [apps/web/prisma/schema/network.prisma](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/prisma/schema/network.prisma)
- [apps/web/ui/program-marketplace/program-marketplace-card.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/program-marketplace-card.tsx)
- [apps/web/app/ee/api/track/application/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/track/application/route.ts)
- [apps/web/app/ee/api/network/partners/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/network/partners/route.ts)
- [apps/web/app/ee/api/cron/network/calculate-program-similarities/calculate-performance-similarity.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/calculate-performance-similarity.ts)
- [apps/web/ui/program-marketplace/pages/marketplace-programs-list-page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/pages/marketplace-programs-list-page.tsx)
- [apps/web/app/app.dub.co/marketplace/...segments/page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/marketplace/%5B%5B...segments%5D%5D/page.tsx)
- [apps/web/app/ee/api/cron/network/calculate-program-similarities/calculate-partner-similarity.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/calculate-partner-similarity.ts)
- [apps/web/app/ee/admin.dub.co/dashboard/programs/page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/admin.dub.co/(dashboard)/programs/page.tsx)
- [apps/web/lib/fetchers/get-marketplace-programs-summary.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/fetchers/get-marketplace-programs-summary.ts)
- [apps/web/ui/program-marketplace/featured-program-card.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/featured-program-card.tsx)
- [apps/web/ui/program-marketplace/program-marketplace-banner.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/program-marketplace-banner.tsx)
- [apps/web/ui/program-marketplace/pages/marketplace-home-page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/pages/marketplace-home-page.tsx)
- [apps/web/app/api/og/program/categories/route.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/og/program/categories/route.tsx)
- [apps/web/app/ee/api/cron/network/calculate-program-similarities/calculate-category-similarity.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/calculate-category-similarity.ts)
- [apps/web/ui/program-marketplace/external/marketplace-external-program-page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/external/marketplace-external-program-page.tsx)
- [apps/web/app/ee/api/marketplace/programs/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/marketplace/programs/route.ts)
- [apps/web/ui/program-marketplace/program-card.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/program-card.tsx)
- [apps/web/app/sitemap.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/sitemap.ts)
- [apps/web/ui/program-marketplace/marketplace-program-hero.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/marketplace-program-hero.tsx)
- [apps/web/ui/program-marketplace/external/marketplace-external-list-page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/external/marketplace-external-list-page.tsx)
- [apps/web/app/ee/partners.dub.co/dashboard/programs/page-client.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/partners.dub.co/(dashboard)/programs/page-client.tsx)
- [apps/web/app/app.dub.co/dashboard/slug/ee/program/program-empty-state.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/(ee)/program/program-empty-state.tsx)
- [apps/web/lib/fetchers/get-public-network-programs.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/fetchers/get-public-network-programs.ts)
- [apps/web/app/app.dub.co/onboarding/onboarding/steps/program/page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/(onboarding)/onboarding/(steps)/program/page.tsx)
- [apps/web/app/ee/api/admin/partners/partnerId/shared-platforms/route.ts](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/admin/partners/%5BpartnerId%5D/shared-platforms/route.ts)
- [apps/web/app/ee/admin.dub.co/dashboard/programs/recent/page.tsx](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/admin.dub.co/(dashboard)/programs/recent/page.tsx)
</details>

## Overview

The Program Marketplace serves as Dub's centralized directory for discovering, exploring, and applying to network affiliate and referral programs. It connects prospective affiliates with top SaaS programs through public listings, category filtering, and targeted search capabilities. By bridging public-facing discovery directories with internal workspace tooling, the marketplace enables administrators to curate featured collections and metadata while giving partners streamlined access to program enrollments and [conversion tracking](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/analytics-pipeline/conversion-tracking).

Sources: [apps/web/app/app.dub.co/marketplace/...segments/page.tsx:22-39](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/marketplace/%5B%5B...segments%5D%5D/page.tsx#L22-L39), [apps/web/lib/fetchers/get-public-network-programs.ts:26-96](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/fetchers/get-public-network-programs.ts#L26-L96), [apps/web/app/ee/admin.dub.co/dashboard/programs/page.tsx:27-37](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/admin.dub.co/(dashboard)/programs/page.tsx#L27-L37)

## Public Marketplace Data Fetching

### Server Fetchers and Prisma Queries

Discoverable network programs are retrieved through server-side fetchers powered by Prisma and cached via React's `cache` utility. The primary data retrieval function, `getPublicNetworkPrograms`, accepts query parameters including `category`, `rewardType`, `featured`, `search`, sorting fields (`popularity`, `recency`), pagination limits, and sort orders. Programs are filtered to include only those where `addedToMarketplaceAt` is not null. When search queries are present, Prisma evaluates an `OR` condition matching program name, slug, domain, url, or description. Reward type filtering evaluates the default partner group (`DEFAULT_PARTNER_GROUP.slug`) for specific relation IDs such as `saleRewardId`, `leadRewardId`, `clickRewardId`, or `discountId`.

```typescript
export const getPublicNetworkPrograms = cache(
  async (
    params: Partial<z.infer<typeof getPublicNetworkProgramsQuerySchema>> = {},
  ) => {
    const {
      category,
      rewardType,
      featured,
      search,
      sortBy = "popularity",
      sortOrder = "desc",
      page = 1,
      pageSize = PROGRAM_NETWORK_MAX_PAGE_SIZE,
    } = params;

    const programs = await prisma.program.findMany({
      where: {
        addedToMarketplaceAt: {
          not: null,
        },
        ...(featured && {
          featuredOnMarketplaceAt: {
            not: null,
          },
        }),
        ...(search && {
          OR: [
            { name: { contains: search } },
            { slug: { contains: search } },
            { domain: { contains: search } },
            { url: { contains: search } },
            { description: { contains: search } },
          ],
        }),
        ...(category && {
          categories: {
            some: {
              category,
            },
          },
        }),
        ...(rewardType && {
          groups: {
            some: {
              slug: DEFAULT_PARTNER_GROUP.slug,
              ...(rewardType === "sale" && {
                saleRewardId: { not: null },
              }),
              ...(rewardType === "lead" && {
                leadRewardId: { not: null },
              }),
              ...(rewardType === "click" && {
                clickRewardId: { not: null },
              }),
              ...(rewardType === "discount" && {
                discountId: { not: null },
              }),
            },
          },
        }),
      },
      include: {
        groups: {
          where: {
            slug: DEFAULT_PARTNER_GROUP.slug,
          },
          include: {
            clickReward: true,
            leadReward: true,
            saleReward: true,
            referralReward: true,
            discount: true,
          },
        },
        categories: true,
      },
      orderBy:
        sortBy === "popularity"
          ? { marketplaceRanking: "asc" }
          : {
```
Sources: [apps/web/lib/fetchers/get-public-network-programs.ts:11-96](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/fetchers/get-public-network-programs.ts#L11-L96)

Summary summaries for marketplace home pages are fetched via `getMarketplaceProgramsSummary`, which queries marketplace-enabled programs, groups them into featured sets, most popular lists, recent additions, and category rows according to `MARKETPLACE_HOME_CATEGORIES`.

```typescript
export const getMarketplaceProgramsSummary = cache(async () => {
  const programs = await prisma.program.findMany({
    where: {
      addedToMarketplaceAt: {
        not: null,
      },
    },
    include: programInclude,
  });

  const programMeta = programs.map(toProgramMeta);
  const usedIds = new Set<string>();

  const selectRow = (
    candidates: ProgramRecord[],
    sortFn: (a: ProgramRecord, b: ProgramRecord) => number,
  ) =>
    selectPrograms(
      candidates,
      usedIds,
      MARKETPLACE_HOME_ROW_PAGE_SIZE,
      sortFn,
    ).map(formatNetworkProgram);

  const featuredPrograms = programs
    .filter((program) => program.featuredOnMarketplaceAt)
    .sort(() => Math.random() - 0.5)
    .map(formatNetworkProgram);

  const mostPopular = selectRow(programs, byMarketplaceRanking);
  const newPrograms = selectPrograms(
    programs,
    new Set(),
    MARKETPLACE_HOME_ROW_PAGE_SIZE,
    byRecency,
  ).map(formatNetworkProgram);
  const categoryRows = selectCategoryRows(programMeta, usedIds);

  const categories = Object.fromEntries(
    Object.values(Category).map((category) => [
      category,
      (categoryRows.get(category) ?? []).map(formatNetworkProgram),
    ]),
  );

  return MarketplaceProgramsSummarySchema.parse({
    featuredPrograms,
    mostPopular,
    newPrograms,
    categories,
  });
});
```
Sources: [apps/web/lib/fetchers/get-marketplace-programs-summary.ts:146-197](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/fetchers/get-marketplace-programs-summary.ts#L146-L197)

> [!NOTE]
> Reward extraction logic inspects `program.groups[0]`, collecting click, lead, and sale rewards into an array while attaching the associated discount object. Programs lacking groups return empty reward arrays.

Sources: [apps/web/lib/fetchers/get-public-network-programs.ts:98-112](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/fetchers/get-public-network-programs.ts#L98-L112)

### Public API Routes and Edge Caching

The public marketplace HTTP endpoint exposes filtered and searched program lists at `/api/marketplace/programs`. The route handler parses incoming URL search parameters using `parsePublicMarketplaceQuery` before delegating execution to `getPublicNetworkPrograms`.

```typescript
export async function GET(req: Request) {
  const searchParams = Object.fromEntries(new URL(req.url).searchParams);
  const params = parsePublicMarketplaceQuery(searchParams);

  const programs = await getPublicNetworkPrograms(params);

  return NextResponse.json(programs);
}
```
Sources: [apps/web/app/ee/api/marketplace/programs/route.ts:9-16](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/marketplace/programs/route.ts#L9-L16)

> [!TIP]
> Route responses are configured with an edge revalidation window of 3,600 seconds (`revalidate = 3600`), caching filtered directory listings publicly at the edge without partner-scoped session overhead.

Sources: [apps/web/app/ee/api/marketplace/programs/route.ts:5-6](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/marketplace/programs/route.ts#L5-L6)

### Program Categories

Network programs can be categorized across thirteen industry domains defined by the `Category` enum in the Prisma schema.

| Category Enum Value | Description / Domain |
| :--- | :--- |
| `AI` | Artificial intelligence applications and tools |
| `Development` | Developer tools and engineering infrastructure |
| `Design` | Design systems, assets, and creative software |
| `Productivity` | Workflow enhancement and productivity applications |
| `Finance` | Financial technology, banking, and accounting |
| `Marketing` | Marketing automation, SEO, and growth tools |
| `Ecommerce` | E-commerce platforms and merchant tooling |
| `Security` | Cybersecurity, identity management, and compliance |
| `Education` | Learning management systems and educational resources |
| `Health` | Healthtech, fitness, and wellness applications |
| `Consumer` | Consumer-facing software and hardware services |
| `Support` | Customer support, helpdesk, and ticketing systems |

```
enum Category {
  AI
  Development
  Design
  Productivity
  Finance
  Marketing
  Ecommerce
  Security
  Education
  Health
  Consumer
  Support
}
```
Sources: [apps/web/prisma/schema/network.prisma:1-14](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/prisma/schema/network.prisma#L1-L14)

## Program Similarity Computation Engine

### Scheduled Execution and Batching Strategy

The network similarity engine operates via a scheduled QStash cron job running every 12 hours (`0 */12 * * *`) at the endpoint `/api/cron/network/calculate-program-similarities`. The handler validates incoming QStash signatures, parses request payloads using Zod with `currentProgramId` and `comparisonBatchCursor` parameters, and processes programs in batches of 10 (`PROGRAMS_PER_BATCH`).

```typescript
const payloadSchema = z.object({
  currentProgramId: z
    .string()
    .optional()
    .describe("Current program being compared."),
  comparisonBatchCursor: z
    .string()
    .optional()
    .describe("Cursor for programs to compare against."),
});

const PROGRAMS_PER_BATCH = 10;

// This route is used to calculate program similarities in the network
// Runs once every 12 hours (0 */12 * * *)
// POST /api/cron/network/calculate-program-similarities
export async function POST(req: Request) {
```
Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/route.ts:14-30](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/route.ts#L14-L30)

Programs are targeted for comparison if they are not deactivated (`deactivatedAt: null`) and are visible in either the marketplace (`addedToMarketplaceAt` is not null) or the partner network (`partnerNetworkEnabledAt` is not null). When a batch finishes processing, the job automatically schedules the next batch or advances to the subsequent target program via QStash JSON publishing.

```typescript
const programs = await prisma.program.findMany({
  where: {
    id: {
      gt: currentProgram.id,
    },
    deactivatedAt: null,
    OR: [
      {
        addedToMarketplaceAt: {
          not: null,
        },
      },
      {
        partnerNetworkEnabledAt: {
          not: null,
        },
      },
    ],
  },
  ...(comparisonBatchCursor && {
    cursor: {
      id: comparisonBatchCursor,
    },
  }),
  skip: comparisonBatchCursor ? 1 : 0,
  orderBy: {
    id: "asc",
  },
  take: PROGRAMS_PER_BATCH,
  select: {
    id: true,
    name: true,
  },
});
```
Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/route.ts:64-97](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/route.ts#L64-L97)

> [!NOTE]
> Program pairs resulting in a combined similarity score exceeding `PROGRAM_SIMILARITY_SCORE_THRESHOLD` are persisted bidirectionally (`programId` and `similarProgramId` entries) within a Prisma transaction after purging stale records for the batch.

Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/route.ts:147-183](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/route.ts#L147-L183)

### Multi-Dimensional Similarity Metrics

The final similarity score is computed as a weighted aggregate of three distinct vector and set-based dimensions: category similarity (weighted at 50%), partner similarity (weighted at 30%), and performance similarity (weighted at 20%).

```typescript
const [
  categorySimilarityScore,
  partnerSimilarityScore,
  performanceSimilarityScore,
] = await Promise.all([
  calculateCategorySimilarity(program1.id, program2.id),
  calculatePartnerSimilarity(program1.id, program2.id),
  calculatePerformanceSimilarity(program1.id, program2.id),
]);

const similarityScore =
  categorySimilarityScore * 0.5 +
  partnerSimilarityScore * 0.3 +
  performanceSimilarityScore * 0.2;

```
Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/route.ts:122-136](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/route.ts#L122-L136)

| Similarity Dimension | Mathematical Approach | Implementation File | Weight |
| :--- | :--- | :--- | :--- |
| **Category Similarity** | Jaccard Similarity | `calculate-category-similarity.ts` | 0.5 |
| **Partner Similarity** | Jaccard Similarity | `calculate-partner-similarity.ts` | 0.3 |
| **Performance Similarity** | Cosine Similarity | `calculate-performance-similarity.ts` | 0.2 |

```typescript
const similarityScore =
  categorySimilarityScore * 0.5 +
  partnerSimilarityScore * 0.3 +
  performanceSimilarityScore * 0.2;
```
Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/route.ts:132-135](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/route.ts#L132-L135)

### Component Algorithm Implementation

Category and partner similarities utilize Jaccard coefficients calculated from database relations. Category similarity compares distinct assigned categories via `prisma.programCategory`, while partner similarity executes a raw SQL join across `ProgramEnrollment` records to evaluate shared affiliate overlap.

```typescript
export async function calculateCategorySimilarity(
  program1Id: string,
  program2Id: string,
): Promise<number> {
  const [categories1, categories2] = await Promise.all([
    prisma.programCategory.findMany({
      where: {
        programId: program1Id,
      },
      select: {
        category: true,
      },
    }),

    prisma.programCategory.findMany({
      where: {
        programId: program2Id,
      },
      select: {
        category: true,
      },
    }),
  ]);

  const categories1Set = new Set(categories1.map(({ category }) => category));
  const categories2Set = new Set(categories2.map(({ category }) => category));

  const sharedCount = [...categories1Set].filter((c) =>
    categories2Set.has(c),
  ).length;

  const totalUniqueCount =
    categories1Set.size + categories2Set.size - sharedCount;

  if (totalUniqueCount === 0) {
    return 0;
  }

  return sharedCount / totalUniqueCount;
}
```
Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/calculate-category-similarity.ts:4-43](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/calculate-category-similarity.ts#L4-L43)

Performance similarity builds vectors from averaged enrollment metrics across five specific keys: `totalClicks`, `totalLeads`, `totalConversions`, `totalSales`, and `totalSaleAmount`. It then computes the cosine similarity between the resulting vectors for the two programs.

```typescript
const METRIC_KEYS = [
  "totalClicks",
  "totalLeads",
  "totalConversions",
  "totalSales",
  "totalSaleAmount",
] as const;
```
Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/calculate-performance-similarity.ts:3-9](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/calculate-performance-similarity.ts#L3-L9)

```typescript
export async function calculatePerformanceSimilarity(
  program1Id: string,
  program2Id: string,
): Promise<number> {
  const [performance1, performance2] = await Promise.all([
    prisma.programEnrollment.aggregate({
      where: {
        programId: program1Id,
      },
      _avg: {
        totalClicks: true,
        totalLeads: true,
        totalSales: true,
        totalConversions: true,
        totalSaleAmount: true,
      },
    }),

    prisma.programEnrollment.aggregate({
      where: {
        programId: program2Id,
      },
      _avg: {
        totalClicks: true,
        totalLeads: true,
        totalSales: true,
        totalConversions: true,
        totalSaleAmount: true,
      },
    }),
  ]);

  const program1Vector = METRIC_KEYS.map((key) => performance1._avg[key] ?? 0);
  const program2Vector = METRIC_KEYS.map((key) => performance2._avg[key] ?? 0);

  const dotProduct = program1Vector.reduce(
    (sum, val, i) => sum + val * program2Vector[i],
    0,
  );

  const magnitude1 = Math.sqrt(
    program1Vector.reduce((sum, val) => sum + val ** 2, 0),
  );

  const magnitude2 = Math.sqrt(
    program2Vector.reduce((sum, val) => sum + val ** 2, 0),
  );

  if (magnitude1 === 0 || magnitude2 === 0) {
    return 0;
  }

  return dotProduct / (magnitude1 * magnitude2);
}
```
Sources: [apps/web/app/ee/api/cron/network/calculate-program-similarities/calculate-performance-similarity.ts:12-65](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/cron/network/calculate-program-similarities/calculate-performance-similarity.ts#L12-L65)

## Partner Ranking and Discovery Scoring

### Discoverability and Ranking Algorithm

Workspace partner search operations evaluate eligibility and calculate scores via `calculatePartnerRanking`. Partners must meet baseline criteria: their network status must be either `"approved"` or `"trusted"`, their click-to-conversion rate must be strictly below 1, they must not have an active enrollment (`enrolled.id IS NULL`), and they must not be marked as ignored (`dp.ignoredAt IS NULL`).

```typescript
const conditions: Prisma.Sql[] = [
  Prisma.sql`p.networkStatus IN ("approved", "trusted")`,
  Prisma.sql`COALESCE(pe.clickToConversionRate, 0) < 1`,
  Prisma.sql`(dp.ignoredAt IS NULL OR dp.id IS NULL)`,
  Prisma.sql`enrolled.id IS NULL`,
];
```
Sources: [apps/web/lib/api/network/calculate-partner-ranking.ts:77-82](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/network/calculate-partner-ranking.ts#L77-L82)

The scoring model for the discovery tab yields a total score between 0 and 265+ points, synthesized from three additive components:

| Scoring Component | Point Range | Weight / Calculation Mechanics |
| :--- | :--- | :--- |
| **Trusted Partner Bonus** | 200 points | Applied exclusively when `networkStatus = "trusted"` to ensure top-tier priority boost. |
| **Similarity Score** | 0–50 points | Weighted performance across similar programs where similarity score exceeds 0.3. |
| **Program Match Score** | 0–15 points | 2 points per similar program the partner is enrolled in (capped at 15 points). |

```typescript
* Scoring Breakdown (0-65+ points):
*
* 1. Trusted Partner Bonus (200 points): Top priority boost
*    - Partners with networkStatus = "trusted" get 200 bonus points
*    - This ensures trusted partners appear at the very top
*
* 2. Similarity Score (0-50 points): Performance in similar programs
*    - Sums weighted performance across similar programs (similarityScore > 0.3)
*    - Each program's performance weighted by its similarity score
*    - Partners with more similar programs score higher (capped at 50)
*    - Includes: consistency (20%), conversion rate (10%), LTV (15%), commissions (5%)
*
* 3. Program Match Score (0-15 points): Count of similar programs
*    - Rewards partners enrolled in many similar programs
*    - 2 points per similar program (capped at 15)
*
* Final Score = Trusted Bonus + Similarity + Match (0-265+ points)
*
```
Sources: [apps/web/lib/api/network/calculate-partner-ranking.ts:18-35](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/network/calculate-partner-ranking.ts#L18-L35)

> [!NOTE]
> The similarity score sums individual program performance metrics using specific internal weights: consistency at 20%, conversion rate at 10%, LTV at 15%, and commissions at 5%.

Sources: [apps/web/lib/api/network/calculate-partner-ranking.ts:28-29](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/network/calculate-partner-ranking.ts#L28-L29)

### Sorting and Shared Platform Resolution

The sorting mechanism is handled by `buildOrderByClause`, which accepts `starred`, `sortBy`, and `platform` arguments. If `starred` is true, results are ordered descending by `dp.starredAt`. If sorting by subscribers with an active platform filter, the query extracts the maximum verified subscriber count for that specific platform type before ordering.

```typescript
function buildOrderByClause({
  starred,
  sortBy,
  platform,
}: {
  starred?: boolean | null;
  sortBy?: "relevance" | "subscribers";
  platform?: PlatformType;
}) {
  if (starred === true) {
    return Prisma.sql`dp.starredAt DESC`;
  }

  if (sortBy === "subscribers" && platform) {
    return Prisma.sql`(
      SELECT COALESCE(MAX(pp_sort.subscribers), 0)
      FROM PartnerPlatform pp_sort
      WHERE pp_sort.partnerId = p.id
        AND pp_sort.type = ${platform}
        AND pp_sort.verifiedAt IS NOT NULL
    ) DESC, p.id ASC`;
  }

  return Prisma.sql`finalScore DESC, p.id ASC`;
}
```
Sources: [apps/web/lib/api/network/calculate-partner-ranking.ts:40-64](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/lib/api/network/calculate-partner-ranking.ts#L40-L64)

Administrators can inspect overlapping partners across the network via the shared platforms route. Verified platforms are filtered, and website identifiers are stripped of their `www` prefix using `getDomainWithoutWWW` to evaluate domain-level matches rather than exact URL strings.

```typescript
const verifiedPlatforms = partner.platforms.filter(
  (platform) => platform.verifiedAt !== null,
);

// a partner has at most one platform per type, so there is at most one website
const websitePlatform = verifiedPlatforms.find(
  (platform) => platform.type === "website",
);

const websiteDomain = websitePlatform
  ? getDomainWithoutWWW(websitePlatform.identifier) ?? null
  : null;

const matchConditions: Prisma.PartnerPlatformWhereInput[] = [];

for (const platform of verifiedPlatforms) {
  if (platform.type === "website") {
    // website identifiers are full URLs with inconsistent normalization,
    // so match on the domain instead of the exact identifier
    if (websiteDomain) {
      matchConditions.push({
        identifier: {
          contains: websiteDomain,
        },
        type: platform.type,
      });
    }
```
Sources: [apps/web/app/ee/api/admin/partners/partnerId/shared-platforms/route.ts:23-49](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/admin/partners/%5BpartnerId%5D/shared-platforms/route.ts#L23-L49)

> [!WARNING]
> Website matches utilizing `contains` queries are post-processed to verify exact domain equality (`getDomainWithoutWWW(match.identifier) === websiteDomain`), preventing partial substring collisions such as matching `example.com` against `notexample.com`.

Sources: [apps/web/app/ee/api/admin/partners/partnerId/shared-platforms/route.ts:95-103](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/admin/partners/%5BpartnerId%5D/shared-platforms/route.ts#L95-L103)

## Marketplace UI and Navigation Surfaces

### Public Directory Layout and Navigation Surfaces

The program marketplace features multiple navigation entry points and directory layouts. In public navigation menus, `ProgramMarketplaceSection` renders a navigation link pointing to `/marketplace` with dynamic UTM tracking parameters (`NAV_UTM_PARAMS`) where `utm_content` is explicitly set to `"Program Marketplace"`. The navigation item incorporates a decorative floating grid pattern via `Grid` with a cell size of 60 and pattern offsets of `[-12, -32]`. It also features animated company logo nodes floating via motion transitions.

```tsx
export function ProgramMarketplaceSection({ domain }: { domain: string }) {
  return (
    <NavigationMenuLink asChild>
      <Link
        href={createHref("/marketplace", domain, {
          ...NAV_UTM_PARAMS,
          utm_campaign: domain,
          utm_content: "Program Marketplace",
        })}
        className={cn(
          "group relative isolate flex items-center overflow-hidden bg-neutral-50 px-6 py-3 transition-colors duration-75",
          "hover:bg-neutral-100 dark:border-white/[0.15] dark:bg-white/[0.03] dark:hover:bg-white/[0.06]",
        )}
      >
        {/* offset so a horizontal cell edge sits just below the middle of the bar */}
        <Grid
          cellSize={60}
          patternOffset={[-12, -32]}
          className="text-neutral-200/60 [mask-image:linear-gradient(90deg,transparent_20%,black)] dark:text-white/10"
        />
```
Sources: [packages/ui/src/nav/content/program-marketplace.tsx:100-119](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/packages/ui/src/nav/content/program-marketplace.tsx#L100-L119)

> [!NOTE]
> The floating logo canvas (`ProgramMarketplaceLogos`) uses deterministic drift physics based on slot indices, calculating movement amplitudes (`amplitude = 0.6 + depth * 0.8`) with `Math.sin` and `Math.cos` to simulate water buoyancy and parallax depth across 16 predefined company logo slots.

Sources: [packages/ui/src/nav/content/program-marketplace.tsx:9-50](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/packages/ui/src/nav/content/program-marketplace.tsx#L9-L50)

External routing is structured around dynamic segments at `/marketplace/[[...segments]]`. Metadata generation (`generateMetadata`) parses segment lengths to construct context-aware SEO titles, descriptions, and OpenGraph images. Specifically, category paths matching `/marketplace/c/[category]` resolve human-readable labels from `PROGRAM_CATEGORIES_MAP` and generate category-specific OpenGraph image endpoints.

```tsx
export async function generateMetadata(props: {
  params: Promise<{ segments?: string[] }>;
}): Promise<Metadata> {
  const { segments = [] } = await props.params;
  const pathname = `/marketplace${segments.length > 0 ? `/${segments.join("/")}` : ""}`;

  const currentYear = new Date().getFullYear();

  let title = `Best SaaS affiliate programs in ${currentYear}`;
  let description = `Browse and apply to the best SaaS affiliate programs on Dub's Partner Network.`;
  let image = "https://assets.dub.co/og/marketplace.jpg";

  if (segments.length === 1 && segments[0] === "all") {
    title = `Top SaaS affiliate programs in ${currentYear}`;
  } else if (
    segments.length === 1 &&
    segments[0] !== "all" &&
    segments[0] !== "popular"
  ) {
    const program = await getNetworkProgram({ slug: segments[0] });

    if (program) {
      title = `${program.name} Affiliate Program`;
      description =
        program.description ||
        `Join the ${program.name} affiliate program on Dub's Program Marketplace.`;
      image =
        program.marketplaceHeaderImage ||
        `${APP_DOMAIN}/api/og/program?slug=${program.slug}`;
    }
  } else if (segments.length === 2 && segments[0] === "c") {
    const category = Object.values(Category).find(
      (value) => value.toLowerCase() === segments[1],
    );

    if (category) {
      // categoryMeta should always return a value, but just in case
      const categoryMeta = PROGRAM_CATEGORIES_MAP[category];
      const label = categoryMeta?.label ?? category.replaceAll("_", " ");
      title = `Best ${label} SaaS Affiliate Programs in ${currentYear}`;
      description =
        categoryMeta?.listPageDescription ??
        `Browse the best ${label} affiliate programs on Dub's Program Marketplace.`;
      image = `${APP_DOMAIN}/api/og/program/categories?categorySlug=${category.toLowerCase()}`;
    }
  }
```
Sources: [apps/web/app/app.dub.co/marketplace/...segments/page.tsx:14-59](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/marketplace/%5B%5B...segments%5D%5D/page.tsx#L14-L59)

### Category Navigation and Promo Banners

The marketplace home view (`MarketplaceHomePage`) aggregates summary statistics via `/api/network/programs/summary` using SWR, rendering featured programs, category sections via `MarketplaceCategoriesSection`, and organized rows using `MarketplaceProgramRow` driven by `MARKETPLACE_HOME_ROWS`.

```tsx
export function MarketplaceHomePage() {
  const { data: summary } = useSWR<MarketplaceProgramsSummary>(
    "/api/network/programs/summary",
    fetcher,
    { revalidateOnFocus: false },
  );

  return (
    <div className="flex flex-col gap-10">
      {summary ? (
        <FeaturedPrograms programs={summary.featuredPrograms} />
      ) : (
        <FeaturedProgramsSkeleton />
      )}
      <MarketplaceCategoriesSection />
      {MARKETPLACE_HOME_ROWS.map((row) => (
        <MarketplaceProgramRow
          key={row.key}
          title={row.title}
          viewAllHref={row.viewAllHref}
          showViewAllCard={row.showViewAllCard}
          programs={
            summary
              ? summary.categories[row.key] ?? summary[row.key]
              : undefined
          }
        />
      ))}
    </div>
```
Sources: [apps/web/ui/program-marketplace/pages/marketplace-home-page.tsx:19-47](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/pages/marketplace-home-page.tsx#L19-L47)

For partner dashboards, promotional visibility is handled by `ProgramMarketplaceBanner` and `ProgramMarketplaceCard`, which check partner profile state and promo status. The banner supports animated expansion and collapse states, offering a direct call-to-action to view the marketplace alongside a dismiss button that downgrades the promo state to a card variant.

```tsx
export function ProgramMarketplaceBanner() {
  const { partner } = usePartnerProfile();
  const { status, setStatus } = useProgramMarketplacePromo();

  return (
    <AnimatePresence>
      {partner && status === "banner" && (
        <motion.div
          initial={{ opacity: 0, height: 0 }}
          animate={{ opacity: 1, height: "auto" }}
          exit={{ opacity: 0, height: 0 }}
          transition={{ duration: 0.2 }}
          className="overflow-hidden"
        >
```
Sources: [apps/web/ui/program-marketplace/program-marketplace-banner.tsx:12-25](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/program-marketplace-banner.tsx#L12-L25)

The internal programs list page (`MarketplaceProgramsListPage`) manages pagination and active filters by querying `/api/network/programs` with query parameters derived from the active category pathname. Pagination uses `PROGRAM_NETWORK_MAX_PAGE_SIZE` and renders `PaginationControls` inside a sticky bottom bar when program counts exceed single-page limits.

```tsx
export function MarketplaceProgramsListPage() {
  const pathname = usePathname();
  const { getQueryString, searchParamsObj } = useRouterStuff();

  const categoryParam = getMarketplaceCategoryFromPathname(pathname);

  const queryString = getQueryString(
    categoryParam ? { category: categoryParam } : undefined,
  );

  const { data: programsCount, error: countError } = useNetworkProgramsCount({
    query: categoryParam ? { category: categoryParam } : undefined,
  });

  const {
    data: programs,
    error,
    isValidating,
  } = useSWR<NetworkProgramProps[]>(
    `/api/network/programs${queryString}`,
    fetcher,
    { revalidateOnFocus: false, keepPreviousData: true },
  );

  const { pagination, setPagination } = usePagination(
    PROGRAM_NETWORK_MAX_PAGE_SIZE,
  );

  const { activeFilters, onRemoveAll } = useProgramNetworkFilters();

  const hasActiveFilters =
    activeFilters.length > 0 || Boolean(searchParamsObj.search);

  return (
    <div className="flex flex-col gap-4">
      <MarketplaceListToolbar variant="internal" />

      {error || countError ? (
        <div className="text-content-subtle py-12 text-sm">
          Failed to load programs
        </div>
      ) : !programs || programs?.length ? (
        <div>
          <div className="min-h-[300px]">
            {programs ? (
              <MarketplaceProgramGrid
                programs={programs}
                className={cn(isValidating && "opacity-50")}
              />
            ) : (
              <MarketplaceProgramGridSkeleton />
            )}
          </div>
          <div className="sticky bottom-0 mt-4 rounded-b-[inherit] border-t border-neutral-200 bg-white px-3.5 py-2">
            <PaginationControls
              pagination={pagination}
              setPagination={setPagination}
              totalCount={programsCount || 0}
              unit={(p) => `program${p ? "s" : ""}`}
            />
          </div>
```
Sources: [apps/web/ui/program-marketplace/pages/marketplace-programs-list-page.tsx:19-79](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/pages/marketplace-programs-list-page.tsx#L19-L79)

### Card Visualizations and Accent Color Extraction

Program representations utilize specialized card components including `FeaturedProgramCard`, `MarketplaceProgramCard`, and `MarketplaceProgramHero`. Each card dynamically computes aesthetic backdrops using image accent color extraction hooks (`useImageAccentColor`). If color extraction is pending or blocked, cards fall back to deterministic palette arrays.

| Card Component | Layout Structure | Primary Visual Elements |
| :--- | :--- | :--- |
| **FeaturedProgramCard** | Responsive flex layout with header banner integration (`sm:min-h-[340px]`) | Program logo, status badge, description, rewards summary, and external website link with globe icon. |
| **MarketplaceProgramCard** | Vertical grid cell card with hover drop shadow (`hover:drop-shadow-card-hover`) | Circular avatar, program name, truncated 2-line description, and structured reward badges. |
| **MarketplaceProgramHero** | Extended full-width hero header with banner image slot (`min-h-[640px]` when banner active) | Large avatar, title, category tags via `ProgramCategory`, website link, and action slot container. |

```tsx
export function FeaturedProgramCard({
  program,
  externalMarketplace = false,
  colorIndex = 0,
}: {
  program: NetworkProgramProps;
  externalMarketplace?: boolean;
  colorIndex?: number;
}) {
  const router = useRouter();
  const rewards = program.rewards;

  // Derive the tint from the program's image
  // Fall back to the palette if extraction is blocked
  const { color: accentColor, ready: accentReady } = useImageAccentColor(
    program.marketplaceHeaderImage ?? program.logo,
  );
  const backgroundColor = accentColor ?? getFeaturedCardBackground(colorIndex);

  return (
    <article className="relative flex h-full flex-col-reverse gap-4 overflow-hidden rounded-2xl border border-neutral-100 p-2 sm:min-h-[340px] sm:flex-row">
```
Sources: [apps/web/ui/program-marketplace/featured-program-card.tsx:35-55](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/featured-program-card.tsx#L35-L55)

> [!IMPORTANT]
> `FeaturedProgramCard` cycles through an array of 10 pastel background hex codes (`FEATURED_CARD_BACKGROUNDS`) using modulo arithmetic (`colorIndex % length`), guaranteeing consistent chromatic distribution across items when image accent extraction is not yet ready.

Sources: [apps/web/ui/program-marketplace/featured-program-card.tsx:14-33](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/featured-program-card.tsx#L14-L33)

## Application Analytics and Conversion Tracking

### Visitor Device Context and URL Path Routing

Tracking marketplace visitor interactions and application clicks involves capturing client device contexts and resolving incoming request URLs into network program slugs. The tracking subsystem parses user-agent headers to record operating system details, browser context, architecture, and referral origins. When an external program page initializes via `MarketplaceExternalProgramPage`, it mounts an `ApplicationAnalytics` component alongside the external shell wrapper and apply button.

```tsx
return (
  <>
    <ApplicationAnalytics />
    <MarketplaceExternalShell variant="none">
      <MarketplaceProgramDetailsLayout
```
Sources: [apps/web/ui/program-marketplace/external/marketplace-external-program-page.tsx:26-30](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/external/marketplace-external-program-page.tsx#L26-L30)

> [!NOTE]
> If `getNetworkProgram` fails to resolve a valid program record for a given `programSlug`, `MarketplaceExternalProgramPage` immediately triggers a server-side redirect to `/marketplace`.

Sources: [apps/web/ui/program-marketplace/external/marketplace-external-program-page.tsx:18-24](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/ui/program-marketplace/external/marketplace-external-program-page.tsx#L18-L24)

### Program Slug Identification Routing

The route tracking logic uses `identityProgramSlug` to map incoming request URLs to specific program slugs or marketplace directory partitions. The parser evaluates pathname segments against known route patterns to determine whether a request targets an individual program, a category view, or platform registration.

| Route Pattern | Parsed Path Structure | Output Slug and Marketplace Flag |
| :--- | :--- | :--- |
| Root Marketplace | `/marketplace` | `programSlug: null`, `isMarketplace: true` |
| Category View | `/marketplace/c/{category}` | `programSlug: null`, `isMarketplace: true` |
| Reserved Slug Check | `/marketplace/{reservedSlug}` | `programSlug: null`, `isMarketplace: true` |
| Direct Marketplace Program | `/marketplace/{programSlug}` | `programSlug: parts[1].toLowerCase()`, `isMarketplace: true` |
| Legacy Marketplace Program | `/programs/marketplace/{programSlug}` | `programSlug: parts[2].toLowerCase()`, `isMarketplace: true` |
| Standard Program Path | `/programs/{programSlug}` | `programSlug: parts[1].toLowerCase()`, `isMarketplace: false` |
| Platform Registration | `/register` | `programSlug: NETWORK_PROGRAM_SLUG`, `isMarketplace: false` |
| Short Slug Route | `/{programSlug}` | `programSlug: parts[0].toLowerCase()`, `isMarketplace: false` |

```typescript
function identityProgramSlug(url: string) {
  try {
    const urlObj = new URL(url);
    const parts = urlObj.pathname.split("/").filter(Boolean);

    if (parts.length === 0) {
      return { programSlug: null, isMarketplace: false };
    }

    if (parts[0] === "register") {
      return { programSlug: NETWORK_PROGRAM_SLUG, isMarketplace: false };
    }

    if (parts[0] === "marketplace") {
      if (parts.length === 1) {
        return { programSlug: null, isMarketplace: true };
      }

      if (parts[1] === "c") {
        return { programSlug: null, isMarketplace: true };
      }

      if (parts.length === 2) {
        if (MARKETPLACE_RESERVED_SLUGS.has(parts[1])) {
          return { programSlug: null, isMarketplace: true };
        }

        return {
          programSlug: parts[1].toLowerCase(),
          isMarketplace: true,
        };
      }

      return { programSlug: null, isMarketplace: true };
    }

    if (parts[0] === "programs" && parts[1] === "marketplace") {
      if (!parts[2] || MARKETPLACE_RESERVED_SLUGS.has(parts[2])) {
        return { programSlug: null, isMarketplace: false };
      }

      return {
        programSlug: parts[2].toLowerCase(),
        isMarketplace: true,
      };
    }

    if (parts[0] === "programs") {
      if (!parts[1]) {
        return { programSlug: null, isMarketplace: false };
      }

      return {
        programSlug: parts[1].toLowerCase(),
        isMarketplace: false,
      };
    }

    return {
      programSlug: parts[0].toLowerCase(),
      isMarketplace: false,
    };
```
Sources: [apps/web/app/ee/api/track/application/route.ts:419-480](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/track/application/route.ts#L419-L480)

> [!CAUTION]
> The `identityProgramSlug` function wraps its URL parsing inside a `try...catch` block; malformed or unparseable URL strings do not throw errors but instead silently return a fallback object with `programSlug: null` and `isMarketplace: false`.

Sources: [apps/web/app/ee/api/track/application/route.ts:481-484](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/api/track/application/route.ts#L481-L484)

## Marketplace Administration and Metadata Operations

### Admin Dashboard and Program Sorting

Administrative management of marketplace listings is handled through designated administrative dashboard views where operators can update program descriptions, manage category classifications, and alter display rankings. The administrative interface manages ranking positions by tracking original and modified index states, computing updates when programs are reordered, and falling back to `MAX_MARKETPLACE_RANKING` (`2147483647`) for unranked items.

```tsx
const MAX_MARKETPLACE_RANKING = 2147483647;

type MarketplaceProgram = {
  id: string;
  name: string;
  slug: string;
  logo: string | null;
  url: string | null;
  description: string | null;
  addedToMarketplaceAt: string | Date;
  marketplaceRanking: number;
  categories: Category[];
};

```
Sources: [apps/web/app/ee/admin.dub.co/dashboard/programs/page.tsx:25-38](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/admin.dub.co/(dashboard)/programs/page.tsx#L25-L38)

> [!WARNING]
> Programs assigned `MAX_MARKETPLACE_RANKING` are excluded from standard positional sequencing calculations, ensuring unranked entries do not disrupt custom ordering among featured marketplace listings.

Sources: [apps/web/app/ee/admin.dub.co/dashboard/programs/page.tsx:97-138](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/admin.dub.co/(dashboard)/programs/page.tsx#L97-L138)

Operators can also review recent programs via a dedicated administration view that filters workspaces by plan tier and exposes direct actions to add unlisted programs into the public marketplace directory.

```tsx
const filters = useMemo(
  () => [
    {
      key: "plan",
      icon: CreditCard,
      label: "Plan",
      singleSelect: true,
      options: plans.map((value) => ({
        value,
        label: capitalize(value) ?? value,
      })),
    },
  ],
  [],
);
```
Sources: [apps/web/app/ee/admin.dub.co/dashboard/programs/recent/page.tsx:59-73](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/(ee)/admin.dub.co/(dashboard)/programs/recent/page.tsx#L59-L73)

### Sitemap Generation and Category Metadata

Search engine optimization and metadata generation leverage automated sitemap compilation routines alongside dynamic Open Graph image generators. The sitemap generation routine inspects incoming request host headers to determine domain context and construct corresponding URL entries.

| Host Context | Query Target | Generated Sitemap Paths |
| :--- | :--- | :--- |
| Partners Hostname (`PARTNERS_HOSTNAMES`) | `prisma.program.findMany` with published default groups | `https://partners.dub.co/{program.slug}` |
| Application Hostname (`isAppHostname`) | `prisma.program.findMany` where `addedToMarketplaceAt` is not null | `https://dub.co/marketplace`, `https://dub.co/marketplace/programs`, category paths, and individual program URLs |

```typescript
if (PARTNERS_HOSTNAMES.has(domain)) {
  const programs = await prisma.program.findMany({
    where: {
      groups: {
        some: {
          slug: "default",
          landerData: {
            not: Prisma.AnyNull,
          },
          landerPublishedAt: {
            not: null,
          },
        },
      },
    },
    orderBy: {
      slug: "asc",
    },
  });

  return programs.map((program) => ({
    url: `https://partners.dub.co/${program.slug}`,
    lastModified: new Date(),
  }));
}
```
Sources: [apps/web/app/sitemap.ts:20-44](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/sitemap.ts#L20-L44)

> [!IMPORTANT]
> When executing on application hostnames, the sitemap generator iterates over all values of the Prisma `Category` enum to construct category-specific directory links alongside individual marketplace program records.

Sources: [apps/web/app/sitemap.ts:78-86](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/sitemap.ts#L78-L86)

Category-specific Open Graph images are dynamically rendered using Next.js image response utilities. The category OG route extracts the `categorySlug` search parameter, matches it against `PROGRAM_CATEGORIES`, and renders a styled SVG grid layout with Inter typography and category metadata.

```tsx
export async function GET(req: NextRequest) {
  const categorySlug = req.nextUrl.searchParams.get("categorySlug");

  if (!categorySlug) {
    return new Response("Missing 'categorySlug' parameter", {
      status: 400,
    });
  }

  const category = PROGRAM_CATEGORIES.find(
    ({ id }) => id.toLowerCase() === categorySlug.toLowerCase(),
  );

  if (!category) {
    return new Response("Category not found", {
      status: 404,
    });
  }
```
Sources: [apps/web/app/api/og/program/categories/route.tsx:15-32](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/api/og/program/categories/route.tsx#L15-L32)

### Onboarding Integration

Workspace onboarding connects new affiliate programs to the application routing flow by validating active user sessions and project domain bindings. The onboarding step retrieves domain records stored in Redis or workspace configurations, redirecting unconfigured users to domain setup before rendering the client onboarding page.

```tsx
const data = await redis.get<{ domain: string; userId: string }>(
  `onboarding-domain:${workspace.id}`,
);

const onboardingDomain =
  data && data.domain && data.userId === user.id ? data.domain : null;

const domain = onboardingDomain || workspace.domains[0]?.slug;

if (!domain)
  redirect(`/onboarding/domain?workspace=${slug}&product=partners`);

```
Sources: [apps/web/app/app.dub.co/onboarding/onboarding/steps/program/page.tsx:38-49](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/(onboarding)/onboarding/(steps)/program/page.tsx#L38-L49)

Workspaces lacking configured programs display an empty state component featuring partner preview cells that utilize repeating coordinate mappings over a shared asset sprite.

```tsx
const EXAMPLE_PARTNERS = [
  {
    name: "Lauren Anderson",
    country: "US",
    revenue: 1_800,
    payouts: 550,
    index: 0,
  },
  {
    name: "Elias Weber",
    country: "DE",
    revenue: 783,
    payouts: 235,
    index: 4,
  },
  {
    name: "Hiroshi Tanaka",
    country: "JP",
    revenue: 19_200,
    payouts: 5_700,
    index: 3,
  },
  {
    name: "Mia Taylor",
    country: "US",
    revenue: 22_600,
    payouts: 6_800,
    index: 1,
  },
];

```
Sources: [apps/web/app/app.dub.co/dashboard/slug/ee/program/program-empty-state.tsx:60-90](https://github.com/blade47/dub/blob/7c93d97abf0ecd37fb669c6a68a1f7ef9109daea/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/(ee)/program/program-empty-state.tsx#L60-L90)

## Related

- [Partner Programs](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/partner-programs/partner-programs)


## Sitemap

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