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

> ## Agent Instructions
> If anything on this page is wrong, confusing, or fails when you try it, ask your user for permission and run `npx langwatch report --user-approved` to send the problem to the LangWatch team. No login is needed and secrets are redacted locally. `npx langwatch report --help` has the details.

# Go SDK API Reference

> Complete API reference for the LangWatch Go SDK, including the exporter, tracer, span helpers, provider instrumentations, data capture and the REST client.

The LangWatch Go SDK and its instrumentation packages expose the public APIs below.

The SDK is made of three kinds of module:

* **Core tracing** (`github.com/langwatch/langwatch/sdks/go`): the exporter, the `Tracer`, the `LangWatchSpan` helpers, span filtering and data capture. No provider dependencies.
* **Provider instrumentations** (`github.com/langwatch/langwatch/sdks/go/instrumentation/<provider>`): one module per provider, so importing one never pulls in the others' SDKs.
* **REST client** (`github.com/langwatch/langwatch/sdks/go/client`): a typed client for the LangWatch API.

> [!TIP]
>
> For a quick start guide with step-by-step instructions, see the [Go Integration Guide](https://langwatch.ai/docs/integration/go/guide.md). For practical examples of creating traces and spans, see the [Core Concepts section](https://langwatch.ai/docs/integration/go/guide.md#core-concepts) in the guide.

## Installation

```bash theme={null}
go get github.com/langwatch/langwatch/sdks/go
go get github.com/langwatch/langwatch/sdks/go/instrumentation/openai
```

Requires Go 1.25+. The OpenAI client is `github.com/openai/openai-go/v3`.

## Core SDK (`langwatch`)

### Setup

Create a LangWatch exporter and configure it as your tracer provider. `NewExporter` reads `LANGWATCH_API_KEY` from your environment automatically:

```go theme={null}
func setupLangWatch(ctx context.Context) func(context.Context) {
	exporter, err := langwatch.NewExporter(ctx)
	if err != nil {
		log.Fatalf("failed to create exporter: %v", err)
	}

	tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
	otel.SetTracerProvider(tp)

	return func(ctx context.Context) {
		if err := tp.Shutdown(ctx); err != nil {
			log.Printf("Error shutting down: %v", err)
		}
	}
}
```

> [!WARNING]
>
> **Always call shutdown!** Traces are buffered in memory before being sent. Without `defer
>     shutdown(ctx)`, your traces will be lost when the application exits. This is critical for CLI
> tools, serverless functions, and any short-lived process.

For custom configuration, pass options to `NewExporter`:

```go theme={null}
exporter, err := langwatch.NewExporter(ctx,
	langwatch.WithAPIKey("your-api-key"),
	langwatch.WithEndpoint("https://custom.endpoint.ai"),
	langwatch.WithFilters(langwatch.LangWatchOnly()),
	langwatch.WithDataCapture(langwatch.DataCaptureNone),
)
```

| Option | Description |
| - | - |
| `WithAPIKey(key)` | Set API key (defaults to `LANGWATCH_API_KEY` env var) |
| `WithEndpoint(url)` | Set endpoint (defaults to `https://app.langwatch.ai`) |
| `WithFilters(...)` | Apply span filters (see [Filtering](#filtering)) |
| `WithDataCapture(mode)` | Strip input/output content at export time (see [Data Capture](#data-capture)) |
| `WithDataCaptureFunc(fn)` | Decide capture mode per span from its type / name / attributes |

There are two exporter constructors:

| Constructor | Description |
| - | - |
| `NewExporter(ctx, opts...)` | Exporter with **no** filter by default |
| `NewDefaultExporter(ctx, opts...)` | Exporter with the `ExcludeHTTPRequests` filter applied automatically |

`langwatch.NewFilteringExporter(wrapped, filters...)` wraps any existing OTel exporter with LangWatch's span filtering.

### Tracer

`Tracer()` retrieves a `LangWatchTracer` instance, which is a thin wrapper around an OpenTelemetry `Tracer`.

```go theme={null}
func Tracer(instrumentationName string, opts ...trace.TracerOption) LangWatchTracer
```

| Parameter | Type | Description |
| - | - | - |
| `instrumentationName` | `string` | Name of the library or application being instrumented. |
| `opts` | `...trace.TracerOption` | Optional OpenTelemetry tracer options (e.g., `trace.WithInstrumentationVersion`). |

**Example:**

```go theme={null}
// Basic usage
tracer := langwatch.Tracer("my-app")

// With instrumentation version
tracer := langwatch.Tracer("my-app",
    trace.WithInstrumentationVersion("1.0.0"))
```

### LangWatchTracer

`Tracer()` returns a `*langwatch.LangWatchTracer`, which provides a `Start` method that mirrors OpenTelemetry's but returns a `*langwatch.Span`, plus `WithActiveSpan` for scoped spans.

```go theme={null}
func (t *LangWatchTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, *Span)

func (t *LangWatchTracer) WithActiveSpan(ctx context.Context, name string, fn func(ctx context.Context, span *Span) error, opts ...trace.SpanStartOption) error
```

**Example:**

```go theme={null}
tracer := langwatch.Tracer("my-app")
ctx, span := tracer.Start(ctx, "HandleUserRequest")
defer span.End()

span.SetType(langwatch.SpanTypeLLM)
span.SetInput("User query")
```

`WithActiveSpan` runs `fn` with a span that auto-ends and records error status, so you don't manage `defer span.End()` or error recording yourself:

```go theme={null}
err := tracer.WithActiveSpan(ctx, "RetrieveDocuments", func(ctx context.Context, span *langwatch.Span) error {
	span.SetType(langwatch.SpanTypeRAG)
	span.SetInput(query)
	// ... your logic; return an error to mark the span failed ...
	return nil
})
```

### Span

The `*langwatch.Span` returned by `tracer.Start` embeds the standard `trace.Span` (so all OpenTelemetry span methods are available) and adds the LangWatch helper methods below. **Every helper returns the span for chaining.**

```go theme={null}
span.
    SetType(langwatch.SpanTypeLLM).
    SetInput("What is the capital of France?").
    SetOutput("The capital of France is Paris.").
    SetRequestModel("gpt-6-luna").
    SetThreadID("conversation-123")
```

#### Input / Output

`SetInput` / `SetOutput` infer the value type (text, json, chat\_messages, list, …) from the Go value; the typed variants force a specific type.

**SetInput(input any) / SetOutput(output any)** `function`

Records a value as the span's input/output, inferring the type (`text` for a string, `json` for a struct/map, …). Recorded as `langwatch.input` / `langwatch.output`.

**Example:**

```go theme={null}
span.SetInput("What is the weather like today?")
span.SetOutput(response.Choices[0].Message.Content)
```

**SetInputText / SetOutputText** `function`

Force the `text` value type.

**SetInputJSON / SetOutputJSON** `function`

Force the `json` value type. Use for complex request/response objects.

**Example:**

```go theme={null}
span.SetInputJSON(map[string]any{"query": "weather", "units": "metric"})
```

**SetInputChatMessages / SetOutputChatMessages** `function`

Force the `chat_messages` value type (`[]langwatch.ChatMessage`).

**Example:**

```go theme={null}
span.SetOutputChatMessages([]langwatch.ChatMessage{
    langwatch.TextMessage(langwatch.ChatRoleAssistant, "It's sunny."),
})
```

**SetInputRaw / SetOutputRaw, SetInputList / SetOutputList, SetInputTyped / SetOutputTyped** `function`

Force the `raw` type, a `list` of nested typed values, or an explicit `langwatch.TypedValue` respectively.

**SetInputGuardrailResult / SetOutputGuardrailResult, SetInputEvaluationResult / SetOutputEvaluationResult** `function`

Record an `EvaluationResult` as the span input/output.

#### Multimodal / binary content

Build chat messages with text and binary attachments (audio / image / video / file). Inline bytes are externalised to a stored object by the ingest pipeline.

```go theme={null}
span.SetInputChatMessages([]langwatch.ChatMessage{
    langwatch.MultiContentMessage(langwatch.ChatRoleUser,
        langwatch.TextPart("What is in this image?"),
        langwatch.BinaryPart("image/png", pngBytes, "screenshot.png"), // inline base64
        // or reference an already-hosted file / stored object:
        // langwatch.BinaryURLPart("audio/mpeg", "https://example.com/a.mp3"),
        // langwatch.BinaryRefPart("application/pdf", "file-123"),
    ),
})
```

Helpers: `TextMessage`, `MultiContentMessage` (build `ChatMessage` values); `TextPart`, `ImageURLPart`, `BinaryPart`, `BinaryURLPart`, `BinaryRefPart` (build content parts).

#### Metrics, metadata & identity

**SetGenAIUsage(usage GenAIUsage)** `function`

Records token counts via the OTel-native `gen_ai.usage.*` attributes (input / output / total / cached / reasoning).

**Example:**

```go theme={null}
span.SetGenAIUsage(langwatch.GenAIUsage{
    InputTokens:  langwatch.Int(120),
    OutputTokens: langwatch.Int(48),
    TotalTokens:  langwatch.Int(168),
})
```

**SetMetrics(metrics SpanMetrics)** `function`

Records cost and the `tokens_estimated` flag (`langwatch.metrics`). **Token counts go via `SetGenAIUsage`, not here.**

**Example:**

```go theme={null}
span.SetMetrics(langwatch.SpanMetrics{Cost: langwatch.Float64(0.0021)})
```

**SetMetadata(metadata map\[string]any)** `function`

Metadata blob hoisted to the trace (`langwatch.metadata`). Reserved keys (`thread_id`, `user_id`, `customer_id`, `labels`) become trace identity; every other key is hoisted as a `metadata.<key>` trace attribute.

**Example:**

```go theme={null}
span.SetMetadata(map[string]any{
    "thread_id": "conversation-123",
    "user_id":   "user-42",
    "labels":    []string{"production"},
    "feature":   "checkout",
})
```

**SetThreadID / SetUserID / SetCustomerID / SetLabels(...string)** `function`

Reserved trace identity. All spans within the same trace share the same thread ID, grouping related interactions into a conversation.

**Example:**

```go theme={null}
span.SetThreadID("conversation-123")
span.SetUserID("user-abc-123")
span.SetLabels("production", "qa")
```

**SetParams(params map\[string]any)** `function`

LLM invocation parameters (`langwatch.params`).

**SetSelectedPrompt(prompt SelectedPrompt)** `function`

Attach a saved LangWatch prompt to the trace.

#### Model, provider & GenAI semconv

Handy when instrumenting manually. These map directly to OpenTelemetry GenAI semantic conventions.

**SetRequestModel(model string) / SetResponseModel(model string)** `function`

Sets `gen_ai.request.model` (the model you requested) and `gen_ai.response.model` (the model that actually answered). Providers answer with the dated build they served, so the response model is normally a longer identifier than the one you requested. Setting both is what lets you see, later, which build produced a given output.

**Example:**

```go theme={null}
span.SetRequestModel("gpt-6-luna")
span.SetResponseModel("gpt-6-luna")
```

**SetGenAIProvider(provider string) / SetGenAIOperation(op string)** `function`

Sets `gen_ai.provider.name` and `gen_ai.operation.name`.

**SetGenAIRequestParams(params GenAIRequestParams)** `function`

Sets `gen_ai.request.*`: temperature, top\_p, max\_tokens, stop, reasoning\_effort, and so on.

**SetGenAIResponseFinishReasons(...string)** `function`

Sets `gen_ai.response.finish_reasons`.

#### Categorization & context

**SetType(spanType SpanType)** `function`

Sets the span type for categorization in LangWatch, enabling specialized UI treatment and analytics. See [LangWatch Span Types](#langwatch-span-types).

**Example:**

```go theme={null}
span.SetType(langwatch.SpanTypeLLM)  // For LLM calls
span.SetType(langwatch.SpanTypeRAG)  // For RAG operations
span.SetType(langwatch.SpanTypeTool) // For tool/function calls
```

**SetRAGContexts(contexts \[]SpanRAGContextChunk) / SetRAGContext(context SpanRAGContextChunk)** `function`

Attach retrieved context chunks for RAG analysis (`langwatch.rag.contexts`).

**Example:**

```go theme={null}
span.SetRAGContexts([]langwatch.SpanRAGContextChunk{
    {DocumentID: "doc1", ChunkID: "chunk1", Content: "Relevant context..."},
    {DocumentID: "doc2", ChunkID: "chunk2", Content: "More context..."},
})
```

**SetTimestamps(timestamps SpanTimestamps)** `function`

Fine-grained timing, including first-token time for streaming.

**Example:**

```go theme={null}
firstTokenAt := firstTokenTime.UnixMilli()
span.SetTimestamps(langwatch.SpanTimestamps{
    StartedAtUnix:    startTime.UnixMilli(),
    FirstTokenAtUnix: &firstTokenAt,
    FinishedAtUnix:   endTime.UnixMilli(),
})
```

#### Evaluations & events

**RecordEvaluation(eval Evaluation)** `function`

Records an evaluation result against the span's trace (e.g. an LLM judge or guardrail score). The same `langwatch.Evaluation` value can be submitted later by trace id via the [API client](#api-client).

**RecordEvent(event Event), RecordThumbsUp(feedback ...string), RecordThumbsDown(feedback ...string)** `function`

Records a tracked event on the span's trace. `RecordThumbsUp` / `RecordThumbsDown` are conveniences for capturing thumbs feedback live; the optional string is freeform feedback.

**SetTraceMetadata(attrs ...attribute.KeyValue) / SetTraceMetadataMap(metadata map\[string]any)** `function`

Sets metadata directly on the trace from any span within it. `SetTraceMetadata` takes OTel key-values; `SetTraceMetadataMap` is the bulk map convenience.

## Automatic Instrumentation

Each provider instrumentation is a **separate Go module** so importing one never pulls in the others' SDKs. Add only the one(s) you use. Every instrumentation captures request + response model, all token types (input / output / total / cached / reasoning / cache-creation), cost where the provider returns it, finish reasons, request params, system instructions, tool definitions and (capture-gated) input/output.

| Module | Underlying client | Hook | Guide |
| - | - | - | - |
| `…/instrumentation/openai` | `github.com/openai/openai-go/v3` | `option.Middleware` | [OpenAI](https://langwatch.ai/docs/integration/go/integrations/open-ai.md) |
| `…/instrumentation/anthropic` | `github.com/anthropics/anthropic-sdk-go` | `option.Middleware` | [Anthropic](https://langwatch.ai/docs/integration/go/integrations/anthropic.md) |
| `…/instrumentation/googlegenai` | `google.golang.org/genai` | `http.RoundTripper` | [Google Gemini](https://langwatch.ai/docs/integration/go/integrations/google-gemini.md) |
| `…/instrumentation/bedrock` | `aws-sdk-go-v2` bedrockruntime | smithy-go middleware | [Amazon Bedrock](https://langwatch.ai/docs/integration/go/integrations/amazon-bedrock.md) |
| `…/instrumentation/azureopenai` | `openai-go/v3` (azure) | `option.Middleware` | [Azure OpenAI](https://langwatch.ai/docs/integration/go/integrations/azure-openai.md) |
| `…/instrumentation/ollama` | `github.com/ollama/ollama/api` | `http.RoundTripper` | [Ollama](https://langwatch.ai/docs/integration/go/integrations/ollama.md) |
| `…/instrumentation/gopenai` | `github.com/sashabaranov/go-openai` | `http.RoundTripper` | (no dedicated guide) |
| `…/instrumentation/genkit` | `github.com/firebase/genkit/go` | OTel-native | [Genkit](https://langwatch.ai/docs/integration/go/integrations/genkit.md) |

### OpenAI middleware

The `…/instrumentation/openai` package provides middleware for the official `openai-go/v3` client. It dispatches by request/response **shape**, so Chat Completions, the Responses API and Embeddings are all captured (with a generic fallback for any other OpenAI-compatible endpoint), with full streaming reconstruction.

```go theme={null}
func Middleware(instrumentationName string, opts ...Option) option.Middleware
```

```go theme={null}
client := openai.NewClient(
	option.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
	option.WithMiddleware(otelopenai.Middleware("my-app")),
)
```

**Configuration Options (`...Option`):**

**WithDataCapture(mode langwatch.DataCaptureMode)** `function`

Controls whether request (input) and response (output) **content** is recorded at the source. **Defaults to `langwatch.DataCaptureAll`**, so input and output are captured by default.

**Example:**

```go theme={null}
otelopenai.Middleware("my-app", otelopenai.WithDataCapture(langwatch.DataCaptureInput)) // input only
otelopenai.Middleware("my-app", otelopenai.WithDataCapture(langwatch.DataCaptureNone))  // neither
```

**WithGenAIProvider(provider attribute.KeyValue)** `function`

Sets the `gen_ai.provider.name` attribute on spans. Defaults to `"openai"`. Use this when pointing the middleware at an OpenAI-compatible provider.

**Example:**

```go theme={null}
import semconv "go.opentelemetry.io/otel/semconv/v1.40.0"

otelopenai.Middleware("my-app",
    otelopenai.WithGenAIProvider(semconv.GenAIProviderNameKey.String("groq")),
)
```

> [!NOTE]
>
> `gen_ai.provider.name` is the GenAI semantic-convention attribute for the provider. `WithGenAISystem` is a thin alias for this option.

**WithTracerProvider(provider trace.TracerProvider)** `function`

Specifies the `trace.TracerProvider` to use. Defaults to the global provider.

**WithPropagators(propagators propagation.TextMapPropagator)** `function`

Specifies OTel propagators. Defaults to the global propagators.

The native [Anthropic](https://langwatch.ai/docs/integration/go/integrations/anthropic.md) and [Azure OpenAI](https://langwatch.ai/docs/integration/go/integrations/azure-openai.md) middlewares share the same option set (`WithDataCapture`, `WithGenAIProvider`, `WithTracerProvider`); the HTTP-transport instrumentations ([Gemini](https://langwatch.ai/docs/integration/go/integrations/google-gemini.md), [Ollama](https://langwatch.ai/docs/integration/go/integrations/ollama.md), `gopenai`) and the [Bedrock](https://langwatch.ai/docs/integration/go/integrations/amazon-bedrock.md) smithy middleware expose `WithDataCapture`, `WithGenAIProvider` and `WithTracerProvider` too. See each provider's guide for its exact constructor.

### Collected Attributes

The instrumentations add attributes to the client span, following the [OpenTelemetry GenAI Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/) where applicable. The OpenAI middleware records:

**Request Attributes:**

* `gen_ai.provider.name` (e.g. `openai`)
* `gen_ai.request.model`
* `gen_ai.request.temperature`, `gen_ai.request.top_p`
* `gen_ai.request.max_tokens` (from `max_completion_tokens` / `max_tokens` / `max_output_tokens`)
* `gen_ai.request.frequency_penalty`, `gen_ai.request.presence_penalty`
* `gen_ai.request.stop_sequences`, `gen_ai.request.seed`, `gen_ai.request.choice_count`
* `gen_ai.request.reasoning_effort`
* `gen_ai.request.encoding_formats`, `gen_ai.embeddings.dimension.count` (embeddings)
* `langwatch.instructions` (Responses API)
* `gen_ai.request.stream` (boolean)
* `gen_ai.operation.name` (e.g. `chat`, `embeddings`, `responses`)
* `langwatch.input` (when input capture is enabled)

**Response Attributes:**

* `gen_ai.response.id`
* `gen_ai.response.model`
* `gen_ai.response.finish_reasons`
* `gen_ai.response.status` (Responses API)
* `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`, `gen_ai.usage.total_tokens`
* `gen_ai.usage.cached_input_tokens`, `gen_ai.usage.reasoning.output_tokens` (when returned)
* `gen_ai.openai.response.system_fingerprint`
* `langwatch.output` (when output capture is enabled; for streaming this is the reconstructed textual content)

Standard HTTP client attributes (`http.request.method`, `url.path`, `server.address`, `http.response.status_code`) are also included. Other providers map their native token kinds onto the same `gen_ai.usage.*` attributes; for example Anthropic and Bedrock add `gen_ai.usage.cache_creation.input_tokens` for cache-creation tokens. See each provider's guide for the exact list.

## Data Capture

Data capture controls whether span **input/output content** leaves the process. It is enforced at export time, so one setting governs every instrumentation (the provider middlewares and your manual spans alike). Span structure, metrics, metadata, models and identity are always kept; only the content attributes (`langwatch.input` / `langwatch.output` and the `gen_ai.*` message/prompt/completion equivalents) are stripped. The default, unconfigured, captures everything.

```go theme={null}
// Fixed mode for all spans.
exporter, _ := langwatch.NewExporter(ctx, langwatch.WithDataCapture(langwatch.DataCaptureNone))

// Or decide per span from its type / name / attributes.
exporter, _ := langwatch.NewExporter(ctx, langwatch.WithDataCaptureFunc(
	func(c langwatch.DataCaptureContext) langwatch.DataCaptureMode {
		if c.SpanType == "tool" {
			return langwatch.DataCaptureNone // never capture tool I/O
		}
		return langwatch.DataCaptureAll
	},
))
```

| Mode | Captures |
| - | - |
| `langwatch.DataCaptureAll` | Input and output content (default) |
| `langwatch.DataCaptureInput` | Input content only |
| `langwatch.DataCaptureOutput` | Output content only |
| `langwatch.DataCaptureNone` | Neither (structure, models, usage and metadata only) |

Each instrumentation also accepts `WithDataCapture(mode)` to gate content **at the source** for a single client. The two **compose**: the middleware gates content at the source, and the exporter strips content at export time. The exporter option is the recommended place for a uniform policy; the per-instrumentation option is handy when you only want to change behaviour for one client.

## Filtering

Control which spans are exported to reduce noise and focus on what matters.

### Preset Filters

```go theme={null}
// NewDefaultExporter automatically uses ExcludeHTTPRequests.
exporter, err := langwatch.NewDefaultExporter(ctx)

// Or choose your own filters.
exporter, err := langwatch.NewExporter(ctx,
	langwatch.WithFilters(
		langwatch.ExcludeHTTPRequests(), // Remove GET, POST, etc. spans.
		langwatch.LangWatchOnly(),       // Keep only LangWatch instrumentation.
	),
)
```

| Filter | Description |
| - | - |
| `ExcludeHTTPRequests()` | Removes HTTP verb spans (GET, POST, etc.) |
| `LangWatchOnly()` | Keeps only spans from LangWatch instrumentation |

### Custom Filters

Use `Include()` to keep matching spans or `Exclude()` to remove them:

```go theme={null}
exporter, err := langwatch.NewExporter(ctx,
	langwatch.WithFilters(
		// Keep only spans from specific scopes.
		langwatch.Include(langwatch.Criteria{
			ScopeName: []langwatch.Matcher{
				langwatch.StartsWith("github.com/langwatch/"),
				langwatch.StartsWith("my-app/"),
			},
		}),
		// Remove database spans.
		langwatch.Exclude(langwatch.Criteria{
			SpanName: []langwatch.Matcher{
				langwatch.StartsWith("database."),
			},
		}),
	),
)
```

A `Criteria` matches on `ScopeName` (the `InstrumentationScope.Name`) and/or `SpanName` (the `Span.Name`).

### Matchers

| Matcher | Description |
| - | - |
| `Equals(s)` | Exact match |
| `EqualsIgnoreCase(s)` | Case-insensitive exact match |
| `StartsWith(prefix)` | Prefix match |
| `StartsWithIgnoreCase(prefix)` | Case-insensitive prefix match |
| `MatchRegex(re)` | Regex match (compiled `*regexp.Regexp`) |
| `MustMatchRegex(pattern)` | Regex match from a pattern string (panics on invalid pattern) |

> [!NOTE]
>
> Multiple filters use AND semantics (a span must pass all). Within a `Criteria`, matchers use OR semantics (a span matches if any matcher matches); multiple fields in a `Criteria` use AND.

## LangWatch Span Types

`SpanType` is a string constant used with `span.SetType()` to categorize spans in LangWatch for specialized UI treatment and analytics.

| Constant | Description |
| - | - |
| `SpanTypeLLM` | A call to a Large Language Model |
| `SpanTypeChain` | A sequence of related operations or a sub-pipeline |
| `SpanTypeTool` | A call to an external tool or function |
| `SpanTypeAgent` | An autonomous agent's operation or decision-making step |
| `SpanTypeRAG` | A Retrieval-Augmented Generation operation |
| `SpanTypeGuardrail` | A guardrail check |
| `SpanTypeEvaluation` | An evaluation |
| `SpanTypePrompt` | Prompt rendering |
| `SpanTypeWorkflow` | A workflow / component / module |
| `SpanTypeTask` | A background task |
| `SpanTypeSpan` | Generic span (the default) |

> [!NOTE]
>
> Using these span types is optional but highly recommended, as it enables LangWatch to provide more
> tailored insights and visualizations for your traces.

## API Client

The `github.com/langwatch/langwatch/sdks/go/client` module is a typed REST client for the LangWatch API. It is separate from tracing and lives in its own module so its generated code stays out of the core graph.

### Installation

```bash theme={null}
go get github.com/langwatch/langwatch/sdks/go/client
```

### Quick start

```go theme={null}
import "github.com/langwatch/langwatch/sdks/go/client"

// Reads LANGWATCH_API_KEY, LANGWATCH_PROJECT_ID and LANGWATCH_ENDPOINT from the
// environment when the corresponding options are omitted.
lw, err := client.New()
if err != nil {
	log.Fatal(err)
}

prompt, err := lw.Prompts.Get(context.Background(), "support-greeting", nil)
```

Authentication uses the same credential families as the exporter, configured via options or environment variables:

| Option | Environment variable | Purpose |
| - | - | - |
| `client.WithAPIKey(key)` | `LANGWATCH_API_KEY` | API key (`sk-lw-*`) or Personal Access Token (`pat-lw-*`) |
| `client.WithProjectID(id)` | `LANGWATCH_PROJECT_ID` | Project ID (required for PATs) |
| `client.WithEndpoint(url)` | `LANGWATCH_ENDPOINT` | Base URL (self-hosted / staging) |

### Services

Reach resources through the service fields on `*client.Client`. Every method takes a `context.Context` first and returns a typed error on failure.

| Field | Resource |
| - | - |
| `lw.Prompts` | Prompt configs, versions and tags |
| `lw.Datasets` | Datasets and records |
| `lw.Traces` | Trace search and retrieval |
| `lw.Annotations` | Human annotations on traces |
| `lw.Events` | Tracked events by trace id, incl. thumbs up/down feedback |
| `lw.Evaluations` | Submit evaluation results by trace id |
| `lw.Triggers` | Alerting / automation triggers |
| `lw.Monitors` | Evaluation monitors |
| `lw.Scenarios` | Simulation scenarios and runs |
| `lw.Projects` | Organization projects (admin key) |

**Prompts**, the flagship service:

```go theme={null}
// Fetch the latest version, a pinned version, or a tagged version.
latest, err := lw.Prompts.Get(ctx, "support-greeting", nil)
v4, err := lw.Prompts.Get(ctx, "support-greeting", &client.GetPromptOptions{Version: 4})
prod, err := lw.Prompts.Get(ctx, "support-greeting", &client.GetPromptOptions{Tag: "production"})

// Create a prompt (Update creates a new version).
p, err := lw.Prompts.Create(ctx, client.CreatePromptParams{
	Handle: "support-greeting",
	Model:  "openai/gpt-6-luna",
	Messages: []client.Message{
		{Role: client.RoleSystem, Content: "You are a friendly support agent."},
	},
})
```

**Feedback & evaluations by trace id**: capture the trace id from a span while tracing, then submit later. These reuse the same `langwatch.Event` / `langwatch.Evaluation` types as the live `span.RecordEvent` / `span.RecordEvaluation`, so a value can be recorded live or submitted later interchangeably:

```go theme={null}
traceID := span.SpanContext().TraceID().String() // captured earlier

err := lw.Events.ThumbsUp(ctx, traceID, "spot on")

err = lw.Evaluations.Create(ctx, traceID, client.Evaluation{
	Name:   "answer relevancy",
	Passed: langwatch.Bool(true),
	Score:  langwatch.Float64(0.92),
})
```

### Error handling

Every method returns a typed `*client.APIError` for any non-2xx response, carrying the HTTP status, decoded message, the operation that failed and the raw body. Convenience helpers cover the common cases:

```go theme={null}
prompt, err := lw.Prompts.Get(ctx, "missing", nil)
if client.IsNotFound(err)     { /* 404 */ }
if client.IsUnauthorized(err) { /* 401 */ }
if client.IsConflict(err)     { /* 409 */ }
```

### Pagination & iterators

The API uses offset (`page` / `limit`) and cursor pagination. The single-page `List` / `Search` / `ListRuns` methods return the page plus its metadata for manual paging. For large result sets, each heavy service exposes a range-over-func iterator (`iter.Seq2[T, error]`) that fetches one page at a time, so memory stays flat:

```go theme={null}
for rec, err := range lw.Datasets.AllRecords(ctx, "golden-examples", client.ListDatasetsParams{}) {
	if err != nil {
		log.Fatal(err)
	}
	process(rec) // one record at a time; pages are fetched lazily
}
```

> [!TIP]
>
> For retries, timeouts, the full pagination story and the complete service surface, see the [client module README](https://github.com/langwatch/langwatch/tree/main/sdks/go/client).

## Manual OpenTelemetry Setup

If you prefer manual setup or need more control, point a standard OTLP exporter at LangWatch's collector endpoint:

```go theme={null}
import (
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func setupLangWatch(ctx context.Context, apiKey string) func() {
	exporter, err := otlptracehttp.New(ctx,
		otlptracehttp.WithEndpointURL("https://app.langwatch.ai/api/otel/v1/traces"),
		otlptracehttp.WithHeaders(map[string]string{
			"Authorization": "Bearer " + apiKey,
		}),
	)
	if err != nil {
		log.Fatalf("failed to create OTLP exporter: %v", err)
	}

	tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
	otel.SetTracerProvider(tp)

	return func() { tp.Shutdown(ctx) }
}
```

## Environment Variables

| Variable | Description |
| - | - |
| `LANGWATCH_API_KEY` | Your LangWatch API key (required) |
| `LANGWATCH_PROJECT_ID` | Your LangWatch project ID (required for service keys / PATs) |
| `LANGWATCH_ENDPOINT` | Custom endpoint (defaults to `https://app.langwatch.ai`) |

## Version Compatibility

* **Go Version:** 1.25 or later
* **OpenAI Go SDK:** `github.com/openai/openai-go/v3`
* **OpenTelemetry:** the SDK tracks current OTel Go releases

## Support

For additional help:

* [GitHub Issues](https://github.com/langwatch/langwatch/issues)
* [Documentation](https://docs.langwatch.ai)
* [Community Support](https://docs.langwatch.ai/support)

> [!TIP]
>
> For common setup issues and troubleshooting tips, see the [Troubleshooting section](https://langwatch.ai/docs/integration/go/guide.md#troubleshooting) in the integration guide.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.