Programs + memberships
A program is a merchant's commission offer. A membership is one partner on one program. Programs are how you express "I'll pay $5 per install and 20% on every subscription renewal." Memberships are how a specific partner's rate and history live alongside the program's default.
Program shape
| Field | Meaning |
|---|---|
name | Public title, shown in marketplace + partner portal |
description | Marketplace card body, ≤ 600 characters |
vertical | saas / ecommerce / fintech / marketplace / hardware / enterprise / creator_tools — drives marketplace filters |
status | active / paused / archived — paused + archived reject new memberships |
visibility | public (listed in marketplace) or private (invite-only) |
approvalMode | auto (instant join from marketplace) or manual (merchant reviews each application) |
commissionRules | Array of CommissionRule objects — each scoped to an event type (install, purchase, subscription_renewal) with its own rate, caps, and optional time bounds. See Commission rules. |
commissionWindow | How long after first click a conversion still counts (days) |
endsAt | Optional ISO timestamp. When the date passes, the program stops accepting new memberships and no new conversions are attributed. Existing approved commissions still pay out. |
couponSyncMode | stripe / webhook / manual — how coupon codes are synced to Stripe Promotion Codes and attributed back to partners. See Coupon tracking. |
autoPayoutPolicy | manual / auto / auto_under_cap — when transfers fire |
autoPayoutCapUsd | Ceiling for auto_under_cap (per payout, not per day) |
approvalRate, approvalMedianSeconds, paymentOnTimeRate | Reputation counters (nightly cron) |
Commission rules
Programs now carry an array of commissionRules instead of a single commissionStructure. Each rule targets one event type and has its own rate:
commissionRules: [
{
event: "install",
type: "cpa",
amountUsd: 5,
},
{
event: "purchase",
type: "revshare",
percentage: 20,
},
{
event: "subscription_renewal",
type: "revshare",
percentage: 15,
maxCredits: 12, // stop after 12 renewals
},
]
Time-bounded promotional bonuses use effectiveFrom / effectiveTo:
{
event: "purchase",
type: "cpa",
amountUsd: 10,
effectiveFrom: "2026-06-01T00:00:00Z",
effectiveTo: "2026-06-30T23:59:59Z",
}
The rule only applies to conversions that occur within the window. Outside the window, the partner earns the base rule for that event. See Commission rules for the full type reference.
Program expiry
Set endsAt to auto-sunset a program:
- Before
endsAt: program operates normally. - After
endsAt: no new memberships accepted; new conversion events are ignored; existing approved commissions continue through the payout pipeline. endsAtis mutable — extend or remove it at any time while the program is stillactive.
Membership shape
Each AffiliateMembership lives at (projectKey, profileId) and carries:
programId— which program this membership is bound tocommissionHistory— append-only list ofCommissionChangeEntryrecords, each with{commissionRules, effectiveFrom, reason, source}status—active/paused/terminated- Performance counters:
totalClicks,approvedConversions,totalEarnedUsd,totalPaidUsd
The source field in commission history tracks why the rate is what it is:
| Source | Meaning |
|---|---|
program_default | Comes from the program's current default commission rules |
invite_override | Set at invite time (sticky — survives default updates) |
Commission rates are always inherited from the program. Per-membership overrides are set only at invite time via the InviteCreatorDialog flow.
Commission inheritance model
When you create new program defaults and call applyProgramDefaultToMembers(programId), only memberships where the current source is program_default get updated. Sticky invite overrides are preserved.
This gives you two knobs:
- Bulk rate change — update the program, fan out to everyone on the default rate
- Individual negotiation — set a custom rate at invite time for a specific partner
Lifecycle
- Merchant creates a program:
createProgram({...}) - Partners join via marketplace (public+auto), invite (any), or application (public+manual)
- Conversions arrive → commission rules evaluate per event type →
AffiliateConversionrecords accumulate - Merchant approves payouts in batches → HoldbackEntry records created
- Release cron fires past-due entries → available balance accumulates
- Payout orchestrator fires transfers per merchant's
autoPayoutPolicycycle
Marketplace discovery
Public programs show up in partners.apex.inc/programs ranked by:
- Merchant reputation (program
approvalRate,paymentOnTimeRate) - Commission attractiveness (amount or percentage)
- Volume (recent conversions)
- Freshness (newer programs get a small boost for discovery)
Private programs never appear; partners must be invited.
Related
- Commission rules — deep dive on multi-event rules and promotional bonuses
- Coupon tracking — promo code attribution
- Inviting partners
- Trust tiers — the layer that gates payouts
- Facilitated payouts