Quickstart: Java
This quickstart uses java.net.http.HttpClient (built into Java 11 and later) and Jackson for JSON. The examples use text blocks and records, so they need Java 17 or later as written.
Add Jackson
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>Send a request
// Hello.java
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Hello {
static final String URL = "https://app.syntic.ai/v1/chat/completions";
static final ObjectMapper JSON = new ObjectMapper();
static final HttpClient HTTP = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
public static void main(String[] args) throws Exception {
ObjectNode body = JSON.createObjectNode()
.put("model", "Amara S1.2")
.put("stream", false)
.put("max_tokens", 300);
body.putArray("messages")
.add(JSON.createObjectNode().put("role", "system").put("content", "Answer in two sentences."))
.add(JSON.createObjectNode().put("role", "user").put("content", "Why is the sea salty?"));
HttpRequest request = HttpRequest.newBuilder(URI.create(URL))
.timeout(Duration.ofMinutes(5))
.header("Authorization", "Bearer " + System.getenv("SYNTIC_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(JSON.writeValueAsString(body)))
.build();
HttpResponse<String> response = HTTP.send(request, HttpResponse.BodyHandlers.ofString());
JsonNode data = JSON.readTree(response.body());
if (response.statusCode() != 200) {
throw new IllegalStateException(response.statusCode() + " " + data.path("error").asText()
+ " (request " + response.headers().firstValue("x-syntic-request-id").orElse("?") + ")");
}
System.out.println(data.at("/choices/0/message/content").asText());
System.out.println("tokens: " + data.at("/usage/total_tokens").asInt());
}
}Building the body with ObjectNode escapes quotes and newlines in user text correctly. Avoid assembling JSON with string concatenation.
Run it
mvn -q compile exec:java -Dexec.mainClass=HelloStream the reply
BodyHandlers.ofLines() hands you the event stream line by line as it arrives:
import java.util.stream.Stream;
ObjectNode body = JSON.createObjectNode().put("model", "Amara Quick"); // stream defaults to true
body.putArray("messages").add(JSON.createObjectNode().put("role", "user").put("content", "Name five Scottish islands."));
HttpRequest request = HttpRequest.newBuilder(URI.create(URL))
.header("Authorization", "Bearer " + System.getenv("SYNTIC_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(JSON.writeValueAsString(body)))
.build();
HttpResponse<Stream<String>> response = HTTP.send(request, HttpResponse.BodyHandlers.ofLines());
if (response.statusCode() != 200) {
throw new IllegalStateException("HTTP " + response.statusCode() + ": " + String.join("\n", response.body().toList()));
}
try (Stream<String> lines = response.body()) {
lines.filter(l -> l.startsWith("data: ") && !l.equals("data: [DONE]"))
.map(l -> {
try { return JSON.readTree(l.substring(6)); } catch (Exception e) { throw new RuntimeException(e); }
})
.forEach(event -> {
if (event.has("error")) throw new IllegalStateException("stream failed: " + event.at("/error/code").asText());
String piece = event.at("/choices/0/delta/content").asText("");
System.out.print(piece);
System.out.flush();
});
}
System.out.println();Closing the stream (leaving the try block early) closes the connection and stops generation.
Call a function
ObjectNode tool = (ObjectNode) JSON.readTree("""
{"type": "function", "function": {
"name": "train_times",
"description": "Next departures between two stations.",
"parameters": {"type": "object",
"properties": {"from": {"type": "string"}, "to": {"type": "string"}},
"required": ["from", "to"]}}}
""");
ObjectNode body = JSON.createObjectNode().put("model", "Amara S1.2").put("stream", false);
body.putArray("tools").add(tool);
var messages = body.putArray("messages");
messages.add(JSON.createObjectNode().put("role", "user").put("content", "When is the next train from Leeds to York?"));
JsonNode reply = send(body); // a helper wrapping the request code above
JsonNode msg = reply.at("/choices/0/message");
if (msg.has("tool_calls")) {
messages.add(msg); // the assistant turn, unchanged
for (JsonNode call : msg.get("tool_calls")) {
JsonNode a = JSON.readTree(call.at("/function/arguments").asText());
String result = "{\"departures\":[\"14:05\",\"14:21\"]}"; // replace with a real lookup
messages.add(JSON.createObjectNode()
.put("role", "tool").put("tool_call_id", call.get("id").asText()).put("content", result));
}
reply = send(body);
}
System.out.println(reply.at("/choices/0/message/content").asText());Handle errors
| Status | What to do |
|---|---|
400, 413 | Fix the request (error says what). |
401, 403 | Check the key and its restrictions. |
402 | quota_exhausted: see Handle 402. |
429 | Wait Retry-After seconds, then retry. |
500, 502, 503 | Retry with exponential back-off. |
response.headers().firstValue("retry-after") gives the wait on a 429. A reusable retry loop is in Retries with back-off.
Reuse one HttpClient for the whole application. It keeps connections open between requests, which makes later requests faster.
Next steps
- Guides
- Chat completions reference
- Other languages: the planned official Java client.
Was this page helpful?