DEV Community

Anand Rathnas
Anand Rathnas

Posted on Originally published at jo4.io

Why We Killed Hold Windows in Our Affiliate Marketplace

This article was originally published on Jo4 Blog.

We spent weeks building a settlement system for our affiliate marketplace. Hold windows. Clawbacks. Carry-forwards. Commission auto-approval schedulers. The works.

Then we deleted it all.

What We Built (and Why)

The idea was straightforward: when an affiliate drives a conversion, don't pay them immediately. Hold the commission for X days. If the customer refunds, claw back the commission. If there's a remainder below the payout threshold, carry it forward to next month.

Sounds reasonable, right? Every major affiliate network does something like this.

So we built:

  • Hold windows — configurable per campaign (7, 14, 30 days)
  • Clawback logic — refunds during hold period reduce the affiliate's balance
  • Carry-forwards — sub-threshold amounts roll to next settlement period
  • Auto-approval scheduler — commissions move from HELD → APPROVED after the hold window

What Went Wrong

Legal review flagged it.

The problem wasn't technical — it was regulatory. Holding affiliate funds creates obligations:

  1. Money transmission concerns — holding and releasing funds on a schedule starts to look like money transmission in some jurisdictions
  2. Dispute resolution requirements — clawbacks need a formal dispute process, not just an automatic deduction
  3. Accounting complexity — carry-forwards create accrued liabilities that need proper bookkeeping
  4. Tax reporting — when was the income earned? When the conversion happened, when the hold expired, or when the payout was made?

We're a URL shortener that added an affiliate marketplace. We're not a payment processor. Building the compliance infrastructure for hold windows was going to cost more than the feature was worth.

What We Did Instead

Deleted it. All of it.

- CommissionAutoApprovalScheduler.java (deleted)
- holdWindowDays field (removed from campaigns)
- clawbackAmount, previousCarryForward (removed from settlements)
- HELD commission status (removed)
Enter fullscreen mode Exit fullscreen mode

Replaced with:

  1. Firm offers — brands mark campaigns as non-negotiable. Publishers accept the commission as-is. No back-and-forth.
  2. Immediate settlement — conversions are confirmed by Stripe webhooks. When Stripe says the charge succeeded, the commission is earned. Period.
  3. Monthly payouts — simple monthly settlement with no holds. If there's a refund, the brand eats it (they can adjust their commission rates accordingly).

What We Added

The simplification freed up time for features that actually matter:

  • Partnership lifecycle — pause, resume, terminate partnerships with full event tracking
  • Multi-channel notifications — email, in-app, and push notifications for partnership events
  • Campaign budgets and expiry — brands set a maximum spend and end date, campaigns auto-pause when limits are hit
  • Firm offers — skip the negotiation dance when the brand knows what they want to pay

The Numbers

Metric Before After
Settlement-related DB tables 5 2
Commission statuses 6 (PENDING, HELD, APPROVED, CLAWED_BACK, PAID, FAILED) 3 (PENDING, APPROVED, PAID)
Settlement logic (lines) ~800 ~200
Legal questions Many Few

Lessons Learned

  • Legal review before building, not after — we should have asked "can we hold affiliate funds?" before writing a single line of code. Would have saved weeks.
  • Complexity is a liability — every line of settlement logic was a potential bug, a potential legal issue, and a potential support ticket. Less code = less risk.
  • Copy the leader carefully — "Amazon Associates does hold windows" doesn't mean you should. Amazon has a legal team. You have a Notion doc.
  • Simpler products attract more users — publishers don't want to learn about hold windows and carry-forwards. They want to drive traffic and get paid.
  • KISS isn't lazy, it's strategic — deleting working code feels wrong. It's not. It's the highest-ROI engineering decision you can make.

Ever deleted a feature you spent weeks building? What was the hardest "kill your darlings" moment in your product? Share below.

Building jo4.io — a URL shortener with an affiliate marketplace that pays publishers without the complexity.

Top comments (0)