Java and Spring Boot Integration
Use the Java HTTP client with the documented gateway route for Chat Completions.
A Spring Boot service can register the same class as a bean. This integration does not use a version-specific AI framework adapter.
Prerequisites
- Complete the Quickstart. Keep the gateway running.
- Create a client API token for the application.
- Use Java 17 or a subsequent version. Spring Boot includes Jackson.
- If a plain Java application uses this example, add Jackson Databind.
Set application configuration from secrets or environment variables:
export VERDICTAN_GATEWAY_URL="http://127.0.0.1:41002/v1"
export VERDICTAN_REQUEST_TOKEN="vdt_..."
export VERDICTAN_MODEL="replace-with-configured-model"
Create a governed client
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
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.List;
import java.util.Map;
public final class GovernedChatService {
private final HttpClient http = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
private final ObjectMapper json;
private final URI completionsUri;
private final String token;
private final String model;
public GovernedChatService(
ObjectMapper json,
String gatewayUrl,
String token,
String model) {
this.json = json;
this.completionsUri = URI.create(
gatewayUrl.replaceAll("/+$", "") + "/chat/completions");
this.token = token;
this.model = model;
}
public String complete(String input) throws Exception {
var payload = Map.of(
"model", model,
"messages", List.of(Map.of("role", "user", "content", input))
);
var request = HttpRequest.newBuilder(completionsUri)
.timeout(Duration.ofSeconds(60))
.header("Authorization", "Bearer " + token)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json.writeValueAsString(payload)))
.build();
var response = http.send(request, HttpResponse.BodyHandlers.ofString());
JsonNode body = null;
try {
body = json.readTree(response.body());
} catch (JsonProcessingException ignored) {
// Do not expose a provider error body that is not JSON.
}
if (response.statusCode() == 400
&& body != null
&& "content_policy_violation".equals(
body.path("error").path("code").asText())) {
throw new IllegalStateException("Request blocked by policy");
}
if (response.statusCode() < 200 || response.statusCode() >= 300) {
throw new IllegalStateException(
"Gateway returned HTTP " + response.statusCode());
}
if (body == null) {
throw new IllegalStateException("Gateway returned invalid JSON");
}
JsonNode content = body.path("choices").path(0).path("message").path("content");
if (!content.isTextual()) {
throw new IllegalStateException("Gateway returned invalid content");
}
return content.asText();
}
}
Create this service from Spring configuration. Register the service as a bean:
import org.springframework.context.annotation.Bean;
@Bean
GovernedChatService governedChatService(ObjectMapper json) {
return new GovernedChatService(
json,
requireEnv("VERDICTAN_GATEWAY_URL"),
requireEnv("VERDICTAN_REQUEST_TOKEN"),
requireEnv("VERDICTAN_MODEL")
);
}
private static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) {
throw new IllegalStateException(name + " is required");
}
return value;
}
The application token is not the upstream provider credential. The model name must match a configured target that the gateway returns.
Verify health and authorization in different steps
GET /healthzreports gateway process health.- An authenticated
GET /v1/modelsrequest verifies the client token and the available model catalog.
Use the second check for application readiness. It uses the same authorization boundary as model requests.
Retry carefully
Do not retry a 400 content_policy_violation, 401, or 403 response.
A 429 or 5xx response can be temporary. A timeout can occur after an upstream provider accepts the request.
If the application has a policy for duplicate work and cost, it can add retries.
Verify the governed path
curl -fsS \
-H "Authorization: Bearer ${VERDICTAN_REQUEST_TOKEN}" \
"${VERDICTAN_GATEWAY_URL}/models"
verdictan events tail --since 10m --follow
While the event tail is active, send a representative Java request. A matching request ID shows that the gateway delivered Event evidence to the control plane.
Next steps
- Read Runtime Request Families.
- Read Debugging AI Requests.
- Read API Token Management.