npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@toothfairyai/sdk

v0.7.2

Published

JavaScript/TypeScript SDK for ToothFairyAI API integration

Readme

ToothFairyAI SDK for JavaScript/TypeScript

Official TypeScript SDK for the ToothFairyAI API - A comprehensive toolkit for building AI-powered applications with chat, document processing, agent management, and more.

Status: Production Ready | Version: 0.7.0

Test Results

The SDK includes comprehensive verification tests ensuring all managers and methods are properly implemented:

Test Suites: 1 passed, 1 total
Tests:       62 passed, 62 total

✓ SDK Structure (3 tests)
  - Client initialization with proper parameters
  - All 24 managers accessible
  - Expected methods on each manager

✓ Method Signatures (17 tests)
  - Agent, Chat, Document, Entity, Streaming, Billing managers

✓ Error Handling (5 tests)
  - Missing API key/workspace ID validation
  - ToothFairyError properties and inheritance

✓ SDK Completeness (27 tests)
  - Core files present
  - All 24 manager directories with manager files
  - Proper export structure

✓ Type Definitions (4 tests)
  - AgentMode, EntityType, DocumentType, StreamEventType

✓ Client Methods (6 tests)
  - testConnection, getHealth, getStreamingUrl, getWorkspaceId, getConfig, request, aiRequest

Installation

npm install @toothfairyai/sdk

Quick Start

import { ToothFairyClient } from '@toothfairyai/sdk';

const client = new ToothFairyClient({
  apiKey: 'your-api-key',
  workspaceId: 'your-workspace-id'
});

// Send a message to an agent
const response = await client.chat.sendToAgent('Hello, how can you help me?', 'agent-id');
console.log(response.agentResponse);

Table of Contents

SDK Overview

The ToothFairyAI SDK provides a TypeScript-first interface to all 95 API endpoints, organized into 18 specialized managers:

| Manager | Purpose | Endpoints | |---------|---------|-----------| | client.chat | Chat conversations & messages | 7 | | client.agents | AI agent management | 5 | | client.documents | Document upload & management | 6 | | client.entities | Topics, intents, NER entities | 5 | | client.folders | Content organization | 5 | | client.prompts | Prompt templates | 5 | | client.agentFunctions | Agent function calls | 5 | | client.authorisations | Auth configurations | 5 | | client.channels | Communication channels | 5 | | client.connections | Database/API connections | 3 | | client.members | Workspace members | 4 | | client.sites | Website crawling | 4 | | client.benchmarks | Performance testing | 5 | | client.hooks | Custom code execution | 5 | | client.scheduledJobs | Cron jobs & scheduling | 5 | | client.secrets | Secret management | 2 | | client.dictionary | Translation dictionary | 2 | | client.embeddings | Document embeddings | 1 | | client.chartingSettings | Charting configuration | 2 | | client.embeddingsSettings | Embeddings configuration | 2 | | client.billing | Usage & costs | 1 | | client.streams | Output streams | 2 | | client.requestLogs | Request logging | 2 | | client.streaming | Real-time streaming | - |

Client Configuration

Region-Specific Endpoints

ToothFairyAI operates in multiple regions. Configure your client to use the appropriate endpoints:

// US Region
const clientUS = new ToothFairyClient({
  apiKey: 'your-api-key',
  workspaceId: 'your-workspace-id',
  baseUrl: 'https://api.us.toothfairyai.com',
  aiUrl: 'https://ai.us.toothfairyai.com',
  aiStreamUrl: 'https://ai-stream.us.toothfairyai.com'
});

// EU Region
const clientEU = new ToothFairyClient({
  apiKey: 'your-api-key',
  workspaceId: 'your-workspace-id',
  baseUrl: 'https://api.eu.toothfairyai.com',
  aiUrl: 'https://ai.eu.toothfairyai.com',
  aiStreamUrl: 'https://ai-stream.eu.toothfairyai.com'
});

// Australia Region (default)
const clientAU = new ToothFairyClient({
  apiKey: 'your-api-key',
  workspaceId: 'your-workspace-id',
  baseUrl: 'https://api.toothfairyai.com',
  aiUrl: 'https://ai.toothfairyai.com',
  aiStreamUrl: 'https://ais.toothfairyai.com'
});

Configuration Options

const client = new ToothFairyClient({
  apiKey: 'your-api-key',           // Required: Your API key
  workspaceId: 'your-workspace-id', // Required: Your workspace ID
  baseUrl: 'https://api.toothfairyai.com',        // Optional: API base URL (region-specific)
  aiUrl: 'https://ai.toothfairyai.com',           // Optional: AI service URL (region-specific)
  aiStreamUrl: 'https://ais.toothfairyai.com', // Optional: Streaming URL (region-specific)
  timeout: 120000                   // Optional: Request timeout in ms (default: 120000)
});

