# Go SDK

> Official Go SDK for PromptRails with functional options, typed errors, and automatic retries.

Source: https://0.0.0.0:8080/docs/go-sdk

The official Go SDK for PromptRails provides a fully typed client for interacting with the PromptRails API from any Go application.

<div style={{ display: 'flex', gap: '0.75rem', marginBottom: '0.5rem' }}>
  <a href="https://github.com/promptrails/go-sdk" target="_blank" rel="noopener noreferrer">
    GitHub
  </a>
  <span style={{ color: 'var(--color-muted-foreground)' }}>·</span>
  <a
    href="https://pkg.go.dev/github.com/promptrails/go-sdk"
    target="_blank"
    rel="noopener noreferrer"
  >
    pkg.go.dev
  </a>
</div>

## Installation

```bash
go get github.com/promptrails/go-sdk@v0.3.1
```

Requires Go 1.21 or later.

Current release: **v0.3.1** — streaming chat & executions via
`*ChatStream`, typed `AgentConfig` interface, `promptrails.Version`
constant. See the
[changelog](https://github.com/promptrails/go-sdk/releases).

## Client Initialization

```go
import promptrails "github.com/promptrails/go-sdk"

client := promptrails.NewClient("your-api-key")
```

### With Options

```go
import (
    "time"
    promptrails "github.com/promptrails/go-sdk"
)

client := promptrails.NewClient("your-api-key",
    promptrails.WithBaseURL("https://api.promptrails.ai"),  // default
    promptrails.WithTimeout(30 * time.Second),               // default
    promptrails.WithMaxRetries(3),                           // default
)
```

## Configuration

| Option           | Type          | Default                      | Description                   |
| ---------------- | ------------- | ---------------------------- | ----------------------------- |
| `WithBaseURL`    | string        | `https://api.promptrails.ai` | API base URL                  |
| `WithTimeout`    | time.Duration | 30s                          | HTTP request timeout          |
| `WithMaxRetries` | int           | 3                            | Maximum retry attempts on 5xx |

The API key is sent via the `X-API-Key` header with every request.

## Available Resources

| Resource         | Field                    | Description                             |
| ---------------- | ------------------------ | --------------------------------------- |
| Agents           | `client.Agents`          | Agent CRUD, versioning, execution       |
| Prompts          | `client.Prompts`         | Prompt CRUD, versioning, execution      |
| Executions       | `client.Executions`      | Execution listing and details           |
| Credentials      | `client.Credentials`     | Credential management                   |
| Data Sources     | `client.DataSources`     | Data source CRUD, versioning, execution |
| Chat             | `client.Chat`            | Send messages to chat sessions          |
| Traces           | `client.Traces`          | Trace listing and filtering             |
| Costs            | `client.Costs`           | Cost analysis and summaries             |
| MCP Tools        | `client.MCPTools`        | MCP tool management                     |
| Approvals        | `client.Approvals`       | Approval request management             |
| Scores           | `client.Scores`          | Scoring and evaluation                  |
| A2A              | `client.A2A`             | Agent-to-Agent protocol                 |
| Webhook Triggers | `client.WebhookTriggers` | Webhook trigger management              |

## Common Operations

### Execute an Agent

```go
ctx := context.Background()

result, err := client.Agents.Execute(ctx, "agent-id", &promptrails.ExecuteAgentParams{
    Input: map[string]any{"message": "Hello, world!"},
    Sync:  true,
})
if err != nil {
    log.Fatal(err)
}

fmt.Println(result.Data.Output)
```

### List Agents

```go
agents, err := client.Agents.List(ctx, &promptrails.ListAgentsParams{
    Page:  1,
    Limit: 20,
})
if err != nil {
    log.Fatal(err)
}

for _, agent := range agents.Data {
    fmt.Printf("%s (%s)\n", agent.Name, agent.Type)
}
```

### Create a Prompt

```go
prompt, err := client.Prompts.Create(ctx, &promptrails.CreatePromptParams{
    Name:        "Summarizer",
    Description: "Summarizes text",
})
if err != nil {
    log.Fatal(err)
}

fmt.Println(prompt.Data.ID)
```

### View Executions

```go
executions, err := client.Executions.List(ctx, &promptrails.ListExecutionsParams{
    Page:  1,
    Limit: 10,
})
if err != nil {
    log.Fatal(err)
}

for _, exec := range executions.Data {
    fmt.Printf("ID: %s, Status: %s\n", exec.ID, exec.Status)
}
```

### Approve an Execution

```go
err := client.Approvals.Decide(ctx, "approval-id", &promptrails.DecideApprovalParams{
    Decision: "approved",
    Reason:   "Looks good",
})
```

### Stream a Chat Turn

`Chat.SendMessageStream` posts a user message and returns a `*ChatStream`
that yields typed events on the same HTTP connection. Cancel by
cancelling `ctx`; always `defer stream.Close()`.

```go
session, err := client.Chat.CreateSession(ctx, &promptrails.CreateSessionParams{
    AgentID: "agent-id",
})
if err != nil {
    log.Fatal(err)
}

stream, err := client.Chat.SendMessageStream(ctx, session.ID, &promptrails.SendMessageParams{
    Content: "What is PromptRails?",
})
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

for stream.Next() {
    switch e := stream.Event().(type) {
    case *promptrails.ExecutionEvent:
        log.Printf("execution_id: %s", e.ExecutionID)
    case *promptrails.ThinkingEvent:
        log.Printf("[thinking] %s", e.Content)
    case *promptrails.ToolStartEvent:
        log.Printf("[tool_start] %s", e.Name)
    case *promptrails.ToolEndEvent:
        log.Printf("[tool_end] %s (%s)", e.Name, e.Summary)
    case *promptrails.ContentEvent:
        fmt.Print(e.Content)
    case *promptrails.DoneEvent:
        fmt.Printf("\n[done] %d tokens\n", e.TokenUsage.TotalTokens)
    case *promptrails.ErrorEvent:
        log.Fatalf("[error] %s", e.Message)
    }
}
if err := stream.Err(); err != nil {
    log.Fatal(err)
}
```

### Stream an Existing Execution

When an execution was started outside a chat (e.g. `Agents.Execute`),
subscribe to its live event stream:

```go
stream, err := client.Executions.Stream(ctx, executionID)
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

for stream.Next() {
    if e, ok := stream.Event().(*promptrails.ContentEvent); ok {
        fmt.Print(e.Content)
    }
}
```

## Typed Agent Config

`Agents.CreateVersion` takes a typed `AgentConfig` — an interface
implemented by the five concrete variants. Each concrete type's
`MarshalJSON` injects the required `type` discriminator.

```go
import promptrails "github.com/promptrails/go-sdk"

// Simple agent — one prompt per execution
simple := promptrails.SimpleAgentConfig{
    PromptID: "prompt-id",
}

// Chain agent — prompts run sequentially
chain := promptrails.ChainAgentConfig{
    PromptIDs: []promptrails.PromptLink{
        {PromptID: "p1", Role: "step1", SortOrder: 0},
        {PromptID: "p2", Role: "step2", SortOrder: 1},
    },
}

_, err := client.Agents.CreateVersion(ctx, "agent-id", &promptrails.CreateVersionParams{
    Version:    "1.0.0",
    Config:     simple, // or chain, MultiAgentConfig, WorkflowAgentConfig, CompositeAgentConfig
    SetCurrent: true,
})
```

See [Agent Versioning](/docs/agent-versioning) for the per-type field
reference.

## SDK Version

```go
import promptrails "github.com/promptrails/go-sdk"

fmt.Println(promptrails.Version) // "0.3.1"
```

Every request is sent with `User-Agent: promptrails-go/<version>` so
backend telemetry can attribute traffic to the SDK release.

## Error Handling

The SDK returns typed errors for different HTTP status codes. Use `errors.As` to handle specific error types:

```go
import "errors"

result, err := client.Agents.Execute(ctx, "invalid-id", &promptrails.ExecuteAgentParams{
    Input: map[string]any{},
})
if err != nil {
    var notFound *promptrails.NotFoundError
    var validation *promptrails.ValidationError
    var unauthorized *promptrails.UnauthorizedError
    var forbidden *promptrails.ForbiddenError
    var rateLimit *promptrails.RateLimitError
    var serverErr *promptrails.ServerError

    switch {
    case errors.As(err, &notFound):
        fmt.Printf("Agent not found: %s\n", notFound.Message)
    case errors.As(err, &validation):
        fmt.Printf("Invalid input: %s\n", validation.Message)
    case errors.As(err, &unauthorized):
        fmt.Printf("Invalid API key: %s\n", unauthorized.Message)
    case errors.As(err, &forbidden):
        fmt.Printf("Insufficient permissions: %s\n", forbidden.Message)
    case errors.As(err, &rateLimit):
        fmt.Printf("Rate limited: %s\n", rateLimit.Message)
    case errors.As(err, &serverErr):
        fmt.Printf("Server error: %s\n", serverErr.Message)
    default:
        fmt.Printf("Unexpected error: %v\n", err)
    }
}
```

### Error Types

| Error                | HTTP Status | Description                                       |
| -------------------- | ----------- | ------------------------------------------------- |
| `ValidationError`    | 400         | Invalid request parameters                        |
| `UnauthorizedError`  | 401         | Invalid or missing API key                        |
| `QuotaExceededError` | 402         | Plan quota exceeded                               |
| `ForbiddenError`     | 403         | Insufficient permissions or IP/origin restriction |
| `NotFoundError`      | 404         | Resource not found                                |
| `RateLimitError`     | 429         | Rate limit exceeded                               |
| `ServerError`        | 5xx         | Server-side error                                 |
| `APIError`           | Any         | Base type for all API errors                      |

All error types embed `APIError` which includes:

- `StatusCode` -- HTTP status code
- `Message` -- Human-readable error message
- `Code` -- Optional error code string
- `Details` -- Optional map with additional error details

## Context Support

All SDK methods require a `context.Context` as the first argument, enabling cancellation and timeouts:

```go
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

result, err := client.Agents.Execute(ctx, "agent-id", &promptrails.ExecuteAgentParams{
    Input: map[string]any{"message": "Hello"},
    Sync:  true,
})
```

## Pagination

List endpoints return paginated responses:

```go
// Page-based pagination
page1, _ := client.Agents.List(ctx, &promptrails.ListAgentsParams{Page: 1, Limit: 20})
page2, _ := client.Agents.List(ctx, &promptrails.ListAgentsParams{Page: 2, Limit: 20})

fmt.Printf("Total: %d, Pages: %d\n", page1.Meta.Total, page1.Meta.TotalPages)
```

## Related Topics

- [Examples](https://github.com/promptrails/examples/tree/main/go) -- Ready-to-run code examples
- [Quickstart](/docs/quickstart) -- Getting started guide
- [Python SDK](/docs/python-sdk) -- Python alternative
- [JavaScript SDK](/docs/javascript-sdk) -- JavaScript/TypeScript alternative
- [CLI](/docs/cli) -- Command-line interface
- [API Keys and Scopes](/docs/api-keys-and-scopes) -- API key management
- [REST API Reference](/docs/rest-api-reference) -- Underlying REST API
