Skip to main content
Node.js client library for the Skald API.

Installation

npm install @skald-labs/skald-node

Requirements

  • Node 18.0.0 or higher

Usage

Initialize the client

import { Skald } from '@skald-labs/skald-node';

const skald = new Skald('your-api-key-here');

Memo Management

Create a Memo

Create a new memo that will be automatically processed (summarized, tagged, chunked, and indexed for search):
const result = await skald.createMemo({
  title: 'Meeting Notes',
  content: 'Full content of the memo...',
  metadata: {
    type: 'notes',
    author: 'John Doe'
  },
  reference_id: 'external-id-123',
  tags: ['meeting', 'q1'],
  source: 'notion',
  expiration_date: '2024-12-31T23:59:59Z'
});

console.log(result); // { memo_uuid: '550e8400-e29b-41d4-a716-446655440000' }
Required Fields:
  • title (string, max 255 chars) - The title of the memo
  • content (string) - The full content of the memo
Optional Fields:
  • project_id (string) - Project UUID (required when using Token Authentication)
  • metadata (object) - Custom JSON metadata
  • reference_id (string, max 255 chars) - An ID from your side that you can use to match Skald memo UUIDs with e.g. documents on your end
  • tags (array of strings) - Tags for categorization
  • source (string, max 255 chars) - An indication from your side of the source of this content, useful when building integrations
  • expiration_date (string) - ISO 8601 timestamp for automatic memo expiration

Create a Memo from File Upload

Upload a document file (PDF, DOC, DOCX, PPTX) that will be processed asynchronously and converted into a memo:
import * as fs from 'fs';

const fileBuffer = fs.readFileSync('./document.pdf');

const result = await skald.createMemoFromFile({
  file: fileBuffer,
  filename: 'document.pdf',
  metadata: {
    type: 'report',
    department: 'engineering'
  },
  tags: ['report', '2024'],
  source: 'google-drive',
  reference_id: 'external-file-123'
});

console.log(result); // { ok: true, memo_uuid: '550e8400-e29b-41d4-a716-446655440000' }

// Poll for processing status
const status = await skald.checkMemoStatus(result.memo_uuid);
console.log(status.status); // 'processing' | 'processed' | 'error'
Supported File Types:
  • PDF (.pdf)
  • Microsoft Word (.doc, .docx)
  • Microsoft PowerPoint (.pptx)
  • Maximum file size: 100MB
Required Fields:
  • file (Buffer | Blob) - The file content
  • filename (string) - The name of the file including extension
Optional Fields:
  • reference_id (string, max 255 chars) - Your external reference ID
  • metadata (object) - Custom JSON metadata
  • tags (array of strings) - Tags for categorization
  • source (string, max 255 chars) - Source system identifier
Note: File processing is asynchronous. Use checkMemoStatus() to monitor the processing status.

Check Memo Processing Status

Monitor the processing status of a memo, especially useful after uploading files:
// Check status by UUID
const status = await skald.checkMemoStatus('550e8400-e29b-41d4-a716-446655440000');
console.log(status.status); // 'processing' | 'processed' | 'error'

if (status.status === 'error') {
  console.error('Processing failed:', status.error_reason);
}

// Check by reference ID
const status2 = await skald.checkMemoStatus('external-id-123', 'reference_id');

// Poll until processing completes
while (true) {
  const status = await skald.checkMemoStatus(memoUuid);

  if (status.status === 'processed') {
    console.log('Processing complete!');
    break;
  } else if (status.status === 'error') {
    console.error('Processing failed:', status.error_reason);
    break;
  }

  // Wait 2 seconds before checking again
  await new Promise(resolve => setTimeout(resolve, 2000));
}
Status Values:
  • processing - The memo is currently being processed (parsed, summarized, chunked, indexed)
  • processed - Processing completed successfully, memo is ready to use
  • error - An error occurred during processing, check error_reason for details
Parameters:
  • memoId (string, required) - The memo UUID or client reference ID
  • idType (string, optional) - Either 'memo_uuid' or 'reference_id' (default: 'memo_uuid')

Get a Memo

Retrieve a memo by its UUID or your reference ID:
// Get by UUID
const memo = await skald.getMemo('550e8400-e29b-41d4-a716-446655440000');

// Get by reference ID
const memo = await skald.getMemo('external-id-123', 'reference_id');

console.log(memo.title);
console.log(memo.content);
console.log(memo.summary);
console.log(memo.tags);
console.log(memo.chunks);
The getMemo() method returns complete memo details including content, AI-generated summary, tags, and content chunks.

List Memos

List all memos with pagination:
// Get first page with default page size (20)
const memos = await skald.listMemos();

// Get specific page with custom page size
const memos = await skald.listMemos({ page: 2, page_size: 50 });

