A modern, type-safe Python client for the VoIP.ms API with Pydantic validation, async support, and a rich CLI.
- π Complete API Coverage: All 217 VoIP.ms API methods dynamically generated from the official WSDL
- π Full Type Safety: Pydantic models with comprehensive type hints and validation
- β‘ Async Support: Both sync and async clients for optimal performance
- π¨ Rich CLI: Beautiful command-line interface with auto-completion and formatted output
- π Cross-Reference Decoding: Automatic translation of numeric codes to human-readable values
- π Smart Validation: Automatic type coercion, date parsing, and constraint validation
- π€ Smart Defaults: Intelligent parameter defaults (timezone, date ranges, etc.)
- π§ IDE Friendly: Generated .pyi stubs for perfect auto-completion
- π Data-Driven: Two-stage API definition - source from WSDL/HTML, runtime with corrections
pip install voipmsOr install from source:
git clone https://github.com/your-username/voipms.git
cd voipms
pip install -e .- Set your credentials:
# Get your API credentials from https://voip.ms/m/api.php
export VOIPMS_USERNAME='[email protected]'
export VOIPMS_API_PASSWORD='your-api-password'- Use the client:
from voipms import VoIPMSClient
# Create client (credentials from environment)
client = VoIPMSClient()
# Get account balance
balance = client.getBalance()
print(f"Balance: ${balance['balance']['current_balance']}")
# Get CDR (with automatic date range)
cdr = client.getCDR(answered=True)
print(f"Found {len(cdr['cdr'])} calls")- Async example:
from voipms import VoIPMSAsyncClient
import asyncio
async def main():
async with VoIPMSAsyncClient() as client:
balance = await client.getBalance()
print(f"Balance: ${balance['balance']['current_balance']}")
asyncio.run(main())- CLI usage:
# After installation
voipms getBalance
voipms getServersInfo
voipms getCDR --date_from="7 days ago" --date_to="today"
# Use the new decode feature for human-readable output
voipms --decode getSubAccounts --account="100001_mydomain"
# Get help
voipms --help
voipms list-methods
voipms help getCDRThe CLI can automatically decode numeric codes to human-readable values:
# Without decode - shows numeric codes
voipms getSubAccounts --account="100001_mydomain"
# Output: "auth_type": "1", "device_type": "2", ...
# With decode - shows human-readable values
voipms --decode getSubAccounts --account="100001_mydomain"
# Output: "auth_type": "1", "auth_type_decoded": "User/Password Authentication",
# "device_type": "2", "device_type_decoded": "ATA device, IP Phone or Softphone", ...The decoder:
- Automatically identifies fields with cross-references (127 fields across the API)
- Makes parallel API calls to fetch lookup values (2.5x faster than serial)
- Caches results within a single CLI execution
- Adds
_decodedfields without modifying original values
The client properly validates string parameters that represent numeric values:
# These all work correctly:
client.setDIDInfo(did="5551234567", callerid_prefix="1") # Valid: "1" is numeric
client.setDIDInfo(did="5551234567", callerid_prefix=1) # Also valid: converts to "1"
# This raises ValidationError:
client.setDIDInfo(did="5551234567", callerid_prefix="abc") # Invalid: not numericThe client uses dateparser for flexible date inputs:
# All of these work:
client.getCDR(date_from="2025-01-01", date_to="2025-01-31")
client.getCDR(date_from="yesterday", date_to="today")
client.getCDR(date_from="1 week ago", date_to="now")Every method has full type hints with detailed parameter information:
# Your IDE will show:
def getCDR(
self,
date_from: Annotated[str, Field(description="Start date for CDR retrieval")],
date_to: Annotated[str, Field(description="End date for CDR retrieval")],
answered: Annotated[Literal["0", "1"], Field(description="Include answered calls")] = None,
# ... all parameters with descriptions and constraints
) -> Dict[str, Any]:voipms/
βββ src/voipms/ # Main package
β βββ __init__.py
β βββ base_client.py # Base client with shared functionality
β βββ client.py # Synchronous client implementation
β βββ async_client.py # Asynchronous client implementation
β βββ method_factory.py # Dynamic method generation
β βββ validation.py # Pydantic model factory
β βββ type_validators.py # Type validators and converters
β βββ cli.py # Rich CLI interface
β βββ cli_decoder.py # Cross-reference decoder for CLI
β βββ api_definition.json # Complete API metadata
β βββ cli_smart_defaults.json # CLI smart defaults
β βββ *.pyi # Generated type stubs
βββ resources/ # Source files for API definition
β βββ server.wsdl # VoIP.ms WSDL file
β βββ voipms_api_documentation.html # HTML docs
βββ tools/ # API definition generation tools
β βββ cli.py # Unified tools CLI
β βββ commands/ # CLI command implementations
β βββ parsers/ # WSDL and HTML parsers
β βββ config/ # Tool configurations
β βββ generate_api_definition.py
β βββ generate_type_stubs.py
β βββ download_api_docs.py
βββ examples/ # Example scripts
β βββ basic_usage.py
β βββ async_usage.py
β βββ backup_account_complete.py
βββ tests/ # Test suite
## Regenerating the API Definitions
### Quick Regeneration
To regenerate all API definitions and type stubs:
```bash
# Regenerate everything (recommended)
uv run tools/cli.py generate
This runs three stages automatically:
- Source Generation: Creates
tools/generated/api_definition_source.jsonfrom WSDL/HTML - Quirks Application: Creates
api_definition.jsonwith runtime adjustments - Type Stubs: Updates
.pyifiles for IDE support
Regenerate the API definitions when:
-
VoIP.ms Updates Their API:
# First, update HTML documentation cp /path/to/voipms_api_documentation.html resources/ # Then regenerate uv run tools/cli.py generate
-
You Add New Corrections:
# Edit tools/config/api_corrections.json # Then regenerate (only stages 2-3 needed) uv run tools/cli.py generate --stage=quirks uv run tools/cli.py generate --stage=stubs
-
You Update Type Overrides:
# Edit tools/config/api_corrections.json # Then regenerate everything uv run tools/cli.py generate
# Stage 1: Generate source definition from WSDL/HTML
uv run tools/cli.py generate --stage=source
# Stage 2: Apply quirks to source definition
uv run tools/cli.py generate --stage=quirks
# Stage 3: Generate Python type stubs
uv run tools/cli.py generate --stage=stubsThe system uses a two-stage approach:
| File | Purpose | Generated By |
|---|---|---|
tools/generated/api_definition_source.json |
Pure API definition from WSDL/HTML | --stage=source |
api_definition.json |
Runtime definition with corrections applied | --stage=quirks |
tools/config/api_corrections.json |
Behavioral adjustments and fixes | Manually edited |
The client loads api_definition.json at runtime, which includes all quirks and adjustments.
This project uses uv for dependency management:
# Install dependencies
uv sync
# Run tests
uv run pytest
# Run linting
uv run ruff check .
uv run mypy src/Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
This is an independent project and is not affiliated with or endorsed by VoIP.ms.