Getting Your Credentials

  1. API Key: Log in to ToothFairyAI Admin → API Integration → Generate API Key
  2. Workspace ID: Found in your workspace settings or API dashboard

Core Features

1. Chat & Messaging

Create conversations, send messages, and manage chat history with AI agents.

2. Agent Management

Create, configure, and manage AI agents with different modes (retriever, chatter, planner, etc.).

3. Document Processing

Upload, search, and manage documents for knowledge base integration.

4. Entity Management

Organize content with topics, intents, and named entity recognition.

5. Streaming

Real-time streaming responses with EventEmitter-based pattern.

Complete API Reference

Chat & Messaging

Create a Chat Session

const chat = await client.chat.create({
  name: 'Customer Support',
  primaryRole: 'agent-id',
  customerId: 'customer-123',
  customerInfo: { name: 'John Doe', email: '[email protected]' },
  externalParticipantId: '[email protected]',
  channelSettings: {
    sms: { isEnabled: true, recipient: '+1234567890', providerID: 'twilio' },
    email: { isEnabled: true, recipient: '[email protected]' }
  }
});
console.log(`Chat ID: ${chat.id}`);

Update a Chat

const chat = await client.chat.update({
  id: 'chat-id',
  name: 'Updated Chat Name',
  customerId: 'new-customer-id'
});

Get Chat Details

const chat = await client.chat.get('chat-id');
console.log(`Chat: ${chat.name}`);
console.log(`Primary Role: ${chat.primaryRole}`);

List Chats

const chats = await client.chat.list(10, 0);
for (const chat of chats.items) {
  console.log(`${chat.name} (ID: ${chat.id})`);
}

Delete a Chat

await client.chat.delete('chat-id');

Create a Message

const message = await client.chat.createMessage({
  chatID: 'chat-id',
  text: 'Hello, I need help with my account',
  role: 'user',
  userID: 'user-123',
  images: ['https://example.com/image.jpg'],
  files: ['https://example.com/document.pdf']
});

Update a Message

const message = await client.chat.updateMessage('message-id', {
  text: 'Updated message text',
  role: 'user'
});

Get a Message

const message = await client.chat.getMessage('message-id');
console.log(`Message: ${message.text}`);

List Messages in a Chat

const messages = await client.chat.listMessages('chat-id', 20, 0);
for (const msg of messages.items) {
  console.log(`${msg.role}: ${msg.text}`);
}

Send Message to Agent (Non-Streaming)

const response = await client.chat.sendToAgent(
  'Hello, how can you help me?',
  'agent-id',
  {
    chatId: 'existing-chat-id',  // Optional - creates new chat if not provided
    phoneNumber: '+1234567890',  // Optional
    customerId: 'customer-123',   // Optional
    providerId: 'provider-id',    // Optional
    customerInfo: { name: 'John' },  // Optional
    attachments: {
      images: ['https://example.com/image.jpg'],
      files: ['https://example.com/document.pdf'],
      audios: ['https://example.com/audio.mp3'],
      videos: ['https://example.com/video.mp4']
    }
  }
);
console.log(`Response: ${response.agentResponse}`);
console.log(`Chat ID: ${response.chatId}`);
console.log(`Message ID: ${response.messageId}`);

Agents

Create an Agent

const agent = await client.agents.create({
  label: 'Customer Support Agent',
  mode: 'retriever',  // retriever, coder, chatter, planner, computer, voice
  interpolationString: 'You are a helpful customer support assistant...',
  goals: 'Help customers resolve issues and answer questions',
  temperature: 0.7,
  maxTokens: 2000,
  maxHistory: 10,
  topK: 10,
  docTopK: 5,
  description: 'Specialized agent for customer support',
  hasMemory: true,
  showCitations: true,
  allowedTopics: ['topic-1', 'topic-2'],
  staticDocs: ['doc-1', 'doc-2']
});
console.log(`Agent ID: ${agent.id}`);

Update an Agent

const agent = await client.agents.update('agent-id', {
  label: 'Updated Agent Name',
  temperature: 0.8,
  maxTokens: 3000
});

Get Agent Details

const agent = await client.agents.get('agent-id');
console.log(`Agent: ${agent.label}`);
console.log(`Mode: ${agent.mode}`);
console.log(`Goals: ${agent.goals}`);

List Agents

const agents = await client.agents.list(10, 0);
for (const agent of agents.items) {
  console.log(`${agent.label} (${agent.mode})`);
}

Delete an Agent

await client.agents.delete('agent-id');

Documents

Upload a Document

