Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Infrastructure & Operations11 min read

Referral Programs That Pay: A Technical Method for Verifying Payouts and Tracking Referral Events

A reproducible engineering workflow for auditing referral programs that pay, instrumenting referral events, and reconciling rewards against on-chain or API evidence.

TL;DR

Referral programs that pay are only trustworthy when the qualifying event, attribution window, payout trigger, and settlement path are all observable. This article gives engineers and operators a reproducible method: model the referral lifecycle, instrument events with idempotent identifiers, reconcile expected rewards against provider APIs or on-chain transfers, and diagnose the common failure modes that make a payout appear missing. It separates documented program behavior from the measurements you must run against your own accounts and audience.

Scope and the direct answer

A referral program that pays is a system with four observable stages: attribution (who referred whom), qualification (what the referred party must do), approval (when the program accepts the event), and settlement (how value reaches the referrer). Most disputes about unpaid referrals come from a mismatch between the referrer's mental model of qualification and the program's documented trigger, not from a missing payment rail.

This guide is written for people who need to evaluate or operate referral programs at a technical level: growth engineers instrumenting referral links, finance operators reconciling commissions, and developers building referral features into their own products. It does not rank specific brands. Concrete endpoint or provider choices belong in RPC Assistant; this page focuses on the verification methodology you can apply to any program.

  • Documented behavior: what the program's terms say about triggers, caps, and payout timing.
  • Measured behavior: what your own event logs and settlement records show.
  • Recommendation: how to structure tracking so the two can be compared without guesswork.

Assumptions and prerequisites

The workflow assumes you can create test referral links, observe the referred account's lifecycle, and read settlement records through an API, dashboard export, or block explorer. For crypto-native programs, you also need read access to the relevant chain through an RPC endpoint; you can source one via the multi-chain RPC endpoints guide and compare service tiers on the RPC pricing page.

You should have a spreadsheet or database table with at least these columns: referral_id, referrer_account, referred_account, link_created_at, signup_at, qualifying_event_at, approval_at, settlement_at, expected_reward, observed_reward, and evidence_url. Every later step writes into this table.

Mental model: the referral lifecycle as a state machine

Treat each referral as a record that moves through states: created, clicked, signed_up, qualified, approved, paid, or rejected. A program that pays reliably will expose or imply transitions between these states. A program that appears not to pay usually has a referral stuck in qualified or approved, or has silently moved to rejected because of a cap, a duplicate-account rule, or a minimum holding period.

The critical design question is which transition the program treats as the payout trigger. Some programs pay on signup, some on first purchase, some after a deposit remains for a fixed period, and some only after the referred account completes a revenue-generating action. Your tracking must record the timestamp of each transition so you can compare it to the program's stated window.

  • Attribution window: how long after a click a signup still counts.
  • Qualification rule: the exact action that makes a referral eligible.
  • Approval delay: the review period before a reward is confirmed.
  • Settlement path: cash, credit, points, or an on-chain transfer.
  • Caps and exclusions: per-account limits, geography, or duplicate detection.

Instrumenting referral events with idempotent identifiers

The most common instrumentation mistake is generating a new identifier at each step, which makes reconciliation impossible. Generate one referral_id when the link is created and carry it through every event. Use a deterministic composite key such as hash(referrer_account + referred_account + campaign_id) so retries and duplicate webhooks collapse into the same record.

The following Node.js example records lifecycle events into a local JSON-lines file. It is intentionally dependency-free so you can run it against a test account before wiring it into production.

const fs = require('fs');
const crypto = require('crypto');

function referralId(referrer, referred, campaign) {
  return crypto
    .createHash('sha256')
    .update(`${referrer}:${referred}:${campaign}`)
    .digest('hex')
    .slice(0, 16);
}

function recordEvent({ referrer, referred, campaign, state, evidence }) {
  const event = {
    referral_id: referralId(referrer, referred, campaign),
    state,
    at: new Date().toISOString(),
    evidence: evidence || null
  };
  fs.appendFileSync('referrals.jsonl', JSON.stringify(event) + '\n');
  return event;
}

recordEvent({
  referrer: 'acct_referrer_001',
  referred: 'acct_referred_001',
  campaign: 'spring_test',
  state: 'qualified',
  evidence: 'order_id=ORD-123'
});

Reconciling expected rewards against settlement records

Reconciliation means joining your expected-reward table to the program's settlement export on referral_id or a documented equivalent. Where the program exposes an API, pull the settlement list and compare counts and amounts. Where the program settles on-chain, query the transfer events for the reward contract and match them to your referrer addresses.

