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

@voicetel.com/voiceml

v0.9.2

Published

Official TypeScript/Node SDK for the VoiceML REST API (Twilio-compatible voice + AMD service from VoiceTel)

Readme

📞 VoiceML TypeScript SDK

The official TypeScript / Node client for the VoiceML REST API — Twilio-compatible outbound voice and answering-machine-detection from VoiceTel, with end-to-end TypeScript types and dual ESM / CJS distribution.

Version Node License Tests Typed

📚 Table of Contents

✨ Features

🛡️ Strongly Typed End-to-End

  • TypeScript interfaces for every one of the 81 API operations — request params, response payloads, and entity shapes.
  • Strict mode-friendly. Authored against strict TypeScript; consumers get full inference and red squigglies on shape drift.
  • Autocomplete everywhere. Your IDE knows the shape of every field — Call.sid, Recording.duration, Queue.current_size are all typed.
  • Twilio-compatible wire shapesaccount_sid, from_number, to_number, status callbacks, pagination envelopes — match what Twilio's Programmable Voice API documents.

⚡ Modern Runtime

  • Built on the standard global fetch API. No node-fetch, no axios, no third-party HTTP dependency.
  • Dual package — ships ESM (import) and CJS (require) with .d.ts declarations in one tarball.
  • Runs on Node.js 18.17+; works in Deno, Bun, and modern runtimes that ship fetch.

🔁 Production-Grade Transport

  • Automatic retry with exponential backoff on 429 / 5xx — honors Retry-After headers.
  • Configurable timeout per client (timeoutMs) — backed by an internal AbortController.
  • HTTP Basic auth with AccountSid:ApiKey — exactly what the Twilio SDK uses, so existing credentials work unchanged.
  • Pluggable fetch. Swap in a mock for tests or a proxying fetch for outbound network policy.
  • Structured exception hierarchyRateLimitError, AuthenticationError, NotFoundError, etc. all subclasses of ApiError you can catch broadly or narrowly.

📞 Complete API Coverage

  • Calls — originate, fetch, terminate, update + per-call recordings, streams, siprec, transcriptions, notifications, events, user-defined messages, and the /Calls/{sid}/Payments lifecycle (Pay TwiML companion).
  • Conferences — list, fetch, end conferences, plus participants (mute / hold / kick) and conference-scoped recordings.
  • Queues — create, list, update, delete, peek, dequeue (front or specific member).
  • Applications — CRUD on stored TwiML + callback bundles.
  • Recordings — account-wide list, metadata fetch, audio fetch (follows S3 redirect), delete.
  • Messages — create, fetch, list (To/From/DateSent filters + pagination), update (Body redaction; Status=canceled), delete.
  • IncomingPhoneNumbers — list, fetch, update.
  • Notifications — fetch, list.
  • SIP — SIP Trunking: Domains (CRUD), CredentialLists + Credentials (CRUD), IpAccessControlLists + IpAddresses (CRUD), Domain↔ACL/CredentialList mappings (historical, Auth/Calls, Auth/Registrations namespaces).
  • Routes V2 — Twilio Inbound Processing Region API: client.routesV2.sipDomains.fetch(name) / update(name, { VoiceRegion, FriendlyName }).
  • Diagnostics/health deep probe, OpenAPI spec.

🧪 Tested

  • 148 unit tests with a mocked transport (vitest + fetch stub) — request shapes, response parsing, error mapping, retry behavior, per-product host routing, and pagination iterators all covered.
  • Structural conformance harness in tests/integration/conformance.test.ts validates SDK interfaces against canonical Twilio response fixtures — opt-in via VOICEML_CONFORMANCE_FIXTURES, so CI stays fast.

📦 Clean Distribution

  • Zero codegen footprint — every byte hand-written.
  • Built with tsc; ships ESM + CJS + .d.ts declarations.
  • Zero runtime dependencies.

🚀 Installation

