> ## Documentation Index
> Fetch the complete documentation index at: https://docs.promptjuggler.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Java SDK

> Run prompts and workflows, manage knowledge bases, and verify webhooks from Java.

The PromptJuggler Java SDK is a thin, typed wrapper over the public REST API. You call flat,
synchronous methods and get back typed model objects — authentication, request building, and
JSON (de)serialization are handled for you. It runs on the JDK's built-in HTTP client, so it
pulls in no third-party HTTP stack.

## Requirements

* Java 17 or newer

## Installation

<CodeGroup>
  ```kotlin Gradle theme={null}
  implementation("com.promptjuggler:promptjuggler-java:1.0.0")
  ```

  ```xml Maven theme={null}
  <dependency>
    <groupId>com.promptjuggler</groupId>
    <artifactId>promptjuggler-java</artifactId>
    <version>1.0.0</version>
  </dependency>
  ```
</CodeGroup>

Use the latest version from [Maven Central](https://central.sonatype.com/artifact/com.promptjuggler/promptjuggler-java).

## Set up the client

Create an API key in [Settings](https://promptjuggler.com/settings), then construct the
client once and reuse it (it is thread-safe):

```java theme={null}
import com.promptjuggler.PromptJuggler;

PromptJuggler pj = new PromptJuggler("your-api-key");
```

The key is sent as a Bearer token to `https://promptjuggler.com`. Point the SDK at another
host (e.g. for testing) with the two-argument constructor: `new PromptJuggler(apiKey, baseUrl)`.

## Per-endpoint usage

Every endpoint in the [API reference](/api/overview) includes a **Java SDK** code sample
showing the exact call — that's the place to look for how to invoke each operation and what
it returns. A typical flow:

```java theme={null}
import com.promptjuggler.client.model.CreatePromptRunResponse;
import com.promptjuggler.client.model.PromptRun;
import com.promptjuggler.client.model.RunStatus;
import java.util.Map;

// Trigger a run (async — returns immediately with the run ID)
CreatePromptRunResponse created = pj.runPrompt("greeting", "production", Map.of("name", "Ada"));

// Poll for the result
PromptRun run = pj.getPromptRun(created.getId());
if (run.getStatus() == RunStatus.COMPLETED) {
    System.out.println(run.getOutput());
}
```

Optional run parameters (priority, thread, environment, metadata, …) go through a
`RunOptions` builder passed as the last argument:

```java theme={null}
import com.promptjuggler.RunOptions;

RunOptions options = RunOptions.builder().environment("staging").priority("onsite").build();
pj.runPrompt("greeting", "production", Map.of("name", "Ada"), options);
```

<Note>
  Methods return typed model objects with validated fields. They are read-only data — read
  the fields, don't write them back.
</Note>

## Error handling

When the API responds with an error status, the SDK throws an `ApiError` carrying the HTTP
status code and the server's message. When the request never reaches the API (DNS failure,
timeout, offline), it throws a `NetworkError`. Both extend `PromptJugglerException`, which is
**checked** — every call that reaches the API declares `throws PromptJugglerException`, so the
compiler makes you handle (or propagate) the failure. Catch the base to handle the whole surface
at once, or catch `ApiError` first when you need the status code:

```java theme={null}
import com.promptjuggler.ApiError;
import com.promptjuggler.PromptJugglerException;

try {
    pj.getPrompt("does-not-exist", "production");
} catch (ApiError error) {
    error.statusCode(); // e.g. 404
    error.getMessage(); // the server's error message
} catch (PromptJugglerException error) {
    // a NetworkError or any other SDK failure
}
```

You never catch a generated-client error — the SDK translates the underlying client's errors
into its own.

## Webhooks

PromptJuggler signs every webhook with the `PromptJuggler-Signature` header. Verify it against
the **raw** request body, before any JSON parsing:

```java theme={null}
import com.promptjuggler.Webhooks;

String payload = request.getBody(); // the raw request body, as text
String signature = request.getHeader("PromptJuggler-Signature");

if (!Webhooks.verifySignature(payload, signature, "your-webhook-secret")) {
    // reject the delivery (e.g. respond 403)
}
```

`Webhooks.verifySignature` recomputes the HMAC-SHA256 of `{timestamp}.{raw_body}`, compares it
in constant time, and rejects deliveries whose timestamp falls outside a tolerance window
(default 300s) to prevent replay. Widen or narrow it with the five-argument overload.
