Skip to main content
All RebelFi APIs use consistent, industry-standard data formats for monetary values, rates, timestamps, and other common types.

Design Philosophy

These data shapes follow industry standards from Fireblocks, Coinbase, and Circle. Priority is smallest responses with minimal redundancy. Clients format display strings and cache token metadata.

Standard Types

Amount

Used for all single-token monetary values. Format:
{
  "amount": "1.11",      // Decimal string (human-readable)
  "currency": "USDC"     // Token symbol
}
Industry Standard: Matches Fireblocks, Coinbase, Circle exactly (2 fields) Why Strings?: Avoids JavaScript floating-point precision issues (e.g., 1.11 becoming 1.1099999999) Client Usage:
// Display
const display = `${amount.amount} ${amount.currency}`; // "1.11 USDC"

// Calculations (use Decimal.js or BigDecimal for precision)
import Decimal from 'decimal.js';
const value = new Decimal(amount.amount);

// Convert to base units (if needed for transaction construction)
const baseUnits = parseFloat(amount.amount) * Math.pow(10, tokenDecimals);
Examples:
{ "amount": "1.11", "currency": "USDC" }
{ "amount": "0.5", "currency": "SOL" }
{ "amount": "1000.00", "currency": "USDT" }
Token Metadata: Decimals, mintAddress, etc. available via GET /api/tokens (cache client-side)
Base units are NOT included in GET responses (blockchain implementation detail). Responses use human-readable decimal amounts only.

USD Aggregate

Used for cross-token totals and portfolio summaries. Simple String (when currency is obvious from context):
{
  "totalUsd": "3.61",
  "availableUsd": "1.50",
  "allocatedUsd": "2.11"
}
Object Form (when multiple currencies possible):
{
  "amount": "3.61",
  "currency": "USD"
}
Usage:
  • Cross-token aggregates (total balances, total yield, performance metrics)
  • Portfolio-level summaries
  • Org-level reporting
Client Formatting:
const display = `$${totalUsd} USD`; // "$3.61 USD"
Stablecoin = $1 USD Assumption: This API assumes all stablecoins have a 1:1 peg with USD for aggregation purposes.This simplification works because:
  • Platform currently only deals with stablecoins (USDC, USDT, etc.)
  • Avoids price oracle dependency and complexity
  • Provides intuitive USD totals for proving ROI
Future enhancement: Integrate price oracle for non-stablecoin tokens.

APY / Rate

Annual Percentage Yield as a simple percentage string. Format:
{
  "currentAPY": "6.25",      // 6.25% APY
  "weightedAvgAPY": "5.83",  // 5.83% weighted average
  "currentApy": "6.0"        // Some endpoints use lowercase
}
Why Percentage Format?:
  • APY = Annual Percentage Yield (semantically correct)
  • Matches traditional finance APIs
  • More intuitive than decimal format (0.0625)
Client Usage:
// Display
const display = `${currentAPY}%`; // "6.25%"

// As decimal for calculations
const decimal = parseFloat(currentAPY) / 100; // 0.0625

// As basis points
const bps = Math.round(parseFloat(currentAPY) * 100); // 625
Field Names: Implementation uses currentAPY, currentApy, weightedAvgAPY (not just apy)
Store and transmit as percentage string to avoid floating-point precision issues. Convert to decimal only for calculations.

Timestamp

All date/time values use dual representation. Format:
{
  "iso": "2025-01-30T10:30:00Z",   // ISO 8601 (primary)
  "unix": 1706611800                // Unix timestamp (seconds)
}
When to Use Each:
  • iso: Human-readable, timezone-aware, sortable, primary format for display
  • unix: Epoch calculations, time comparisons, systems that prefer Unix time
Client Usage:
// Parse ISO string
const date = new Date(timestamp.iso);

// Use Unix timestamp
const epochSeconds = timestamp.unix;
const epochMillis = timestamp.unix * 1000;

Pagination

Used for all paginated list endpoints. Format:
{
  "total": 150,       // Total items matching query
  "limit": 50,        // Items per page (requested)
  "offset": 0,        // Starting position
  "hasMore": true     // More pages available
}
Request Parameters:
  • limit - Number of items to return (default: 50, max: 100)
  • offset - Number of items to skip (default: 0)
Example:
// Request next page
const response = await fetch(
  `/api/core/operations/history?limit=20&offset=20`
);

const { items, total, hasMore } = await response.json();
if (hasMore) {
  // Fetch next page with offset=40
}

Error Response

Standard error format for HTTP 4xx/5xx responses. Format:
{
  "code": "INVALID_TOKEN_SYMBOL",         // Machine-readable error code
  "message": "Token 'XYZ' not found",     // Human-readable message
  "details": {                            // Additional context (optional)
    "availableTokens": ["USDC", "USDT", "SOL"],
    "requested": "XYZ"
  }
}
Usage:
  • HTTP status code indicates error category (400, 404, 500)
  • code: Use for programmatic error handling
  • message: Display to users or log
  • details: Include helpful context (available options, validation errors)