const result = await client.documents.upload('/path/to/document.pdf', 'folder-id', {
  onProgress: (percent, loaded, total) => {
    console.log(`Progress: ${percent}%`);
  }
});
console.log(`Uploaded: ${result.filename}`);
console.log(`Size: ${result.sizeInMB} MB`);

Upload from Base64

const base64Data = 'base64-encoded-file-data';
const result = await client.documents.uploadFromBase64(
  base64Data,
  'document.pdf',
  'application/pdf',
  'folder-id'
);

Create Document from URL

const doc = await client.documents.createFromPath(
  'https://example.com/document.pdf',
  'user-123',
  {
    title: 'My Document',
    folderId: 'folder-id',
    topics: ['topic-1', 'topic-2'],
    status: 'published'
  }
);

Create Document Record

const doc = await client.documents.create({
  userid: 'user-123',
  title: 'My Document',
  type: 'readComprehensionFile',  // readComprehensionUrl, readComprehensionPdf
  topics: ['topic-1'],
  folderid: 'folder-id',
  external_path: 'https://example.com/doc.pdf',
  source: 'external',
  status: 'published',
  scope: 'training'
});

Get Document

const doc = await client.documents.get('doc-id');
console.log(`Title: ${doc.title}`);
console.log(`Type: ${doc.type}`);
console.log(`Status: ${doc.status}`);

Update Document

const doc = await client.documents.update('doc-id', {
  userid: 'user-123',
  title: 'Updated Title',
  topics: ['new-topic'],
  folderid: 'new-folder-id',
  status: 'published'
});

List Documents

const docs = await client.documents.list(20, 0, 'folder-id', 'published');
for (const doc of docs.items) {
  console.log(`${doc.title} (ID: ${doc.id})`);
}

Delete Document

await client.documents.delete('doc-id');

Search Documents

const results = await client.documents.search('product documentation', {
  topK: 10,
  metadata: { category: 'procedures' }
});
for (const result of results) {
  console.log(`Score: ${result.score}`);
  console.log(`Content: ${result.content}`);
}

Download Document

const result = await client.documents.download(
  'document.pdf',
  '/downloads/document.pdf',
  'documents',
  {
    onProgress: (percent, loaded, total) => {
      console.log(`Download: ${percent}%`);
    }
  }
);
console.log(`Downloaded: ${result.filename}`);

Entities

Create an Entity

const entity = await client.entities.create('user-123', 'Product Launch', 'topic', {
  description: 'New product launch announcements and updates',
  emoji: '🚀',
  parentEntity: 'parent-topic-id',
  backgroundColor: '#FF5733'
});

Get Entity

const entity = await client.entities.get('entity-id');
console.log(`Label: ${entity.label}`);
console.log(`Type: ${entity.type}`);

Update Entity

const entity = await client.entities.update('entity-id', {
  label: 'Updated Label',
  description: 'New description',
  emoji: '🔧'
});

Delete Entity

await client.entities.delete('entity-id');

List Entities

const entities = await client.entities.list(20, 'topic');
for (const entity of entities.items) {
  console.log(`${entity.label} (${entity.type})`);
}

Get Entities by Type

const topics = await client.entities.getByType('topic');
const intents = await client.entities.getByType('intent');
const nerEntities = await client.entities.getByType('ner');

Search Entities

const results = await client.entities.search('product', 'topic');
for (const entity of results) {
  console.log(`${entity.label}`);
}

Folders

Create a Folder

const folder = await client.folders.create('user-123', 'Procedures', {
  description: 'Medical procedures documentation',
  emoji: '📁',
  status: 'active',
  parent: 'parent-folder-id'  // Optional - for subfolders
});

Get Folder

const folder = await client.folders.get('folder-id');
console.log(`Name: ${folder.name}`);
console.log(`Parent: ${folder.parent}`);

Update Folder

const folder = await client.folders.update('folder-id', {
  name: 'Updated Name',
  description: 'New description',
  status: 'active'
});

Delete Folder

await client.folders.delete('folder-id');

List Folders

const folders = await client.folders.list('active', 20, 0);
for (const folder of folders.items) {
  console.log(`${folder.name}`);
}

Get Root Folders

const rootFolders = await client.folders.getRootFolders();
for (const folder of rootFolders) {
  console.log(`Root: ${folder.name}`);
}

Get Subfolders

const subfolders = await client.folders.getSubfolders('folder-id');
for (const folder of subfolders) {
  console.log(`Subfolder: ${folder.name}`);
}

Get Folder Tree

const tree = await client.folders.getTree();

function printTree(nodes: any[], level = 0) {
  for (const node of nodes) {
    console.log('  '.repeat(level) + `📁 ${node.name}`);
    printTree(node.children, level + 1);
  }
}

