> ## 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.

# Go SDK

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

The PromptJuggler Go SDK is a thin, typed wrapper over the public REST API. You call flat,
synchronous methods that take a `context.Context` and get back typed structs — authentication,
request building, and JSON (de)serialization are handled for you. It runs on the standard
library's `net/http`, so it pulls in no third-party HTTP stack.

## Requirements

* Go 1.22 or newer

## Installation

```bash theme={null}
go get go.promptjuggler.com/sdk
```

## Set up the client

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

```go theme={null}
import "go.promptjuggler.com/sdk"

pj := promptjuggler.New("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 `promptjuggler.WithBaseURL(url)`, or swap the HTTP client (timeouts,
proxy) with `promptjuggler.WithHTTPClient(h)`.

## Per-endpoint usage

Every endpoint in the [API reference](/api/overview) includes a **Go SDK** code sample showing
the exact call — that's the place to look for how to invoke each operation and what it returns.
Typed models are returned from the `client` sub-package. A typical flow:

```go theme={null}
import (
	"context"
	"fmt"

	"go.promptjuggler.com/sdk/client"
)

ctx := context.Background()

// Trigger a run (async — returns immediately with the run ID)
created, err := pj.RunPrompt(ctx, "greeting", "production", map[string]string{"name": "Ada"})
if err != nil {
	// handle err
}

// Poll for the result
run, err := pj.GetPromptRun(ctx, created.Id)
if err != nil {
	// handle err
}
if run.Status == client.RUNSTATUS_COMPLETED {
	fmt.Println(run.GetOutput())
}
```

Optional run parameters (priority, environment, metadata, …) are functional options on
`RunPrompt` / `RunWorkflow`:

```go theme={null}
pj.RunPrompt(ctx, "greeting", "production", map[string]string{"name": "Ada"},
	promptjuggler.WithEnvironment("staging"),
	promptjuggler.WithPriority("onsite"),
)
```

## Error handling

When the API responds with an error status, the SDK returns an `*APIError` carrying the HTTP
status code and the server's message. When the request never reaches the API (DNS failure,
timeout, offline), it returns a `*NetworkError`. If the API replies with a success status whose
body can't be decoded (schema drift, an unknown enum value), it returns a `*DecodeError`. Match
them with `errors.As`:

```go theme={null}
import "errors"

_, err := pj.GetPrompt(ctx, "does-not-exist", "production")

var apiErr *promptjuggler.APIError
if errors.As(err, &apiErr) {
	apiErr.StatusCode // e.g. 404
	apiErr.Message    // the server's error message
}
```

You never inspect 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:

```go theme={null}
payload := string(rawBody) // the raw request body, as received
signature := r.Header.Get("PromptJuggler-Signature")

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

`VerifyWebhookSignature` 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. A missing (empty) signature header returns false. Use
`VerifyWebhookSignatureAt` to supply an explicit tolerance and clock.