npm install @voicetel.com/voiceml
# or
pnpm add @voicetel.com/voiceml
# or
yarn add @voicetel.com/voiceml

Requires Node.js 18.17 or later (for the global fetch API). Both ESM and CJS entrypoints ship in the package; TypeScript declarations are included.

🏁 Quickstart

import { Client } from '@voicetel.com/voiceml';

const c = new Client({ accountSid: 'AC…', apiKey: '…' });

const call = await c.calls.create({
  To: '+18005551234',
  From: '+18005550000',
  Url: 'https://example.com/twiml',
  MachineDetection: 'DetectMessageEnd',
});
console.log(call.sid, call.status);

// Stream every completed call across all pages
for await (const completed of c.calls.iterate({ Status: 'completed', PageSize: 200 })) {
  console.log(completed.sid, completed.duration);
}

🔑 Authentication

Every endpoint uses HTTP Basic with your AccountSid as the username and your per-tenant API key as the password — identical to Twilio's auth shape, so credentials issued for Twilio code work here unchanged.

import { Client } from '@voicetel.com/voiceml';

const c = new Client({ accountSid: 'AC…', apiKey: '…' });
const health = await c.diagnostics.health(); // uses your AccountSid + key on every call

Don't have credentials yet? See voicetel.com/docs/api/v0.9/voiceml/ for issuance and rotation.

🗺️ Resource Reference

| Resource | Field on Client | Covers | |---|---|---| | Calls | client.calls | originate, fetch, list, terminate, update + per-call recordings, streams, siprec, transcriptions, notifications, events, user-defined messages, payments | | Conferences | client.conferences | list, fetch, end, participants (mute / hold / kick), conference-scoped recordings | | Queues | client.queues | create, list, update, delete, peek, dequeue (front or specific member) | | Applications | client.applications | CRUD on TwiML + callback bundles | | Recordings | client.recordings | account-wide list, metadata, audio fetch (follows S3 redirect), delete | | Messages | client.messages | create, fetch, list, update, delete (To/From/DateSent filters; Body redaction; Status=canceled) | | IncomingPhoneNumbers | client.incomingPhoneNumbers | list, fetch, update | | Messaging Service | client.messagingV1.services | create, list, fetch, update, delete — served on messaging.voicetel.com | | Pricing | client.pricing | read-only voice / messaging / phone-number / trunking pricing under v1 + v2 | | Notifications | client.notifications | fetch, list | | Diagnostics | client.diagnostics | /health, OpenAPI spec |

Product hosts

VoiceML mirrors Twilio's product-per-subdomain model. Most resources answer on the default host (voiceml.voicetel.com), but two products ride their own subdomains:

| Product | Host | Client option | |---|---|---| | Conversations (client.conversationsV1) | conversations.voicetel.com | conversationsBaseUrl | | Messaging Service (client.messagingV1) | messaging.voicetel.com | messagingBaseUrl |

The host is what disambiguates a Messaging Service (MG…) from a Conversation Service (IS…) — they share the /v1/Services path shape. Hosts are derived automatically from baseUrl: a voiceml.*.voicetel.com base swaps the voiceml label per product, while any other base URL (a self-hosted instance) keeps every product on that single host. Point messagingBaseUrl / conversationsBaseUrl at a custom subdomain to override:

const c = new Client({
  accountSid: 'AC…',
  apiKey: '…',
  baseUrl: 'https://pbx.example.com',
  messagingBaseUrl: 'https://msg.example.com',
});

All request and response shapes are exported from the package — destructure what you need:

import {
  Client,
  type CreateCallParams,
  type CreatePaymentParams,
} from '@voicetel.com/voiceml';

const c = new Client({ accountSid: 'AC…', apiKey: '…' });

const callParams: CreateCallParams = {
  To: '+18005551234',
  From: '+18005550000',
  Url: 'https://example.com/twiml',
};
const call = await c.calls.create(callParams);