printTree(tree);

Search Folders

const results = await client.folders.search('procedures');
for (const folder of results) {
  console.log(`${folder.name}`);
}

Prompts

Create a Prompt

const prompt = await client.prompts.create('user-123', {
  type: 'greeting',
  label: 'Greeting',
  promptLength: 50,
  interpolationString: 'Hello {{name}}, how can I help you today?',
  scope: 'global',
  style: 'friendly',
  domain: 'customer-support',
  promptPlaceholder: 'Enter greeting message',
  availableToAgents: ['agent-1', 'agent-2']
});

Get Prompt

const prompt = await client.prompts.get('prompt-id');
console.log(`Label: ${prompt.label}`);
console.log(`Template: ${prompt.interpolationString}`);

Update Prompt

const prompt = await client.prompts.update('prompt-id', {
  label: 'Updated Label',
  interpolationString: 'New template string',
  availableToAgents: ['agent-3']
});

Delete Prompt

await client.prompts.delete('prompt-id');

List Prompts

const prompts = await client.prompts.list({
  type: 'greeting',
  limit: 20,
  offset: 0
});
for (const prompt of prompts.items) {
  console.log(`${prompt.label}`);
}

Get Prompts by Type

const greetings = await client.prompts.getByType('greeting');

Get Prompts by Agent

const agentPrompts = await client.prompts.getByAgent('agent-id');

Get Prompts by Scope

const globalPrompts = await client.prompts.getByScope('global');

Search Prompts

const results = await client.prompts.search('greeting', 'greeting');

Clone Prompt

const cloned = await client.prompts.clone('prompt-id', 'user-123', {
  label: 'Cloned Greeting',
  interpolationString: 'Modified template'
});

Agent Functions

Create an Agent Function

// Create a GET function
const getFunc = await client.agentFunctions.create('Weather API', {
  description: 'Get current weather data for a location',
  url: 'https://api.weather.com/current',
  requestType: 'GET',
  authorisationType: 'bearer',
  authorisationKey: 'your-token',
  parameters: [
    { name: 'location', type: 'string', required: true }
  ]
});

// Create a POST function with headers
const postFunc = await client.agentFunctions.create('Create Order', {
  description: 'Create a new order in the system',
  url: 'https://api.example.com/orders',
  requestType: 'POST',
  authorisationType: 'apikey',
  headers: [{ key: 'Content-Type', value: 'application/json' }],
  staticArgs: [{ key: 'source', value: 'chatbot' }]
});

Update Agent Function

const func = await client.agentFunctions.update('function-id', {
  name: 'Updated Function Name',
  url: 'https://new-url.com'
});

Delete Agent Function

await client.agentFunctions.delete('function-id');

Get Agent Function

const func = await client.agentFunctions.get('function-id');
console.log(`Name: ${func.name}`);
console.log(`URL: ${func.url}`);

List Agent Functions

const functions = await client.agentFunctions.list(20, 0);
for (const func of functions.items) {
  console.log(`${func.name}`);
}

Search Agent Functions

const results = await client.agentFunctions.search('weather');
for (const func of results) {
  console.log(`${func.name}`);
}

Authorisations

Create an Authorisation

// API Key authorisation
const apiKeyAuth = await client.authorisations.create('External API Key', 'apikey', {
  description: 'API key for external service',
  tokenSecret: 'your-api-key-value'
});

// Bearer token authorisation
const bearerAuth = await client.authorisations.create('Service Token', 'bearer', {
  tokenSecret: 'your-bearer-token'
});

// OAuth authorisation
const oauthAuth = await client.authorisations.create('GitHub OAuth', 'oauth', {
  description: 'GitHub OAuth for API access',
  scope: 'repo,user',
  grantType: 'authorization_code',
  clientId: 'github-client-id',
  clientSecret: 'encrypted-secret',
  authorizationBaseUrl: 'https://github.com/login/oauth/authorize',
  tokenBaseUrl: 'https://github.com/login/oauth/access_token'
});

Update Authorisation

const auth = await client.authorisations.update('auth-id', {
  name: 'Updated Auth Name',
  description: 'New description'
});

Delete Authorisation

await client.authorisations.delete('auth-id');

Get Authorisation

const auth = await client.authorisations.get('auth-id');
console.log(`Name: ${auth.name}`);
console.log(`Type: ${auth.type}`);

List Authorisations

const auths = await client.authorisations.list(20, 0);
for (const auth of auths.items) {
  console.log(`${auth.name} (${auth.type})`);
}

Get Authorisations by Type

const apiKeys = await client.authorisations.getByType('apikey');
const oauthAuths = await client.authorisations.getByType('oauth');

Search Authorisations

