Skip to main content

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

Project description

📞 VoiceML Python SDK

The official Python client for the VoiceML REST API — Twilio-compatible outbound voice and answering-machine-detection from VoiceTel, with type-safe, async-ready Python.

Version Python License Tests Typed

📚 Table of Contents

✨ Features

🛡️ Strongly Typed End-to-End

  • Pydantic v2 models for every one of the 81 API operations — request bodies validated before they leave your machine, responses validated when they arrive.
  • 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.

⚡ Sync + Async, Same Surface

  • Client for blocking calls, AsyncClient for await-based async — identical method names, identical return types.
  • Built on httpx — HTTP/2 ready, connection pooling, custom transports if you need them.
  • TLS session cache + persistent connections out of the box.

🔁 Production-Grade Transport

  • Automatic retry with exponential backoff on 429 / 5xx — honors Retry-After headers.
  • Configurable timeouts per client or per call.
  • HTTP Basic auth with AccountSid:ApiKey — exactly what the Twilio SDK uses, so existing credentials work unchanged.
  • 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, 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.
  • Diagnostics/health deep probe, OpenAPI spec.

🧪 Tested

  • 57 unit tests with mocked HTTP layer (respx) and real Pydantic validation on every fixture — spec drift gets caught at parse time.
  • Integration test suite that runs against a callBroadcast / VoiceML instance — gated by env vars, safe for CI.

📦 Clean Distribution

  • Zero codegen footprint — every byte hand-written.
  • Built with hatchling; ships as wheel + sdist.
  • py.typed marker — downstream type checkers see your imports natively.

🚀 Installation

pip install voiceml

Requires Python 3.10 or later. Tested against 3.10, 3.11, 3.12, and 3.13.

🏁 Quickstart

from voiceml import Client
from voiceml.models import CreateCallRequest

with Client(account_sid="AC…", api_key="…") as c:
    call = c.calls.create(
        CreateCallRequest(
            To="+18005551234",
            From="+18005550000",
            Url="https://example.com/twiml",
            MachineDetection="DetectMessageEnd",
        )
    )
    print(call.sid, call.status)

    for q in c.queues.list().queues:
        print(q.friendly_name, q.current_size)

🔑 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.

from voiceml import Client

with Client(account_sid="AC…", api_key="…") as c:
    me = c.diagnostics.health()  # uses your AccountSid + key on every call

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

🗺️ Resource Reference

Resource Sync + Async Covers
client.calls originate, fetch, list, terminate, update + per-call recordings, streams, siprec, transcriptions, notifications, events, payments
client.conferences list, fetch, end participants (mute / hold / kick), conference-scoped recordings
client.queues create, list, update, delete peek, dequeue (front or specific member)
client.applications CRUD on TwiML + callback bundles
client.recordings account-wide list, metadata, audio fetch, delete follows S3 redirect for audio
client.messages create, fetch, list, update, delete To/From/DateSent filters; Body redaction; Status=canceled
client.incoming_phone_numbers list, fetch, update
client.notifications fetch, list
client.diagnostics /health, OpenAPI spec

Every method that takes a request body accepts a typed Pydantic model imported from voiceml.models:

from voiceml import Client
from voiceml.models import CreateCallRequest, StartPaymentRequest

with Client(account_sid="AC…", api_key="…") as c:
    call = c.calls.create(CreateCallRequest(
        To="+18005551234",
        From="+18005550000",
        Url="https://example.com/twiml",
    ))
    # On a live call, open a Pay session:
    session = c.calls.start_payment(call.sid, StartPaymentRequest(
        IdempotencyKey="order-482917",
        StatusCallback="https://example.com/pay-status",
    ))
    print(session.sid, session.status)

🚨 Error Handling

All HTTP errors raise subclasses of voiceml.ApiError. Catch broadly or narrowly:

Status Exception
400 BadRequestError
401 AuthenticationError
403 PermissionDeniedError
404 NotFoundError
409 ConflictError
410 GoneError
429 RateLimitError
501 NotImplementedAPIError
5xx ServerError
other ApiError
from voiceml import Client, NotFoundError, RateLimitError

