A Java 21 client SDK for the FastBI API — a Business Intelligence platform with KPI management, AI-powered analytics, MCP orchestration, real-time streaming, and multi-format report export.
Note on Authentication: The FastBI API uses short-lived JWT tokens (10-minute TTL) issued via
client_id+secret. The SDK obtains and refreshes tokens automatically. OAuth 2.0 support will be added in a future release.
- Java 21 or later
- Maven 3.8+
- A running FastBI API instance
Add the dependency to your pom.xml:
<dependency>
<groupId>io.fastbi</groupId>
<artifactId>fastbi-app-sdk-java</artifactId>
<version>0.1.0</version>
</dependency>import io.fastbi.sdk.FastBiClient;
import io.fastbi.sdk.FastBiConfig;
import io.fastbi.sdk.kpi.Kpi;
FastBiClient client = FastBiClient.create(
FastBiConfig.builder()
.baseUrl("https://your-fastbi-instance.com/api/v1")
.clientId("your-client-id")
.secret("your-secret")
.build()
);
// The client automatically obtains and refreshes tokens.
Kpi kpi = client.kpis().get("REVENUE_GROWTH", "Acme Corp");
System.out.printf("Current: %.2f / Target: %.2f%n", kpi.getCurrentValue(), kpi.getTargetValue());| Property | Type | Default | Description |
|---|---|---|---|
baseUrl |
String |
http://localhost:3050/api/v1 |
FastBI API root URL |
clientId |
String |
— | API client identifier |
secret |
String |
— | API client secret |
timeout |
Duration |
30s |
Per-request timeout (does not apply to SSE streams) |
httpClient |
HttpClient |
default (10 s connect timeout) | Override for custom TLS, proxy, or testing |
Add the dependency (same as above) and configure application.yml:
fastbi:
base-url: https://your-fastbi-instance.com/api/v1
client-id: your-client-id
secret: your-secret
timeout: 30sA FastBiClient bean is registered automatically. Inject it where needed:
@Service
public class ReportingService {
private final FastBiClient fastBi;
public ReportingService(FastBiClient fastBi) {
this.fastBi = fastBi;
}
public Kpi getKpi(String code, String org) {
return fastBi.kpis().get(code, org);
}
}Tokens are obtained automatically before the first authenticated request and refreshed 30 seconds before expiry. You can also manage tokens explicitly:
// Generate a token explicitly
TokenResponse tok = client.auth().generateToken("my_client", "my_secret");
System.out.println("Expires at: " + tok.getExpiresAt());
// Validate an existing token
ValidateResponse result = client.auth().validateToken(someToken);
System.out.println("Status: " + result.getStatus()); // "AUTHORIZED"// Create
Kpi kpi = client.kpis().create(CreateKpiRequest.builder()
.code("NET_MARGIN")
.name("Net Profit Margin")
.organizationName("Acme Corp")
.category("financial")
.currentValue(18.5)
.targetValue(22.0)
.unit("%")
.reportingPeriod("quarterly")
.build());
// Read
Kpi kpi = client.kpis().get("NET_MARGIN", "Acme Corp");
// Update
Kpi updated = client.kpis().update("NET_MARGIN", UpdateKpiRequest.builder()
.organizationName("Acme Corp")
.currentValue(19.1)
.build());
// Delete
client.kpis().delete("NET_MARGIN", "Acme Corp");
// List with filters
KpiListResponse list = client.kpis().list("Acme Corp", KpiListFilter.builder()
.category("financial")
.limit(20)
.orderBy("created_at")
.orderDir("DESC")
.build());
// History
KpiHistoryResponse history = client.kpis().history("NET_MARGIN", "Acme Corp", 50, 0);// Revenue insights with optional date/category/region filters
Map<String, Object> insights = client.analytics().revenueInsights(
RevenueInsightsFilter.builder()
.startDate("2025-01-01")
.endDate("2025-12-31")
.region("southeast")
.build());
// Revenue forecast
Map<String, Object> forecast = client.analytics().forecastRevenue(
ForecastRequest.builder()
.organizationName("Acme Corp")
.monthsAhead(6)
.includeExternalFactors(true)
.build());
// Product mix optimization
Map<String, Object> mix = client.analytics().optimizeProductMix(
ProductMixRequest.builder()
.organizationName("Acme Corp")
.build());// Create a global template
DashboardTemplate tmpl = client.dashboards().createTemplate(
CreateDashboardTemplateRequest.builder()
.name("Executive Overview")
.description("C-level KPI dashboard")
.build());
// Create an organization dashboard from the template
Dashboard dash = client.dashboards().create(CreateDashboardRequest.builder()
.name("Q4 Executive Dashboard")
.organizationName("Acme Corp")
.templateId(tmpl.getId())
.build());
// Add a widget
Widget widget = client.dashboards().addWidget(dash.getSlug(), AddWidgetRequest.builder()
.title("Revenue Trend")
.type("line_chart")
.config(Map.of("metric", "REVENUE_GROWTH", "period", "12m"))
.build());// Register a connection
client.connections().register(RegisterConnectionRequest.builder()
.organizationName("Acme Corp")
.connectionName("prod-db")
.host("db.example.com")
.port(5432)
.databaseName("analytics")
.username("reader")
.password("s3cr3t")
.dbType(DbType.POSTGRES)
.sslMode("require")
.build());
// Execute a query
Map<String, Object> rows = client.connections().executeQuery("prod-db",
ExecuteQueryRequest.builder()
.organizationName("Acme Corp")
.query("SELECT product, SUM(revenue) FROM sales GROUP BY product LIMIT 10")
.useCache(true)
.cacheTtlSeconds(300)
.build());// Create a report run
ReportRun run = client.reports().createRun(CreateRunRequest.builder()
.templateName("monthly-financial")
.organizationName("Acme Corp")
.build());
// Advance through lifecycle stages
client.reports().transitionRun(run.getCreatedAt(),
TransitionRunRequest.builder().targetStage("in_review").build());
// Approve review
client.reports().approveReview(run.getId(), ApproveReviewRequest.builder()
.approvedBy("cfo@acme.com")
.build());
// Schedule recurring reports
ReportSchedule schedule = client.reports().createSchedule(CreateScheduleRequest.builder()
.name("monthly-close")
.templateName("monthly-financial")
.cronExpr("0 6 1 * *") // 06:00 on the 1st of every month
.build());
// Generate AI narrative
Map<String, Object> narrative = client.reports().generateNarrative(
GenerateNarrativeRequest.builder()
.runId(run.getId())
.organizationName("Acme Corp")
.language("en")
.build());// Export to Excel
client.exports().exportExcel(runId,
ExcelExportRequest.builder().organizationName("Acme Corp").build());
String excelUrl = client.exports().downloadExcelUrl(runId);
// Export to CSV
client.exports().exportCsv(createdAt,
CsvExportRequest.builder().organizationName("Acme Corp").build());
// Render a brand-aware PDF
client.exports().renderPdf(runId, PdfRenderRequest.builder()
.organizationName("Acme Corp")
.brandName("acme-brand")
.build());
String pdfUrl = client.exports().downloadPdfUrl(runId);Map<String, Object> exec = client.revenue().executiveReport();
Map<String, Object> region = client.revenue().regionalReport("southeast");
Map<String, Object> bu = client.revenue().businessUnitReport("retail");
Map<String, Object> ts = client.revenue().timeSeries("Acme Corp", "2025-01-01", "2025-12-31");Map<String, Object> overall = client.grossMargin().overall();
Map<String, Object> byProd = client.grossMargin().byProduct();
Map<String, Object> byTier = client.grossMargin().byCustomerTier();
Map<String, Object> forecast = client.grossMargin().forecast();// Natural language question
Map<String, Object> answer = client.sales().ask(AskQuestionRequest.builder()
.question("What are our top 3 revenue drivers this quarter?")
.organizationName("Acme Corp")
.build());
// Trend prediction
Map<String, Object> trend = client.sales().predictTrend(PredictSalesTrendRequest.builder()
.organizationName("Acme Corp")
.monthsAhead(3)
.region("southeast")
.build());// Train a linear regression model
client.ml().trainLinearRegression(TrainLinearRegressionRequest.builder()
.features(List.of(List.of(1.0, 2.0), List.of(3.0, 4.0)))
.labels(List.of(10.0, 20.0))
.build());
// Single prediction
Map<String, Object> pred = client.ml().predictLinearRegression(
PredictLinearRegressionRequest.builder()
.features(List.of(5.0, 6.0))
.build());// Create a tax workflow template
AccountingTemplate tmpl = client.accounting().createTemplate(
CreateAccountingTemplateRequest.builder()
.name("ICMS Monthly")
.category("tax")
.structure(Map.of("steps", List.of("collect", "calculate", "file")))
.build());
// Instantiate a workflow for a fiscal year
Map<String, Object> wf = client.accounting().createWorkflowFromTemplate(
CreateWorkflowFromTemplateRequest.builder()
.templateName("ICMS Monthly")
.templateCategory("tax")
.fiscalYear(2025)
.build());
// Compliance calendar
Map<String, Object> calendar = client.accounting().getComplianceCalendar("2025");Pure statistical analysis and RAG — no external LLM calls.
// Customer intelligence
Map<String, Object> atRisk = client.ai().identifyAtRiskCustomers();
Map<String, Object> segments = client.ai().getCustomerSegments();
// Financial analysis
Map<String, Object> cogs = client.ai().getCogsBreakdown();
Map<String, Object> leaks = client.ai().getMarginLeaks();
// KPI intelligence
Map<String, Object> devs = client.ai().getKpiDeviations();
Map<String, Object> corr = client.ai().correlateKpis();
// Product portfolio
Map<String, Object> bcg = client.ai().getBcgMatrix();
Map<String, Object> under = client.ai().getUnderperformingProducts();
// Organization health
Map<String, Object> score = client.ai().getOrganizationHealthScore();
// Automotive (fleet management)
Map<String, Object> fleet = client.ai().getFleetUtilization();
Map<String, Object> overdue = client.ai().getOverdueRentals();// Full analysis across all dimensions
Map<String, Object> result = client.mcp().fullAnalysis(McpAnalyticsRequest.builder()
.organizationName("Acme Corp")
.startDate("2025-01-01")
.endDate("2025-12-31")
.maxSkills(6)
.build());
// Route a natural language query
Map<String, Object> route = client.mcp().routeQuery(McpRouteQueryRequest.builder()
.query("which KPIs are below target this quarter?")
.topK(5)
.build());
// Execute a high-level goal
Map<String, Object> goal = client.mcp().executeGoal(McpExecuteGoalRequest.builder()
.organizationName("Acme Corp")
.goal("analyze_kpis")
.build());
// Generate AI narrative
Map<String, Object> narrative = client.mcp().generateNarrative(McpNarrativeRequest.builder()
.organizationName("Acme Corp")
.language("en")
.build());Streaming methods return a Stream<SseEvent> that must be closed after use (try-with-resources) to release the HTTP connection.
// Live KPI feed
try (Stream<SseEvent> events = client.stream().kpis()) {
events.limit(100).forEach(e -> System.out.printf("[%s] %s%n", e.event(), e.data()));
}
// Anomaly monitoring for a specific organization
try (Stream<SseEvent> anomalies = client.stream().anomaliesByOrg("Acme Corp")) {
anomalies.forEach(e -> log.warn("ANOMALY: {}", e.data()));
}
// Stream a specific KPI
try (Stream<SseEvent> kpi = client.stream().kpiByCode("REVENUE_GROWTH")) {
kpi.limit(50).forEach(e -> System.out.println(e.data()));
}
// Streaming pipeline
client.stream().createPipeline(CreatePipelineRequest.builder()
.name("revenue-agg")
.organizationName("Acme Corp")
.build());
try (Stream<SseEvent> pipeline = client.stream().streamPipeline("revenue-agg")) {
pipeline.limit(200).forEach(e -> System.out.println(e.data()));
}
// Real-time report generation
try (Stream<SseEvent> report = client.stream().generateReport(
GenerateReportStreamRequest.builder()
.templateName("monthly-financial")
.organizationName("Acme Corp")
.build())) {
report.forEach(e -> System.out.println(e.data()));
}BrandConfig brand = client.organizations().createBrandConfig(
CreateBrandConfigRequest.builder()
.organizationName("Acme Corp")
.primaryColor("#0056b3")
.logoUrl("https://cdn.acme.com/logo.svg")
.build());
client.organizations().updateBrandConfig("Acme Corp",
UpdateBrandConfigRequest.builder()
.primaryColor("#003d8c")
.build());All API errors surface as FastBiApiException (unchecked):
try {
Kpi kpi = client.kpis().get("UNKNOWN", "Acme Corp");
} catch (FastBiApiException e) {
System.out.println("HTTP status: " + e.getStatusCode());
System.out.println("Body: " + e.getResponseBody());
}Network errors and serialization problems throw RuntimeException wrapping the root cause.
All examples read credentials from environment variables:
export FASTBI_BASE_URL=http://localhost:3050/api/v1
export FASTBI_CLIENT_ID=your_client
export FASTBI_SECRET=your_secret| Directory | What it covers |
|---|---|
examples/kpi-lifecycle/ |
Full KPI lifecycle: create → read → update → list with filters → history → delete |
examples/report-pipeline/ |
Report run lifecycle: create → stage transitions → review → narrative → export → schedule |
examples/streaming/ |
Concurrent SSE streams: KPI feed, anomaly alerts, aggregation pipeline |
examples/mcp-analytics/ |
MCP orchestration: query routing, full analysis, anomaly detection, narrative |
examples/ai-intelligence/ |
All AI analysis endpoints: customers, financial, KPIs, products, automotive |
examples/accounting-workflow/ |
Accounting templates → fiscal year workflows → compliance calendar |
examples/database-connections/ |
Register connections, execute queries, discover schema, manage saved queries |
examples/ml-regression/ |
Train linear regression → inspect model → single and batch predictions |
FastBiClient is fully thread-safe and should be created once and shared. Token refresh uses a ReentrantLock to prevent concurrent refresh storms.
- OAuth 2.0 client credentials flow
- Automatic retry with exponential back-off
- Request/response logging interceptor hook
- Typed response records for all endpoints (currently
Map<String, Object>where schema is not yet stable) - Reactive (Project Reactor / WebFlux) SSE adapter
Apache 2.0 — see LICENSE.