Common Error Codes:
CodeHTTP StatusDescription
UNAUTHORIZED401Invalid or missing authentication credentials
FORBIDDEN403Insufficient permissions for this action
NOT_FOUND404Resource not found
VALIDATION_ERROR400Request validation failed
INSUFFICIENT_BALANCE400Not enough available funds
INVALID_TOKEN_SYMBOL400Token symbol not recognized
OPERATION_IN_PROGRESS409Another operation is already running on this wallet
RATE_LIMIT_EXCEEDED429Too many requests
INTERNAL_ERROR500Internal server error
Example:
{
  "code": "INSUFFICIENT_BALANCE",
  "message": "Available balance is less than requested amount",
  "details": {
    "available": "500.00",
    "requested": "1000.00",
    "shortfall": "500.00"
  }
}

Response Optimization

These data shapes prioritize:
  • Smallest responses: 2-field Amount vs 5-field MoneyAmount (~70% size reduction)
  • Minimal redundancy: Token metadata fetched separately, cached client-side
  • Industry alignment: Matches Fireblocks/Coinbase/Circle standards
  • Developer experience: Predictable, consistent formats across all endpoints

Token Metadata

Token metadata (decimals, mintAddress, blockchain) is available via a separate endpoint:
GET /api/tokens
Pattern: Fetch once, cache client-side. Why Not in Every Response?:
  • Reduces response size by ~70%
  • Matches industry standards (Fireblocks, Coinbase, Circle)
  • Metadata rarely changes
Example:
// Fetch and cache tokens once
const tokens = await fetch('/api/tokens').then(r => r.json());
const tokenMap = new Map(tokens.map(t => [t.symbol, t]));

// Use cached metadata
const usdcDecimals = tokenMap.get('USDC').decimals; // 6

Best Practices

Never use JavaScript Number type for monetary values. Always use string representation and parse with a decimal library.
// ✅ CORRECT
import Decimal from 'decimal.js';
const amount = new Decimal("1.11");

// ❌ WRONG
const amount = 1.11; // Floating-point precision issues!
Fetch token metadata once and cache it client-side. Don’t fetch for every API call.
// ✅ CORRECT - Fetch once, cache
const tokens = await getTokens(); // Cache this

// ❌ WRONG - Fetching repeatedly
for (const position of positions) {
  const token = await getToken(position.currency); // Slow!
}
The API provides raw data. Format for display in your client application.
// Format money
const formatted = `${amount.amount} ${amount.currency}`; // "1.11 USDC"

// Format APY
const apyFormatted = `${currentAPY}%`; // "6.25%"

// Format USD
const usdFormatted = `$${totalUsd} USD`; // "$3.61 USD"
Use the error code field for programmatic error handling, not the message.
try {
  const result = await createOperation(...);
} catch (error) {
  if (error.code === 'INSUFFICIENT_BALANCE') {
    // Handle insufficient balance
    showError(`Need ${error.details.shortfall} more ${token}`);
  } else if (error.code === 'OPERATION_IN_PROGRESS') {
    // Handle conflict
    showError('Another operation is running, please wait');
  }
}

Migration Notes

If you’re upgrading from a previous API version or custom implementation:
  1. Switch to 2-Field Amount: Replace any 5-field MoneyAmount with { amount, currency }
  2. Use Percentage for APY: Replace decimal APY (0.0625) with percentage string (“6.25”)
  3. Add Timestamp Dual Format: Include both iso and unix in timestamp fields
  4. Handle USD Aggregates as Strings: Parse USD totals as simple strings, not objects

Examples by Use Case

Creating an Operation

const response = await fetch('/api/v1/allocations', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    opWalletId: 123,
    amount: "1000.00",  // String, not number
    asset: "USDC"       // Symbol, not ID
  })
});

const result = await response.json();
// result.reservedAmount is an Amount object: { amount: "1000.00", currency: "USDC" }

Displaying Portfolio Summary

const stats = await fetch('/api/core/wallets/stats')
  .then(r => r.json());

// Display USD totals
console.log(`Total: $${stats.data.balances.totalUsd} USD`);
console.log(`Available: $${stats.data.balances.availableUsd} USD`);

// Display APY
console.log(`Average APY: ${stats.data.performance.weightedAvgAPY}%`);

// Display per-token breakdown
stats.data.balances.byToken.forEach(tokenBalance => {
  console.log(`${tokenBalance.total} ${tokenBalance.currency}`);
});

Handling Errors

try {
  const result = await createSupplyOperation(opWalletId, "500.00", "USDC");
} catch (error) {
  // error is an Error Response object
  console.error(`Error [${error.code}]: ${error.message}`);

  if (error.details) {
    console.error('Details:', error.details);
  }

  // Programmatic handling
  switch (error.code) {
    case 'INSUFFICIENT_BALANCE':
      alert(`You need ${error.details.shortfall} more USDC`);
      break;
    case 'INVALID_TOKEN_SYMBOL':
      alert(`Token not supported. Available: ${error.details.availableTokens.join(', ')}`);
      break;
  }
}