Go client library for the Skald API.
go get github.com/skaldlabs/skald-go- Go 1.18 or higher
import "github.com/skaldlabs/skald-go"
client := skald.NewClient("your-api-key-here")You can optionally specify a custom base URL (e.g., for self-hosted instances):
client := skald.NewClient("your-api-key-here", "https://custom-api.example.com")Create a new memo that will be automatically processed (summarized, tagged, chunked, and indexed for search):
refID := "external-id-123"
source := "notion"
expirationDate := time.Now().Add(30 * 24 * time.Hour)
result, err := client.CreateMemo(ctx, skald.MemoData{
Title: "Meeting Notes",
Content: "Full content of the memo...",
Metadata: map[string]interface{}{
"type": "notes",
"author": "John Doe",
},
ReferenceID: &refID,
Tags: []string{"meeting", "q1"},
Source: &source,
ExpirationDate: &expirationDate,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.MemoUUID) // UUID of created memoRequired Fields:
Title(string, max 255 chars) - The title of the memoContent(string) - The full content of the memo
Optional Fields:
Metadata(map[string]interface{}) - Custom JSON metadataReferenceID(*string, max 255 chars) - An ID from your side that you can use to match Skald memo UUIDs with e.g. documents on your endTags([]string) - Tags for categorizationSource(*string, max 255 chars) - An indication from your side of the source of this content, useful when building integrationsExpirationDate(*time.Time) - Timestamp for automatic memo expiration
Upload a document file to create a memo. Supported formats include PDF, DOC, DOCX, and PPTX (max 100MB):
title := "Q4 Business Report"
source := "reports"
refID := "report-2024-q4"
result, err := client.CreateMemoFromFile(ctx, "document.pdf", &skald.MemoFileData{
Title: &title,
Source: &source,
ReferenceID: &refID,
Tags: []string{"business", "quarterly"},
Metadata: map[string]interface{}{
"year": 2024,
"quarter": "Q4",
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Uploaded memo UUID: %s\n", result.MemoUUID)Parameters:
filePath(string, required) - Path to the file to uploadmemoData(*MemoFileData, optional) - Optional metadata for the memo
MemoFileData Fields (all optional):
Title(*string, max 255 chars) - The title of the memoSource(*string, max 255 chars) - Source identifierReferenceID(*string, max 255 chars) - Your external reference IDTags([]string) - Tags for categorizationMetadata(map[string]interface{}) - Custom JSON metadataExpirationDate(*time.Time) - Timestamp for automatic memo expiration
Note: File uploads are processed asynchronously. Use CheckMemoStatus() to monitor processing status.
Monitor the processing status of a memo, especially useful after uploading files:
// Check status by UUID
status, err := client.CheckMemoStatus(ctx, memoUUID)
// Check status by reference ID
status, err := client.CheckMemoStatus(ctx, "report-2024-q4", skald.IDTypeReferenceID)
if err != nil {
log.Fatal(err)
}
switch status.Status {
case skald.MemoStatusProcessed:
fmt.Println("Memo is ready!")
case skald.MemoStatusProcessing:
fmt.Println("Still processing...")
case skald.MemoStatusError:
fmt.Printf("Processing failed: %s\n", *status.ErrorReason)
}Status Values:
MemoStatusProcessing- The memo is currently being processedMemoStatusProcessed- The memo has been successfully processed and is readyMemoStatusError- Processing failed (checkErrorReasonfield for details)
Example: Polling for completion
maxAttempts := 30
pollInterval := 2 * time.Second
for attempt := 1; attempt <= maxAttempts; attempt++ {
status, err := client.CheckMemoStatus(ctx, memoUUID)
if err != nil {
log.Fatal(err)
}
if status.Status == skald.MemoStatusProcessed {
fmt.Println("Processing complete!")
break
} else if status.Status == skald.MemoStatusError {
log.Fatalf("Processing failed: %s", *status.ErrorReason)
}
time.Sleep(pollInterval)
}Retrieve a memo by its UUID or your reference ID:
// Get by UUID
memo, err := client.GetMemo(ctx, "550e8400-e29b-41d4-a716-446655440000")
// Get by reference ID
memo, err := client.GetMemo(ctx, "external-id-123", skald.IDTypeReferenceID)
if err != nil {
log.Fatal(err)
}
fmt.Println(memo.Title)
fmt.Println(memo.Content)
fmt.Println(memo.Summary)
fmt.Println(memo.Tags)
fmt.Println(memo.Chunks)The GetMemo() method returns complete memo details including content, AI-generated summary, tags, and content chunks.
List all memos with pagination:
// Get first page with default page size (20)
memos, err := client.ListMemos(ctx, nil)
// Get specific page with custom page size
page := 2
pageSize := 50
memos, err := client.ListMemos(ctx, &skald.ListMemosParams{
Page: &page,
PageSize: &pageSize,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total memos: %d\n", memos.Count)
fmt.Printf("Results: %d\n", len(memos.Results))Parameters:
Page(*int, optional) - Page number (default: 1)PageSize(*int, optional) - Results per page (default: 20, max: 100)
Update an existing memo by UUID or reference ID:
// Update by UUID
title := "Updated Title"
_, err := client.UpdateMemo(ctx, "550e8400-e29b-41d4-a716-446655440000", skald.UpdateMemoData{
Title: &title,
Metadata: map[string]interface{}{
"status": "reviewed",
},
})
// Update by reference ID and trigger reprocessing
content := "New content that will be reprocessed"
_, err := client.UpdateMemo(ctx, "external-id-123", skald.UpdateMemoData{
Content: &content,
}, skald.IDTypeReferenceID)Note: When you update the Content field, the memo will be automatically reprocessed (summary, tags, and chunks regenerated).
Updatable Fields:
Title(*string)Content(*string)Metadata(map[string]interface{})ClientReferenceID(*string)Source(*string)ExpirationDate(*time.Time)
Permanently delete a memo and all associated data:
// Delete by UUID
err := client.DeleteMemo(ctx, "550e8400-e29b-41d4-a716-446655440000")
// Delete by reference ID
err := client.DeleteMemo(ctx, "external-id-123", skald.IDTypeReferenceID)
if err != nil {
log.Fatal(err)
}Warning: This operation permanently deletes the memo and all related data (content, summary, tags, chunks) and cannot be undone.
Search through your memos using semantic search:
// Basic semantic search
limit := 10
results, err := client.Search(ctx, skald.SearchRequest{
Query: "quarterly goals",
Limit: &limit,
})
// Search with filters
filtered, err := client.Search(ctx, skald.SearchRequest{
Query: "python tutorial",
Filters: []skald.Filter{
{
Field: "source",
Operator: skald.FilterOperatorEq,
Value: "notion",
FilterType: skald.FilterTypeNativeField,
},
{
Field: "level",
Operator: skald.FilterOperatorEq,
Value: "beginner",
FilterType: skald.FilterTypeCustomMetadata,
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d results\n", len(filtered.Results))
for _, memo := range filtered.Results {
fmt.Printf("- %s (distance: %.4f)\n", memo.Title, *memo.Distance)
}Query(string, required) - The search queryLimit(*int, optional) - Maximum results to return (1-50, default 10)Filters([]Filter, optional) - Array of filter objects to narrow results (see Filters section below)
type SearchResponse struct {
Results []SearchResult
}
type SearchResult struct {
UUID string
Title string
Summary string
ContentSnippet string
Distance *float64 // Only populated for semantic search
}UUID- Unique identifier for the memoTitle- Memo titleSummary- Auto-generated summary for the memoContentSnippet- A snippet containing the beginning of the memoDistance- A decimal from 0 to 2 determining how close the result was deemed to be to the query.
Ask questions about your memos using an AI agent. The agent retrieves relevant context and generates answers with inline citations.
result, err := client.Chat(ctx, skald.ChatParams{
Query: "What were the main points discussed in the Q1 meeting?",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Response)
// "The main points discussed in the Q1 meeting were:
// 1. Revenue targets [[1]]
// 2. Hiring plans [[2]]
// 3. Product roadmap [[1]][[3]]"
fmt.Println(result.OK) // trueFor real-time responses, use streaming chat:
eventChan, errChan := client.StreamedChat(ctx, skald.ChatParams{
Query: "What are our quarterly goals?",
})
for event := range eventChan {
if event.Type == "token" && event.Content != nil {
// Write each token as it arrives
fmt.Print(*event.Content)
} else if event.Type == "done" {
fmt.Println("\nDone!")
break
}
}
// Check for errors
select {
case err := <-errChan:
if err != nil {
log.Fatal(err)
}
default:
}query(string, required) - The question to asksystem_prompt(string, optional) - A system prompt to guide the AI's behaviorfilters([]Filter, optional) - Array of filter objects to focus chat context on specific sources (see Filters section below)
Non-streaming responses include:
OK(bool) - Success statusResponse(string) - The AI's answer with inline citations in format[[N]]IntermediateSteps([]interface{}) - Steps taken by the agent (for debugging)
Streaming responses yield events:
{ Type: "token", Content: *string }- Each text token as it's generated{ Type: "done" }- Indicates the stream has finished
Filters allow you to narrow down results based on memo metadata. You can filter by native fields or custom metadata fields. Filters are supported in Search(), Chat(), StreamedChat(), GenerateDoc(), and StreamedGenerateDoc().
type Filter struct {
Field string // Field name to filter on
Operator FilterOperator // Comparison operator
Value interface{} // Value(s) to compare against (string or []string)
FilterType FilterType // 'native_field' or 'custom_metadata'
}Native fields are built-in memo properties:
title- Memo titlesource- Source system (e.g., "notion", "confluence")client_reference_id- Your external reference IDtags- Memo tags (array)
You can filter on any field from the Metadata map you provided when creating the memo.
FilterOperatorEq- Equals (exact match)FilterOperatorNeq- Not equalsFilterOperatorContains- Contains substring (case-insensitive)FilterOperatorStartsWith- Starts with prefix (case-insensitive)FilterOperatorEndsWith- Ends with suffix (case-insensitive)FilterOperatorIn- Value is in array (requires array value)FilterOperatorNotIn- Value is not in array (requires array value)
// Filter by source
skald.Filter{
Field: "source",
Operator: skald.FilterOperatorEq,
Value: "notion",
FilterType: skald.FilterTypeNativeField,
}
// Filter by multiple tags
skald.Filter{
Field: "tags",
Operator: skald.FilterOperatorIn,
Value: []string{"security", "compliance"},
FilterType: skald.FilterTypeNativeField,
}
// Filter by title containing text
skald.Filter{
Field: "title",
Operator: skald.FilterOperatorContains,
Value: "meeting",
FilterType: skald.FilterTypeNativeField,
}
// Filter by custom metadata field
skald.Filter{
Field: "department",
Operator: skald.FilterOperatorEq,
Value: "engineering",
FilterType: skald.FilterTypeCustomMetadata,
}
// Exclude specific sources
skald.Filter{
Field: "source",
Operator: skald.FilterOperatorNotIn,
Value: []string{"draft", "archive"},
FilterType: skald.FilterTypeNativeField,
}When you provide multiple filters, they are combined with AND logic (all filters must match):
results, err := client.Search(ctx, skald.SearchRequest{
Query: "security best practices",
Filters: []skald.Filter{
{
Field: "source",
Operator: skald.FilterOperatorEq,
Value: "security-docs",
FilterType: skald.FilterTypeNativeField,
},
{
Field: "tags",
Operator: skald.FilterOperatorIn,
Value: []string{"approved", "current"},
FilterType: skald.FilterTypeNativeField,
},
{
Field: "status",
Operator: skald.FilterOperatorNeq,
Value: "draft",
FilterType: skald.FilterTypeCustomMetadata,
},
},
})Focus chat context on specific sources:
result, err := client.Chat(ctx, skald.ChatParams{
Query: "What are our security practices?",
Filters: []skald.Filter{
{
Field: "tags",
Operator: skald.FilterOperatorIn,
Value: []string{"security", "compliance"},
FilterType: skald.FilterTypeNativeField,
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Response)Control which memos are used for document generation:
rules := "Use technical language with code examples"
doc, err := client.GenerateDoc(ctx, "Create an API integration guide", &rules, []skald.Filter{
{
Field: "source",
Operator: skald.FilterOperatorIn,
Value: []string{"api-docs", "technical-specs"},
FilterType: skald.FilterTypeNativeField,
},
{
Field: "document_type",
Operator: skald.FilterOperatorEq,
Value: "specification",
FilterType: skald.FilterTypeCustomMetadata,
},
})result, err := client.CreateMemo(ctx, skald.MemoData{
Title: "My Memo",
Content: "Content here",
})
if err != nil {
log.Printf("Error: %v", err)
return
}
fmt.Println("Success:", result)package main
import (
"context"
"fmt"
"log"
"time"
"github.com/skaldlabs/skald-go"
)
func main() {
// Initialize client
client := skald.NewClient("your-api-key-here")
ctx := context.Background()
// Create a memo
refID := "example-123"
source := "example-app"
result, err := client.CreateMemo(ctx, skald.MemoData{
Title: "Go Programming Best Practices",
Content: "Go is a statically typed, compiled programming language...",
Metadata: map[string]interface{}{
"category": "programming",
"level": "intermediate",
},
ReferenceID: &refID,
Tags: []string{"go", "programming"},
Source: &source,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created memo: %+v\n", result)
// Search for memos
limit := 5
searchResults, err := client.Search(ctx, skald.SearchRequest{
Query: "golang best practices",
Limit: &limit,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d results:\n", len(searchResults.Results))
for _, result := range searchResults.Results {
fmt.Printf("- %s\n", result.Title)
}
// Chat with knowledge base
chatResp, err := client.Chat(ctx, skald.ChatParams{
Query: "What are Go best practices?",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Answer: %s\n", chatResp.Response)
}The SDK exports the following types for use in your Go code:
// ID and filter types
type IDType string
type FilterOperator string
type FilterType string
// Memo types
type MemoData struct { ... }
type MemoFileData struct { ... }
type CreateMemoResponse struct { ... }
type UpdateMemoData struct { ... }
type UpdateMemoResponse struct { ... }
type Memo struct { ... }
type MemoListItem struct { ... }
type ListMemosParams struct { ... }
type ListMemosResponse struct { ... }
type MemoTag struct { ... }
type MemoChunk struct { ... }
type MemoStatus string
type MemoStatusResponse struct { ... }
// Filter types
type Filter struct { ... }
// Search types
type SearchRequest struct { ... }
type SearchResponse struct { ... }
type SearchResult struct { ... }
// Chat types
type ChatRequest struct { ... }
type ChatResponse struct { ... }
type ChatStreamEvent struct { ... }See the types.go file for complete type definitions.
See the examples directory for complete working examples demonstrating all SDK features.
Run the test suite:
go test -v -coverMIT