Facilitated payouts
Apex runs the money rail for partner commissions via Stripe Connect. Two critical properties:
- Apex never advances funds. Money only moves if the merchant has balance in their Stripe account. No platform overdraft.
- Money movement happens in the merchant's Stripe context. Transfers fire under
{stripeAccount: merchantStripeAccountId}— Apex's platform balance is never touched.
This makes Apex a facilitator, not an escrow. The tradeoff: the partner has to wait until the merchant's Stripe balance is there + the scheduled payout cycle hits.
The shape of a payout
merchant's Stripe balance
↓ transfer (principal, in merchant's account context)
partner's Stripe Express account
+
merchant's Stripe balance
↓ transfer (fee, in merchant's account context)
Apex Connect platform account
Two stripe.transfers.create calls per payout. Same idempotency batch (payout:<id>:principal + payout:<id>:fee). If the principal fires but the fee fails, the retry only re-attempts the fee leg.
The full lifecycle of one commission
- Conversion attribution — partner's link (or coupon code) drives a signup; the contact is stamped with
referringAffiliateId - Purchase / subscription event — commission rules evaluate the event type (
install,purchase,subscription_renewal), auto-credit fires, creates anAffiliateConversionwithstatus: "pending"(or"pending_review"if risk score ≥ 0.5) - Merchant approves (or auto-approve per
autoPayoutPolicy) — transitions to"approved", creates aHoldbackEntrysplitting commission into available + held per tier - Release cron fires past-due holdback entries (after 30 / 60 / 90 days by tier) — flips
releasedAt - Payout orchestrator runs per merchant's
autoPayoutPolicyschedule (daily / weekly / manual) — batches all released-available entries per partner, fires the pair of transfers - Held portion stays reserved — only releases + pays out after the hold window. Chargebacks in the interim debit this portion first.
Recurring subscription payouts
For subscription_renewal events, the flow repeats on every renewal cycle:
- Stripe fires
invoice.paidfor the renewal - Apex matches the subscription to the original referring partner via
referringAffiliateId - The
subscription_renewalcommission rule evaluates — creates a newAffiliateConversion - If
maxCreditsis set on the rule and the partner has already earned that many renewal commissions, the event is skipped - The new conversion enters the same approval → holdback → release → payout pipeline
Partners see recurring earnings accumulate in their portal with a clear breakdown per subscription and renewal period.
autoPayoutPolicy — merchant control of when transfers fire
| Policy | Trigger |
|---|---|
manual | Merchant approves each batch from /dashboard/partners/payouts/queue |
auto | Daily cron — every released-available balance over $0 pays out |
auto_under_cap | Weekly cron — auto for partners under autoPayoutCapUsd, manual above |
Set per-project (default) or per-program (override). Merchants choose their own cadence based on cash flow.
Insufficient balance
When the merchant's Stripe balance is short, the executor returns insufficient_balance with the shortfall USD and a 24-hour retryAtMs. The cron schedules a retry. We don't pull funds from Apex.
A graduated, non-accusatory escalation kicks in:
| Day | Failures | Action |
|---|---|---|
| 0 | 1st | Silent — retry in 24h |
| 1 | 2nd (~48h) | Merchant email + internal notification |
| 2 | 3rd (~72h) | Dashboard card + 2nd email + partner visibility flips per trust tier |
| 7 | 7th | Open a support case (ruleId: merchant_cash_flow, severity: medium, not labeled as fraud). Retries pause until human review. |
Automation never freezes autoPayoutPolicy. A human (Fraud Ops or account manager) is always the one who pulls that trigger.
Partner-visible state during delays
Delay visibility is tiered by trust:
| Partner tier | Day 0-2 | Day 3-6 | Day 7+ |
|---|---|---|---|
| NEW | "Your payout is processing" | "Your payout is still processing" | "This payout is delayed — our team is working with the merchant" |
| TRUSTED | "Your payout is processing" | "Your merchant is topping up their Stripe balance" | "This payout is delayed — our team is working with the merchant" |
| VERIFIED | "Your payout is processing" | "Your merchant is topping up their Stripe balance" | "This payout is delayed — our team is working with the merchant" |
Partner-facing copy never uses the word "fraud". Merchant-facing emails use "cash flow" and "action needed" — never "fraud" — at every escalation level.
Fees
Apex charges a facilitation fee on every transfer: 25 basis points + $0.50 flat by default. Enterprise merchants can negotiate custom rates via /admin/fees. See Fee policy.
Fees are snapshotted on the HoldbackEntry at approval time. Admin edits after approval don't change what a merchant owes on already-approved commissions.
1099 tax compliance
Stripe handles 1099-NEC issuance for US partners earning $600+ in a calendar year. W-9/W-8BEN forms are collected during Stripe Express onboarding. See Tax compliance for the full flow.
Clawbacks
When a charge.refunded webhook lands, Apex:
- Finds every conversion linked to the
paymentIntent - Flips them to
clawed_back - Debits partner holdback entries oldest-first per
applyClawback - If the reserve is insufficient, logs the shortfall + flags for Fraud Ops review
Disputes (charge.dispute.created) flip the conversion to pending_review and wait for dispute resolution before debiting.