Referral API
The FarFISH referral system rewards both referrer and referee with 20 FRH each (40 FRH total per successful referral). Referral relationships are immutable once recorded.
Your referral code works across all FarFISH apps because it's derived from your wallet address.
Base URL
https://app.farfish.xyz/api/referral
All referral endpoints work identically across all FarFISH apps. Your referral code is universal - share once, works everywhere.
POST /api/referral/record
Record a new referral relationship and distribute rewards to both parties.
Request
Endpoint: POST /api/referral/record
Headers:
Content-Type: application/json
Body Schema:
{
wallet: string; // Referee wallet address (42 chars, 0x-prefixed)
refCode: string; // Referrer's 8-character code (lowercase)
}
Example Request:
{
"wallet": "0x742d35cc6634c0532925a3b844bc9e7595f0beb9",
"refCode": "95f0beb9"
}
Response
Success (200):
{
"success": true,
"referrer": "0x1234567890abcdef1234567890abcdef12345678"
}
Already Recorded (200):
{
"success": true,
"referrer": "0x1234567890abcdef1234567890abcdef12345678",
"alreadyRecorded": true
}
Error (400/404):
{
"error": "Cannot refer yourself"
}
{
"error": "RefCode not found"
}
Validation Rules
| Rule | Description |
|---|---|
| Wallet format | Must match /^0x[a-fA-F0-9]{40}$/ |
| RefCode length | Exactly 8 characters |
| Self-referral | Rejected if wallet.slice(-8) === refCode |
| Duplicate | Idempotent - returns success if already recorded |
| RefCode existence | Must resolve to valid wallet address |
Reward Distribution
When a referral is successfully recorded:
- Referrer receives: +20 FRH (leaderboard score +1, ×20 = 20 FRH)
- Referee receives: +20 FRH (leaderboard score +1, ×20 = 20 FRH)
- Total distributed: 40 FRH per referral
Backend Operations:
1. Store referral relationship (wallet → referrer, createdAt)
2. Increment referrer's referral count
3. Update leaderboard score for referrer (+20 FRH)
4. Update leaderboard score for wallet (+20 FRH)
5. Track active referrers
Cache Rebuild Mechanism
If referral code cache entry is missing, the API automatically rebuilds it by scanning stored referral data.
This ensures referral links continue working even after cache eviction.
cURL Example
curl -X POST https://app.farfish.xyz/api/referral/record \
-H "Content-Type: application/json" \
-d '{
"wallet": "0x742d35cc6634c0532925a3b844bc9e7595f0beb9",
"refCode": "95f0beb9"
}'
JavaScript Example
async function recordReferral(wallet: string, refCode: string) {
const response = await fetch('https://app.farfish.xyz/api/referral/record', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wallet, refCode })
});
const data = await response.json();
if (data.success) {
console.log('Referral recorded! Referrer:', data.referrer);
if (data.alreadyRecorded) {
console.log('(Already had a referrer)');
}
} else {
console.error('Error:', data.error);
}
}
GET /api/referral/check
Check if a wallet already has a referrer assigned.
Request
Endpoint: GET /api/referral/check?wallet={address}
Query Parameters:
wallet(required) - Ethereum wallet address
Example:
GET /api/referral/check?wallet=0x742d35cc6634c0532925a3b844bc9e7595f0beb9
Response
Success (200):
{
"hasReferrer": true,
"wallet": "0x742d35cc6634c0532925a3b844bc9e7595f0beb9"
}
{
"hasReferrer": false,
"wallet": "0x742d35cc6634c0532925a3b844bc9e7595f0beb9"
}
cURL Example
curl "https://app.farfish.xyz/api/referral/check?wallet=0x742d35cc6634c0532925a3b844bc9e7595f0beb9"
JavaScript Example
async function hasReferrer(wallet: string): Promise<boolean> {
const response = await fetch(
`https://app.farfish.xyz/api/referral/check?wallet=${wallet}`
);
const data = await response.json();
return data.hasReferrer;
}
GET /api/referral/link
Get user's referral link and referral statistics.
Request
Endpoint: GET /api/referral/link?user={address}
Query Parameters:
user(required) - Ethereum wallet address
Alternative: Header x-user-wallet can provide wallet address
Example:
GET /api/referral/link?user=0x1234567890abcdef1234567890abcdef12345678
Response
Success (200):
{
"bound": false,
"referrer": "",
"link": "https://app.farfish.xyz?ref=12345678",
"referralsCount": 5
}
Fields:
bound- Does this user have a referrer?referrer- Referrer's wallet address (empty if not bound)link- This user's shareable referral linkreferralsCount- Number of users this wallet has referred
RefCode Generation
RefCode is deterministic - always the last 8 characters of wallet address in lowercase:
Wallet: 0x1234567890abcdef1234567890abcdef12345678
RefCode: 12345678
Link: https://app.farfish.xyz?ref=12345678
cURL Example
curl "https://app.farfish.xyz/api/referral/link?user=0x1234567890abcdef1234567890abcdef12345678"
JavaScript Example
async function getReferralLink(wallet: string) {
const response = await fetch(
`https://app.farfish.xyz/api/referral/link?user=${wallet}`
);
const data = await response.json();
console.log('Your referral link:', data.link);
console.log('Total referrals:', data.referralsCount);
return data.link;
}
GET /api/referral/wallet-by-code
Reverse lookup: find wallet address from refCode.
Request
Endpoint: GET /api/referral/wallet-by-code?code={refCode}
Query Parameters:
code(required) - 8-character refCode
Example:
GET /api/referral/wallet-by-code?code=12345678
Response
Success (200):
{
"wallet": "0x1234567890abcdef1234567890abcdef12345678",
"refCode": "12345678"
}
Not Found (404):
{
"error": "RefCode not found"
}
Use Cases
- Validate refCode before displaying referral attribution
- Pre-fetch referrer profile information
- Display "You were referred by " message
cURL Example
curl "https://app.farfish.xyz/api/referral/wallet-by-code?code=12345678"
JavaScript Example
async function getWalletFromCode(refCode: string): Promise<string | null> {
const response = await fetch(
`https://app.farfish.xyz/api/referral/wallet-by-code?code=${refCode}`
);
if (response.status === 404) {
return null; // Invalid refCode
}
const data = await response.json();
return data.wallet;
}
Referral Flow Example
Complete user referral journey:
1. Referrer Generates Link
// User connects wallet
const wallet = '0x1234567890abcdef1234567890abcdef12345678';
// Get referral link
const response = await fetch(`/api/referral/link?user=${wallet}`);
const { link } = await response.json();
// link = "https://app.farfish.xyz?ref=12345678"
// User shares link on social media
2. Referee Clicks Link
// URL: https://app.farfish.xyz?ref=12345678
// Frontend automatically caches refCode in localStorage
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const refCode = urlParams.get('ref');
if (refCode && refCode.length === 8) {
localStorage.setItem('ff_pending_referral', refCode);
}
}, []);
3. Referee Connects Wallet
// Referee connects wallet
const refereeWallet = '0x742d35cc6634c0532925a3b844bc9e7595f0beb9';
// Retrieve cached refCode
const refCode = localStorage.getItem('ff_pending_referral');
// Initialize user
await fetch('/api/user/init', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wallet: refereeWallet })
});
// Record referral (if refCode present)
if (refCode) {
const result = await fetch('/api/referral/record', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wallet: refereeWallet, refCode })
});
const data = await result.json();
if (data.success) {
// Both parties receive +20 FRH!
console.log('Referral successful!');
localStorage.removeItem('ff_pending_referral');
}
}
Error Handling
| Error | HTTP Status | Meaning | Action |
|---|---|---|---|
Invalid wallet address | 400 | Wallet format invalid | Validate format client-side |
RefCode not found | 404 | RefCode doesn't exist | Check for typos, verify referrer initialized |
Cannot refer yourself | 400 | Self-referral detected | Clear cached refCode |
Already has referrer | 200 | Duplicate referral (idempotent) | No action needed |
Cross-App Behavior
Referral limitation: Users can only be referred within the same app domain they first connect from. A user who clicks a Web App referral link (app.farfish.xyz?ref=...) and connects wallet there cannot be referred via Base App (baseapp.farfish.xyz) link later.
Why? localStorage is domain-isolated. RefCode cached on app.farfish.xyz is not accessible from baseapp.farfish.xyz.
Best Practice: Use the same app variant for referral campaigns (e.g., all Base App users share Base App links).
Related Documentation
- User Initialization API - Create user profiles and refCode cache
- Leaderboard API - View reward distribution
- Referral System Guide - Complete integration guide
- Tokenomics - FRH reward economics