Skip to main content

Getting Started with Link Quote

This guide will help you integrate with the Link Quote API for digital asset pricing and rate aggregation.

Prerequisites

  • Basic knowledge of REST APIs
  • (Required for operation) CoinMarketCap API key or Pyth Network access
  • HTTPS-enabled endpoints for production
  • Note: Authentication is not currently implemented in Link Quote service itself

Authentication

Link Quote is designed to be accessed through the Link Dashboard, which handles authentication.

Production Architecture:

User/Client
↓ (authenticates)
Link Dashboard (Next.js)
↓ (validates session)
Auth Server/BFF (Port 4000)
↓ (proxies authenticated requests)
Link Quote Service (Port 3000)

Authentication Flow:

  1. Users authenticate with the Link Dashboard (username/password login)
  2. Auth Server validates JWT tokens and session cookies
  3. Authenticated requests are forwarded to Link Quote via /api/link-quote/v1/*
  4. Link Quote processes the request (no auth check at service level)
  5. Response flows back through the chain

There is currently no role-based access control (RBAC). All authenticated users have full access to all Link Quote endpoints.

Direct Service Access (Development Only):

  • Local development: http://localhost:3000/v1/* (no authentication)
  • Ensure network isolation in development/test environments
  • Production deployments should only allow access through Auth Server

Base URLs

  • Production: https://api.ledgerlink.ai/v1
  • Sandbox: https://sandbox-api.ledgerlink.ai/v1
  • Local Development: http://localhost:3000 (default port)

Quick Start

1. Get Current Rate for an Asset

Through Link Dashboard (Production):

# Authenticated via session cookie from Link Dashboard
curl -X POST "https://your-dashboard.ledgerlink.ai/api/link-quote/v1/rates" \
-H "Content-Type: application/json" \
-H "Cookie: session_cookie_here" \
-d '{
"assetCode": "BTC",
"currency": "USD"
}'

Direct to Service (Development):

curl -X POST "http://localhost:3000/v1/rates" \
-H "Content-Type: application/json" \
-d '{
"assetCode": "BTC",
"currency": "USD"
}'

Response:

{
"rate": 67543.50,
"currency": "USD"
}

2. Get Historical Rates

Query historical rate data:

curl -X GET "http://localhost:3000/v1/rates?name=bitcoin&limit=10&sortBy=timestamp&sortOrder=desc"

Response:

{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"assetId": "123e4567-e89b-12d3-a456-426614174000",
"rate": 67543.50,
"currency": "USD",
"timestamp": "2025-11-20T17:00:00.000Z",
"data": {
"assetCode": "BTC",
"providers": {
"coinmarketcap": 67550.00,
"pyth": 67537.00
}
},
"asset": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Bitcoin",
"code": "BTC",
"categoryId": "789e4567-e89b-12d3-a456-426614174000"
}
}
],
"pageInfo": {
"limit": 10,
"offset": 0,
"total": 1547
}
}

3. Create a New Asset

Add a custom digital asset:

curl -X POST "http://localhost:3000/v1/assets" \
-H "Content-Type: application/json" \
-d '{
"name": "Solana",
"code": "SOL",
"categoryId": "789e4567-e89b-12d3-a456-426614174000"
}'

4. List All Assets

Get all configured assets:

curl -X GET "http://localhost:3000/v1/assets?limit=20&offset=0"

Common Parameters

Rate Endpoints

POST /rates

  • assetCode (required): Asset code (e.g., "BTC", "ETH")
  • currency (optional): Target currency, defaults to "USD"

GET /rates

  • name: Filter by asset name (case-insensitive)
  • fromTime: Start date (ISO 8601 format)
  • toTime: End date (ISO 8601 format)
  • limit: Results per page (default: 10)
  • offset: Page offset (default: 0)
  • sortBy: Sort field (rate, timestamp, createdAt, updatedAt)
  • sortOrder: Sort direction (asc, desc)

Asset/Category/Provider Endpoints

Filtering:

  • name: Filter by name
  • code: Filter by code
  • limit: Results per page
  • offset: Page offset
  • sortBy: Sort field
  • sortOrder: Sort direction

How It Works

Provider Selection Logic

  1. Check Asset-Specific Providers

    • If asset has configured providers → use those
  2. Fallback to Category Providers

    • If no asset providers → use category's providers
  3. Use Default Providers

    • If no category providers → use all available providers
  4. Price Aggregation

    • Fetch from multiple providers in parallel
    • Calculate weighted average
    • Handle outliers and errors

Rate Calculation

Link Quote fetches rates from multiple providers and:

  • Validates each provider response
  • Filters out invalid/outlier values
  • Calculates average from valid responses
  • Stores result with provider details in metadata
  • Returns aggregated rate to client

Local Development Setup

To run Link Quote locally:

# Install Node.js (v22.13.1+)
nvm install
nvm use

# Start PostgreSQL with Docker
yarn dev:provision:db

# Install dependencies
yarn install

# Configure environment
cd server
cp .env.example .env
# Add your CoinMarketCap API key to .env

# Run database migrations
yarn db:migrate:up

# Start the server
yarn dev:server:start

Access Swagger docs at: http://localhost:3000/api

Integration Patterns

Dashboard Integration

// Example: Fetch asset rate from Link Dashboard (production pattern)
async function getAssetPrice(assetCode: string): Promise<number> {
// Call through Auth Server BFF proxy - authentication handled automatically
const response = await fetch('/api/link-quote/v1/rates', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include', // Include session cookies
body: JSON.stringify({ assetCode }),
});

if (!response.ok) {
throw new Error(`Failed to fetch rate: ${response.statusText}`);
}

const data = await response.json();
return data.rate;
}

// For direct service access (development/testing only):
async function getAssetPriceDirect(assetCode: string): Promise<number> {
const response = await fetch('http://localhost:3000/v1/rates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ assetCode }),
});

const data = await response.json();
return data.rate;
}

// Example: Subscribe to rate updates via ActiveMQ
function subscribeToRateUpdates(assetId: string) {
// Connect to ActiveMQ and subscribe to valuation events
stompClient.subscribe('/queue/valuation-events', (message) => {
const event = JSON.parse(message.body);
if (event.metadata.asset_id === assetId) {
updatePriceDisplay(event.metadata.value);
}
});
}

Batch Rate Fetching

For multiple assets, make parallel requests:

async function fetchMultipleRates(assetCodes: string[]) {
const promises = assetCodes.map(code =>
fetch('http://localhost:3000/v1/rates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ assetCode: code }),
}).then(r => r.json())
);

return Promise.all(promises);
}

Error Handling

Link Quote returns standard HTTP status codes:

  • 200 OK: Rate successfully created/retrieved
  • 400 Bad Request: Invalid parameters
  • 404 Not Found: Asset/Category/Provider not found
  • 503 Service Unavailable: No providers available or all providers failed
  • 500 Internal Server Error: Unexpected error

See Error Handling Guide for details.

Next Steps


Need Help? Contact helpdesk@ledgerlink.ai