Skip to main content

Installation

go get github.com/raindrop-ai/go
import raindrop "github.com/raindrop-ai/go"

client, err := raindrop.New(
	raindrop.WithWriteKey("rk_..."),
)
if err != nil {
	log.Fatal(err)
}
defer client.Close()
Source code and releases live in 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

package main

import (
	"context"
	"log"

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

func main() {
	client, err := raindrop.New(
		raindrop.WithWriteKey("rk_..."),
	)
	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:
_ = 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():
interaction := client.ResumeInteraction("evt_123")
_ = interaction.SetProperty("stage", "follow-up")
_ = interaction.Finish(raindrop.FinishOptions{
	Output: "Here is a shorter version of that itinerary.",
})
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.

Single-Shot Tracking (TrackAI)

For simple request-response interactions, you can use TrackAI() directly:
_ = 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:
_ = 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():
ParameterTypeDescription
EventIDstringThe ID of the AI event you’re evaluating
NamestringSignal name (e.g. "thumbs_up", "thumbs_down")
Typestring"default", "feedback", "edit", "agent", etc. Defaults to "default"
Sentimentstring"POSITIVE" or "NEGATIVE"
Propertiesmap[string]anyAdditional metadata
AttachmentIDstringOptional attachment ID to associate the signal with
_ = 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

_ = 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.
PropertyTypeDescription
Typestring"code", "text", "image", or "iframe"
NamestringOptional display name
ValuestringContent or URL
Rolestring"input" or "output"
LanguagestringProgramming language (for code attachments)
_ = 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

client, err := raindrop.New(
	raindrop.WithWriteKey("rk_..."),
	raindrop.WithDebug(os.Getenv("ENV") != "production"),
)
OptionDescriptionDefault
WithWriteKey(string)Your Raindrop API key (required)
WithDebug(bool)Print debug logs to the configured loggerfalse
WithEndpoint(string)Override the API endpointRaindrop API
WithHTTPClient(*http.Client)Use a custom HTTP client15s timeout
WithLogger(*slog.Logger)Use a custom slog.Loggerslog.Default()
WithServiceName(string)Override the service name in traces"raindrop.go-sdk"
WithServiceVersion(string)Override the service version in tracesSDK version
WithPartialFlushInterval(time.Duration)How often to flush buffered events1s
WithTraceFlushInterval(time.Duration)How often to flush buffered spans1s
WithTraceBatchSize(int)Max spans per trace export batch50
WithTraceQueueSize(int)Max spans held in the queue5000
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.

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:
// 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
	},
)
ParameterTypeDescription
NamestringName for identification in traces
Propertiesmap[string]anyAdditional metadata
Attributes[]AttributeOTLP attributes

Using WithTool

Use WithTool to trace agent tool calls with automatic input/output capture:
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:
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"})
ParameterTypeDescription
NamestringName of the tool
InputanyInput passed to the tool
OutputanyOutput returned by the tool
Durationtime.DurationDuration of the tool call
StartTimetime.TimeWhen the tool started (defaults to now - Duration)
EndTimetime.TimeWhen the tool ended
ErrorerrorError if the tool failed
Propertiesmap[string]anyAdditional metadata

StartToolSpan – Real-Time Tracking

Use StartToolSpan to track a tool as it executes:
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()
MethodDescription
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:
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:
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 if you get stuck!