Go SDK
Go Package
View on pkg.go.dev
GitHub Repository
Source code and examples
Installation
go get github.com/veriteknik/pluggedinkit-go
Requirements
- Go 1.18 or higher
- No external dependencies beyond the standard library
Quick Start
package main
import (
"context"
"fmt"
"log"
pluggedin "github.com/veriteknik/pluggedinkit-go"
)
func main() {
// Initialize client
client := pluggedin.NewClient("your-api-key")
// For local development:
// client := pluggedin.NewClientWithOptions("your-api-key", "http://localhost:12005", nil)
ctx := context.Background()
// List documents
docs, err := client.Documents.List(ctx, &pluggedin.DocumentFilters{
Source: pluggedin.SourceAll,
Limit: 10,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d documents\n", docs.Total)
// Search documents
results, err := client.Documents.Search(ctx, "machine learning", nil, 10, 0)
if err != nil {
log.Fatal(err)
}
for _, result := range results.Results {
fmt.Printf("%s - Relevance: %.2f\n", result.Title, result.RelevanceScore)
}
// Query RAG
answer, err := client.RAG.AskQuestion(ctx, "What are the main features?")
if err != nil {
log.Fatal(err)
}
fmt.Println(answer)
}
Authentication
Get your API key from your Plugged.in Profile:import (
"os"
pluggedin "github.com/veriteknik/pluggedinkit-go"
)
// Using environment variables (recommended)
client := pluggedin.NewClient(os.Getenv("PLUGGEDIN_API_KEY"))
// With custom configuration
import (
"net/http"
"time"
)
httpClient := &http.Client{
Timeout: 120 * time.Second,
}
client := pluggedin.NewClientWithOptions(
os.Getenv("PLUGGEDIN_API_KEY"),
os.Getenv("PLUGGEDIN_BASE_URL"),
httpClient,
)
// Update API key at runtime
client.SetAPIKey("new-api-key")
// Change base URL
client.SetBaseURL("http://localhost:12005")
Core Features
Document Management
List Documents
filters := &pluggedin.DocumentFilters{
Source: pluggedin.SourceAIGenerated,
Tags: []string{"report", "analysis"},
Category: pluggedin.CategoryDocumentation,
Sort: pluggedin.SortDateDesc,
Limit: 20,
Offset: 0,
ModelProvider: "anthropic",
}
// Time filters
from := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
to := time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)
filters.DateFrom = &from
filters.DateTo = &to
response, err := client.Documents.List(ctx, filters)
if err != nil {
log.Fatal(err)
}
for _, doc := range response.Documents {
fmt.Printf("%s (%d bytes)\n", doc.Title, doc.FileSize)
fmt.Printf(" Created: %s\n", doc.CreatedAt)
fmt.Printf(" Tags: %v\n", doc.Tags)
}
Get Document
// Get document metadata only
doc, err := client.Documents.Get(ctx, "document-id", false, false)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Title: %s\n", doc.Title)
fmt.Printf("Version: %d\n", doc.Version)
// Get document with content and version history
docWithContent, err := client.Documents.Get(ctx, "document-id", true, true)
if err != nil {
log.Fatal(err)
}
fmt.Println(docWithContent.Content)
// Check last access time
if docWithContent.LastAccessAt != nil {
fmt.Printf("Last accessed: %s\n", *docWithContent.LastAccessAt)
}
Search Documents
filters := map[string]interface{}{
"modelProvider": "anthropic",
"dateFrom": "2024-01-01T00:00:00Z",
"tags": "finance,q4",
}
results, err := client.Documents.Search(ctx, "quarterly report", filters, 10, 0)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d results for query: %s\n", results.Total, results.Query)
for _, result := range results.Results {
fmt.Printf("\n%s\n", result.Title)
fmt.Printf(" Document ID: %s\n", result.DocumentID)
fmt.Printf(" Relevance: %.2f\n", result.RelevanceScore)
fmt.Printf(" Snippet: %s\n", result.Snippet)
fmt.Printf(" Tags: %v\n", result.Tags)
}
Create AI-Generated Document
request := &pluggedin.CreateDocumentRequest{
Title: "API Integration Guide",
Content: "# API Integration Guide\n\n## Introduction\n\n...",
Metadata: map[string]interface{}{
"format": "md",
"category": "documentation",
"tags": []string{"api", "guide"},
"model": map[string]string{
"name": "claude-3-opus",
"provider": "anthropic",
"version": "20240229",
},
"prompt": "Create an API integration guide",
"visibility": "workspace",
},
}
doc, err := client.Documents.Create(ctx, request)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created document: %s\n", doc.ID)
fmt.Printf("Version: %d\n", doc.Version)
Update Document
request := &pluggedin.UpdateDocumentRequest{
Operation: pluggedin.OpAppend,
Content: "\n\n## New Section\n\nAdditional content here.",
Metadata: map[string]interface{}{
"changeSummary": "Added implementation details section",
"model": map[string]string{
"name": "gpt-4",
"provider": "openai",
"version": "0613",
},
},
}
response, err := client.Documents.Update(ctx, "document-id", request)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Updated to version %d\n", response.Version)
fmt.Printf("Updated at: %s\n", response.UpdatedAt)
Delete Document
err := client.Documents.Delete(ctx, "document-id")
if err != nil {
log.Fatal(err)
}
fmt.Println("Document deleted successfully")
RAG Operations
Simple Query
// Basic question answering
answer, err := client.RAG.AskQuestion(ctx, "What are the deployment procedures?")
if err != nil {
log.Fatal(err)
}
fmt.Println("Answer:", answer)
// Check availability
status, err := client.RAG.CheckAvailability(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("RAG Available: %v\n", status["available"])
// Get storage statistics
stats, err := client.RAG.GetStorageStats(ctx, "user-id-from-dashboard")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Documents: %d\n", stats.DocumentsCount)
fmt.Printf("Estimated storage: %.2f MB\n", stats.EstimatedStorageMB)
Query with Sources
result, err := client.RAG.QueryWithSources(
ctx,
"Explain the authentication flow",
"",
)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Answer: %s\n", result.Answer)
fmt.Println("\nSources:")
for index, source := range result.Sources {
var documentID string
if index < len(result.DocumentIDs) {
documentID = result.DocumentIDs[index]
}
fmt.Printf("- %s (%s)\n", source, documentID)
}
Find Relevant Documents
documents, err := client.RAG.FindRelevantDocuments(
ctx,
"user authentication",
"",
5, // Top 5 documents
)
if err != nil {
log.Fatal(err)
}
for _, doc := range documents {
fmt.Printf("- %s (%s)\n", doc.Source, doc.DocumentID)
}
File Uploads
Important: The Go SDK no longer exposes binary upload helpers. All previous
upload functions now return descriptive errors so legacy code fails fast.
Continue using DocumentsService.Create for AI-generated content and the
Plugged.in web interface (or forthcoming ingestion tools) for binary files.
Error Handling
The SDK provides typed error structs for comprehensive error handling:doc, err := client.Documents.Get(ctx, "invalid-id", false, false)
if err != nil {
switch e := err.(type) {
case *pluggedin.AuthenticationError:
fmt.Println("Invalid API key - please check your credentials")
// Refresh API key logic
case *pluggedin.RateLimitError:
fmt.Printf("Rate limited. Retry after %d seconds\n", e.RetryAfter)
// Implement retry with backoff
time.Sleep(time.Duration(e.RetryAfter) * time.Second)
case *pluggedin.NotFoundError:
fmt.Printf("Document not found: %s\n", e.ID)
// Handle missing document
case *pluggedin.ValidationError:
fmt.Printf("Validation error on field '%s': %s\n", e.Field, e.Message)
// Fix validation issues
case *pluggedin.APIError:
fmt.Printf("API error (%d): %s\n", e.StatusCode, e.Message)
if e.Details != nil {
fmt.Printf("Details: %v\n", e.Details)
}
default:
fmt.Printf("Unexpected error: %v\n", err)
}
}
PAP Agents
The SDK provides full support for managing PAP (Plugged.in Agent Protocol) agents:Creating Agents
result, err := client.Agents.Create(ctx, pluggedinkit.CreateAgentRequest{
Name: "my-agent",
Description: stringPtr("My autonomous PAP agent"),
Resources: &pluggedinkit.ResourceRequirements{
CPURequest: stringPtr("100m"),
MemoryRequest: stringPtr("256Mi"),
CPULimit: stringPtr("1000m"),
MemoryLimit: stringPtr("1Gi"),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Agent created: %s\n", result.Agent.UUID)
fmt.Printf("DNS: %s\n", result.Agent.DNSName) // my-agent.is.plugged.in
func stringPtr(s string) *string { return &s }
Listing and Managing Agents
// List all agents
agents, err := client.Agents.List(ctx)
// Get agent details
details, err := client.Agents.Get(ctx, agentID)
fmt.Println(details.RecentHeartbeats)
fmt.Println(details.RecentMetrics)
fmt.Println(details.LifecycleEvents)
// Export agent data
exportData, err := client.Agents.Export(ctx, agentID, true, 1000)
// Delete agent
deleteResp, err := client.Agents.Delete(ctx, agentID)
Heartbeats and Metrics
CRITICAL: Heartbeats contain ONLY liveness data. Resource telemetry goes in metrics (separate channel). This separation is PAP’s zombie prevention superpower.
// Send heartbeat (liveness only)
_, err = client.Agents.Heartbeat(ctx, agentID, "IDLE", 330.0)
// Send metrics (resource telemetry)
customMetrics := map[string]interface{}{
"queue_depth": 3,
"cache_hit_rate": 0.85,
}
_, err = client.Agents.Metrics(ctx, agentID, 12.5, 128, 45, customMetrics)
Concurrent Agent Operations
func monitorAgents(client *pluggedinkit.Client, agentIDs []string) {
ctx := context.Background()
var wg sync.WaitGroup
for _, agentID := range agentIDs {
wg.Add(1)
go func(id string) {
defer wg.Done()
// Send heartbeat and metrics concurrently
errChan := make(chan error, 2)
go func() {
_, err := client.Agents.Heartbeat(ctx, id, "IDLE", 330.0)
errChan <- err
}()
go func() {
_, err := client.Agents.Metrics(ctx, id, 10.0, 100, 20, nil)
errChan <- err
}()
// Wait for both to complete
for i := 0; i < 2; i++ {
if err := <-errChan; err != nil {
log.Printf("Error for agent %s: %v", id, err)
}
}
}(agentID)
}
wg.Wait()
}
Agent States
Agents follow the PAP lifecycle state machine:NEW → PROVISIONED → ACTIVE ↔ DRAINING → TERMINATED
↓ (error)
KILLED
// Poll until agent is active
for {
details, err := client.Agents.Get(ctx, agentID)
if err != nil {
return err
}
if details.Agent.State == "ACTIVE" {
fmt.Println("Agent is ready!")
break
}
time.Sleep(5 * time.Second)
}
Context and Cancellation
All SDK methods accept a context for cancellation and timeout control:// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
docs, err := client.Documents.List(ctx, nil)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Println("Request timed out")
}
}
// With cancellation
ctx, cancel := context.WithCancel(context.Background())
// Cancel from another goroutine
go func() {
time.Sleep(5 * time.Second)
cancel()
}()
// This will be cancelled after 5 seconds
answer, err := client.RAG.AskQuestion(ctx, "Long running query")
if err != nil {
if errors.Is(err, context.Canceled) {
fmt.Println("Request was cancelled")
}
}
Concurrent Operations
Leverage Go’s concurrency for parallel operations:func fetchDocumentsConcurrently(client *pluggedin.Client) {
ctx := context.Background()
documentIDs := []string{"doc1", "doc2", "doc3", "doc4", "doc5"}
type result struct {
doc *pluggedin.Document
err error
}
results := make(chan result, len(documentIDs))
// Fetch documents concurrently
for _, id := range documentIDs {
go func(docID string) {
doc, err := client.Documents.Get(ctx, docID, true, false)
results <- result{doc, err}
}(id)
}
// Collect results
for i := 0; i < len(documentIDs); i++ {
r := <-results
if r.err != nil {
fmt.Printf("Error fetching document: %v\n", r.err)
} else {
fmt.Printf("Fetched: %s\n", r.doc.Title)
}
}
}
Testing
Unit Testing with Interfaces
// Define interface for testing
type DocumentService interface {
List(ctx context.Context, filters *DocumentFilters) (*ListDocumentsResponse, error)
Get(ctx context.Context, id string, includeContent, includeVersions bool) (*Document, error)
}
// Test function
func TestDocumentList(t *testing.T) {
// Mock implementation
mockService := &MockDocumentService{
ListFunc: func(ctx context.Context, filters *DocumentFilters) (*ListDocumentsResponse, error) {
return &ListDocumentsResponse{
Documents: []Document{{Title: "Test Doc"}},
Total: 1,
}, nil
},
}
// Test
response, err := mockService.List(context.Background(), nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if response.Total != 1 {
t.Errorf("expected 1 document, got %d", response.Total)
}
}
Integration Testing
func TestDocumentLifecycle(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
client := pluggedin.NewClient(os.Getenv("PLUGGEDIN_API_KEY"))
ctx := context.Background()
// Create document
doc, err := client.Documents.Create(ctx, &CreateDocumentRequest{
Title: "Test Document",
Content: "Test content",
Metadata: map[string]interface{}{
"tags": []string{"test"},
},
})
if err != nil {
t.Fatalf("failed to create document: %v", err)
}
// Cleanup
defer func() {
_ = client.Documents.Delete(ctx, doc.ID)
}()
// Update document
updateResp, err := client.Documents.Update(ctx, doc.ID, &UpdateDocumentRequest{
Operation: OpAppend,
Content: "\nUpdated content",
})
if err != nil {
t.Fatalf("failed to update document: %v", err)
}
if updateResp.Version <= doc.Version {
t.Errorf("expected version to increase, got %d", updateResp.Version)
}
}
Benchmarking
func BenchmarkDocumentSearch(b *testing.B) {
client := pluggedin.NewClient(os.Getenv("PLUGGEDIN_API_KEY"))
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := client.Documents.Search(ctx, "test query", nil, 10, 0)
if err != nil {
b.Fatalf("search failed: %v", err)
}
}
}
Advanced Configuration
Custom HTTP Transport
import (
"net/http"
"net/url"
"time"
)
// Custom transport with proxy
proxyURL, _ := url.Parse("http://proxy.example.com:8080")
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
}
httpClient := &http.Client{
Transport: transport,
Timeout: 2 * time.Minute,
}
client := pluggedin.NewClientWithOptions(
"your-api-key",
"https://plugged.in",
httpClient,
)
Environment Variables
// Load from environment
type Config struct {
APIKey string
BaseURL string
Timeout time.Duration
}
func LoadConfig() *Config {
timeout, _ := time.ParseDuration(os.Getenv("PLUGGEDIN_TIMEOUT"))
if timeout == 0 {
timeout = 60 * time.Second
}
return &Config{
APIKey: os.Getenv("PLUGGEDIN_API_KEY"),
BaseURL: os.Getenv("PLUGGEDIN_BASE_URL"),
Timeout: timeout,
}
}
// Use configuration
config := LoadConfig()
httpClient := &http.Client{Timeout: config.Timeout}
client := pluggedin.NewClientWithOptions(config.APIKey, config.BaseURL, httpClient)
Rate Limiting
The SDK handles rate limiting automatically:- API Endpoints: 60 requests per minute
- Document Search: 10 requests per hour for AI documents
- RAG Queries: Subject to plan limits
// Implement retry with exponential backoff
func retryWithBackoff(ctx context.Context, fn func() error) error {
maxRetries := 5
baseDelay := 1 * time.Second
for i := 0; i < maxRetries; i++ {
err := fn()
if err == nil {
return nil
}
// Check if it's a rate limit error
if rateLimitErr, ok := err.(*pluggedin.RateLimitError); ok {
delay := time.Duration(rateLimitErr.RetryAfter) * time.Second
fmt.Printf("Rate limited, waiting %v\n", delay)
time.Sleep(delay)
continue
}
// Exponential backoff for other errors
delay := baseDelay * time.Duration(1<<i)
fmt.Printf("Error: %v, retrying in %v\n", err, delay)
time.Sleep(delay)
}
return fmt.Errorf("max retries exceeded")
}
Examples
Complete working examples are available in the GitHub repository:- Basic Usage
- Document Management
- RAG Queries
- File Upload (deprecated)
- Error Handling
- Concurrent Operations
API Reference
Client Functions
| Function | Description |
|---|---|
NewClient(apiKey) | Create a new client with default settings |
NewClientWithOptions(apiKey, baseURL, httpClient) | Create a client with custom options |
Client Methods
| Method | Description |
|---|---|
SetAPIKey(apiKey) | Update the API key |
SetBaseURL(baseURL) | Update the base URL |
Document Service Methods
| Method | Description |
|---|---|
List(ctx, filters) | List documents with optional filters |
Get(ctx, id, includeContent, includeVersions) | Get a specific document |
Search(ctx, query, filters, limit, offset) | Search documents |
Create(ctx, request) | Create a new document |
Update(ctx, id, request) | Update an existing document |
Delete(ctx, id) | Delete a document |
RAG Service Methods
| Method | Description |
|---|---|
Query(ctx, query) | Returns RAGResponse with answer and metadata |
AskQuestion(ctx, query) | Simple RAG query (answer text only) |
QueryWithSources(ctx, query, projectUUID) | Query with source metadata (project argument optional) |
FindRelevantDocuments(ctx, query, projectUUID, limit) | Find relevant documents |
CheckAvailability(ctx) | Check RAG service availability |
GetStorageStats(ctx, userID) | Get storage statistics |
Upload Service Methods
Upload helpers now emit descriptive errors because binary ingestion is no longer exposed via the public API.Clipboard Service Methods
| Method | Description |
|---|---|
List(ctx) | List all clipboard entries |
Get(ctx, filters) | Get entry by name or index |
GetByName(ctx, name) | Get entry by name |
GetByIndex(ctx, idx) | Get entry by stack index |
Set(ctx, request) | Create or update named entry |
Push(ctx, request) | Push value to indexed stack |
Pop(ctx) | Pop and remove most recent entry |
Delete(ctx, request) | Delete specific entry |
DeleteByName(ctx, name) | Delete entry by name |
DeleteByIndex(ctx, idx) | Delete entry by index |
ClearAll(ctx) | Delete all entries |
Clipboard (Memory) Service
The Clipboard service provides persistent key-value storage for MCP tools and AI agents.Named Access
ctx := context.Background()
// Set a named entry
entry, err := client.Clipboard.Set(ctx, &pluggedin.ClipboardSetRequest{
Name: "user_preferences",
Value: `{"theme": "dark", "lang": "en"}`,
ContentType: "application/json",
Encoding: pluggedin.EncodingUTF8,
Visibility: pluggedin.ClipboardVisibilityPrivate,
TTLSeconds: 86400, // 24 hours
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created entry: %s\n", entry.UUID)
// Get by name
entry, err = client.Clipboard.GetByName(ctx, "user_preferences")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Value: %s\n", entry.Value)
// Delete by name
deleted, err := client.Clipboard.DeleteByName(ctx, "user_preferences")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted: %v\n", deleted)
Indexed (Stack) Access
ctx := context.Background()
// Push to stack
_, err := client.Clipboard.Push(ctx, &pluggedin.ClipboardPushRequest{
Value: "Step 1 result",
ContentType: "text/plain",
})
_, err = client.Clipboard.Push(ctx, &pluggedin.ClipboardPushRequest{
Value: "Step 2 result",
ContentType: "text/plain",
})
// Get by index (0 = most recent)
latest, err := client.Clipboard.GetByIndex(ctx, 0)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Latest: %s\n", latest.Value) // "Step 2 result"
// Pop removes and returns the most recent
popped, err := client.Clipboard.Pop(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Popped: %s\n", popped.Value) // "Step 2 result"
List All Entries
ctx := context.Background()
entries, err := client.Clipboard.List(ctx)
if err != nil {
log.Fatal(err)
}
for _, entry := range entries {
var nameOrIdx string
if entry.Name != nil {
nameOrIdx = *entry.Name
} else if entry.Idx != nil {
nameOrIdx = fmt.Sprintf("idx:%d", *entry.Idx)
}
fmt.Printf("%s: %s\n", nameOrIdx, entry.Value)
if entry.ExpiresAt != nil {
fmt.Printf(" Expires: %s\n", entry.ExpiresAt.Format(time.RFC3339))
}
}
Type Definitions
// ClipboardEntry represents a clipboard entry
type ClipboardEntry struct {
UUID string `json:"uuid"`
Name *string `json:"name,omitempty"`
Idx *int `json:"idx,omitempty"`
Value string `json:"value"`
ContentType string `json:"contentType"`
Encoding ClipboardEncoding `json:"encoding"`
SizeBytes int `json:"sizeBytes"`
Visibility ClipboardVisibility `json:"visibility"`
Source ClipboardSource `json:"source"`
CreatedByTool *string `json:"createdByTool,omitempty"`
CreatedByModel *string `json:"createdByModel,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
}
// Source options - tracks where entries originate
const (
ClipboardSourceUI ClipboardSource = "ui" // Created via web interface
ClipboardSourceSDK ClipboardSource = "sdk" // Created via SDK (automatically set)
ClipboardSourceMCP ClipboardSource = "mcp" // Created via MCP tools
)
// The SDK automatically sets Source to ClipboardSourceSDK for all entries.
// DefaultClipboardSource is ClipboardSourceUI for backward compatibility.
// Encoding options
const (
EncodingUTF8 ClipboardEncoding = "utf-8"
EncodingBase64 ClipboardEncoding = "base64"
EncodingHex ClipboardEncoding = "hex"
)
// Visibility options
const (
ClipboardVisibilityPrivate ClipboardVisibility = "private"
ClipboardVisibilityWorkspace ClipboardVisibility = "workspace"
ClipboardVisibilityPublic ClipboardVisibility = "public"
)
Support
- Go Package: https://pkg.go.dev/github.com/veriteknik/pluggedinkit-go
- GitHub Issues: Report bugs and request features
- Documentation: https://docs.plugged.in
- Discord: Join our community

