Getting Started with Link Core
This guide will help you integrate with the Link Core API for transaction processing, balance management, and asset operations.
Authentication
Link Core 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 Core Service (Port 3000)
Authentication Flow:
- Users authenticate with the Link Dashboard
- Auth Server validates JWT tokens and session cookies
- Authenticated requests are forwarded to Link Core
- Link Core processes the request (no auth check at service level)
- Response flows back through the chain
Direct Service Access (Development Only):
- Local development:
http://localhost:3000(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. List All Transactions
Through Link Dashboard (Production):
curl -X GET "https://your-dashboard.ledgerlink.ai/api/link-core/v1/transactions" \
-H "Cookie: session_cookie_here"
Direct to Service (Development):
curl -X GET "http://localhost:3000/transactions"
Response:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "DEPOSIT",
"status": "COMPLETED",
"amount": "1000.00",
"assetId": "btc-asset-id",
"customerId": "customer-123",
"createdAt": "2025-11-20T17:00:00.000Z",
"updatedAt": "2025-11-20T17:05:00.000Z"
}
]
2. Get a Specific Transaction
curl -X GET "http://localhost:3000/transactions/550e8400-e29b-41d4-a716-446655440000"
3. Request a Withdrawal
curl -X POST "http://localhost:3000/transactions/withdrawal" \
-H "Content-Type: application/json" \
-d '{
"customerId": "customer-123",
"assetId": "btc-asset-id",
"amount": "0.5",
"destinationAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
}'
Response:
{
"id": "tx-withdrawal-123",
"type": "WITHDRAWAL",
"status": "PENDING",
"amount": "0.5",
"assetId": "btc-asset-id",
"customerId": "customer-123",
"destinationAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"createdAt": "2025-11-20T18:00:00.000Z"
}
4. Get Customer Balances
curl -X GET "http://localhost:3000/balances/customer/customer-123"
Response:
[
{
"assetId": "btc-asset-id",
"assetCode": "BTC",
"available": "2.5",
"frozen": "0.5",
"total": "3.0"
},
{
"assetId": "eth-asset-id",
"assetCode": "ETH",
"available": "10.0",
"frozen": "0.0",
"total": "10.0"
}
]
5. Create a Digital Asset
curl -X POST "http://localhost:3000/assets" \
-H "Content-Type: application/json" \
-d '{
"code": "BTC",
"name": "Bitcoin",
"decimals": 8,
"blockchain": "bitcoin",
"contractAddress": null
}'
Transaction Lifecycle
Deposit Flow
-
Blockchain Event Detected
- Blockchain Listener detects incoming transaction
- Transaction created with status
PENDING
-
Awaiting External Screening
- Transaction awaits external AML/compliance screening
- External system (outside LedgerLink) performs checks
- Status remains
PENDINGuntil screening callback received
-
Screening Completed (via webhook callback)
- External system calls
POST /transactions/complete-screening - If approved → Status
APPROVED, balance updated - If rejected → Status
FROZEN, awaits manual review
- External system calls
-
Manual Review (if frozen)
- POST
/transactions/:id/approve-frozen-deposit→ Complete processing - POST
/transactions/:id/reject-frozen-deposit→ Reject and notify customer
- POST
-
Completion
- Status
COMPLETED - Balance available to customer
- Status
Withdrawal Flow
-
Customer Requests Withdrawal
- POST
/transactions/withdrawal - Transaction created with status
PENDING
- POST
-
Awaiting External Screening
- Transaction awaits external AML/compliance screening
- External system (outside LedgerLink) performs checks
- Status remains
PENDINGuntil screening callback received
-
Screening Completed (via webhook callback)
- External system calls
POST /transactions/complete-screening - If approved → Blockchain transaction initiated
- If rejected → Status
FROZEN, awaits manual review
- External system calls
-
Manual Review (if frozen)
- POST
/transactions/:id/approve-frozen-withdrawal→ Initiate blockchain transaction - POST
/transactions/:id/reject-frozen-withdrawal→ Reject and refund
- POST
-
Blockchain Execution
- Wallet Manager executes blockchain transaction
- Status updated as transaction confirms
-
Completion
- Status
COMPLETED - Balance deducted from customer account
- Status
Transaction Statuses
- PENDING: Transaction created, awaiting external screening callback
- FROZEN: Flagged by external screening, requires manual review
- APPROVED: Approved (either automatically or after manual review), processing
- COMPLETED: Fully processed
- REJECTED: Rejected by external screening or manual review
- FAILED: Processing failed
Note: There is no separate "SCREENING" status. Transactions remain in PENDING status while external AML/compliance systems perform their checks and call back via the /transactions/complete-screening webhook.
Balance Queries
Filter by Asset
curl -X GET "http://localhost:3000/balances/customer/customer-123?filter[assetCode]=BTC"
Get Account-Specific Balances
curl -X GET "http://localhost:3000/balances/account/account-456"
Local Development Setup
To run Link Core locally:
# Install Node.js (v16+)
nvm install
nvm use
# Start PostgreSQL with Docker
npm run docker
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env with your configuration
# Run database migrations
npm run migration:run
# Start the server
npm run start:dev
Access Swagger docs at: http://localhost:3000/api
Integration Patterns
Dashboard Integration
// Example: Fetch customer balances from Link Core
async function getCustomerBalances(customerId: string) {
// In production, call through Auth Server BFF:
const response = await fetch(`/api/link-core/v1/balances/customer/${customerId}`, {
credentials: 'include', // Include session cookies
});
if (!response.ok) {
throw new Error(`Failed to fetch balances: ${response.statusText}`);
}
return response.json();
}
// Example: Request withdrawal
async function requestWithdrawal(data: WithdrawalRequest) {
const response = await fetch('/api/link-core/v1/transactions/withdrawal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(data),
});
return response.json();
}
Monitoring Transaction Status
// Poll transaction status
async function monitorTransaction(transactionId: string) {
const checkStatus = async () => {
const response = await fetch(`/api/link-core/v1/transactions/${transactionId}`);
const transaction = await response.json();
if (transaction.status === 'COMPLETED') {
console.log('Transaction completed!');
return transaction;
} else if (transaction.status === 'FAILED' || transaction.status === 'REJECTED') {
console.error('Transaction failed:', transaction);
return transaction;
}
// Still processing, check again in 5 seconds
setTimeout(checkStatus, 5000);
};
return checkStatus();
}
Error Handling
Link Core returns standard HTTP status codes:
- 200 OK: Request successful
- 201 Created: Resource created successfully
- 400 Bad Request: Invalid parameters
- 401 Unauthorized: Authentication failed (handled by Auth Server)
- 404 Not Found: Resource not found
- 409 Conflict: Resource conflict (e.g., duplicate transaction)
- 422 Unprocessable Entity: Validation errors
- 500 Internal Server Error: Unexpected error
See Error Handling Guide for details.
Job Processing
Link Core uses Bull queues for background processing:
Screening Complete Queue
Processes transactions after screening approval:
- Updates transaction status
- Updates customer balances
- Notifies downstream services
Omnibus Balance Jobs
Monitors omnibus wallet balances:
- Tracks omnibus account balances
- Alerts on low balance conditions
- Ensures sufficient liquidity
Integration with Other Services
Link Quote Integration
// Get asset price for transaction valuation
async function getAssetPrice(assetCode: string): Promise<number> {
const response = await fetch('/api/link-quote/v1/rates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ assetCode }),
});
const data = await response.json();
return data.rate;
}
Link Tracker Integration
Link Core automatically logs events to Link Tracker:
- Transaction state changes
- Balance updates
- Error conditions
- Screening results
Next Steps
Need Help? Contact helpdesk@ledgerlink.ai