Java SDK for OpenPanel Analytics — server-side tracking for JVM backends.
This SDK targets Java 11+ and is designed for backend applications (Spring Boot, Quarkus, Micronaut, plain Java, etc.).
For Android/Kotlin applications, see the official kotlin-sdk.
<dependency>
<groupId>fr.idcapture</groupId>
<artifactId>openpanel-java</artifactId>
<version>0.4.0</version>
</dependency>implementation 'fr.idcapture:openpanel-java:0.4.0'implementation("fr.idcapture:openpanel-java:0.4.0")import fr.idcapture.openpanel.OpenPanel;
import fr.idcapture.openpanel.OpenPanelOptions;
OpenPanel op = OpenPanel.create(
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.clientSecret("YOUR_CLIENT_SECRET") // required for server-side calls
.build()
);
// Track an event (fire-and-forget)
op.track("user_signed_up", Map.of("plan", "pro"));
// Shutdown cleanly on app stop
op.close();All methods return CompletableFuture<Void>. You can ignore the future (fire-and-forget) or chain callbacks:
op.track("purchase", Map.of("amount", 99.99))
.exceptionally(e -> { logger.error("Tracking failed", e); return null; });OpenPanel op = OpenPanel.create(
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID") // required
.clientSecret("YOUR_CLIENT_SECRET") // optional, but required server-side
.apiUrl("https://api.openpanel.dev") // optional, default shown
.disabled(false) // optional, set true to mute all events
.filter(name -> !name.startsWith("debug_")) // optional event filter
.connectTimeoutSeconds(10) // optional, default 10
.readTimeoutSeconds(30) // optional, default 30
.maxRetries(3) // optional, default 3 (0 to disable)
.initialRetryDelayMs(500) // optional, default 500ms (doubles each attempt)
.verbose(false) // optional, set true for debug logging
.build()
);// Minimal
op.track("page_viewed");
// With properties
op.track("button_clicked", Map.of("button_id", "submit_form"));
// With user
op.track("checkout_started", Map.of("cart_value", 149.99), "user123");
// With user + groups
op.track("report_exported",
Map.of("format", "pdf"),
"user123",
List.of("org_acme")
);// With custom properties only
op.identify("user123", Map.of("plan", "enterprise"));
// With standard fields + custom properties
op.identify("user123", "John", "Doe", "john@example.com", Map.of("tier", "premium"));
// With avatar
op.identify("user123", "John", "Doe", "john@example.com",
"https://example.com/avatar.png", Map.of("tier", "premium"));op.increment("user123", "login_count", 1);
op.decrement("user123", "credits", 5);// Simple
op.revenue(99.99);
// With user
op.revenue(99.99, "user123");
// With user + extra properties
op.revenue(49.99, "user123", Map.of("currency", "EUR", "plan", "pro"));// Create or update a group
op.group("org_acme", "company", "Acme Inc", Map.of("plan", "enterprise", "seats", 25));
// Assign a user to groups
op.assignGroup("user123", List.of("org_acme"));Note: groups are not automatically attached to track events. Pass them explicitly on each track() call where needed.
Properties set here are merged into every track() call. Caller properties take precedence over global ones.
op.setGlobalProperties(Map.of("app_version", "2.1.0", "env", "production"));
// All subsequent track() calls will include app_version and env automatically
op.track("feature_used", Map.of("feature", "export"));// Explicit
op.close();
// Or via try-with-resources
try (OpenPanel op = OpenPanel.create(options)) {
op.track("startup");
}OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.apiUrl("https://your-openpanel-instance.com")
.build();Useful for local development or testing — no HTTP calls are made:
OpenPanelOptions.builder()
.clientId("id")
.disabled(true)
.build();OpenPanelOptions.builder()
.clientId("id")
.filter(eventName -> !eventName.startsWith("internal_"))
.build();On API errors (4xx/5xx), the CompletableFuture is completed exceptionally with an HttpTracker.OpenPanelApiException containing the HTTP status code:
op.track("event")
.exceptionally(e -> {
if (e.getCause() instanceof HttpTracker.OpenPanelApiException apiEx) {
logger.warn("OpenPanel returned {}", apiEx.getStatusCode());
}
return null;
});Failed requests (5xx and network errors) are retried automatically with exponential backoff. Defaults: 3 retries, starting at 500ms (500ms → 1s → 2s). Client errors (4xx) are not retried.
// Customize retry behavior
OpenPanelOptions.builder()
.clientId("id")
.maxRetries(5) // more retries
.initialRetryDelayMs(1000) // start at 1s
.build();
// Disable retries entirely
OpenPanelOptions.builder()
.clientId("id")
.maxRetries(0)
.build();Enable verbose mode to log all HTTP requests and responses via java.util.logging:
OpenPanelOptions.builder()
.clientId("id")
.verbose(true)
.build();All property maps use JetBrains @Nullable type-use annotations (Map<String, @Nullable Object>), so Kotlin sees Map<String, Any?> natively — no @Suppress("UNCHECKED_CAST") needed.
op.track("checkout", mapOf("amount" to 49.99, "coupon" to null))| Dependency | Version | Scope |
|---|---|---|
com.squareup.okhttp3:okhttp |
4.12.0 | compile |
com.fasterxml.jackson.core:jackson-databind |
2.17.x | compile |
org.jetbrains:annotations |
24.1.0 | compile |
org.junit.jupiter:junit-jupiter |
5.10.x | test |
com.squareup.okhttp3:mockwebserver |
4.12.0 | test |
| kotlin-sdk | openpanel-java-sdk (this) | |
|---|---|---|
| Target | Android (Kotlin) | JVM backends (Java 11+) |
| Android Context | Required | N/A |
| Device/screen info | Auto-collected | N/A |
| App lifecycle | Auto-tracked | N/A |
| Async | Coroutines | CompletableFuture |
| Nullable map values | N/A | @Nullable type-use annotations |
| Retry with backoff | Yes (3 retries) | Yes (3 retries, configurable) |
| Maven artifact | dev.openpanel:openpanel |
fr.idcapture:openpanel-java |