On this page
Java
Last updated
Send vTilt events from Java code (Spring Boot, Micronaut, Quarkus, plain Java) by POSTing to the ingest API.
There's no native Java SDK yet, but vTilt's wire format is a plain JSON POST so any Java code can emit events. The pattern below uses the JDK 11+ HttpClient and Jackson; the same shape works inside Spring Boot, Micronaut, Quarkus, and standalone tools.
#1. Add dependencies
For Spring Boot (Gradle):
implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.0'groovy
For Maven:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>xml
#2. Capture client
A singleton client with a virtual-thread (Java 21+) executor for fire-and-forget captures. Replace with Executors.newCachedThreadPool() on Java 17.
// src/main/java/com/example/vtilt/Vtilt.java
package com.example.vtilt;
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;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public final class Vtilt {
private static final String TOKEN = System.getenv("VTILT_TOKEN");
private static final String HOST = System.getenv().getOrDefault("VTILT_HOST", "https://www.vtilt.com");
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final HttpClient CLIENT = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2))
.build();
private static final ExecutorService EXEC = Executors.newVirtualThreadPerTaskExecutor();
private Vtilt() {}
public static void capture(String distinctId, String event, Map<String, Object> properties) {
EXEC.submit(() -> send(distinctId, event, properties == null ? Map.of() : properties));
}
public static void identify(String distinctId, Map<String, Object> properties) {
capture(distinctId, "$identify", Map.of("$set", properties));
}
private static void send(String distinctId, String event, Map<String, Object> properties) {
try {
Map<String, Object> body = new HashMap<>();
body.put("api_key", TOKEN);
body.put("event", event);
body.put("distinct_id", distinctId);
body.put("properties", properties);
HttpRequest req = HttpRequest.newBuilder(URI.create(HOST + "/api/e"))
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(2))
.POST(HttpRequest.BodyPublishers.ofString(MAPPER.writeValueAsString(body)))
.build();
CLIENT.send(req, HttpResponse.BodyHandlers.discarding());
} catch (Exception ignored) {
// never raise on analytics
}
}
}java
#3. Capture events
import com.example.vtilt.Vtilt;
import java.util.Map;
public class CheckoutService {
public void completePurchase(String userId, double amount) {
Vtilt.identify(userId, Map.of("plan", "pro"));
Vtilt.capture(userId, "purchase_completed", Map.of(
"amount", amount,
"currency", "USD"
));
}
}java
#4. Spring Boot controller
@RestController
public class CheckoutController {
@PostMapping("/checkout")
public Map<String, Boolean> complete(@RequestBody CheckoutRequest req) {
Vtilt.capture(req.userId(), "purchase_completed", Map.of("amount", req.amount()));
return Map.of("ok", true);
}
}java
#Next steps
- Identify & alias — full identification model.
- Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
- Reverse proxy — block-resistant ingestion.
- Common mistakes — integration pitfalls to avoid before you ship.