pex

Commission rules

A program's commissionRules field is an array of CommissionRule objects. Each rule targets one conversion event and defines how the partner earns on that event. Multiple rules let you pay partners across the full customer lifecycle — $5 on install, 20% of each purchase, 15% of every subscription renewal — all within one program and one link.

The CommissionRule type

interface CommissionRule {
  /** The conversion event this rule applies to. */
  event: "install" | "purchase" | "subscription_renewal";

  /** CPA pays a flat dollar amount; RevShare pays a percentage of the transaction value. */
  type: "cpa" | "revshare";

  /** For CPA rules: the flat amount in USD. */
  amountUsd?: number;

  /** For RevShare rules: the percentage of the transaction value (e.g. 20 = 20%). */
  percentage?: number;

  /** For subscription_renewal rules: stop crediting after this many renewals.
      Omit for lifetime commissions. */
  maxCredits?: number;

  /** Optional: rule only applies to conversions on or after this ISO timestamp. */
  effectiveFrom?: string;

  /** Optional: rule only applies to conversions on or before this ISO timestamp. */
  effectiveTo?: string;
}

Multi-event rules

A single program can carry rules for different events. When a conversion arrives, Apex finds the matching rule by event type and applies it:

commissionRules: [
  { event: "install", type: "cpa", amountUsd: 5 },
  { event: "purchase", type: "revshare", percentage: 20 },
  { event: "subscription_renewal", type: "revshare", percentage: 15, maxCredits: 12 },
]

With this configuration:

  • Partner earns $5 when a referred user installs
  • Partner earns 20% of every one-time purchase
  • Partner earns 15% of every subscription renewal, up to 12 renewals

If no rule matches the event, no commission is created for that conversion.

Recurring subscription commissions

The subscription_renewal event fires on each renewal cycle (monthly, annual, etc.) when Stripe sends invoice.paid for a subscription originally attributed to a partner.

FieldMeaning
event: "subscription_renewal"Matches renewal invoices
typecpa (flat amount per renewal) or revshare (percentage of the renewal invoice)
maxCreditsCap on total renewal commissions. After this many credits, no more commissions fire for this subscription. Omit for lifetime.

Partners see recurring earnings in their portal with a per-subscription breakdown showing each renewal date and amount.

Time-bounded promotional rules

Use effectiveFrom and effectiveTo to run limited-time bonuses:

{
  event: "purchase",
  type: "cpa",
  amountUsd: 10,
  effectiveFrom: "2026-06-01T00:00:00Z",
  effectiveTo: "2026-06-30T23:59:59Z",
}

Only conversions occurring within the window earn this bonus. The regular rule for the same event type still applies outside the window.

When multiple rules match the same event:

  1. Time-bounded rules take priority over unbounded rules
  2. If multiple time-bounded rules overlap, the most recently created rule wins
  3. The partner earns from the winning rule, not the sum of all matching rules

Rule evaluation order

For each incoming conversion event:

  1. Filter rules to those matching the event type
  2. Among matches, prefer rules where the current time falls within effectiveFrom / effectiveTo
  3. If no time-bounded rule matches, fall back to the unbounded rule for that event
  4. Apply the winning rule's rate to compute the commission amount
  5. Create the AffiliateConversion record

Commission history

Every time a program's commissionRules change, a new CommissionChangeEntry is appended to each affected membership's commissionHistory:

interface CommissionChangeEntry {
  commissionRules: CommissionRule[];
  effectiveFrom: string; // ISO timestamp
  reason: string;
  source: "program_default" | "invite_override";
}

The source field controls inheritance:

  • program_default — inherited from the program; updated when the program's rules change
  • invite_override — set at invite time via InviteCreatorDialog; sticky across program updates

Updating rules

await apex.updateProgram(programId, {
  commissionRules: [
    { event: "install", type: "cpa", amountUsd: 7 },
    { event: "purchase", type: "revshare", percentage: 25 },
  ],
});

// Fan out new defaults to all non-override members
await apex.applyProgramDefaultToMembers(programId);

Members with source: "invite_override" keep their original rates. Only members on program_default receive the updated rules.