SDKs

Node.js SDK

The official Node.js and TypeScript SDK for VoiceDock, with no runtime dependencies.

The official Node.js and TypeScript SDK for VoiceDock, published on npm as hmsovereign. It has no runtime dependencies and covers assistants, calls, phone numbers, campaigns, workflows, SIP trunks, voices, usage, BYOK, tool and analysis templates, domains and organizations. For the chat endpoints and the health check, call the REST API directly.

Installation

npm install hmsovereign

Requires Node.js 22 or later.

Quick Start

import { HMSSovereign } from 'hmsovereign';

const client = new HMSSovereign({
  apiKey: process.env.VOICEDOCK_API_KEY!,
});

// List all assistants
const assistants = await client.assistants.list();

// Create an assistant
const assistant = await client.assistants.create({
  name: 'Customer Support',
  first_message: 'Hello, how can I help you?',
  llm_config: {
    provider: 'openai',
    model: 'gpt-5.4-mini',
    messages: [{ role: 'system', content: 'You are a friendly customer service agent.' }],
  },
  stt_config: {
    provider: 'deepgram',
    model: 'nova-3-general',
    language: 'en',
  },
  tts_config: {
    provider: 'elevenlabs',
    voice_id: 'ukiwGs47sHyibruHJ1vg',
  },
});

Resources

All resources are available as properties on the client:

ResourceDescription
client.assistantsCRUD for AI voice assistants
client.callsList calls, make outbound calls, call control
client.numbersPhone number management
client.campaignsOutbound call campaigns with leads
client.workflowsMulti-step call flows; attach one with client.numbers.update(id, { workflow_id })
client.sipTrunksSIP trunk configuration
client.voicesList available TTS voices
client.usageUsage logs and billing
client.byokBring Your Own Key management
client.toolTemplatesReusable tool/function templates
client.analysisTemplatesPost-call analysis schemas
client.domainsCustom domain configuration
client.organizationsOrganization management

Outbound Calls

Three configuration modes for outbound calls:

Reference mode — use a saved assistant

const call = await client.calls.create({
  destination: '+31612345678',
  assistant_id: 'uuid-of-saved-assistant',
});

console.log(`Call started: ${call.call_id}`);

Transient mode — one-time assistant config

const call = await client.calls.create({
  destination: '+31612345678',
  assistant: {
    first_message: 'Hello John, this is a reminder about your appointment.',
    llm_config: { provider: 'openai', model: 'gpt-5.4-mini' },
    stt_config: { provider: 'deepgram', model: 'nova-3-general', language: 'en' },
    tts_config: { provider: 'elevenlabs', voice_id: 'ukiwGs47sHyibruHJ1vg' },
  },
});

Hybrid mode — saved assistant with overrides

const call = await client.calls.create({
  destination: '+31612345678',
  assistant_id: 'uuid-of-saved-assistant',
  assistant_override: {
    first_message: 'Good afternoon John, calling about your order.',
    metadata: { order_id: '12345', customer_tier: 'premium' },
  },
});

The metadata object is passed through to all webhook events at message.assistant.metadata.


Call Control

Control active calls in real-time:

// Inject context (invisible to caller, visible to LLM)
await client.calls.injectContext(callId, 'Customer is a VIP member', true);

// Make the assistant say something
await client.calls.say(callId, 'One moment please, let me look that up for you.');

// Transfer to a human agent
await client.calls.transfer(callId, '+31201234567', 'Transferring you to a colleague now.');

// End the call
await client.calls.end(callId, 'Thank you for your call. Goodbye!');

Pagination

calls.list() returns a Page with metadata:

// Single page
const page = await client.calls.list({ status: 'ended', limit: 50 });

console.log(page.data);         // Call[]
console.log(page.pagination);   // { total, limit, offset }
console.log(page.hasMore);      // boolean

Auto-pagination

Iterate through all results automatically with listAll():

for await (const call of client.calls.listAll({ status: 'ended' })) {
  console.log(call.id, call.duration_seconds);
}

// Or collect everything into an array
const allCalls = await client.calls.listAll({ status: 'ended' }).toArray();

Auto-pagination is available on client.calls.listAll() and client.usage.listAll().


Campaigns

Run automated outbound call campaigns:

