Voisnap Docs
SDKs

JavaScript / TypeScript SDK

Full reference for the official Voisnap JS/TS SDK — installation, TypeScript types, async iterators, and error handling.

JavaScript / TypeScript SDK

The official Voisnap JavaScript/TypeScript SDK is fully typed and works in Node.js 18+, Deno, Bun, and edge runtimes (Cloudflare Workers, Vercel Edge).

Installation

npm install @voisnap/sdk
# or
yarn add @voisnap/sdk
# or
pnpm add @voisnap/sdk

Initialization

import { VoisnapClient } from '@voisnap/sdk';
 
// API key
const client = new VoisnapClient({ apiKey: 'vsnp_live_...' });
 
// JWT
const client = new VoisnapClient({ accessToken: 'eyJhbGci...' });
 
// Sandbox
const client = new VoisnapClient({
  apiKey: 'vsnp_sb_...',
  baseUrl: 'https://sandbox.api.voisnap.ai',
});
 
// From environment variable VOISNAP_API_KEY
const client = new VoisnapClient();

TypeScript types

All request and response types are exported:

import type {
  Agent,
  CreateAgentRequest,
  UpdateAgentRequest,
  Conversation,
  ConversationTranscript,
  OutboundCall,
  CreateOutboundCallRequest,
  TelephonyNumber,
  KnowledgeBaseDocument,
  WebhookConfig,
  AgentStats,
  PaginatedResponse,
} from '@voisnap/sdk';

Agents

Create an agent

import type { CreateAgentRequest } from '@voisnap/sdk';
 
const request: CreateAgentRequest = {
  name: 'Aria – Support Agent',
  persona: { name: 'Aria', personality: 'friendly, professional' },
  voice: {
    provider: 'elevenlabs',
    voiceId: 'EXAVITQu4vr4xnSDxMaL',
    stability: 0.5,
    similarityBoost: 0.75,
  },
  llm: { provider: 'openai', model: 'gpt-4o', temperature: 0.7, maxTokens: 300 },
  transcription: { provider: 'deepgram', model: 'nova-2', language: 'en-US' },
  systemPromptTemplate: 'You are Aria, a friendly support agent...',
  firstMessage: 'Hello! How can I help you today?',
  firstMessageMode: 'assistant_speaks_first',
  endCallPhrases: ['goodbye', 'thank you, bye'],
  maxDurationSeconds: 600,
  recording: { enabled: true, format: 'mp3', storageRetentionDays: 90 },
};
 
const agent = await client.agents.create(request);
console.log(`Created: ${agent.id}`);

List agents (async iterator)

// Iterate all pages
for await (const agent of client.agents.list({ status: 'active' })) {
  console.log(`${agent.name} — ${agent.status}`);
}
 
// Collect to array
const agents = await client.agents.list({ status: 'active' }).toArray();
 
// First page only
const page = await client.agents.listPage({ page: 1, pageSize: 50 });
console.log(`${page.total} agents total`);
page.data.forEach(a => console.log(a.name));

Get, update, delete

// Get
const agent = await client.agents.get('agt_01HXK8Z3MNPQRS');
 
// Update
const updated = await client.agents.update('agt_01HXK8Z3MNPQRS', {
  systemPromptTemplate: 'Updated prompt...',
  llm: { temperature: 0.5 },
});
 
// Delete
await client.agents.delete('agt_01HXK8Z3MNPQRS');

Lifecycle

await client.agents.activate('agt_01HXK8Z3MNPQRS');
await client.agents.deactivate('agt_01HXK8Z3MNPQRS');
await client.agents.publish('agt_01HXK8Z3MNPQRS', { notes: 'Improved prompt' });
const copy = await client.agents.duplicate('agt_01HXK8Z3MNPQRS', {
  name: 'Aria – Support Agent (Copy)',
});

Stats

const stats = await client.agents.stats('agt_01HXK8Z3MNPQRS', {
  dateFrom: '2025-06-01',
  dateTo: '2025-06-30',
});
console.log(`Conversations: ${stats.totalConversations}`);
console.log(`Avg duration: ${stats.avgDurationSeconds}s`);

Conversations

List (async iterator)

for await (const conv of client.conversations.list({
  agentId: 'agt_01HXK8Z3MNPQRS',
  channel: 'phone',
  status: 'completed',
  dateFrom: '2025-06-01',
  dateTo: '2025-06-30',
})) {
  console.log(`${conv.id}: ${conv.durationSeconds}s`);
}

Transcript

const transcript = await client.conversations.transcript('conv_01HXDEF456GHI');
for (const turn of transcript.turns) {
  const icon = turn.speaker === 'agent' ? '🤖' : '👤';
  console.log(`${icon} [${turn.startTime.toFixed(1)}s] ${turn.text}`);
}

Analysis

const analysis = await client.conversations.analysis('conv_01HXDEF456GHI');
console.log(analysis.summary);
console.log(`Sentiment: ${analysis.sentiment} (${analysis.sentimentScore})`);

Recording URL

const recording = await client.conversations.recording('conv_01HXDEF456GHI');
console.log(recording.url); // pre-signed URL valid 1 hour

