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:
- Users authenticate with the Link Dashboard (username/password login)
- Auth Server validates JWT tokens and session cookies
- Authenticated requests are forwarded to Link Quote via
/api/link-quote/v1/* - Link Quote processes the request (no auth check at service level)
- 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 namecode: Filter by codelimit: Results per pageoffset: Page offsetsortBy: Sort fieldsortOrder: Sort direction
How It Works
Provider Selection Logic
-
Check Asset-Specific Providers
- If asset has configured providers → use those
-
Fallback to Category Providers
- If no asset providers → use category's providers
-
Use Default Providers
- If no category providers → use all available providers
-
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