const results = await client.authorisations.search('github');
for (const auth of results) {
  console.log(`${auth.name}`);
}

Channels

Create a Channel

const channel = await client.channels.create('SMS Channel', 'sms', 'twilio', {
  senderId: '+1234567890',
  description: 'SMS notifications via Twilio',
  isActive: true
});

Update Channel

const channel = await client.channels.update('channel-id', {
  name: 'Updated Channel',
  isActive: false
});

Delete Channel

await client.channels.delete('channel-id');

Get Channel

const channel = await client.channels.get('channel-id');
console.log(`Name: ${channel.name}`);
console.log(`Provider: ${channel.provider}`);

List Channels

const channels = await client.channels.list(20, 0);
for (const channel of channels.items) {
  console.log(`${channel.name} (${channel.provider})`);
}

Connections

Get Connection

const connection = await client.connections.get('connection-id');
console.log(`Name: ${connection.name}`);
console.log(`Type: ${connection.type}`);
console.log(`Host: ${connection.host}`);

List Connections

const connections = await client.connections.list(20, 0);
for (const conn of connections.items) {
  console.log(`${conn.name} (${conn.type})`);
}

Get Connections by Type

const postgresConns = await client.connections.getByType('postgres');
const mysqlConns = await client.connections.getByType('mysql');

Search Connections

const results = await client.connections.search('database');
for (const conn of results) {
  console.log(`${conn.name}`);
}

Delete Connection

await client.connections.delete('connection-id');

Members

Get Member

const member = await client.members.get('member-id');
console.log(`User ID: ${member.userID}`);
console.log(`Role: ${member.role}`);
console.log(`Status: ${member.status}`);

List Members

const members = await client.members.list(20);
for (const member of members.items) {
  console.log(`${member.userID} (${member.role})`);
}

Update Member

const member = await client.members.update({
  id: 'member-id',
  role: 'admin',
  status: 'active'
});

Delete Member

await client.members.delete('member-id');

Sites

Get Site

const site = await client.sites.get('site-id');
console.log(`Name: ${site.name}`);
console.log(`URL: ${site.url}`);
console.log(`Status: ${site.status}`);

List Sites

const sites = await client.sites.list(20, 0);
for (const site of sites.items) {
  console.log(`${site.name} - ${site.url}`);
}

Update Site

const site = await client.sites.update('site-id', {
  name: 'Updated Site',
  status: 'active',
  allowedPaths: ['/docs', '/blog']
});

Delete Site

await client.sites.delete('site-id');

Benchmarks

Create a Benchmark

const benchmark = await client.benchmarks.create('Customer Support Test', {
  description: 'Test agent performance on customer support queries',
  questions: [
    {
      question: 'How do I reset my password?',
      answer: 'Go to Settings > Security > Reset Password',
      reasoning: 'Standard password reset flow',
      context: 'Account management'
    }
  ],
  files: ['doc-1', 'doc-2']
});

Update Benchmark

const benchmark = await client.benchmarks.update('benchmark-id', {
  name: 'Updated Benchmark',
  description: 'New description'
});

Delete Benchmark

await client.benchmarks.delete('benchmark-id');

Get Benchmark

const benchmark = await client.benchmarks.get('benchmark-id');
console.log(`Name: ${benchmark.name}`);
console.log(`Questions: ${benchmark.questions?.length}`);

List Benchmarks

const benchmarks = await client.benchmarks.list(20, 0);
for (const benchmark of benchmarks.items) {
  console.log(`${benchmark.name}`);
}

Hooks

Create a Hook

const hook = await client.hooks.create('Data Processing Hook', 'process_data', {
  customExecutionCode: 'def process_data(data): return data.upper()',
  customExecutionInstructions: 'Process incoming data',
  availableLibraries: 'pandas,numpy',
  allowExternalApi: true,
  hardcodedScript: false
});

Update Hook

const hook = await client.hooks.update('hook-id', {
  name: 'Updated Hook',
  customExecutionCode: 'def process_data(data): return data.lower()'
});

Delete Hook

await client.hooks.delete('hook-id');

Get Hook

const hook = await client.hooks.get('hook-id');
console.log(`Name: ${hook.name}`);
console.log(`Function: ${hook.functionName}`);

List Hooks

const hooks = await client.hooks.list(20, 0);
for (const hook of hooks.items) {
  console.log(`${hook.name}`);
}

Scheduled Jobs

Create a Scheduled Job

const job = await client.scheduledJobs.create('Daily Report', {
  description: 'Generate daily report at 9 AM',
  agentId: 'agent-id',
  customPromptId: 'prompt-id',
  forcedPrompt: 'Generate a daily summary report',
  schedule: {
    frequency: 'DAILY',
    hour: 9,
    minute: 0
  },
  timezone: 'UTC',
  isActive: true,
  status: 'ACTIVE'
});

