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

# Go

> The Raindrop SDK allows you to track user events and AI interactions in your app. This documentation provides a brief overview of how to use the Go SDK.

## Installation

```bash theme={null}
go get github.com/raindrop-ai/go
```

```go theme={null}
import raindrop "github.com/raindrop-ai/go"

client, err := raindrop.New(
	raindrop.WithWriteKey("your-write-key"),
)
if err != nil {
	log.Fatal(err)
}
defer client.Close()
```

Source code and releases live in [raindrop-ai/go](https://github.com/raindrop-ai/go).

***

## Quick Start: Interaction API

The Interaction API uses a simple three-step pattern:

1. **`Begin()`** – Create an interaction and log the initial user input
2. **Update** – Optionally call `SetProperty`, `SetProperties`, `SetInput`, or `AddAttachments`
3. **`Finish()`** – Record the AI's final output and close the interaction

### Example: Chat Completion

```go theme={null}
package main

import (
	"context"
	"log"

	raindrop "github.com/raindrop-ai/go"
)

func main() {
	client, err := raindrop.New(
		raindrop.WithWriteKey("your-write-key"),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	ctx := context.Background()

	// 1. Start the interaction
	interaction := client.Begin(ctx, raindrop.BeginOptions{
		EventID: "evt_123",
		UserID:  "user-123",
		Event:   "chat_message",
		Input:   "Can you suggest a calm Saturday morning in San Francisco?",
		Model:   "gpt-4o",
		ConvoID: "conv-123",
		Properties: map[string]any{
			"system_prompt": "you are a helpful...",
		},
	})

	// 2. Make the LLM call
	output := callLLM()

	// 3. Finish the interaction
	_ = interaction.Finish(raindrop.FinishOptions{
		Output: output,
	})
}
```

### Updating an Interaction

Update an interaction at any point using `SetProperty`, `SetProperties`, `SetInput`, or `AddAttachments`:

```go theme={null}
_ = interaction.SetProperty("stage", "retrieving")
_ = interaction.SetProperties(map[string]any{
	"surface": "chat",
	"region":  "us-west",
})
_ = interaction.SetInput("Can you make it a little more local?")
_ = interaction.AddAttachments([]raindrop.Attachment{{
	Type:  "text",
	Role:  "input",
	Name:  "preferences",
	Value: "Prefers coffee, a quiet walk, and no museum stops.",
}})
```

### Resuming an Interaction

If you no longer have the interaction object returned from `Begin()`, resume it with `ResumeInteraction()`:

```go theme={null}
interaction := client.ResumeInteraction("evt_123")
_ = interaction.SetProperty("stage", "follow-up")
_ = interaction.Finish(raindrop.FinishOptions{
	Output: "Here is a shorter version of that itinerary.",
})
```

<Warning>
  `ResumeInteraction()` recovers an active in-memory interaction created by `Begin()` in the same process. It is not a cross-process restore mechanism. If the event ID is not found in memory, a new interaction handle is created for that ID.
</Warning>

***

## Single-Shot Tracking (`TrackAI`)

For simple request-response interactions, you can use `TrackAI()` directly:

```go theme={null}
_ = client.TrackAI(ctx, raindrop.AIEvent{
	UserID:  "user-123",
	Event:   "chat_message",
	Input:   "Who won the 2023 AFL Grand Final?",
	Output:  "Collingwood by four points!",
	Model:   "gpt-4o",
	ConvoID: "conv-123",
	Properties: map[string]any{
		"ai.usage.prompt_tokens":     10,
		"ai.usage.completion_tokens": 5,
	},
})
```

> We recommend using `Begin()` → `Finish()` for new code to take advantage of partial-event buffering, tracing, and upcoming features like automatic token counts.

Use `TrackEvent()` for non-AI events:

```go theme={null}
_ = client.TrackEvent(ctx, raindrop.Event{
	UserID: "user-123",
	Event:  "session_started",
	Properties: map[string]any{
		"entrypoint": "dashboard",
	},
})
```

***

## Tracking Signals (Feedback)

Signals capture quality ratings on AI events. Use `TrackSignal()` with the same event ID from `Begin()` or `TrackAI()`:

| Parameter      | Type             | Description                                                                  |
| -------------- | ---------------- | ---------------------------------------------------------------------------- |
| `EventID`      | `string`         | The ID of the AI event you're evaluating                                     |
| `Name`         | `string`         | Signal name (e.g. `"thumbs_up"`, `"thumbs_down"`)                            |
| `Type`         | `string`         | `"default"`, `"feedback"`, `"edit"`, `"agent"`, etc. Defaults to `"default"` |
| `Sentiment`    | `string`         | `"POSITIVE"` or `"NEGATIVE"`                                                 |
| `Properties`   | `map[string]any` | Additional metadata                                                          |
| `AttachmentID` | `string`         | Optional attachment ID to associate the signal with                          |

```go theme={null}
_ = client.TrackSignal(ctx, raindrop.Signal{
	EventID:   "evt_123",
	Name:      "thumbs_down",
	Type:      "feedback",
	Sentiment: "NEGATIVE",
	Properties: map[string]any{
		"comment": "Answer was off-topic",
	},
})
```

***

## Identifying Users

```go theme={null}
_ = client.Identify(ctx, raindrop.User{
	UserID: "user-123",
	Traits: map[string]any{
		"name":  "Jane",
		"email": "jane@example.com",
		"plan":  "paid", // we recommend "free", "paid", "trial"
	},
})
```

***

## Attachments

Attachments let you include additional context—documents, images, code, or embedded content—with your events. They work with both `Begin()` interactions and `TrackAI()` calls.

| Property   | Type     | Description                                   |
| ---------- | -------- | --------------------------------------------- |
| `Type`     | `string` | `"code"`, `"text"`, `"image"`, or `"iframe"`  |
| `Name`     | `string` | Optional display name                         |
| `Value`    | `string` | Content or URL                                |
| `Role`     | `string` | `"input"` or `"output"`                       |
| `Language` | `string` | Programming language (for `code` attachments) |

```go theme={null}
_ = interaction.AddAttachments([]raindrop.Attachment{
	{
		Type:     "code",
		Role:     "input",
		Language: "go",
		Name:     "example.go",
		Value:    "fmt.Println(\"hello\")",
	},
	{
		Type:  "text",
		Name:  "Additional Info",
		Value: "Some extra text",
		Role:  "input",
	},
	{Type: "image", Value: "https://example.com/image.png", Role: "output"},
	{Type: "iframe", Value: "https://example.com/embed", Role: "output"},
})
```

***

## Configuration

```go theme={null}
client, err := raindrop.New(
	raindrop.WithWriteKey("your-write-key"),
	raindrop.WithDebug(os.Getenv("ENV") != "production"),
)
```

| Option                                    | Description                                                                                          | Default             |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------- |
| `WithWriteKey(string)`                    | Your Raindrop API key (required)                                                                     | —                   |
| `WithProjectID(string)`                   | Send events to a specific [project](/docs/platform/projects); omit for the default **Production** project | `"default"`         |
| `WithDebug(bool)`                         | Print debug logs to the configured logger                                                            | `false`             |
| `WithEndpoint(string)`                    | Override the API endpoint                                                                            | Raindrop API        |
| `WithHTTPClient(*http.Client)`            | Use a custom HTTP client                                                                             | 15s timeout         |
| `WithLogger(*slog.Logger)`                | Use a custom `slog.Logger`                                                                           | `slog.Default()`    |
| `WithServiceName(string)`                 | Override the service name in traces                                                                  | `"raindrop.go-sdk"` |
| `WithServiceVersion(string)`              | Override the service version in traces                                                               | SDK version         |
| `WithPartialFlushInterval(time.Duration)` | How often to flush buffered events                                                                   | `1s`                |
| `WithTraceFlushInterval(time.Duration)`   | How often to flush buffered spans                                                                    | `1s`                |
| `WithTraceBatchSize(int)`                 | Max spans per trace export batch                                                                     | `50`                |
| `WithTraceQueueSize(int)`                 | Max spans held in the queue                                                                          | `5000`              |

Call `client.Close()` before your process exits to flush any buffered events and spans. If `writeKey` is omitted, the client becomes a no-op instead of failing.

***

## Projects

Pass `WithProjectID` to scope every event from a client to a specific [project](/docs/platform/projects). Under the hood this sets the `X-Raindrop-Project-Id` header on each request.

```go theme={null}
client, err := raindrop.New(
	raindrop.WithWriteKey("your-write-key"),
	raindrop.WithProjectID("support-prod"),
)
```

A project slug is up to 63 lowercase letters, digits, and hyphens, and it must start and end with a letter or digit (`^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`).

Omitting `WithProjectID` (or passing `"default"`) sends to the default **Production** project, which is the existing behavior, so single-project orgs need nothing here. See [Projects](/docs/platform/projects) for isolation, archival, and the full behavior table.

***

## Tracing

Tracing captures detailed execution information from your AI pipelines—multi-model interactions, chained prompts, and tool calls. This helps you:

* Visualize the full execution flow of your AI application
* Debug and optimize prompt chains
* Understand the intermediate steps that led to a response

### Using `WithSpan`

Use `WithSpan` to trace tasks or operations:

```go theme={null}
// Basic span
err := interaction.WithSpan(
	raindrop.SpanOptions{Name: "generate_response"},
	func(ctx context.Context, span *raindrop.Span) error {
		// LLM calls or other work here
		return nil
	},
)

// Span with metadata
err := interaction.WithSpan(
	raindrop.SpanOptions{
		Name:       "embedding_generation",
		Properties: map[string]any{"model": "text-embedding-3-large"},
	},
	func(ctx context.Context, span *raindrop.Span) error {
		if span != nil {
			span.SetAttributes(raindrop.StringAttr("ai.model.id", "text-embedding-3-large"))
		}
		return nil
	},
)
```

| Parameter    | Type             | Description                       |
| ------------ | ---------------- | --------------------------------- |
| `Name`       | `string`         | Name for identification in traces |
| `Properties` | `map[string]any` | Additional metadata               |
| `Attributes` | `[]Attribute`    | OTLP attributes                   |

### Using `WithTool`

Use `WithTool` to trace agent tool calls with automatic input/output capture:

```go theme={null}
result, err := raindrop.WithTool(interaction, "search_tool", raindrop.ToolOptions{
	Input: map[string]any{"query": "best coffee near Dolores Park"},
}, func() (map[string]any, error) {
	return map[string]any{"winner": "Ritual Coffee Roasters"}, nil
})
```

`WithTool` is a generic function—the return type matches your callback's return type.

### Manual Tool Tracking

For more control over tool span tracking, use `TrackTool` or `StartToolSpan`.

#### `TrackTool` – Retroactive Logging

Use `TrackTool` to log a tool call after it has completed:

```go theme={null}
interaction := client.Begin(ctx, raindrop.BeginOptions{
	EventID: "evt_123",
	Event:   "agent_run",
	UserID:  "user-123",
	Input:   "Search for weather data",
})

// Log a completed tool call
interaction.TrackTool(raindrop.TrackToolOptions{
	Name:     "web_search",
	Input:    map[string]any{"query": "weather in NYC"},
	Output:   map[string]any{"results": []string{"Sunny, 72°F", "Clear skies"}},
	Duration: 150 * time.Millisecond,
	Properties: map[string]any{"engine": "google"},
})

// Log a failed tool call
interaction.TrackTool(raindrop.TrackToolOptions{
	Name:     "database_query",
	Input:    map[string]any{"query": "SELECT * FROM users"},
	Duration: 50 * time.Millisecond,
	Error:    fmt.Errorf("connection timeout"),
})

_ = interaction.Finish(raindrop.FinishOptions{Output: "Weather search complete"})
```

| Parameter    | Type             | Description                                          |
| ------------ | ---------------- | ---------------------------------------------------- |
| `Name`       | `string`         | Name of the tool                                     |
| `Input`      | `any`            | Input passed to the tool                             |
| `Output`     | `any`            | Output returned by the tool                          |
| `Duration`   | `time.Duration`  | Duration of the tool call                            |
| `StartTime`  | `time.Time`      | When the tool started (defaults to `now - Duration`) |
| `EndTime`    | `time.Time`      | When the tool ended                                  |
| `Error`      | `error`          | Error if the tool failed                             |
| `Properties` | `map[string]any` | Additional metadata                                  |

#### `StartToolSpan` – Real-Time Tracking

Use `StartToolSpan` to track a tool as it executes:

```go theme={null}
toolSpan := interaction.StartToolSpan("api_call", raindrop.ToolOptions{
	Input:      map[string]any{"method": "GET", "path": "/api/data"},
	Properties: map[string]any{"endpoint": "/api/data"},
})

result, err := fetchData()
if err != nil {
	toolSpan.SetError(err)
} else {
	toolSpan.SetOutput(result)
}
toolSpan.End()
```

| Method            | Description                                 |
| ----------------- | ------------------------------------------- |
| `SetInput(any)`   | Set the input (JSON stringified if object)  |
| `SetOutput(any)`  | Set the output (JSON stringified if object) |
| `SetError(error)` | Mark the span as failed                     |
| `End()`           | End the span (required when done)           |

### Standalone Tracer

Use `Tracer()` for batch jobs or non-conversation work where you still want spans and tool traces:

```go theme={null}
tracer := client.Tracer(map[string]any{"job_id": "batch-123"})

_ = tracer.WithSpan(raindrop.SpanOptions{
	Name:       "offline_enrichment",
	Properties: map[string]any{"step": "embed"},
}, func(ctx context.Context, span *raindrop.Span) error {
	if span != nil {
		span.SetAttributes(raindrop.StringAttr("job.kind", "offline"))
	}
	return nil
})

tracer.TrackTool(raindrop.TrackToolOptions{
	Name:       "vector_lookup",
	Input:      map[string]any{"query": "mission coffee"},
	Output:     map[string]any{"winner": "Ritual Coffee Roasters"},
	Properties: map[string]any{"step": "retrieve"},
})
```

### Span Attributes

The SDK provides helpers for creating OTLP-compatible attributes:

```go theme={null}
span.SetAttributes(
	raindrop.StringAttr("ai.model.id", "gpt-4o"),
	raindrop.IntAttr("ai.usage.prompt_tokens", 150),
	raindrop.FloatAttr("ai.latency_seconds", 1.23),
	raindrop.BoolAttr("ai.stream", true),
	raindrop.StringSliceAttr("ai.tools", []string{"search", "calculator"}),
)
```

***

That's it! You're ready to explore your events in the Raindrop dashboard. Ping us on Slack or [email us](mailto:founders@raindrop.ai) if you get stuck!