// Create a campaign with leads
const campaign = await client.campaigns.create({
  name: 'Appointment Reminders',
  agent_id: assistant.id,
  system_message_template: 'Call {{name}} about the appointment on {{date}}',
  schedule_start_time: '09:00',
  schedule_end_time: '17:00',
  timezone: 'America/New_York',
  leads: [
    {
      phone_number: '+15551234567',
      variables: { name: 'John Smith', date: '2026-10-01', time: '14:00' },
    },
  ],
});

// Add more leads later
await client.campaigns.addLead(campaign.id, {
  phone_number: '+15559876543',
  variables: { name: 'Peter Johnson', date: '2026-10-02', time: '10:00' },
});

Webhook Verification

The SDK provides built-in webhook signature verification. See Webhook Security for background. The signature headers are only sent when a webhook secret is configured.

import { Webhooks } from 'hmsovereign';

// Express example — use raw body for signature verification
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  try {
    const event = Webhooks.verify({
      payload: req.body,
      signature: req.headers['x-webhook-signature'] as string,
      timestamp: req.headers['x-webhook-timestamp'] as string,
      secret: process.env.WEBHOOK_SECRET!,
    });

    switch (event.message.type) {
      case 'assistant-request':
        // Return dynamic config before call is answered
        res.json({ assistant: { first_message: 'Welcome!' } });
        break;

      case 'tool-calls':
        // Handle function calls from the assistant
        const toolCall = event.message.tool_call_list[0];
        res.json({ result: { status: 'ok' } });
        break;

      case 'status-update':
        // Call state: in-progress, ended, ended-with-error
        console.log(event.message.call.status);
        res.sendStatus(200);
        break;

      case 'end-of-call-report':
        // Post-call summary, transcript, analysis
        console.log(event.message.summary);
        res.sendStatus(200);
        break;

      default:
        res.sendStatus(200);
    }
  } catch (error) {
    res.status(400).send('Invalid signature');
  }
});

Raw body required. You must use express.raw() (or equivalent) to get the raw request body. Parsed JSON bodies will fail signature verification because the signature is computed over the original string.


Error Handling

The SDK throws typed errors that you can catch with instanceof:

import {
  AuthenticationError,
  NotFoundError,
  RateLimitError,
  InsufficientCreditsError,
} from 'hmsovereign';

try {
  await client.assistants.get('non-existent-id');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log('Assistant not found');
  } else if (error instanceof AuthenticationError) {
    console.log('Invalid API key');
  } else if (error instanceof RateLimitError) {
    console.log(`Rate limited, retry after ${error.retryAfter}s`);
  } else if (error instanceof InsufficientCreditsError) {
    console.log('Top up your credits in the dashboard');
  }
}

Errors returned by the API extend ApiRequestError, which carries status, message, the parsed response body and, when the API lists validation problems, details. When the API returns both an error and an explanation, the message contains both. Timeouts and network errors are thrown as a plain Error, and webhook verification throws a WebhookVerificationError.


Configuration

const client = new HMSSovereign({
  apiKey: process.env.VOICEDOCK_API_KEY!, // Required
  baseUrl: 'https://...',       // Default: https://api.hmsovereign.com/api/v1
  maxRetries: 2,                // Default: 2
  timeout: 30_000,              // Default: 30s
  debug: true,                  // Default: false — logs requests and responses
});
OptionDefaultDescription
apiKey—Your VoiceDock API key (required)
baseUrlhttps://api.hmsovereign.com/api/v1API base URL
maxRetries2Maximum number of retries for a failed request
timeout30000Request timeout in milliseconds
debugfalseLog requests and responses to the console, with secret values masked

GET, PUT and DELETE requests are retried after a 5xx response, a timeout or a network error, waiting 1, 2, 4 and at most 8 seconds. POST and PATCH requests are only retried after a 429, because a first attempt that failed for another reason may already have been processed. After a 429, the client waits for the Retry-After header, up to 30 seconds.


TypeScript

The SDK is written in TypeScript and exports types for its resources. Import them directly:

import type {
  Assistant,
  Call,
  Campaign,
  CampaignLead,
  PhoneNumber,
  SipTrunk,
  Voice,
  UsageLog,
  WebhookPayload,
  STTConfig,
  LLMConfig,
  TTSConfig,
} from 'hmsovereign';

Source & Issues

On this page