console.log(`Total memos: ${memos.count}`);
console.log(`Results: ${memos.results.length}`);
console.log(`Next page: ${memos.next}`);
Parameters:
  • page (number, optional) - Page number (default: 1)
  • page_size (number, optional) - Results per page (default: 20, max: 100)

Update a Memo

Update an existing memo by UUID or reference ID:
// Update by UUID
await skald.updateMemo('550e8400-e29b-41d4-a716-446655440000', {
  title: 'Updated Title',
  metadata: { status: 'reviewed' }
});

// Update by reference ID and trigger reprocessing
await skald.updateMemo('external-id-123', {
  content: 'New content that will be reprocessed'
}, 'reference_id');
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 (object)
  • client_reference_id (string)
  • source (string)
  • expiration_date (string)

Delete a Memo

Permanently delete a memo and all associated data:
// Delete by UUID
await skald.deleteMemo('550e8400-e29b-41d4-a716-446655440000');

// Delete by reference ID
await skald.deleteMemo('external-id-123', 'reference_id');
Warning: This operation permanently deletes the memo and all related data (content, summary, tags, chunks) and cannot be undone.

Search Memos

Search through your memos using semantic search:
// Basic semantic search
const results = await skald.search({
  query: 'quarterly goals',
  limit: 10
});

// Search with filters
const filtered = await skald.search({
  query: 'python tutorial',
  filters: [
    {
      field: 'source',
      operator: 'eq',
      value: 'notion',
      filter_type: 'native_field'
    },
    {
      field: 'level',
      operator: 'eq',
      value: 'beginner',
      filter_type: 'custom_metadata'
    }
  ]
});

console.log(`Found ${filtered.results.length} results`);
filtered.results.forEach(memo => {
  console.log(`- ${memo.title} (distance: ${memo.distance})`);
});

Search Parameters

  • query (string, required) - The search query
  • limit (integer, optional) - Maximum results to return (1-50, default 10)
  • filters (array, optional) - Array of filter objects to narrow results (see Filters section below)

Search Response

interface SearchResponse {
  results: Array<{
    memo_uuid: string;
    chunk_uuid: string;
    memo_title: string;
    memo_summary: string;
    content_snippet: string;
    distance: number | null;
  }>;
}
  • memo_uuid - Unique identifier for the memo
  • memo_uuid - Unique identifier for the chunk
  • memo_title - Memo title
  • memo_summary - Auto-generated summary for the memo
  • content_snippet - A snippet containing the beginning of the chunk content.
  • distance - A decimal from 0 to 2 determining how close the result was deemed to be to the query when using semantic search (chunk_vector_search). The closer to 0 the more related the content is to the query. null if using title_contains or title_startswith.

Chat with Your Knowledge Base

Ask questions about your memos using an AI agent. The agent retrieves relevant context and generates answers with inline citations.

Non-Streaming Chat

const result = await skald.chat({
  query: 'What were the main points discussed in the Q1 meeting?'
});

console.log(result.response);
// "The main points discussed in the Q1 meeting were:
// 1. Revenue targets
// 2. Hiring plans
// 3. Product roadmap"

console.log(result.ok); // true

Streaming Chat

For real-time responses, use streaming chat:
const stream = skald.streamedChat({
  query: 'What are our quarterly goals?'
});

for await (const event of stream) {
  if (event.type === 'token') {
    // Write each token as it arrives
    process.stdout.write(event.content);
  } else if (event.type === 'done') {
    console.log('\nDone!');
  }
}

Chat Parameters

  • query (string, required) - The question to ask
  • system_prompt (string, optional) - A system prompt to guide the AI’s behavior
  • filters (array, optional) - Array of filter objects to focus chat context on specific sources (see Filters section below)
  • project_id (string, optional) - Project UUID (required when using Token Authentication)

Chat Response

Non-streaming responses include:
  • ok (boolean) - Success status
  • response (string) - The AI’s answer
  • intermediate_steps (array) - 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

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(), generateDoc(), and their streaming variants. See Filters for complete documentation.

Filter Structure

interface Filter {
  field: string;                      // Field name to filter on
  operator: FilterOperator;           // Comparison operator
  value: string | string[];           // Value(s) to compare against
  filter_type: 'native_field' | 'custom_metadata';
}

Native Fields

Native fields are built-in memo properties:
  • title - Memo title
  • source - Source system (e.g., “notion”, “confluence”)
  • client_reference_id - Your external reference ID
  • tags - Memo tags (array)

Custom Metadata Fields

You can filter on any field from the metadata object you provided when creating the memo.

Filter Operators

  • eq - Equals (exact match)
  • neq - Not equals
  • contains - Contains substring (case-insensitive)
  • startswith - Starts with prefix (case-insensitive)
  • endswith - Ends with suffix (case-insensitive)
  • in - Value is in array (requires array value)
  • not_in - Value is not in array (requires array value)

Filter Examples

// Filter by source
{
  field: 'source',
  operator: 'eq',
  value: 'notion',
  filter_type: 'native_field'
}