The Python example below performs a read-only reconciliation against a JSON export. Replace the file paths with your own exports; do not hard-code credentials. If your program settles on-chain, fetch the transfer list through an RPC endpoint first and normalize it to the same schema.

import json
from collections import defaultdict

with open('expected_rewards.json') as f:
    expected = json.load(f)

with open('settlement_export.json') as f:
    settled = json.load(f)

settled_by_id = defaultdict(list)
for row in settled:
    settled_by_id[row['referral_id']].append(row)

report = []
for row in expected:
    matches = settled_by_id.get(row['referral_id'], [])
    observed = sum(float(m['amount']) for m in matches)
    report.append({
        'referral_id': row['referral_id'],
        'expected': float(row['expected_reward']),
        'observed': observed,
        'status': 'paid' if observed >= float(row['expected']) else 'unpaid_or_partial'
    })

unpaid = [r for r in report if r['status'] != 'paid']
print(f"checked={len(report)} unpaid={len(unpaid)}")
for r in unpaid:
    print(r)

Reading on-chain settlement evidence

For crypto referral programs, the settlement record is usually a token or native transfer to the referrer address. You can verify it with a read-only RPC call. The curl example below uses eth_getLogs against a placeholder RPC_URL; substitute the chain and contract you are auditing. Keep the block range narrow to avoid timeouts on public endpoints.

If you are running high-volume reconciliation, a dedicated endpoint avoids rate-limit noise during batch queries; see the dedicated node overview for the operational tradeoffs. For endpoint selection by chain, RPC Assistant maintains short recommendations such as the Ethereum RPC API guide.

curl -s RPC_URL \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_getLogs",
    "params": [{
      "address": "REWARD_CONTRACT_ADDRESS",
      "fromBlock": "0x0",
      "toBlock": "latest",
      "topics": ["TRANSFER_EVENT_TOPIC"]
    }]
  }'

Results table procedure and expected verification output

Record your findings in a table with one row per referral and the columns below. Do not fill in numbers you have not observed; leave cells blank until the evidence exists. A healthy program will show a monotonic timestamp sequence and an observed reward that meets or exceeds the expected reward after the approval delay.

referral_idqualified_atapproved_atsettled_atexpectedobservedevidence
(from your log)(from your log)(from program export)(from export or chain)(from terms)(from export or chain)(URL or tx hash)

Expected verification result: every referral that reached the qualified state either appears in the settlement export within the documented approval window, or has a documented rejection reason. Any row that is qualified but neither settled nor rejected is an open case to escalate with the program's support channel, backed by the evidence column.

Troubleshooting missing or partial payouts

When a referral does not pay, work backward through the state machine rather than assuming fraud or a broken payment rail. The table below maps common symptoms to the most likely cause and the evidence that confirms it.

  • Referral stuck at signed_up: the qualifying action was never completed, or the program counts a different action than you assumed.
  • Referral rejected without notice: duplicate-account detection, a cap, or a geography exclusion matched your test account.
  • Approved but not settled: the payout is inside a holding period, below a minimum threshold, or awaiting a manual batch.
  • Settled amount lower than expected: a tiered commission, a currency conversion, or a partial payout split across periods.
  • On-chain transfer not found: wrong chain, wrong contract, or the reward was issued as an internal credit rather than a transfer.

Limitations and tradeoffs

This method verifies what a program actually did for your accounts; it does not predict what it will pay a different audience. Programs change terms, caps, and reward amounts without notice, so a reconciliation that passes today can fail next month. Re-run the procedure on a fixed cadence and keep the raw exports, because support disputes are won with timestamps and identifiers, not screenshots.

There is also a measurement cost. Instrumenting every referral event adds engineering work, and querying settlement data at scale requires reliable RPC access or API quotas. For small programs, a manual export and spreadsheet may be sufficient; for programs with recurring commissions, automation pays for itself quickly. Choose the level of instrumentation that matches the value at stake.

Next steps for operators and builders

If you are evaluating programs, start with the state-machine table and run the reconciliation on a small test cohort before committing real audience. If you are building a referral feature into your own product, reuse the same lifecycle model: emit idempotent events, expose a settlement export, and document the qualification rule in the same place users find the reward amount.

For the infrastructure side, review the networks directory to confirm chain support, compare RPC service options for read-heavy reconciliation jobs, and consult RPC Assistant for concrete endpoint recommendations when you need to query settlement transfers at volume. The methodology stays the same; only the data source changes.

Never Worry about Infrastructure Again

OnFinality takes away the heavy lifting of DevOps so you can build smarter and faster.

Get Started