Java Examples
Complete, production-ready code examples for integrating Kixago API in Java and Spring Boot applications.
Quick Start
Installation (Maven)
<!-- pom.xml -->
<dependencies>
<!-- HTTP Client (Java 11+) - Built-in -->
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>
<!-- Optional: Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
<!-- Optional: Lombok (reduces boilerplate) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- Optional: Redis Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
Installation (Gradle)
dependencies {
// JSON Processing
implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.0'
// Optional: Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
// Optional: Lombok
compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
// Optional: Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis:3.2.0'
}
Basic Examples (Plain Java)
Example 1: Simple Request
// KixagoExample.java
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class KixagoExample {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("KIXAGO_API_KEY");
String walletAddress = "0xf0bb20865277aBd641a307eCe5Ee04E79073416C";
// Create HTTP client
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
// Build request
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.kixago.com/v1/risk-profile/" + walletAddress))
.header("X-API-Key", apiKey)
.header("Accept", "application/json")
.timeout(Duration.ofSeconds(30))
.GET()
.build();
// Send request
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() == 200) {
// Parse JSON
ObjectMapper mapper = new ObjectMapper();
RiskProfileResponse profile = mapper.readValue(
response.body(),
RiskProfileResponse.class
);
System.out.printf("DeFi Score: %d%n", profile.getDefiScore().getScore());
System.out.printf("Risk Level: %s%n", profile.getDefiScore().getRiskLevel());
System.out.printf("Health Factor: %.2f%n", profile.getGlobalHealthFactor());
} else {
System.err.printf("Error: %d%n", response.statusCode());
}
}
}
Example 2: Complete Type Definitions
// models/RiskProfileResponse.java
package com.kixago.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
import java.util.Map;
@Data
public class RiskProfileResponse {
@JsonProperty("wallet_address")
private String walletAddress;
@JsonProperty("total_collateral_usd")
private BigDecimal totalCollateralUsd;
@JsonProperty("total_borrowed_usd")
private BigDecimal totalBorrowedUsd;
@JsonProperty("global_health_factor")
private BigDecimal globalHealthFactor;
@JsonProperty("global_ltv")
private BigDecimal globalLtv;
@JsonProperty("positions_at_risk_count")
private Integer positionsAtRiskCount;
@JsonProperty("last_updated")
private Instant lastUpdated;
@JsonProperty("aggregation_duration")
private String aggregationDuration;
@JsonProperty("lending_positions")
private List<LendingPosition> lendingPositions;
@JsonProperty("defi_score")
private DeFiScore defiScore;
@JsonProperty("aggregation_errors")
private Map<String, String> aggregationErrors;
}
// models/DeFiScore.java
@Data
public class DeFiScore {
@JsonProperty("defi_score")
private Integer score;
@JsonProperty("risk_level")
private String riskLevel;
@JsonProperty("risk_category")
private String riskCategory;
@JsonProperty("color")
private String color;
@JsonProperty("score_breakdown")
private ScoreBreakdown scoreBreakdown;
@JsonProperty("risk_factors")
private List<RiskFactor> riskFactors;
@JsonProperty("recommendations")
private Recommendations recommendations;
@JsonProperty("liquidation_simulation")
private LiquidationSimulation liquidationSimulation;
@JsonProperty("calculated_at")
private Instant calculatedAt;
}
// models/LendingPosition.java
@Data
public class LendingPosition {
@JsonProperty("protocol")
private String protocol;
@JsonProperty("protocol_version")
private String protocolVersion;
@JsonProperty("chain")
private String chain;
@JsonProperty("user_address")
private String userAddress;
@JsonProperty("collateral_usd")
private BigDecimal collateralUsd;
@JsonProperty("borrowed_usd")
private BigDecimal borrowedUsd;
@JsonProperty("health_factor")
private BigDecimal healthFactor;
@JsonProperty("ltv_current")
private BigDecimal ltvCurrent;
@JsonProperty("is_at_risk")
private Boolean isAtRisk;
@JsonProperty("collateral_details")
private List<TokenDetail> collateralDetails;
@JsonProperty("borrowed_details")
private List<TokenDetail> borrowedDetails;
@JsonProperty("last_updated")
private Instant lastUpdated;
}
// models/TokenDetail.java
@Data
public class TokenDetail {
@JsonProperty("token")
private String token;
@JsonProperty("amount")
private BigDecimal amount;
@JsonProperty("usd_value")
private BigDecimal usdValue;
@JsonProperty("token_address")
private String tokenAddress;
}
// Additional models: ComponentScore, ScoreBreakdown, RiskFactor,
// Recommendations, LiquidationSimulation, LiquidationScenario
// (Follow same pattern as above)
Example 3: Type-Safe Client with Error Handling
// client/KixagoClient.java
package com.kixago.client;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.kixago.models.RiskProfileResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.Map;
public class KixagoClient {
private static final Logger logger = LoggerFactory.getLogger(KixagoClient.class);
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
private final String apiKey;
private final String baseUrl;
public KixagoClient(String apiKey) {
this(apiKey, "https://api.kixago.com");
}
public KixagoClient(String apiKey, String baseUrl) {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule());
}
public RiskProfileResponse getRiskProfile(String walletAddress) throws KixagoException {
if (walletAddress == null || walletAddress.trim().isEmpty()) {
throw new IllegalArgumentException("Wallet address cannot be empty");
}
try {
logger.info("Fetching risk profile for {}", walletAddress);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/v1/risk-profile/" + walletAddress))
.header("X-API-Key", apiKey)
.header("Accept", "application/json")
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
// Handle HTTP errors
if (response.statusCode() != 200) {
handleErrorResponse(response);
}
RiskProfileResponse profile = objectMapper.readValue(
response.body(),
RiskProfileResponse.class
);
// Warn about partial failures
if (profile.getAggregationErrors() != null && !profile.getAggregationErrors().isEmpty()) {
logger.warn("Partial failure - some protocols failed: {}",
profile.getAggregationErrors().keySet());
}
return profile;
} catch (java.io.IOException e) {
throw new KixagoException("Failed to parse response", e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new KixagoException("Request interrupted", e);
} catch (Exception e) {
throw new KixagoException("Request failed: " + e.getMessage(), e);
}
}
private void handleErrorResponse(HttpResponse<String> response) throws KixagoException {
int status = response.statusCode();
String body = response.body();
try {
@SuppressWarnings("unchecked")
Map<String, String> error = objectMapper.readValue(body, Map.class);
String message = error.getOrDefault("error", "Unknown error");
switch (status) {
case 400:
throw new KixagoException("Invalid request: " + message, status);
case 401:
throw new KixagoException("Invalid API key", status);
case 429:
throw new KixagoException("Rate limit exceeded", status);
case 500:
throw new KixagoException("Server error", status);
default:
throw new KixagoException("HTTP " + status + ": " + message, status);
}
} catch (Exception e) {
throw new KixagoException("HTTP " + status, status);
}
}
}
// exception/KixagoException.java
public class KixagoException extends Exception {
private final Integer statusCode;
public KixagoException(String message) {
super(message);
this.statusCode = null;
}
public KixagoException(String message, int statusCode) {
super(message);
this.statusCode = statusCode;
}
public KixagoException(String message, Throwable cause) {
super(message, cause);
this.statusCode = null;
}
public Integer getStatusCode() {
return statusCode;
}
}
Spring Boot Integration
Example 4: Spring Boot Configuration
// config/KixagoConfig.java
package com.kixago.config;
import com.kixago.client.KixagoClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.annotation.EnableCaching;
@Configuration
@EnableCaching
public class KixagoConfig {
@Value("${kixago.api-key}")
private String apiKey;
@Value("${kixago.base-url:https://api.kixago.com}")
private String baseUrl;
@Bean
public KixagoClient kixagoClient() {
return new KixagoClient(apiKey, baseUrl);
}
}
// application.yml
/*
kixago:
api-key: ${KIXAGO_API_KEY}
base-url: https://api.kixago.com
spring:
cache:
type: redis
redis:
host: localhost
port: 6379
*/
Example 5: Spring Service with Caching
// service/KixagoService.java
package com.kixago.service;
import com.kixago.client.KixagoClient;
import com.kixago.client.KixagoException;
import com.kixago.models.RiskProfileResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class KixagoService {
private static final Logger logger = LoggerFactory.getLogger(KixagoService.class);
private final KixagoClient client;
public KixagoService(KixagoClient client) {
this.client = client;
}
@Cacheable(value = "riskProfiles", key = "#walletAddress", unless = "#result == null")
public RiskProfileResponse getRiskProfile(String walletAddress) throws KixagoException {
logger.info("Fetching risk profile for {} (cache miss)", walletAddress);
return client.getRiskProfile(walletAddress);
}
}
// config/CacheConfig.java
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(30))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()
)
);
}
}
Example 6: Spring REST Controller
// controller/WalletController.java
package com.kixago.controller;
import com.kixago.client.KixagoException;
import com.kixago.models.RiskProfileResponse;
import com.kixago.service.KixagoService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("(/docs/api/wallet")
public class WalletController {
private final KixagoService kixagoService;
public WalletController(KixagoService kixagoService) {
this.kixagoService = kixagoService;
}
@GetMapping("/{address}")
public ResponseEntity<?> getRiskProfile(@PathVariable String address) {
try {
RiskProfileResponse profile = kixagoService.getRiskProfile(address);
return ResponseEntity.ok(profile);
} catch (KixagoException e) {
return ResponseEntity
.status(e.getStatusCode() != null ? e.getStatusCode() : 500)
.body(Map.of("error", e.getMessage()));
}
}
}
Real-World Use Cases
Example 7: Credit Underwriting Service
// service/UnderwritingService.java
package com.kixago.service;
import com.kixago.client.KixagoException;
import com.kixago.models.RiskProfileResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service
public class UnderwritingService {
private static final Logger logger = LoggerFactory.getLogger(UnderwritingService.class);
private final KixagoService kixagoService;
public UnderwritingService(KixagoService kixagoService) {
this.kixagoService = kixagoService;
}
public UnderwritingDecision underwrite(String walletAddress, BigDecimal requestedAmount) {
try {
RiskProfileResponse profile = kixagoService.getRiskProfile(walletAddress);
// Check if wallet has DeFi history
if (profile.getDefiScore() == null) {
return new UnderwritingDecision(
Decision.DECLINED,
"No DeFi lending history found"
);
}
int score = profile.getDefiScore().getScore();
String riskCategory = profile.getDefiScore().getRiskCategory();
BigDecimal healthFactor = profile.getGlobalHealthFactor();
BigDecimal collateral = profile.getTotalCollateralUsd();
// Rule 1: Minimum credit score
if (score < 550) {
return new UnderwritingDecision(
Decision.DECLINED,
"DeFi credit score too low: " + score,
score
);
}
// Rule 2: Health factor requirement
if (healthFactor.compareTo(BigDecimal.ZERO) > 0 &&
healthFactor.compareTo(new BigDecimal("1.5")) < 0) {
return new UnderwritingDecision(
Decision.DECLINED,
String.format("Health factor too low: %.2f (minimum 1.5)", healthFactor),
score
);
}
// Rule 3: Collateral requirement (2x loan amount)
BigDecimal minCollateral = requestedAmount.multiply(new BigDecimal("2"));
if (collateral.compareTo(minCollateral) < 0) {
return new UnderwritingDecision(
Decision.DECLINED,
String.format("Insufficient collateral. Need $%,.0f, have $%,.0f",
minCollateral, collateral),
score
);
}
// Rule 4: Risk category checks
if ("URGENT_ACTION_REQUIRED".equals(riskCategory)) {
return new UnderwritingDecision(
Decision.DECLINED,
"Critical risk factors detected - imminent liquidation risk",
score,
riskCategory
);
}
if ("HIGH_RISK".equals(riskCategory)) {
return new UnderwritingDecision(
Decision.MANUAL_REVIEW,
"High risk category - requires underwriter review",
score,
riskCategory
);
}
// Calculate max loan amount (50% of collateral)
BigDecimal maxLoan = collateral.multiply(new BigDecimal("0.5"));
if (requestedAmount.compareTo(maxLoan) > 0) {
return new UnderwritingDecision(
Decision.MANUAL_REVIEW,
"Requested amount exceeds max (50% of collateral)",
score,
null,
maxLoan
);
}
// APPROVED!
return new UnderwritingDecision(
Decision.APPROVED,
String.format("Strong DeFi profile - Score %d, Risk: %s", score, riskCategory),
score,
riskCategory,
maxLoan
);
} catch (KixagoException e) {
logger.error("Underwriting error for {}", walletAddress, e);
return new UnderwritingDecision(
Decision.MANUAL_REVIEW,
"Error fetching DeFi profile - manual review required"
);
}
}
}
// models/UnderwritingDecision.java
public class UnderwritingDecision {
private final Decision decision;
private final String reason;
private final Integer defiScore;
private final String riskCategory;
private final BigDecimal maxLoanAmount;
// Constructor, getters, etc.
}
public enum Decision {
APPROVED,
DECLINED,
MANUAL_REVIEW
}
Example 8: Scheduled Monitoring Task
// service/LiquidationMonitorService.java
package com.kixago.service;
import com.kixago.client.KixagoException;
import com.kixago.models.RiskProfileResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class LiquidationMonitorService {
private static final Logger logger = LoggerFactory.getLogger(LiquidationMonitorService.class);
private final KixagoService kixagoService;
@Value("${kixago.monitored-wallets}")
private List<String> monitoredWallets;
public LiquidationMonitorService(KixagoService kixagoService) {
this.kixagoService = kixagoService;
}
@Scheduled(fixedRate = 300000) // Every 5 minutes
public void monitorWallets() {
logger.info("Monitoring {} wallets for liquidation risk", monitoredWallets.size());
List<LiquidationAlert> alerts = new ArrayList<>();
for (String wallet : monitoredWallets) {
try {
RiskProfileResponse profile = kixagoService.getRiskProfile(wallet);
if (profile.getDefiScore() == null ||
profile.getTotalCollateralUsd().compareTo(BigDecimal.ZERO) == 0) {
continue;
}
BigDecimal buffer = profile.getDefiScore()
.getLiquidationSimulation()
.getBufferPercentage();
if (buffer.compareTo(new BigDecimal("5")) < 0) {
alerts.add(new LiquidationAlert(
wallet,
"CRITICAL",
buffer,
profile.getTotalCollateralUsd(),
String.format("🚨 CRITICAL: Only %.1f%% buffer remaining!", buffer)
));
logger.error("CRITICAL: {} - {}% buffer, ${} at risk",
wallet, buffer, profile.getTotalCollateralUsd());
}
else if (buffer.compareTo(new BigDecimal("10")) < 0) {
alerts.add(new LiquidationAlert(
wallet,
"HIGH",
buffer,
profile.getTotalCollateralUsd(),
String.format("⚠️ HIGH RISK: %.1f%% buffer", buffer)
));
logger.warn("HIGH: {} - {}% buffer", wallet, buffer);
}
} catch (KixagoException e) {
logger.error("Error monitoring {}", wallet, e);
}
}
if (!alerts.isEmpty()) {
sendAlerts(alerts);
}
logger.info("Monitoring complete. Found {} alerts.", alerts.size());
}
private void sendAlerts(List<LiquidationAlert> alerts) {
// Implement notification logic (email, Slack, etc.)
}
}
// Enable scheduling in main application
@SpringBootApplication
@EnableScheduling
public class KixagoApplication {
public static void main(String[] args) {
SpringApplication.run(KixagoApplication.class, args);
}
}
Testing
Example 9: JUnit Tests
// KixagoClientTest.java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class KixagoClientTest {
private KixagoClient client;
@BeforeEach
void setUp() {
client = new KixagoClient("test-api-key");
}
@Test
void getRiskProfile_validAddress_returnsProfile() throws Exception {
// This would require mocking HttpClient
// For brevity, showing structure
String walletAddress = "0xf0bb20865277aBd641a307eCe5Ee04E79073416C";
RiskProfileResponse profile = client.getRiskProfile(walletAddress);
assertNotNull(profile);
assertEquals(walletAddress, profile.getWalletAddress());
}
@Test
void getRiskProfile_emptyAddress_throwsException() {
assertThrows(IllegalArgumentException.class, () -> {
client.getRiskProfile("");
});
}
}
// UnderwritingServiceTest.java (Spring Boot Test)
@SpringBootTest
class UnderwritingServiceTest {
@Autowired
private UnderwritingService underwritingService;
@MockBean
private KixagoService kixagoService;
@Test
void underwrite_highScore_approved() throws Exception {
// Mock response
RiskProfileResponse mockProfile = new RiskProfileResponse();
mockProfile.setDefiScore(new DeFiScore());
mockProfile.getDefiScore().setScore(750);
mockProfile.getDefiScore().setRiskCategory("LOW_RISK");
mockProfile.setGlobalHealthFactor(new BigDecimal("3.2"));
mockProfile.setTotalCollateralUsd(new BigDecimal("100000"));
when(kixagoService.getRiskProfile(anyString()))
.thenReturn(mockProfile);
UnderwritingDecision decision = underwritingService.underwrite(
"0xTest...",
new BigDecimal("10000")
);
assertEquals(Decision.APPROVED, decision.getDecision());
}
@Test
void underwrite_lowScore_declined() throws Exception {
RiskProfileResponse mockProfile = new RiskProfileResponse();
mockProfile.setDefiScore(new DeFiScore());
mockProfile.getDefiScore().setScore(400);
when(kixagoService.getRiskProfile(anyString()))
.thenReturn(mockProfile);
UnderwritingDecision decision = underwritingService.underwrite(
"0xTest...",
new BigDecimal("10000")
);
assertEquals(Decision.DECLINED, decision.getDecision());
}
}
Best Practices
✅ DO
- Use dependency injection - Spring Boot or manual DI
- Implement caching - Spring Cache with Redis
- Use BigDecimal for money - Never use double/float
- Use Lombok - Reduce boilerplate
- Structured logging - SLF4J with context
- Handle timeouts - Set appropriate durations
- Use Optional - For nullable fields
- Type-safe models - Use Jackson annotations
❌ DON'T
- Don't create new HttpClient per request - Reuse instances
- Don't expose API keys - Use environment variables
- Don't ignore aggregation errors - Log warnings
- Don't use float/double for money - Precision loss!
- Don't block threads - Use async if needed
- Don't swallow exceptions - Log and rethrow appropriately
Next Steps
Need Help?
- Code not working? Email api@kixago.com with code snippet
- Want a Java SDK? Coming Q2 2026 - watch our GitHub
- Found a bug? Report it
---