Java
The OpenPanel Java SDK allows you to track user behavior in your JVM backend applications.
The OpenPanel Java SDK allows you to track user behavior in your JVM backend applications. It targets Java 11+ and works with Spring Boot, Quarkus, Micronaut, plain Java, and more.
View the Java SDK on GitHub for the latest updates and source code.
For Android/Kotlin applications, see the Kotlin SDK instead.
Installation
Step 1: Add Dependency
Add the OpenPanel SDK to your project's dependencies:
Maven
<dependency>
<groupId>fr.idcapture</groupId>
<artifactId>openpanel-java</artifactId>
<version>0.4.0</version>
</dependency>Gradle (Groovy)
implementation 'fr.idcapture:openpanel-java:0.4.0'Gradle (Kotlin DSL)
implementation("fr.idcapture:openpanel-java:0.4.0")Step 2: Initialize
Import the SDK and initialize it with your client ID and secret:
import fr.idcapture.openpanel.OpenPanel;
import fr.idcapture.openpanel.OpenPanelOptions;
OpenPanel op = OpenPanel.create(
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.clientSecret("YOUR_CLIENT_SECRET")
.build()
);Configuration Options
Common options
apiUrl- The url of the openpanel API or your self-hosted instanceclientId- The client id of your applicationclientSecret- The client secret of your application (only required for server-side events)filter- A function that will be called before sending an event. If it returns false, the event will not be sentdisabled- If true, the library will not send any events
Additional Java-specific options:
apiUrl- API endpoint URL (default:https://api.openpanel.dev)disabled- Set totrueto disable all event trackingfilter- A function that will be called before tracking an event. If it returnsfalse, the event will not be trackedconnectTimeoutSeconds- Connection timeout in seconds (default:10)readTimeoutSeconds- Read timeout in seconds (default:30)maxRetries- Maximum number of retries for failed requests (default:3, set to0to disable)initialRetryDelayMs- Initial retry delay in milliseconds (default:500, doubles each attempt)verbose- Set totrueto enable verbose logging viajava.util.logging
Full configuration example
OpenPanel op = OpenPanel.create(
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.clientSecret("YOUR_CLIENT_SECRET")
.apiUrl("https://api.openpanel.dev")
.disabled(false)
.filter(name -> !name.startsWith("debug_"))
.connectTimeoutSeconds(10)
.readTimeoutSeconds(30)
.maxRetries(3)
.initialRetryDelayMs(500)
.verbose(false)
.build()
);Usage
All methods return CompletableFuture<Void>. You can ignore the future (fire-and-forget) or chain callbacks for error handling.
Tracking Events
To track an event:
// 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")
);Identifying Users
To identify a user:
// 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"));Setting Global Properties
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"));Incrementing Properties
To increment a numeric property on a user profile:
op.increment("user123", "login_count", 1);Decrementing Properties
To decrement a numeric property on a user profile:
op.decrement("user123", "credits", 5);Tracking Revenue
To track revenue events:
// 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"));Groups
// 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"));Groups are not automatically attached to track events. Pass them explicitly on each track() call where needed.
Shutdown
Shutdown the client cleanly when your application stops:
// Explicit
op.close();
// Or via try-with-resources
try (OpenPanel op = OpenPanel.create(options)) {
op.track("startup");
}Advanced Usage
Error Handling
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;
});Retry Behavior
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();Custom Event Filtering
You can filter events by name before they are sent:
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.filter(eventName -> !eventName.startsWith("internal_"))
.build();Disabling Tracking
Useful for local development or testing — no HTTP calls are made:
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.disabled(true)
.build();Self-Hosted OpenPanel
If you're running a self-hosted OpenPanel instance, set the apiUrl:
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.apiUrl("https://your-openpanel-instance.com")
.build();Debug Logging
Enable verbose mode to log all HTTP requests and responses via java.util.logging:
OpenPanelOptions.builder()
.clientId("YOUR_CLIENT_ID")
.verbose(true)
.build();Kotlin Interop
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))Community SDK
The Java SDK is a community-maintained SDK by idcapture. For issues or feature requests, please file an issue on the Java SDK GitHub repository.