Java SDK
syntic-java is the planned official client for Java and Kotlin. It is generated from the OpenAPI description with immutable models and builders, and adds hand-written helpers for streaming, tool loops, pagination and batches. It ships in the first wave after Python and TypeScript; see Other languages.
Today, use java.net.http.HttpClient as shown in Quickstart: Java. That code keeps working after the SDK ships.
Install
implementation("ai.syntic:syntic-java:1.0.0")| Requirement | Value |
|---|---|
| Java | 11 or newer (Kotlin 1.9 or newer) |
| Artifact | ai.syntic:syntic-java on Maven Central |
| HTTP | Built-in java.net.http client; an OkHttp transport is available as syntic-java-okhttp |
Create a client
import ai.syntic.SynticClient;
SynticClient client = SynticClient.fromEnv(); // SYNTIC_API_KEY, SYNTIC_BASE_URL
SynticClient custom = SynticClient.builder()
.apiKey(System.getenv("SYNTIC_API_KEY"))
.baseUrl("https://app.syntic.ai/v1")
.maxRetries(3)
.timeout(java.time.Duration.ofMinutes(5))
.build();| Builder method | Default | Notes |
|---|---|---|
apiKey | SYNTIC_API_KEY | Private key or workload identity token. |
baseUrl | SYNTIC_BASE_URL, else https://app.syntic.ai/v1 | |
version | The release’s API version | Sent as syntic-version. |
addBeta | none | Sent as syntic-beta; methods that need a flag add it themselves. |
maxRetries | 2 | Retries rate_limit, overloaded, api_error and I/O errors with back-off. |
timeout | 10 minutes | Per request. |
Clients are thread-safe; create one and share it.
A first request
import ai.syntic.models.ChatCompletion;
import ai.syntic.models.ChatCompletionCreateParams;
ChatCompletion reply = client.chat().completions().create(
ChatCompletionCreateParams.builder()
.model("Amara S1.2")
.maxTokens(200)
.addSystemMessage("Answer in one sentence.")
.addUserMessage("Why is the sky blue?")
.build());
System.out.println(reply.choices().get(0).message().content().orElse(""));
System.out.println(reply.usage().totalTokens() + " tokens, request " + reply.requestId());createAsync returns a CompletableFuture<ChatCompletion>.
Streaming
try (var stream = client.chat().completions().createStreaming(
ChatCompletionCreateParams.builder()
.model("Amara Quick")
.addUserMessage("Write a haiku about rain.")
.build())) {
stream.textDeltas().forEach(System.out::print);
var finalCompletion = stream.finalCompletion(); // assembled message, tool calls, usage
System.out.println("\n" + finalCompletion.usage().totalTokens());
}Closing the stream closes the connection, which stops generation.
Tool loop
record OrderArgs(@JsonPropertyDescription("Order id such as 88123") String orderId) {}
var getOrder = Tool.of("get_order", "Look up an order by its id.", OrderArgs.class,
args -> Map.of("order_id", args.orderId(), "status", "shipped"));
var result = client.tools().run(ToolRunParams.builder()
.model("Amara S1.2")
.addUserMessage("Where is order 88123?")
.addTool(getOrder)
.maxRounds(8)
.build());
System.out.println(result.finalMessage().content().orElse(""));The argument record becomes the tool’s JSON Schema. Exceptions thrown by a tool are returned to the model as error results.
Errors
| Exception | error.type |
|---|---|
InvalidRequestException | invalid_request |
AuthenticationException | authentication |
PermissionDeniedException | permission |
NotFoundException | not_found |
QuotaExhaustedException | quota_exhausted |
RateLimitException | rate_limit |
OverloadedException | overloaded |
InternalServerException | api_error |
SynticIoException | No response received |
All extend SynticServiceException, with statusCode(), type(), code(), param(), details() and requestId().
try {
client.chat().completions().create(params);
} catch (QuotaExhaustedException e) {
System.out.println("Paused until " + e.details().get("resets_at"));
}Pagination
for (var agent : client.agents().list(AgentListParams.builder().limit(50).build()).autoPager()) {
System.out.println(agent.id() + " " + agent.name());
}Files, batches, agents and admin
var file = client.files().upload(FileUploadParams.builder()
.file(Path.of("contract.pdf")).purpose("document").build());
var batch = client.batches().create(BatchCreateParams.builder().inputFileId(file.id()).build());
batch = client.batches().waitFor(batch.id(), Duration.ofSeconds(30));
var session = client.sessions().create(SessionCreateParams.builder()
.agentId("agt_01JC4R8W6Q2N9X3T5V7Y").build());
client.sessions().stream(session.id()).forEach(event -> {
if (event.type().equals("message.completed")) System.out.println(event.content());
});
SynticAdminClient admin = SynticAdminClient.fromEnv(); // SYNTIC_ADMIN_KEY
admin.users().list().autoPager().forEach(u -> System.out.println(u.email()));Raw responses
var raw = client.chat().completions().withRawResponse().create(params);
System.out.println(raw.headers().get("x-syntic-billing-source"));
ChatCompletion reply = raw.parse();