// On a live call, open a Pay session:
const payment: CreatePaymentParams = {
  IdempotencyKey: 'order-482917',
  StatusCallback: 'https://example.com/pay-status',
};
const session = await c.calls.startPayment(call.sid, payment);
console.log(session.sid, session.status);

🚨 Error Handling

Every non-2xx response raises a subclass of ApiError, keyed off HTTP status:

| Status | Exception | |--------|-----------| | 400 | BadRequestError | | 401 | AuthenticationError | | 403 | PermissionDeniedError | | 404 | NotFoundError | | 409 | ConflictError | | 410 | GoneError | | 429 | RateLimitError | | 501 | NotImplementedAPIError | | 5xx | ServerError | | other | ApiError |

import { Client, NotFoundError, RateLimitError } from '@voicetel.com/voiceml';

const c = new Client({ accountSid: 'AC…', apiKey: '…' });

try {
  const call = await c.calls.get('CA0000000000000000000000000000aaaa');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log("That call isn't on your account.");
  } else if (err instanceof RateLimitError) {
    const retryAfter = (err.body as { retry_after?: number })?.retry_after ?? '?';
    console.log(`Slow down — retry in ${retryAfter}s`);
  } else {
    throw err;
  }
}

The Twilio-compatible error body (code, message, more_info, status) is parsed into error.code / error.message with the raw payload on error.body.

📄 Pagination

List operations return a …List interface with a Twilio-compatible pagination envelope (page, page_size, total, next_page_uri, previous_page_uri, …). For resources that support it, use the iterate() async generator to walk every page transparently:

for await (const call of c.calls.iterate({ Status: 'completed', PageSize: 200 })) {
  process(call);
}

for await (const msg of c.messages.iterate({ From: '+18005550000', PageSize: 200 })) {
  archive(msg);
}

iterate() is available on calls, conferences, queues, recordings, and messages. For other resources, page manually with client.<resource>.list({ Page: n }).

🔁 Migration from twilio-node

The accountSid + apiKey pair Twilio's Node SDK validates in its constructor works unchanged here:

// Before — Twilio
import twilio from 'twilio';
const client = twilio('AC…', '<auth_token>');

// After — VoiceML (Twilio-compatible)
import { Client } from '@voicetel.com/voiceml';
const client = new Client({ accountSid: 'AC…', apiKey: '<api_key>' });

Method names follow the resource map above (client.calls.create({…}), client.queues.list()) rather than Twilio's nested client.api.v2010.accounts(sid).calls.create({…}) chain — flatter, fewer keystrokes, same wire format on the way out.

⏱️ Rate Limits

VoiceML applies per-tenant rate limits at the edge. The SDK automatically retries 429 responses with Retry-After honored, up to maxRetries (default 2). To bump it:

new Client({
  accountSid: 'AC…',
  apiKey: '…',
  maxRetries: 4,
  timeoutMs: 60_000,
});

Pass a custom fetch for tests or to route through an HTTP proxy:

new Client({ accountSid: 'AC…', apiKey: '…', fetch: myFetch });

🛠️ Development

git clone https://github.com/voicetel/voiceml-node-sdk
cd voiceml-node-sdk
npm install

# Unit tests (fast, no network)
npm test

# Type check
npm run typecheck

# Lint
npm run lint

# Build ESM + CJS + .d.ts
npm run build

# Conformance harness (opt-in; needs callBroadcast fixture corpus)
VOICEML_CONFORMANCE_FIXTURES=/path/to/twilio-conformance-fixtures/fixtures \
  npx vitest run tests/integration/conformance.test.ts

📖 API Documentation

🙌 Contributors

Contributions welcome. Open an issue describing the change you want to make, or send a pull request against main.

💖 Sponsors

| Sponsor | Contribution | |---------|--------------| | VoiceTel Communications | Primary development and production hosting |

📄 License

MIT with the Commons Clause restriction. See LICENSE and voicetel.com/legal/.