// Filter by multiple tags
{
  field: 'tags',
  operator: 'in',
  value: ['security', 'compliance'],
  filter_type: 'native_field'
}

// Filter by title containing text
{
  field: 'title',
  operator: 'contains',
  value: 'meeting',
  filter_type: 'native_field'
}

// Filter by custom metadata field
{
  field: 'department',
  operator: 'eq',
  value: 'engineering',
  filter_type: 'custom_metadata'
}

// Exclude specific sources
{
  field: 'source',
  operator: 'not_in',
  value: ['draft', 'archive'],
  filter_type: 'native_field'
}

Combining Multiple Filters

When you provide multiple filters, they are combined with AND logic (all filters must match):
const results = await skald.search({
  query: 'security best practices',
  filters: [
    {
      field: 'source',
      operator: 'eq',
      value: 'security-docs',
      filter_type: 'native_field'
    },
    {
      field: 'tags',
      operator: 'in',
      value: ['approved', 'current'],
      filter_type: 'native_field'
    },
    {
      field: 'status',
      operator: 'neq',
      value: 'draft',
      filter_type: 'custom_metadata'
    }
  ]
});

Filters with Chat

Focus chat context on specific sources:
const result = await skald.chat({
  query: 'What are our security practices?',
  filters: [
    {
      field: 'tags',
      operator: 'in',
      value: ['security', 'compliance'],
      filter_type: 'native_field'
    }
  ]
});

Error Handling

try {
  const result = await skald.createMemo({
    title: 'My Memo',
    content: 'Content here'
  });
  console.log('Success:', result);
} catch (error) {
  console.error('Error:', error.message);
}

TypeScript Support

This package includes TypeScript type definitions out of the box.
import {
  Skald,
  MemoData,
  CreateMemoResponse,
  MemoFileData,
  CreateMemoFromFileResponse,
  MemoStatus,
  MemoStatusResponse,
  Memo,
  ListMemosResponse,
  UpdateMemoData,
  UpdateMemoResponse,
  IdType,
  Filter,
  FilterOperator,
  FilterType,
  SearchRequest,
  SearchResponse,
  ChatRequest,
  ChatResponse,
  ChatStreamEvent
} from '@skald-labs/skald-node';

const skald = new Skald('your-api-key-here');

// Create a memo with types
const memoData: MemoData = {
  title: 'My Memo',
  content: 'Content here',
  tags: ['tag1', 'tag2'],
  metadata: { department: 'engineering' }
};

const createResponse: CreateMemoResponse = await skald.createMemo(memoData);

// Get memo with types
const memo: Memo = await skald.getMemo('550e8400-e29b-41d4-a716-446655440000');
const memoByRef: Memo = await skald.getMemo('external-id-123', 'reference_id' as IdType);

// List memos with types
const memos: ListMemosResponse = await skald.listMemos({ page: 1, page_size: 20 });

// Update memo with types
const updateData: UpdateMemoData = {
  title: 'Updated Title',
  metadata: { status: 'reviewed' }
};
const updateResponse: UpdateMemoResponse = await skald.updateMemo(
  '550e8400-e29b-41d4-a716-446655440000',
  updateData
);

// Delete memo
await skald.deleteMemo('550e8400-e29b-41d4-a716-446655440000');

// Upload a file
import * as fs from 'fs';
const fileBuffer = fs.readFileSync('./document.pdf');
const fileData: MemoFileData = {
  file: fileBuffer,
  filename: 'document.pdf',
  metadata: { type: 'report' },
  tags: ['document'],
  source: 'local'
};
const uploadResponse: CreateMemoFromFileResponse = await skald.createMemoFromFile(fileData);

// Check processing status
const statusResponse: MemoStatusResponse = await skald.checkMemoStatus(uploadResponse.memo_uuid);
const status: MemoStatus = statusResponse.status; // 'processing' | 'processed' | 'error'

// Search with filters and types
const filters: Filter[] = [
  {
    field: 'source',
    operator: 'eq' as FilterOperator,
    value: 'notion',
    filter_type: 'native_field' as FilterType
  },
  {
    field: 'department',
    operator: 'eq' as FilterOperator,
    value: 'engineering',
    filter_type: 'custom_metadata' as FilterType
  }
];

const searchRequest: SearchRequest = {
  query: 'quarterly goals',
  limit: 10,
  filters
};

const searchResponse: SearchResponse = await skald.search(searchRequest);

// Chat with filters and types
const chatResponse: ChatResponse = await skald.chat({
  query: 'What are our quarterly goals?',
  filters
});

// Streaming chat with types
const stream = skald.streamedChat({
  query: 'What are our quarterly goals?',
  filters
});

for await (const event of stream) {
  const typedEvent: ChatStreamEvent = event;
  if (typedEvent.type === 'token') {
    process.stdout.write(typedEvent.content || '');
  }
}