Affiliate Tracking
A Bijection component for affiliate tracking, attribution, commissions, payouts, and analytics.
git clone https://github.com/bijectionhq/affiliate-tracking.git
cd affiliate-tracking
git checkout 0dee74dc15dbbd40a2a55579d9b4661ae6941ffeBijection repository access is required. Request access.
Description
A Bijection component for affiliate tracking, attribution, commissions, payouts, and analytics.
Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.
Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.
Getting started
- Get the source at the revision above and read its package documentation.
- Check the required Bijection SDK version, dependencies, and external service credentials.
- Follow the package setup instructions and the guide to using components.
- Exercise the component in your deployment according to your project’s verification rules.
Open source
Maintained source: bijectionhq/affiliate-tracking. Based on ezyyeah/affiliate-tracking. License: MIT. Consult the repository for license terms, notices, and contributor attribution.
From the upstream publisher
A Convex component for affiliate tracking, attribution, commissions, payouts, and analytics.
Explore the demo ↗Upstream overview
From the upstream package documentation.
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
From the upstream package documentation.
- 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
From the upstream package documentation.
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 package FAQ
How does the affiliate attribution model work?
The @ezyyeah/affiliate-tracking component uses configurable attribution windows and deduplication rules per program. You can set how long clicks remain eligible for conversion credit, deduplicate clicks by visitor or link, and limit conversions by customer or visitor within rolling time windows.
Can I customize commission rates for different affiliates?
Yes, the component supports both program-level default commissions and campaign-specific overrides. Programs define default commission types (percentage or fixed) and values, while campaigns can override these settings for specific affiliate promotions or products.
How does the component handle authentication and authorization?
The @ezyyeah/affiliate-tracking component intentionally does not handle auth - your host app manages authentication and authorization. You check permissions in your Convex functions and pass actorId strings to component functions for audit trails.
What analytics data does the component provide?
The component tracks clicks, conversions, commissions, and revenue through daily sharded counters. You can access aggregate summaries and daily breakdowns scoped by program, affiliate, campaign, or link using the getSummary and getDailyBreakdown functions.
How do I handle duplicate conversions and clicks?
The @ezyyeah/affiliate-tracking component provides multiple deduplication strategies including visitor-based, link-based, and custom deduplication keys. Conversions use idempotency keys to prevent double-counting, and you can configure rolling windows for both click and conversion deduplication.
Use with agents and CLI
Give your coding agent the component’s source identity and setup guidance.
Help me integrate Affiliate Tracking with Bijection.
Read: https://bijection.com/components/affiliate-tracking/SKILL.md
Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.Upstream README.md
View repository ↗Documentation from ezyyeah/affiliate-tracking, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.
@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
npm install @ezyyeah/affiliate-tracking
Mount the component in your Convex app config:
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:
npx convex dev --typecheck-components
Client Wrapper
Create a small host-side wrapper after codegen has generated components for your app:
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:
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:
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:
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.
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:
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,updateProgramStatusaffiliates:upsertAffiliate,getAffiliate,listAffiliates,updateAffiliateStatuscampaigns:upsertCampaign,getCampaign,listCampaigns,updateCampaignStatuslinks:createLink,upsertLink,getLink,resolveLink,listLinks,updateLinkStatustracking:trackClick,recordConversion,getConversion,listConversionscommissions:updateCommissionStatus,updateConversionStatus,listCommissionspayouts:createPayoutBatch,updatePayoutStatus,getPayout,listPayableCommissions,listPayoutsanalytics:getSummary,getDailyBreakdownmaintenance: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
actorIdfor 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.
cleanupExpiredremoves expired clicks and idempotency records and is safe to call from a cron or admin mutation.resetProgramDatais 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:
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. 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:
pnpm install
pnpm build
pnpm dev
That starts:
convex dev --typecheck-componentsfor the example backend inexamples/convex- Vite on 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:
VITE_CONVEX_SITE_URL=https://your-deployment.convex.site
Local Development
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.