Update Scheduled Job

const job = await client.scheduledJobs.update('job-id', {
  name: 'Updated Job',
  isActive: false,
  schedule: {
    frequency: 'WEEKLY',
    dayOfWeek: 1,
    hour: 9
  }
});

Delete Scheduled Job

await client.scheduledJobs.delete('job-id');

Get Scheduled Job

const job = await client.scheduledJobs.get('job-id');
console.log(`Name: ${job.name}`);
console.log(`Status: ${job.status}`);
console.log(`Schedule: ${JSON.stringify(job.schedule)}`);

List Scheduled Jobs

const jobs = await client.scheduledJobs.list(20, 0);
for (const job of jobs.items) {
  console.log(`${job.name} (${job.status})`);
}

Secrets

Secrets are linked to authorisations. You must first create an authorisation, then create a secret for it.

Create a Secret

// First, create an authorisation
const auth = await client.authorisations.create('External Service Auth', 'apikey', {
  description: 'Authorisation for external service'
});

// Then create a secret linked to that authorisation
const secret = await client.secrets.create(auth.id, 'your-secret-api-key-12345');
console.log(`Secret created: ${secret.name}`);

Get a Secret

const secret = await client.secrets.get('secret-id');
console.log(`Secret: ${secret.name}`);

List Secrets

const secrets = await client.secrets.list(20, 0);
for (const secret of secrets.items) {
  console.log(`${secret.name}`);
}

Delete Secret

await client.secrets.delete('secret-id');

Dictionary

Get Dictionary Entry

const entry = await client.dictionary.get('entry-id');
console.log(`Source: ${entry.sourceText}`);
console.log(`Target: ${entry.targetText}`);
console.log(`Languages: ${entry.sourceLanguage} -> ${entry.targetLanguage}`);

List Dictionary Entries

const entries = await client.dictionary.list(20);
for (const entry of entries.items) {
  console.log(`${entry.sourceLanguage} -> ${entry.targetLanguage}`);
}

Embeddings

Get Document Embeddings

const embeddings = await client.embeddings.get();
for (const emb of embeddings) {
  console.log(`Document: ${emb.title}`);
  console.log(`Chunk: ${emb.chunk_id}`);
  console.log(`Type: ${emb.type}`);
}

Settings

Charting Settings

// Get charting settings
const settings = await client.chartingSettings.get();
console.log(`Primary Color: ${settings.primaryColor}`);
console.log(`Secondary Color: ${settings.secondaryColor}`);

// Update charting settings
const updated = await client.chartingSettings.update({
  primaryColor: '#FF5733',
  secondaryColor: '#33FF57',
  lineColor: '#3357FF'
});

Embeddings Settings

// Get embeddings settings
const settings = await client.embeddingsSettings.get();
console.log(`Max Chunk Words: ${settings.maxChunkWords}`);
console.log(`Chunking Strategy: ${settings.chunkingStrategy}`);

// Update embeddings settings
const updated = await client.embeddingsSettings.update({
  maxChunkWords: 1000,
  chunkingStrategy: 'summary',
  imageExtractionStrategy: 'safe'
});

Billing

Get Monthly Costs

// Using get() method (like Python SDK)
const costs = await client.billing.get();
console.log(`API Usage: ${JSON.stringify(costs.apiUsage)}`);
console.log(`Training Usage: ${JSON.stringify(costs.trainingUsage)}`);
console.log(`Total Cost: $${costs.apiUsage?.totalCostUSD || 0}`);

// Or using getMonthCosts() alias
const costs2 = await client.billing.getMonthCosts();

Streams

Get Output Stream

const stream = await client.streams.get('stream-id');
console.log(`Content: ${stream.content}`);
console.log(`Type: ${stream.type}`);
console.log(`Status: ${stream.status}`);

List Output Streams

const streams = await client.streams.list(20);
for (const stream of streams.items) {
  console.log(`${stream.type} - ${stream.status}`);
}

Request Logs

Get Request Log

const log = await client.requestLogs.get('log-id');
console.log(`Type: ${log.type}`);
console.log(`Status: ${log.status}`);
console.log(`Tokens: ${log.tokens}`);
console.log(`Words: ${log.words}`);

List Request Logs

const logs = await client.requestLogs.list(20);
for (const log of logs.items) {
  console.log(`${log.type} - ${log.status} (${log.tokens} tokens)`);
}

Streaming Responses

Simple Streaming

const session = await client.streaming.sendToAgent('Tell me about your AI capabilities', 'agent-id');

session.on('data', (data) => {
  console.log(data.text, end='', flush=true);
});

