Learn how to design and build a web app that tracks partner clicks, conversions, and revenue. Covers data model, tracking, reporting, payouts, and privacy.

Partner revenue attribution is the system that answers a simple question: which partner should get credit (and how much) for a revenue event? In a web app, that means you’re not just counting clicks—you’re connecting a partner’s referral to a later conversion, turning that into a clear revenue number, and making it auditable.
Start by writing a one-sentence definition that includes (1) what gets attributed, (2) to whom, and (3) under what rules. For example:
This definition becomes the anchor for your requirements, your data model, and the disputes you’ll have to resolve later.
“Partner” often includes several groups with different expectations and workflows:
Avoid forcing all of them into one workflow too early. You can still use a unified system (partners, programs, contracts) while supporting multiple referral methods (links, codes, manual deals).
A practical partner revenue attribution web app must reliably deliver four outcomes:
If any one of these is weak, partners won’t trust the numbers—even if the math is correct.
For an actionable build guide, the goal isn’t to debate attribution philosophy—it’s to help you ship a working system. A realistic first version should:
You can add advanced features (multi-touch attribution, cross-device stitching, complex fraud scoring) after the basics are dependable and testable.
Before you choose an attribution model or design a database, get crisp on what the app must prove to the business. Partner revenue attribution is ultimately a set of answers people trust enough to pay money on.
Most teams build for “partners” first and discover later that finance or support can’t verify anything. List your primary users and the decisions they make:
Write these as plain-language queries your UI and reports must support:
At minimum, plan for: click, lead, trial start, purchase, renewal, and refund/chargeback. Decide which are “commissionable” and which are supporting evidence.
Start with one clear rule set—commonly last-touch within a configurable window—then add multi-touch only when you have strong reporting needs and clean data. Keep the first version easy to explain and audit.
Before you write any code, decide what “gets credit” and when that credit expires. If you don’t set rules upfront, you’ll end up debating edge cases (and partner complaints) on every payout.
Last click assigns 100% credit to the most recent partner click before conversion. It’s simple and widely understood, but can over-reward late-stage coupon traffic.
First click assigns 100% credit to the first partner who introduced the customer. It favors discovery partners, but may under-reward partners who help close the deal.
Linear splits credit evenly across all qualifying partner touches in the window. It can feel “fair,” but is harder to explain and can dilute incentives.
Time-decay assigns more credit to touches closer to the conversion while still recognizing earlier influence. It’s a compromise model, but requires more math and clearer reporting.
Choose one default model for most conversions (many apps start with last click because it’s easiest to explain and reconcile). Then explicitly document exceptions so support and finance can apply them consistently:
Set one or more windows like 7 / 30 / 90 days. A practical approach is a standard window (e.g., 30 days) plus shorter windows for coupon partners if needed.
Also define re-engagement rules: if a customer clicks a different partner link within the window, do you switch credit immediately (last click), split credit, or keep the original partner unless the new click is within a “close window” (for example, 24 hours)?
Decide what you attribute: initial purchase only, or net revenue over time.
Write these rules into a short “Attribution Policy” doc and link it in your partner portal so system behavior matches partner expectations.
A clean data model is the difference between “we think this partner drove the sale” and “we can prove it, reconcile it, and pay correctly.” Start with a small set of core entities and make the relationships explicit through immutable IDs.
partner_id, status, payout terms, default currency.campaign_id, start/end dates.link_id, belongs to partner_id and optionally campaign_id.click_id, references link_id and partner_id.visitor_id (often derived from a first-party cookie ID).conversion_id, references click_id (when available) and visitor_id.order_id, references customer_id and is linked to conversion_id.payout_id, references partner_id and aggregates eligible orders.Your golden path is:
partner_id → link_id → click_id → visitor_id → conversion_id → order_id → payout_id
Keep customer_id alongside order_id so repeat purchases can follow your rules (e.g., “first purchase only” vs “lifetime”). Store both your internal IDs and external ones (e.g., shopify_order_id) for reconciliation.
Orders change. Model that explicitly:
gross_amount, tax_amount, shipping_amount, fee_amount, discount_amount.currency_code plus an fx_rate_to_payout_currency (and the timestamp/source of that rate).order_id (e.g., order_adjustment_id, type = partial_refund). This preserves an auditable history and avoids rewriting totals.Add audit fields everywhere: created_at, updated_at, ingested_at, source (web, server-to-server, import), and immutable identifiers.
For fraud analysis without storing raw personal data, store hashed fields like ip_hash and user_agent_hash. Finally, keep a lightweight change log (entity, entity_id, old/new values, actor) so payout decisions can be explained later.
Click tracking is the foundation of partner revenue attribution: every partner link should create a durable “click record” you can later connect to a conversion.
Use a single canonical link format that partners can copy/paste anywhere. In most systems, the partner-facing link should not include a click_id—your server generates that.
A clean pattern is:
/r/{partner_id}?campaign_id=...&utm_source=...&utm_medium=partner&utm_campaign=...
Practical parameter guidance:
Route all partner traffic through a redirect endpoint (e.g., /r/{partner_id}):
This makes click creation consistent, prevents partners from spoofing click IDs, and centralizes rule enforcement.
Most teams use cookie as primary, localStorage as fallback, and server-side sessions only for short-lived flows.
For mobile web, cookies may be less reliable, so use the redirect endpoint and store click_id in both cookie + localStorage.
For app-to-web, support:
Document the exact link rules inside your partner portal (see /blog/partner-links) so partners don’t “get creative” with parameters.
Conversion tracking is where attribution systems either earn trust—or quietly lose it. Your goal is to record a single, canonical “conversion” event per real purchase (or signup), with enough context to connect it back to a partner click.
Most products can observe conversions from several places:
Recommendation: treat your backend order service as the canonical conversion recorder, and optionally use payment webhooks as a confirmation/update signal (e.g., moving an order from pending to paid). Client-side events can be used for debugging or funnel analytics, not for payout-grade attribution.
To attribute revenue later, the conversion event needs a stable identifier and a way to link to a click.
Common approach:
Your primary join should be conversion.click_id → click.id. If click_id is missing, define explicit fallback rules, such as:
Make these fallbacks visible in admin tooling so support can explain outcomes without guessing.
Webhooks and client calls will retry. You must be able to receive the same conversion multiple times without double-counting.
Implement idempotency keys using a stable unique value, such as:
order_id (best if globally unique)payment_provider_charge_idStore the key on the conversion record with a unique constraint. On retry, return success and do not create a second conversion. This single choice prevents the most common “phantom revenue” payout bugs.
This is the point where tracking turns into money. Your app needs a clear, auditable path from a tracked event to an amount you can pay—while staying aligned with how finance measures revenue.
A practical lifecycle looks like:
Keep timestamps for each state change so you can explain when and why a conversion became payable.
Decide what “revenue” means in your system and store it explicitly:
Common structures you can support without hardcoding a single policy:
Finance teams need data they can reconcile:
A partner program lives or dies on trust. Your portal is where partners validate that clicks turned into conversions and that conversions turned into money. Your admin dashboard is where your team keeps the program clean, responsive, and fair.
Start with a small set of screens that answer the questions partners ask every day:
For the conversion list, include the columns that reduce support tickets: conversion time, order ID (or masked ID), attributed amount, commission rate, status (pending/approved/rejected/paid), and a short “reason” field when rejected.
Partners and admins both need quick ways to slice results without exporting to spreadsheets. Prioritize:
If you track multiple products or plans, add a product filter—but only after the basics are stable.
Admin tooling should focus on speed and accountability:
Keep manual controls limited: you want admins to correct exceptions, not casually rewrite history.
Enforce RBAC from day one:
Implement permission checks at the API level (not just the UI), and log access to sensitive views like payout exports.
A partner revenue attribution app tends to be “write-heavy”: lots of clicks, lots of conversion events, and periodic read-heavy reporting. Design for high-volume ingestion first, then make reporting fast with aggregation.
One workable baseline is Postgres + an API + a modern frontend:
Keep tracking endpoints stateless so you can horizontally scale them behind a load balancer.
If you want to move from spec to working internal tooling quickly, Koder.ai can help you prototype the admin dashboard, partner portal, and core APIs via chat-driven “vibe-coding.” You can use Planning Mode to outline flows (tracking → attribution → payouts), generate a React frontend with a Go + PostgreSQL backend, and still export the source code when you’re ready to productionize.
Don’t do expensive work in the request/response cycle. Use a queue (SQS/RabbitMQ/Redis queues) and workers for:
Workers should be idempotent: if a job runs twice, results stay correct.
Click tables grow fast. Plan retention up front:
In Postgres, consider time-based partitioning for clicks (e.g., monthly partitions) and index by (occurred_at, partner_id) plus any lookup keys like click_id. Partitioning improves vacuum/index maintenance and makes retention as simple as dropping old partitions.
Tracking failures are often silent unless you measure them. Add:
Log with a consistent correlation ID (e.g., click_id/conversion_id) so support can trace a partner’s claim end-to-end.
Fraud controls aren’t just about catching bad actors—they also protect honest partners from being underpaid due to noisy data. A good approach combines automatic safeguards (fast, consistent) with human review (flexible, contextual).
Self-referrals happen when partners attempt to earn commission on their own purchases or sign-ups (often detectable via repeated payment fingerprints, emails, or device signals).
Cookie stuffing and click spam try to “claim” users without real intent—e.g., invisible iframes, forced redirects, or high click volume with near-zero engagement.
Fake leads are low-quality form submissions meant to trigger CPA payouts. Coupon leakage occurs when a private code is shared publicly, shifting attribution away from the real source.
Start with rate limits on clicks and conversions per partner, per IP range, and per user/session. Pair this with bot detection signals: user-agent anomalies, missing JavaScript execution signals, suspiciously consistent timing, data-center IPs, and repeated device fingerprints.
Add anomaly alerts. You don’t need advanced ML to get value: simple thresholds like “conversion rate spikes 5× week-over-week” or “many conversions with identical metadata” catch most issues. Alerts should link to a drill-down view in your admin dashboard (e.g., /admin/partners/:id/attribution).
For data quality, validate inputs at ingestion. Require click IDs or signed partner tokens where applicable, reject malformed UTMs, and normalize country/currency fields. Many investigations stall because logs are incomplete or joins are ambiguous.
Give operators a clear queue: flags (reason + severity), notes, and a timeline of related clicks and conversions.
Support conversion holds (“pending”) so suspicious events don’t immediately enter payouts. Implement partner warnings and escalation (temporary payout delays, traffic restrictions, or program removal), and make actions consistent via templates.
Keep an immutable audit trail for:
This is essential for partner disputes, finance reconciliation, and internal accountability—especially once multiple people can change rules and payouts.
Partner revenue attribution touches tracking, identity, and payments—three areas where small mistakes can create big risk. The goal is to measure referrals and calculate payouts while collecting as little personal data as possible and keeping what you do store protected.
Start from the minimum dataset required to attribute a conversion and reconcile revenue:
Avoid collecting data that is not essential:
If you rely on cookies or similar identifiers, you may need consent depending on region and what you store.
A practical approach is to support server-side tracking (postbacks) for partners who can do it, and only use client-side cookies where allowed and necessary.
Treat attribution and payout data as sensitive business data, and apply standard controls:
Also consider data retention: keep raw event-level records only as long as needed for reconciliation and disputes, then aggregate or delete.
Logs often become an accidental data leak. Make logging rules explicit:
Publish a clear privacy notice and document your data flows. When partners ask how tracking works, you’ll be able to explain it plainly—and safely.
A partner attribution system is only useful if partners trust it and finance can reconcile it. Treat testing and launch as part of the product: you’re validating business rules, data integrity, and operational workflows—not just code.
Start with a small set of “golden” scenarios you can replay end-to-end:
Changing attribution rules will change historical numbers—plan for that explicitly. Keep raw events (clicks, conversions, refunds) immutable, then recompute attribution into versioned tables (e.g., attribution_results_v1, v2). For large histories, backfill in batches (by day/week) with a dry-run mode that produces a diff report finance can review.
Pilot with a small group of partners (5–10). During the pilot:
Ship changes behind feature flags, document rule versions in the portal, and announce any changes that may affect earnings.
Operationally, it helps to have fast rollback for reporting and payout logic. If you’re building quickly in Koder.ai, snapshots and rollback can be useful for safely iterating on rule code and dashboard changes while keeping a known-good version ready.
If you want to explore packaging and onboarding later, see /pricing, or browse related guides in /blog.
Partner revenue attribution is the set of rules and data that determine which partner gets credit for a revenue event (and how much), based on evidence like click IDs, coupon codes, and timing windows.
A useful definition includes:
Start by writing a one-sentence policy, then list exceptions.
A solid V1 policy is often:
Then document exceptions like coupon precedence, renewals, and whether direct traffic breaks attribution.
At minimum, track:
Even if you later add leads or trials, those three give you the ability to connect traffic → revenue → reversals in a payout-safe way.
Use a redirect endpoint (e.g., /r/{partner_id}) that:
This prevents partners from spoofing click IDs and makes tracking consistent across placements.
Prefer server-side order creation (your backend) as the canonical conversion source.
Practically:
click_id (or attribution token) to the order at creation timeThis reduces double-fires and makes finance reconciliation much easier.
Use idempotency keys so retries don’t create duplicate conversions.
Common keys:
order_id (best if globally unique)payment_provider_charge_idEnforce uniqueness in the database (unique constraint). On repeats, return success without creating a second conversion or commission line item.
Aim for a chain you can prove end-to-end:
partner_id → link_id → click_id → visitor_id → conversion_id → order_id → payout_idStore both internal and external IDs (e.g., shopify_order_id) and keep timestamps (created_at, ingested_at) so you can trace disputes and reconcile with your billing system.
Model money with auditability and reversals in mind:
currency_codeThis preserves history and lets you create negative line items in later payout cycles if needed.
Start with a small set of screens that reduce support tickets:
Make every conversion explainable with evidence fields like click time, order ID (masked), and applied rule.
Use lightweight, consistent safeguards:
For privacy, store the minimum needed (pseudonymous IDs), hash sensitive signals (like IP) when possible, and avoid logging secrets or personal/payment data.