Telephony

// Search available numbers
const available = await client.telephony.numbers.search({
  country: 'US',
  areaCode: '415',
  capabilities: ['voice', 'sms'],
});
 
// Provision
const number = await client.telephony.numbers.provision({
  phoneNumber: available[0].phoneNumber,
  provider: 'twilio',
  friendlyName: 'Main Support Line',
});
 
// Assign
await client.telephony.numbers.assign(number.id, 'agt_01HXK8Z3MNPQRS');
 
// List
for await (const num of client.telephony.numbers.list()) {
  console.log(`${num.phoneNumber} → ${num.assignedAgentName ?? 'unassigned'}`);
}
 
// Release
await client.telephony.numbers.release(number.id);

Knowledge Base

// Upload
import { readFileSync } from 'fs';
const fileBuffer = readFileSync('./product-catalog.pdf');
const file = new File([fileBuffer], 'product-catalog.pdf', { type: 'application/pdf' });
 
const doc = await client.knowledgeBase.uploadDocument({
  file,
  name: 'Product Catalog Q2 2025',
  agentIds: ['agt_01HXK8Z3MNPQRS'],
});
 
// Poll for completion
let status = await client.knowledgeBase.status(doc.id);
while (status.status === 'processing') {
  await new Promise(r => setTimeout(r, 2000));
  status = await client.knowledgeBase.status(doc.id);
}
console.log(`Indexed ${status.chunkCount} chunks`);
 
// Semantic search
const results = await client.knowledgeBase.search({
  query: 'What is the return policy?',
  documentIds: [doc.id],
  topK: 5,
  minScore: 0.7,
});
for (const match of results.matches) {
  console.log(`${match.score.toFixed(3)}: ${match.excerpt.slice(0, 80)}...`);
}

Outbound Calls

// Immediate call
const call = await client.outboundCalls.create({
  agentId: 'agt_01HXK8Z3MNPQRS',
  toNumber: '+14155550199',
  metadata: { customerId: 'cust_12345' },
  overrideFirstMessage: 'Hi Jane, calling from Acme about your appointment.',
});
 
// Scheduled call
const scheduled = await client.outboundCalls.create({
  agentId: 'agt_01HXK8Z3MNPQRS',
  toNumber: '+14155550199',
  scheduledFor: '2025-06-17T09:00:00Z',
  maxRetries: 2,
  retryDelayMinutes: 30,
});
 
// Cancel
await client.outboundCalls.cancel(scheduled.id);
 
// List
for await (const c of client.outboundCalls.list({ status: 'completed' })) {
  console.log(`${c.toNumber}: ${c.status} (${c.durationSeconds}s)`);
}

Webhooks

// Create
const webhook = await client.webhooks.create({
  agentId: 'agt_01HXK8Z3MNPQRS',
  url: 'https://your-server.com/webhooks/voisnap',
  events: ['SessionStarted', 'SessionEnded', 'AnalysisCompleted'],
  secret: 'whsec_...',
});
 
// Verify signature (in your Express handler)
import { verifyWebhookSignature } from '@voisnap/sdk/webhooks';
 
app.post('/webhooks/voisnap', express.raw({ type: 'application/json' }), (req, res) => {
  const event = verifyWebhookSignature({
    payload: req.body,
    signature: req.headers['x-webhook-signature'] as string,
    timestamp: req.headers['x-webhook-timestamp'] as string,
    secret: 'whsec_...',
  });
  if (!event) return res.status(401).send('Invalid signature');
 
  switch (event.type) {
    case 'SessionEnded':
      console.log('Call ended:', event.data.conversationId);
      break;
    case 'AnalysisCompleted':
      console.log('Analysis ready:', event.data.summary);
      break;
  }
 
  res.status(200).send('OK');
});

Error handling

import {
  VoisnapError,
  AuthenticationError,
  NotFoundError,
  RateLimitError,
  ValidationError,
} from '@voisnap/sdk';
 
try {
  const agent = await client.agents.get('agt_does_not_exist');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.error('Not found:', err.detail);
  } else if (err instanceof AuthenticationError) {
    console.error('Auth failed:', err.message);
  } else if (err instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${err.retryAfter}s`);
  } else if (err instanceof ValidationError) {
    for (const e of err.errors) {
      console.error(`  ${e.field}: ${e.message}`);
    }
  } else if (err instanceof VoisnapError) {
    console.error(`API error ${err.status}: ${err.detail} (trace: ${err.traceId})`);
  }
}

Configure retries

const client = new VoisnapClient({
  apiKey: 'vsnp_live_...',
  maxRetries: 3,       // default: 3
  timeout: 30_000,     // ms, default: 30000
});

Edge runtime compatibility

The SDK uses the Fetch API and is compatible with edge runtimes. No Node.js-specific APIs are used:

// Cloudflare Workers
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const client = new VoisnapClient({ apiKey: env.VOISNAP_API_KEY });
    const agent = await client.agents.get(env.AGENT_ID);
    return new Response(JSON.stringify({ agentName: agent.name }));
  },
};