with Client(account_sid="AC…", api_key="…") as c:
    try:
        call = c.calls.get("CA0000000000000000000000000000aaaa")
    except NotFoundError:
        print("That call isn't on your account.")
    except RateLimitError as e:
        print(f"Slow down — retry in {e.body.get('retry_after', '?')}s")

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

⚡ Async Support

Identical surface to Client, with await-based methods:

import asyncio
from voiceml import AsyncClient

async def main() -> None:
    async with AsyncClient(account_sid="AC…", api_key="…") as c:
        calls = await c.calls.list(status="in-progress")
        for call in calls.calls:
            print(call.sid, call.duration)

asyncio.run(main())

📄 Pagination

List operations return a …List model with a Twilio-compatible pagination envelope (page, page_size, total, next_page_uri, previous_page_uri, …). For /Calls and /Messages, use the iter() helper to walk all pages transparently:

for call in c.calls.iter(status="completed", page_size=200):
    process(call)

for msg in c.messages.iter(from_number="+18005550000", page_size=200):
    archive(msg)

For other resources, page manually with client.<resource>.list(page=n).

🔁 Migration from twilio-python

The account_sid + api_key pair Twilio's SDK validates in its constructor works unchanged here:

# Before — Twilio
from twilio.rest import Client as TwilioClient
client = TwilioClient("AC…", "<token>", region=None)

# After — VoiceML (Twilio-compatible)
from voiceml import Client
client = Client(account_sid="AC…", api_key="<api-key>")

Method names follow the resource map above (client.calls.create(...), client.queues.list(), …) rather than Twilio's 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 max_retries (default 2). To bump it:

Client(account_sid="AC…", api_key="…", max_retries=4, timeout=60.0)

🛠️ Development

git clone https://github.com/voicetel/voiceml-python-sdk
cd voiceml-python-sdk
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# Unit tests (fast, no network)
pytest tests/unit

# Lint + type-check
ruff check src tests
mypy src

# Integration tests (live, read-only against a configured VoiceML instance)
cp .env.example .env  # fill in VOICEML_ACCOUNT_SID / VOICEML_API_KEY / VOICEML_BASE_URL
pytest tests/integration

# Build wheel + sdist
python -m build
twine check dist/*

📖 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/.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

voiceml-0.7.1.1.tar.gz (31.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

voiceml-0.7.1.1-py3-none-any.whl (45.6 kB view details)

Uploaded Python 3

File details

Details for the file voiceml-0.7.1.1.tar.gz.

File metadata

  • Download URL: voiceml-0.7.1.1.tar.gz
  • Upload date:
  • Size: 31.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for voiceml-0.7.1.1.tar.gz
Algorithm Hash digest
SHA256 b577a9ebe59bcec22919eda98a0d8c4150c68b2be9e550b1f2d3d3ef67d79211
MD5 638d514c951751baa37d38689ee9e98e
BLAKE2b-256 44149699b4b2797b18d75b6e37356038a4054b779f4c8219fda88ad377b9e47a

See more details on using hashes here.

Provenance

The following attestation bundles were made for voiceml-0.7.1.1.tar.gz:

Publisher: publish.yml on voicetel/voiceml-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file voiceml-0.7.1.1-py3-none-any.whl.

File metadata

  • Download URL: voiceml-0.7.1.1-py3-none-any.whl
  • Upload date:
  • Size: 45.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for voiceml-0.7.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0b0623c523d35375bbeb6d9f9debd452cbb9f64b32a0420e394e544272cd0e8d
MD5 330e933b1f2688cf83278efa3caf1aba
BLAKE2b-256 1793681f149244e6840f842c34214daf2b729dfca473264abcca006bbbf96bd2

See more details on using hashes here.

Provenance

The following attestation bundles were made for voiceml-0.7.1.1-py3-none-any.whl:

Publisher: publish.yml on voicetel/voiceml-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page