Error Handling
This guide explains how to handle errors when integrating with the Link Core API.
Standard Error Response Format
All error responses follow a consistent structure:
{
"statusCode": 404,
"message": "Transaction with ID 550e8400-e29b-41d4-a716-446655440000 not found",
"error": "Not Found",
"timestamp": "2025-11-20T17:00:00.000Z",
"path": "/transactions/550e8400-e29b-41d4-a716-446655440000"
}
HTTP Status Codes
Success Codes (2xx)
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request successful |
| 201 | Created | Resource successfully created |
Client Error Codes (4xx)
| Code | Status | Description |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or body |
| 401 | Unauthorized | Authentication failed (handled by Auth Server/Dashboard) |
| 404 | Not Found | Resource not found (transaction, asset, etc.) |
| 409 | Conflict | Resource conflict (duplicate transaction, etc.) |
| 422 | Unprocessable Entity | Validation errors |
| 429 | Too Many Requests | Rate limit exceeded (handled by Auth Server/Dashboard) |
Note:
- Authentication (401) and Rate Limiting (429) are handled by the Link Dashboard's Auth Server
- 403 (Forbidden) is not currently implemented (no RBAC/permissions)
- Link Core service itself returns: 400, 404, 409, 422, and 5xx errors
Server Error Codes (5xx)
| Code | Status | Description |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
| 502 | Bad Gateway | Upstream service error |
| 503 | Service Unavailable | Service temporarily unavailable |
| 504 | Gateway Timeout | Request timeout |
Common Error Scenarios
1. Transaction Not Found
Scenario: Requesting a non-existent transaction
GET /transactions/invalid-id
Error Response:
{
"statusCode": 404,
"message": "Transaction with ID invalid-id not found",
"error": "Not Found"
}
Solution:
- Verify transaction ID is correct
- Check if transaction exists using GET /transactions
2. Invalid Withdrawal Request
Scenario: Missing required fields or invalid data
POST /transactions/withdrawal
{
"customerId": "customer-123"
// Missing required fields
}
Error Response:
{
"statusCode": 400,
"message": ["assetId should not be empty", "amount should not be empty"],
"error": "Bad Request"
}
Solution:
- Include all required fields (customerId, assetId, amount, destinationAddress)
- Validate data types and formats before sending
3. Insufficient Balance
Scenario: Withdrawal amount exceeds available balance
Error Response:
{
"statusCode": 400,
"message": "Insufficient balance for withdrawal",
"error": "Bad Request"
}
Solution:
- Check customer balance before requesting withdrawal
- Ensure amount doesn't exceed available balance
4. Invalid Transaction Status
Scenario: Attempting operation on transaction in wrong status
POST /transactions/:id/approve-frozen-deposit
# Transaction is not in FROZEN status
Error Response:
{
"statusCode": 409,
"message": "Transaction is not in FROZEN status",
"error": "Conflict"
}
Solution:
- Check transaction status before performing operations
- Only approve/reject transactions in FROZEN status
5. Asset Not Found
Scenario: Requesting withdrawal with non-existent asset
Error Response:
{
"statusCode": 404,
"message": "Asset with ID asset-123 not found",
"error": "Not Found"
}
Solution:
- Verify asset ID exists using GET /assets
- Create asset first if needed
6. Validation Errors
Scenario: Invalid data format
POST /transactions/withdrawal
{
"customerId": "customer-123",
"assetId": "btc",
"amount": "invalid-amount",
"destinationAddress": "invalid-address"
}
Error Response:
{
"statusCode": 422,
"message": [
"amount must be a valid number",
"destinationAddress must be a valid blockchain address"
],
"error": "Unprocessable Entity"
}
Solution:
- Validate all input data before sending
- Use proper data types and formats
Best Practices
1. Implement Retry Logic
async function fetchTransactionWithRetry(
id: string,
maxRetries = 3
): Promise<Transaction> {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(`/api/link-core/v1/transactions/${id}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error('Transaction not found');
}
// Retry on 5xx errors
if (response.status >= 500 && i < maxRetries - 1) {
await sleep(Math.pow(2, i) * 1000); // Exponential backoff
continue;
}
}
return response.json();
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}
2. Validate Before Sending
function validateWithdrawalRequest(request: WithdrawalRequest): void {
if (!request.customerId) {
throw new Error('Customer ID is required');
}
if (!request.assetId) {
throw new Error('Asset ID is required');
}
if (!request.amount || parseFloat(request.amount) <= 0) {
throw new Error('Amount must be greater than zero');
}
if (!request.destinationAddress) {
throw new Error('Destination address is required');
}
}
3. Handle Transaction States
async function handleTransaction(transactionId: string) {
const transaction = await fetchTransaction(transactionId);
switch (transaction.status) {
case 'PENDING':
console.log('Transaction is pending screening');
break;
case 'SCREENING':
console.log('Transaction is being screened');
break;
case 'FROZEN':
console.log('Transaction requires manual review');
// Notify admin for review
break;
case 'APPROVED':
console.log('Transaction approved, processing...');
break;
case 'COMPLETED':
console.log('Transaction completed');
break;
case 'REJECTED':
console.error('Transaction rejected');
// Notify customer
break;
case 'FAILED':
console.error('Transaction failed');
// Handle failure
break;
}
}
4. Monitor and Alert
- Track error rates by status code and endpoint
- Alert on sustained 5xx errors
- Monitor transaction failure rates
- Log errors with full context for debugging
- Set up health checks for critical flows
Recovery Strategies
For 5xx Errors (Server Errors)
-
Short-term:
- Retry with exponential backoff
- Queue failed requests for later retry
- Show user-friendly error messages
-
Long-term:
- Implement circuit breaker pattern
- Set up fallback mechanisms
- Monitor and alert on error spikes
For 400 Errors (Bad Request)
- Implement client-side validation
- Provide clear error messages to users
- Log validation failures for analysis
- Update UI to prevent invalid inputs
For 404 Errors (Not Found)
- Verify resource IDs before operations
- Handle deleted/non-existent resources gracefully
- Provide helpful suggestions (e.g., "Transaction may have been deleted")
For 409 Errors (Conflict)
- Check resource state before operations
- Handle state transitions properly
- Implement optimistic locking where needed
- Retry with updated state
Debug Mode
Enable detailed logging in local development:
# In .env
NODE_ENV=development
LOG_LEVEL=debug
This will show:
- Database queries
- External service calls
- Transaction state changes
- Queue job processing
- Detailed error stack traces
Support
If errors persist:
- Review API documentation and examples
- Check JIRA for similar reported issues
- Enable debug logging to investigate
- Contact helpdesk@ledgerlink.ai with:
- Timestamp of error
- Full error response
- Steps to reproduce
- Relevant log output
Next: FAQ | API Reference