# Affiliate Tracking A Bijection component for affiliate tracking, attribution, commissions, payouts, and analytics. - Package: @bijectionhq/affiliate-tracking - Category: Integrations - Status: Preview - License: MIT - Bijection source (repository access required): https://github.com/bijectionhq/affiliate-tracking/tree/0dee74dc15dbbd40a2a55579d9b4661ae6941ffe - Upstream: https://github.com/ezyyeah/affiliate-tracking/tree/d55c638da75e522fe4e34fbeb546d56cb5051f71 - Documentation: https://docs.bijection.com/components/using - Directory: https://bijection.com/components/affiliate-tracking ## Get the source Bijection repository access is required. Request access at https://bijection.com/contact. ```sh git clone https://github.com/bijectionhq/affiliate-tracking.git cd affiliate-tracking git checkout 0dee74dc15dbbd40a2a55579d9b4661ae6941ffe ``` ## Before using this component Read the package README at the recorded revision for setup, dependencies, and API examples. Upstream documentation describes the original package and may differ from the Bijection adaptation. This is a source candidate for evaluation. Catalog inclusion does not establish npm availability or runtime qualification. Check compatibility with your SDK and deployment before adoption. External providers run their own compute. ## Upstream package documentation Recorded 2026-10-23. This describes @ezyyeah/affiliate-tracking; check the Bijection adaptation separately. A complete affiliate tracking system for Convex applications that handles programs, affiliate enrollment, referral link creation, click tracking, conversion attribution, and commission payouts. It provides isolated database tables and comprehensive analytics while letting your app handle authentication and billing. The component offers flexible attribution models with configurable deduplication and commission structures. ### Use cases • **E-commerce platforms** need to track affiliate referrals, attribute sales to the correct affiliates, calculate commissions, and manage bulk payouts • **SaaS applications** want to implement referral programs with custom attribution windows and commission rates for different affiliate tiers • **Marketplaces** require detailed analytics on affiliate performance, click-through rates, and conversion tracking across multiple campaigns • **Content platforms** need to manage affiliate links for creators while preventing duplicate clicks and ensuring proper revenue attribution • **Subscription services** want to track recurring commission structures and automate payout workflows for their affiliate networks ### How it works The component mounts into your Convex app and provides an `AffiliateTracking` wrapper class that exposes typed methods for managing programs, affiliates, campaigns, and links. You create programs with configurable attribution windows and commission structures, then enroll affiliates and generate trackable links. Click tracking happens through HTTP actions that call `trackClick()` and redirect visitors, while conversions are recorded with `recordConversion()` using idempotency keys to prevent double-counting. The system automatically attributes conversions to clicks within the attribution window based on your deduplication settings. Commissions flow through pending, approved, rejected, and paid states using functions like `updateConversionStatus()` and `createPayoutBatch()`. Daily analytics counters track performance metrics using `@convex-dev/sharded-counter`, with `getSummary()` and `getDailyBreakdown()` providing dashboard data. The component maintains isolated tables while your app handles authentication - you pass resolved user IDs as strings and check permissions before calling component functions with an `actorId` for audit trails. ### Upstream README # @ezyyeah/affiliate-tracking Affiliate tracking for Convex: programs, affiliates, campaigns, referral links, click tracking, conversion attribution, commissions, payouts, audit events, cleanup, and daily analytics counters. This package is component-first. It owns its isolated Convex tables, while your app keeps authentication, authorization, billing, checkout, and user records in the host app. Host functions pass app-resolved user, visitor, admin, and order identifiers into the component as strings. ## Install ```bash npm install @ezyyeah/affiliate-tracking ``` Mount the component in your Convex app config: ```ts import affiliateTracking from "@ezyyeah/affiliate-tracking/convex.config.js"; import { defineApp } from "convex/server"; const app = defineApp(); app.use(affiliateTracking); export default app; ``` Run Convex codegen after registering the component: ```bash npx convex dev --typecheck-components ``` ## Client Wrapper Create a small host-side wrapper after codegen has generated `components` for your app: ```ts import { AffiliateTracking } from "@ezyyeah/affiliate-tracking"; import { components } from "./_generated/api"; export const affiliates = new AffiliateTracking(components.affiliateTracking); ``` The wrapper is intentionally thin. It gives you typed, ergonomic calls from host Convex functions while leaving auth and business policy in your app. ## Usage Create or update a program from a host mutation after checking that the caller is allowed to manage affiliate settings: ```ts const programId = await affiliates.upsertProgram(ctx, { slug: "default", name: "Default Program", defaultCommissionType: "percentage", defaultCommissionValue: 20, defaultCurrency: "USD", attributionWindowMs: 30 * 24 * 60 * 60 * 1000, clickDeduplicationMode: "dedupeKey", clickDeduplicationMs: 30 * 60 * 1000, conversionLimitMode: "none", actorId: adminId, }); ``` Enroll an affiliate and create a link: ```ts const affiliateId = await affiliates.upsertAffiliate(ctx, { programId, externalId: userId, status: "active", actorId: adminId, }); await affiliates.upsertLink(ctx, { programId, affiliateId, code: "creator-20", destinationUrl: "https://convex.dev/pricing", actorId: adminId, }); ``` Track clicks from a host HTTP action and redirect the visitor: ```ts const result = await ctx.runMutation( components.affiliateTracking.tracking.trackClick, { programId, code, visitorId, dedupeKey: `${programId}:${code}:${visitorId}`, source: "newsletter", referrer: request.headers.get("referer") ?? undefined, userAgent: request.headers.get("user-agent") ?? undefined, }, ); return Response.redirect(result.destinationUrl, 302); ``` Record purchases with an idempotency key. The component attributes to an explicit click, an explicit link code, or the latest valid visitor click inside the program attribution window. ```ts await affiliates.recordConversion(ctx, { programId, externalId: orderId, idempotencyKey: `order:${orderId}`, visitorId, customerId, amountCents: 12_500, currency: "USD", }); ``` Approve conversions and create payout batches from approved commissions: ```ts await affiliates.updateConversionStatus(ctx, { conversionId, status: "approved", actorId: financeAdminId, }); const payoutId = await affiliates.createPayoutBatch(ctx, { programId, currency: "USD", actorId: financeAdminId, }); ``` ## Attribution Model Each program controls how clicks and conversions are deduplicated: - `attributionWindowMs`: how long a click can receive conversion credit. - `clickDeduplicationMode`: `"dedupeKey"`, `"visitor"`, `"visitorAndLink"`, or `"none"`. - `clickDeduplicationMs`: rolling duplicate click window. - `conversionLimitMode`: `"none"`, `"visitor"`, `"customer"`, or `"customerOrVisitor"`. - `conversionLimitMs`: rolling duplicate conversion window for the selected conversion rule. `recordConversion()` is retry-safe through idempotency records. Reusing the same `idempotencyKey` returns the existing conversion result instead of double-counting commissions. ## Commission Model Programs define default commission settings: - `defaultCommissionType`: `"percentage"` or `"fixed"`. - `defaultCommissionValue`: percentage points or fixed minor units, depending on type. - `defaultCurrency`: the expected commission currency. Campaigns can override the program commission type and value. Conversions store their calculated commission and can move through pending, approved, rejected, and paid operational states. ## Public API Wrapper methods: - `upsertProgram(ctx, args)` - `getProgram(ctx, args)` - `upsertAffiliate(ctx, args)` - `upsertCampaign(ctx, args)` - `upsertLink(ctx, args)` - `trackClick(ctx, args)` - `recordConversion(ctx, args)` - `updateConversionStatus(ctx, args)` - `updateCommissionStatus(ctx, args)` - `createPayoutBatch(ctx, args)` - `updatePayoutStatus(ctx, args)` - `getSummary(ctx, args)` - `cleanupExpired(ctx, args)` - `resetProgramData(ctx, args)` Component modules: - `programs`: `upsertProgram`, `getProgram`, `listPrograms`, `updateProgramStatus` - `affiliates`: `upsertAffiliate`, `getAffiliate`, `listAffiliates`, `updateAffiliateStatus` - `campaigns`: `upsertCampaign`, `getCampaign`, `listCampaigns`, `updateCampaignStatus` - `links`: `createLink`, `upsertLink`, `getLink`, `resolveLink`, `listLinks`, `updateLinkStatus` - `tracking`: `trackClick`, `recordConversion`, `getConversion`, `listConversions` - `commissions`: `updateCommissionStatus`, `updateConversionStatus`, `listCommissions` - `payouts`: `createPayoutBatch`, `updatePayoutStatus`, `getPayout`, `listPayableCommissions`, `listPayouts` - `analytics`: `getSummary`, `getDailyBreakdown` - `maintenance`: `cleanupExpired`, `resetProgramData` ## Analytics Clicks and conversions update daily sharded counters for program, affiliate, campaign, and link scopes through `@convex-dev/sharded-counter`. Use `getSummary` for aggregate dashboard numbers and `getDailyBreakdown` for bounded date ranges. Analytics read exact per-day counter values, so keep dashboard ranges intentionally bounded. ## Operational Notes - Host app auth is intentionally not embedded. Check permissions in your app, then pass `actorId` for auditability. - IDs at the component boundary are strings in generated host APIs, as expected for Convex components. - All public component functions validate arguments with Convex validators. - `cleanupExpired` removes expired clicks and idempotency records and is safe to call from a cron or admin mutation. - `resetProgramData` is destructive and clears a program's component-owned records and counter totals. - Payout creation only includes approved commissions that are not already attached to a payout. ## Test Helper Package consumers can use the test helper with `convex-test`: ```ts import { api, initAffiliateTrackingTest, } from "@ezyyeah/affiliate-tracking/test"; const t = initAffiliateTrackingTest(); const programId = await t.mutation(api.programs.upsertProgram, { slug: "default", name: "Default Program", defaultCommissionType: "percentage", defaultCommissionValue: 20, defaultCurrency: "USD", }); ``` ## Package Exports - `@ezyyeah/affiliate-tracking` - `@ezyyeah/affiliate-tracking/convex.config.js` - `@ezyyeah/affiliate-tracking/convex.config` - `@ezyyeah/affiliate-tracking/_generated/component.js` - `@ezyyeah/affiliate-tracking/_generated/component` - `@ezyyeah/affiliate-tracking/test` ## Local Example The repository includes a runnable Vite + React example app in [`examples`](./examples). It demonstrates program creation and selection, affiliate enrollment, link creation, redirect tracking, conversion recording, analytics, approvals, payout creation, paid payout marking, and cleanup. From the repository root: ```bash pnpm install pnpm build pnpm dev ``` That starts: - `convex dev --typecheck-components` for the example backend in `examples/convex` - Vite on [http://127.0.0.1:5173](http://127.0.0.1:5173) Convex writes `VITE_CONVEX_URL` to `examples/.env.local`. If your deployment site URL cannot be derived from the Convex URL, add: ```bash VITE_CONVEX_SITE_URL=https://your-deployment.convex.site ``` ## Local Development ```bash pnpm install pnpm typecheck pnpm test pnpm build pnpm pack ``` Run `pnpm codegen` only after configuring a Convex deployment or inside a host app that has registered the component. Deployment-backed smoke checks still require a Convex project because packaged components need app-side codegen after registration. ## References - [Convex Components](https://docs.convex.dev/components) - [Convex HTTP Actions](https://docs.convex.dev/functions/http-actions) - [Convex Scheduled Functions](https://docs.convex.dev/scheduling/scheduled-functions) - [Building and publishing NPM package components](https://docs.convex.dev/components/authoring#building-and-publishing-npm-package-components)