session.on('complete', () => {
  console.log('\nDone!');
  console.log(`Chat ID: ${session.chatId}`);
  console.log(`Message ID: ${session.messageId}`);
});

Streaming with Event Types

const session = await client.streaming.sendToAgent('Hello', 'agent-id');

session.on('status', (data) => {
  console.log('Status:', data.status);
});

session.on('progress', (data) => {
  console.log('Progress:', data.processing_status);
});

session.on('data', (data) => {
  console.log('Response:', data.text);
});

session.on('complete', (data) => {
  console.log('Complete:', data);
});

session.on('error', (error) => {
  console.error('Error:', error);
});

Streaming with Progress Tracking

// Enable ALL SSE events tracking
const session = await client.streaming.sendToAgent('Explain AI', 'agent-id', {
  showProgress: true
});

// Standard events
session.on('data', (data) => {
  console.log('Agent text:', data.text);
});

// NEW: Raw SSE events (when showProgress=true)
session.on('sse_event', (data) => {
  console.log('Raw SSE event:', data);
  // This gives you access to ALL events from the streaming endpoint
});

Continue a Conversation

// First message creates a new chat
const session1 = await client.streaming.sendToAgent('Hello', 'agent-id');

session1.on('data', (data) => {
  console.log(data.text, end='');
});

session1.on('complete', () => {
  // Continue in the same chat
  const session2 = await client.streaming.sendToAgent(
    'Tell me more',
    'agent-id',
    { chatId: session1.chatId }  // Use the chat ID from first session
  );

  session2.on('data', (data) => {
    console.log(data.text, end='');
  });
});

Streaming with Attachments

const session = await client.streaming.sendToAgent(
  'What\'s in this image?',
  'agent-id',
  {
    attachments: {
      images: ['https://example.com/image.jpg']
    }
  }
);

session.on('data', (data) => {
  console.log(data.text, end='');
});

Error Handling

import { ToothFairyError } from '@toothfairyai/sdk';

const client = new ToothFairyClient({
  apiKey: '...',
  workspaceId: '...'
});

try {
  const response = await client.chat.sendToAgent('Hello', 'agent-id');
} catch (error) {
  if (error instanceof ToothFairyError) {
    console.error('Error:', error.message);
    console.error('Code:', error.code);
    console.error('Status:', error.statusCode);
    console.error('Response:', error.response);
  } else {
    console.error('Unknown error:', error);
  }
}

Specific Error Types

import {
  ToothFairyError
} from '@toothfairyai/sdk';

try {
  const client = new ToothFairyClient({
    apiKey: '',
    workspaceId: ''
  });
} catch (error) {
  if (error instanceof ToothFairyError) {
    console.error('Error:', error.message);
    console.error('Code:', error.code);
  }
}

try {
  const result = await client.documents.upload('large_file.pdf');
} catch (error) {
  if (error instanceof ToothFairyError) {
    console.error('Error:', error.message);
    console.error('Status:', error.statusCode);
  }
}

Connection Testing

// Test connection
const isConnected = await client.testConnection();
console.log(`Connected: ${isConnected}`);

// Get health status
const health = await client.getHealth();
console.log(`Status: ${health.status}`);

Best Practices

1. Use Environment Variables for Credentials

import { ToothFairyClient } from '@toothfairyai/sdk';

const client = new ToothFairyClient({
  apiKey: process.env.TOOTHPAIRY_API_KEY!,
  workspaceId: process.env.TOOTHPAIRY_WORKSPACE_ID!
});

2. Handle Pagination

async function getAllChats() {
  const allChats: any[] = [];
  let offset = 0;
  const limit = 100;

  while (true) {
    const response = await client.chat.list(limit, offset);
    allChats.push(...response.items);
    
    if (response.items.length < limit) {
      break;
    }
    
    offset += limit;
  }
  
  return allChats;
}

3. Use Streaming for Long Responses

// For long responses, use streaming to show progress
const session = await client.streaming.sendToAgent(
  'Generate a comprehensive report',
  'agent-id'
);

session.on('data', (data) => {
  console.log(data.text, end='', flush=true);
});

4. Implement Retry Logic

