FarFISH Logo
FarFISHDocs

User API

Initialize user profiles and manage user data across the FarFISH ecosystem.

Base URL

text
https://app.farfish.xyz/api/user

POST /api/user/init

Initialize user profile on first wallet connection. Creates refCode cache entry and leaderboard record.

Request

Endpoint: POST /api/user/init

Headers:

text
Content-Type: application/json

Body Schema:

typescript
{
  wallet: string  // Ethereum wallet address (42 chars, 0x-prefixed)
}

Example Request:

json
{
  "wallet": "0x1234567890abcdef1234567890abcdef12345678"
}

Response

Success - New User (200):

json
{
  "success": true
}

Success - Existing User (200):

json
{
  "success": true,
  "alreadyExists": true
}

Error (400):

json
{
  "error": "Invalid wallet address format"
}

Operations Performed

When a new user is initialized, the API performs:

  1. Extract RefCode: Last 8 characters of wallet address (lowercase)

    typescript
    const refCode = wallet.slice(-8).toLowerCase();
    // Example: "12345678"
    
  2. Create RefCode Cache: Maps refCode → wallet for referral lookups

  3. Initialize Leaderboard: Add user to leaderboard with score 0

Idempotency

Calling /api/user/init multiple times with same wallet is safe.

  • First call: Creates cache and leaderboard entry
  • Subsequent calls: Returns {success: true, alreadyExists: true}
  • No data is overwritten or duplicated

RefCode Generation

RefCode is deterministic - same wallet always generates same code:

text
Wallet:  0x1234567890ABCDEF1234567890ABCDEF12345678
RefCode: 12345678 (lowercase)

Wallet:  0x742d35cc6634c0532925a3b844bc9e7595f0beb9
RefCode: 95f0beb9

Collision Probability: 1 in 4.3 billion (2^32 combinations)

cURL Example

bash
curl -X POST https://app.farfish.xyz/api/user/init \
  -H "Content-Type: application/json" \
  -d '{"wallet":"0x1234567890abcdef1234567890abcdef12345678"}'

JavaScript Example

typescript
async function initializeUser(wallet: string) {
  const response = await fetch('https://app.farfish.xyz/api/user/init', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ wallet })
  });
  
  const data = await response.json();
  
  if (data.success) {
    if (data.alreadyExists) {
      console.log('User already initialized');
    } else {
      console.log('New user created');
    }
  } else {
    console.error('Error:', data.error);
  }
}

Auto-Initialization on Wallet Connection

Typically called automatically when user connects wallet:

typescript
// Get wallet address from your Web3 connection library
const { address } = useWalletConnection();
import { useEffect } from 'react';

function useAutoInit() {
  const { address, isConnected } = useAccount();
  
  useEffect(() => {
    if (isConnected && address) {
      // Initialize user on wallet connection
      fetch('/api/user/init', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ wallet: address })
      });
    }
  }, [isConnected, address]);
}

User Data Structure

Backend Keys Created

When user initializes, the following data entries are created or updated:

Key PatternTypeValuePurpose
refcode:{last8}StringFull wallet addressRefCode → wallet lookup
leaderboardSorted SetMember: wallet, Score: 0Leaderboard ranking

Additional Keys (Created by Other APIs)

Key PatternTypeValuePurpose
referral:{wallet}JSON{referrer, createdAt}Referral attribution
refcount:{wallet}IntegerCount of referralsReferral count
user:{wallet}JSONUser profileSteam tasks, preferences

Identity Model

FarFISH uses wallet-based identity - no usernames, passwords, or emails required.

  • Primary ID: Wallet address (42 characters, 0x-prefixed)
  • Secondary ID: RefCode (last 8 characters, deterministic)
  • Farcaster Mini App: FID (Farcaster ID) as additional identifier

Wallet Address Properties

  • Unique: Ethereum address space (2^160 possible addresses)
  • Immutable: Cannot be changed once created
  • Public: Visible on blockchain and in leaderboard
  • Self-sovereign: User controls via private key

Cross-App Identity

Same wallet address works across all FarFISH applications:

text
Web App:     app.farfish.xyz     → Shared backend database
Base App:     baseapp.farfish.xyz → Shared backend database
Mini App:     miniapp.farfish.xyz → Shared backend database

User data (referrals, leaderboard, tasks) is automatically synchronized across all apps.


Profile Data (Future Enhancement)

Current Status: User profiles are minimal (wallet + refCode only).

Future enhancements may include customizable profiles with display names, avatars, social links, etc.

Potential Future Fields:

typescript
{
  wallet: string;
  refCode: string;
  displayName?: string;      // Custom username
  avatar?: string;           // IPFS/CDN URL
  bio?: string;              // Profile description
  twitter?: string;          // Social link
  farcaster?: number;        // FID
  createdAt: number;         // Unix timestamp
  lastActive: number;        // Last activity timestamp
}

Error Handling

ErrorHTTP StatusMeaningAction
Invalid wallet address format400Malformed addressValidate format before calling
Missing wallet parameter400Request body incompleteInclude wallet field
Server error500Backend connection issueRetry with exponential backoff

Wallet Format Validation

Validate wallet client-side before API call:

typescript
function isValidWallet(wallet: string): boolean {
  return /^0x[a-fA-F0-9]{40}$/.test(wallet);
}

async function initUser(wallet: string) {
  if (!isValidWallet(wallet)) {
    throw new Error('Invalid wallet address format');
  }
  
  // Proceed with API call...
}

Integration Example

Complete user initialization flow:

typescript
// Get wallet address from your Web3 connection library
const { address } = useWalletConnection();
import { useEffect, useState } from 'react';

function useUserInit() {
  const { address, isConnected } = useAccount();
  const [initialized, setInitialized] = useState(false);
  const [error, setError] = useState<string | null>(null);
  
  useEffect(() => {
    if (!isConnected || !address) {
      setInitialized(false);
      return;
    }
    
    async function initUser() {
      try {
        const response = await fetch('/api/user/init', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ wallet: address })
        });
        
        const data = await response.json();
        
        if (data.success) {
          setInitialized(true);
          setError(null);
        } else {
          setError(data.error);
        }
      } catch (err) {
        setError('Failed to initialize user');
      }
    }
    
    initUser();
  }, [isConnected, address]);
  
  return { initialized, error };
}

// Usage
function App() {
  const { initialized, error } = useUserInit();
  
  if (error) return <div>Error: {error}</div>;
  if (!initialized) return <div>Initializing...</div>;
  
  return <div>Welcome to FarFISH!</div>;
}

Last updated: October 12, 2026