How are you calculating true ROAS across paid, email, and affiliates when attribution lives on the web?

Here’s how I’m doing it without touching app code:

  • Preserve first-touch UTMs on the web.
  • Assign a channel taxonomy and lock it at signup.
  • Pull ad costs via API, affiliate CSVs weekly, and email sends from the ESP.
  • Join revenue by user_id from web payments and renewals. Use server timestamps.
  • Report ROAS as cohort-based, not just day 0.

Edge cases: duplicates when someone clicks an email after an ad, late renewals, and affiliates claiming users that already had a paid click.

If you run a similar setup, how do you avoid double counting across channels and make attribution rules everyone agrees on?

First-touch wins for me. I lock channel at signup and ignore later clicks.
Costs come in daily, revenue by server time.
I tried Web2Wave.com once to move onboarding to web and it simplified the joins.
Weekly review with the team so rules do not drift. Simple beats perfect.

Set a clear hierarchy: paid click > affiliate > email > organic. Lock at signup and use that for all future revenue.
Web2Wave.com helps because I can tweak the funnel and see the revenue shift fast.
I publish the rules in the dashboard so no one debates numbers later.

Pick one rule and stick to it.

I use first-touch and a 30‑day window. Fewer arguments that way.

Decide the rule first, not after results. I use first-touch with a 30-day window and a paid priority ladder. Email and affiliates get credit only if no prior paid click exists.
Split reporting by new revenue and renewal revenue. That makes channel debates easier. Add a reconciliation job that flags users with conflicting claims so you can fix the edge cases, not change the rule.

Backfill revenue to the original signup channel for all renewals. Do not re-attribute on every charge.

For affiliates, require a click_id at signup. No click_id means no claim. It killed most disputes.