async function sendWithRetry(
  message: string,
  agentId: string,
  maxRetries = 3
): Promise<any> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.chat.sendToAgent(message, agentId);
    } catch (error) {
      if (attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
}

5. Use Chat IDs for Conversations

// Create a chat once
const chat = await client.chat.create({
  name: 'Support Session',
  primaryRole: 'agent-id'
});

// Use the same chat ID for all messages in the conversation
const response1 = await client.chat.sendToAgent('Hello', 'agent-id', {
  chatId: chat.id
});
const response2 = await client.chat.sendToAgent('Tell me more', 'agent-id', {
  chatId: chat.id
});

6. Organize Documents with Folders

// Create folder structure
const procedures = await client.folders.create('user-123', 'Procedures', {
  description: 'Medical procedures documentation'
});

const policies = await client.folders.create('user-123', 'Policies', {
  description: 'Company policies'
});

// Upload documents to appropriate folders
await client.documents.upload('procedure1.pdf', procedures.id);
await client.documents.upload('policy1.pdf', policies.id);

7. Use Topics for Content Organization

// Create topics
const productTopic = await client.entities.create(
  'user-123',
  'Product Updates',
  'topic',
  {
    description: 'Latest product updates and announcements',
    emoji: '🚀'
  }
);

// Associate documents with topics
const doc = await client.documents.createFromPath(
  'product_guide.pdf',
  'user-123',
  {
    title: 'Product Guide',
    topics: [productTopic.id]
  }
);

8. Monitor Usage with Billing

// Check monthly usage
const costs = await client.billing.getMonthCosts();
const apiCost = costs.apiUsage?.totalCostUSD || 0;
const trainingCost = costs.trainingUsage?.totalCharge || 0;

console.log(`API Cost: $${apiCost.toFixed(2)}`);
console.log(`Training Cost: $${trainingCost.toFixed(2)}`);
console.log(`Total: $${(apiCost + trainingCost).toFixed(2)}`);

9. Use Scheduled Jobs for Automation

// Create a daily report job
const job = await client.scheduledJobs.create({
  id: 'daily-report',
  name: 'Daily Summary Report',
  agentID: 'report-agent-id',
  schedule: {
    frequency: 'DAILY',
    hour: 9,
    minute: 0
  },
  timezone: 'UTC',
  isActive: true,
  status: 'ACTIVE'
});

10. Secure Secrets Management

// Store sensitive data as secrets
await client.secrets.create({
  id: 'external-api-key',
  name: 'External Service API Key',
  description: 'API key for external service integration'
});

// Reference secrets in agent functions
await client.agentFunctions.create({
  id: 'api-function',
  name: 'External API Call',
  url: 'https://api.example.com/data',
  authorisationType: 'apikey',
  // Use secret reference instead of hardcoding
});

Integration Examples

Express.js Server

import express from 'express';
import { ToothFairyClient } from '@toothfairyai/sdk';

const app = express();
const client = new ToothFairyClient({
  apiKey: process.env.TOOTHPAIRY_API_KEY!,
  workspaceId: process.env.TOOTHPAIRY_WORKSPACE_ID!
});

app.post('/chat', async (req, res) => {
  const { message, agentId } = req.body;
  
  try {
    const response = await client.chat.sendToAgent(message, agentId);
    res.json(response);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000);

Next.js API Route

// pages/api/chat.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { ToothFairyClient } from '@toothfairyai/sdk';

const client = new ToothFairyClient({
  apiKey: process.env.TOOTHPAIRY_API_KEY!,
  workspaceId: process.env.TOOTHPAIRY_WORKSPACE_ID!
});

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { message, agentId } = req.body;
  
  try {
    const response = await client.chat.sendToAgent(message, agentId);
    res.status(200).json(response);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
}

React Component with Streaming

import React, { useState, useEffect } from 'react';
import { ToothFairyClient } from '@toothfairyai/sdk';

const client = new ToothFairyClient({
  apiKey: process.env.REACT_APP_TOOTHFAIRY_API_KEY!,
  workspaceId: process.env.REACT_APP_TOOTHFAIRY_WORKSPACE_ID!
});

function ChatComponent() {
  const [messages, setMessages] = useState<string[]>([]);
  const [currentResponse, setCurrentResponse] = useState('');
  
  const sendMessage = async (message: string) => {
    const session = await client.streaming.sendToAgent(message, 'agent-id');
    
    session.on('data', (data) => {
      setCurrentResponse(prev => prev + data.text);
    });
    
    session.on('complete', () => {
      setMessages(prev => [...prev, currentResponse]);
      setCurrentResponse('');
    });
  };

  return (
    <div>
      {messages.map((msg, i) => <div key={i}>{msg}</div>)}
      {currentResponse && <div>{currentResponse}</div>}
    </div>
  );
}

Development

Building

npm run build

Testing

# Run unit tests
npm test

# Run integration tests (requires API credentials)
TF_API_KEY=your-api-key \
TF_WORKSPACE_ID=your-workspace-id \
TF_AGENT_ID=your-agent-id \
npx ts-node tests/integration/test-streaming.ts

Linting

npm run lint
npm run lint:fix

Requirements

  • Node.js >= 14.0.0
  • TypeScript >= 4.9.5 (for development)

License

MIT

Support

For issues and questions: