Node.js SDK
Official TypeScript/Node.js SDK for the HMS Sovereign Voice AI Platform with zero dependencies and full type safety.
Official TypeScript/Node.js SDK for the HMS Sovereign Voice AI Platform. Zero dependencies, full type safety, and support for all API endpoints.
Installation
npm install hmsovereignRequires Node.js 20 or later.
Quick Start
import { HmsSovereign } from 'hmsovereign';
const client = new HmsSovereign({
apiKey: 'fl_live_...',
});
// 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-4o-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:
| Resource | Description |
|---|---|
client.assistants | CRUD for AI voice assistants |
client.calls | List calls, make outbound calls, call control |
client.numbers | Phone number management |
client.campaigns | Outbound call campaigns with leads |
client.sipTrunks | SIP trunk configuration |
client.voices | List available TTS voices |
client.usage | Usage logs and billing |
client.byok | Bring Your Own Key management |
client.toolTemplates | Reusable tool/function templates |
client.analysisTemplates | Post-call analysis schemas |
client.domains | Custom domain configuration |
client.organizations | Organization 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-4o-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, I'm 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', 'I'm transferring you to a colleague.');
// End the call
await client.calls.end(callId, 'Thank you for your call. Goodbye!');Pagination
List endpoints return 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); // booleanAuto-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',
name: 'John Smith',
variables: { date: '2026-03-25', time: '14:00' },
},
],
});
// Add more leads later
await client.campaigns.addLead(campaign.id, {
phone_number: '+15559876543',
name: 'Peter Johnson',
variables: { date: '2026-03-26', time: '10:00' },
});Webhook Verification
The SDK provides built-in webhook signature verification. See Webhook Security for background.
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');
}
});WarningRaw 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 at app.hmsovereign.com');
}
}All errors extend HmsSovereignError and include status, message, and code properties.
Configuration
const client = new HmsSovereign({
apiKey: 'fl_live_...', // Required
baseUrl: 'https://...', // Default: https://api.hmsovereign.com/api/v1
maxRetries: 2, // Default: 2 (retries on 5xx and 429)
timeout: 30_000, // Default: 30s
debug: true, // Default: false — logs requests and responses
});| Option | Default | Description |
|---|---|---|
apiKey | — | Your HMS Sovereign API key (required) |
baseUrl | https://api.hmsovereign.com/api/v1 | API base URL |
maxRetries | 2 | Automatic retries on 5xx and 429 responses |
timeout | 30000 | Request timeout in milliseconds |
debug | false | Log all HTTP requests and responses to console |
The SDK automatically retries failed requests with exponential backoff. On 429 responses, it respects the Retry-After header.
TypeScript
The SDK is written in TypeScript and exports types for all resources. Import them directly:
import type {
Assistant,
Call,
Campaign,
Lead,
PhoneNumber,
SipTrunk,
Voice,
UsageRecord,
WebhookPayload,
SttConfig,
LlmConfig,
TtsConfig,
} from 'hmsovereign';