/v1/referrals endpoints and client.referrals.*. For the step-by-step integration, see Add Referrals. For the business view (where the pool comes from and how it is sized), see Referrals in the Campaigns section.
Campaigns created without a referral pool have no referral surface. Binds still succeed, but they earn nothing in those campaigns, and
referrals.stats never lists them.The Referral Code
A referral code is the referrer’s wallet address, or their Rabbithole username. There is nothing to mint or register: a referral link just carries one of the two.- Address. Pass it straight to
referrals.bind. - Username. Resolve it to the account’s payout wallet first with
GET /v1/referrals/usernames/{username}, then bind to the address that comes back. Users can re-point their payout wallet, so resolve when you bind rather than caching it. See Accept a Username Link.
Binding
The referee links to a referrer once by signing an EIP-712 message. The bind is global: it names no campaign and applies to every campaign on every chain. The signed domain carries nochainId, so the wallet can sign while connected to any network.
The per-campaign timing check runs when each campaign’s final distribution is built. It allows five minutes of grace for a deposit that lands just before its bind, which covers the race between signing and the deposit confirming. It does not cover a referee who deposited hours ago.
The signature carries a
deadline in unix seconds. The SDK defaults it to ten minutes from now and rejects anything more than an hour out (REFERRAL_BIND_MAX_DEADLINE_SECONDS) before asking the wallet to sign.
Earnings
A referrer earns 25% of what their referees earned in a campaign, scaled into that campaign’s referral pool.referrals.stats(address) returns one entry per referral campaign where at least one of the referrer’s referees first deposited after binding.
All amounts are in
rewardToken base units.
The pool is a hard cap. When the sum of every referrer’s uncappedAmount exceeds referralPool, every payout scales down pro rata to fit. That is why amount can be less than uncappedAmount, and why an "estimated" amount moves as other referrers’ referees earn, not just your own.
amount is typed bigint | null. Current servers always set it; null only appears in payloads from servers that predate the live projection. Treat null as “no estimate” rather than zero.Claiming Payouts
Referral payouts are a separate flow from reward claims.tbi.claim and tbi.claimAll do not apply.
- Each referral campaign deploys its own ReferralDistributor contract on the reward chain, which holds the pool.
- After the campaign finalizes, Rabbithole builds the referral merkle tree and publishes its root to the distributor. That publication starts the claim window.
referrals.proof(id, referrer)returns the proof, the payout amount, and the distributor’s address. It throwsTbiNotFoundErroruntil the distribution exists, which is the normal state for any campaign that is still running.- The claim is submitted to the TBI Manager’s
claimReferral, which forwards it to the distributor. The distributor only accepts calls from the Manager, so calling it directly reverts.
- One-shot. The proof’s
amountis the full payout, claimed exactly once per campaign. It is not a cumulative total that grows over time. - Claim window. Once the window closes, the unclaimed pool is swept back to the protocol and the payout is gone. Surface referral claims prominently and show
claimWindowEnd. - Permissionless submission. Anyone can submit the transaction; the payout always goes to the referrer named in the proof, so relayers work without special support.
claimTxHash and claimedAt lag the chain by the indexer’s confirmation delay. To decide whether a claim button should be enabled right after a claim, read claimed(referrer) on the distributor instead. See Contracts.
Keep Exploring
Add Referrals
Bind from a link, show earnings, and claim the payout.
Referrals for Protocols
How the pool is funded and sized at campaign creation.
Contracts
claimReferral, the distributor, and its revert selectors.