voip-ms
Async client for the voip.ms REST API.
A thin, idiomatic Rust wrapper around every method exposed by the voip.ms
REST endpoint (https://voip.ms/api/v1/rest.php). Each WSDL operation gets a
typed *Params request struct and methods on [Client]. Each method returns
raw serde_json::Value, and each also has a *_typed variant for direct
deserialization into your own type (or into the crate's starter response
types).
Installation
[]
= "0.1"
= { = "1", = ["macros", "rt-multi-thread"] }
By default the crate enables rustls with system root certificates. To use a
different TLS backend:
# Embed Mozilla's roots (good for scratch/distroless images):
= { = "0.1", = false, = ["rustls-tls-webpki-roots"] }
# Use the platform's native TLS stack:
= { = "0.1", = false, = ["native-tls"] }
Authentication
voip.ms uses two pieces of credential, both of which you control entirely:
api_username— your account email.api_password— a distinct password generated on the SOAP and REST/JSON API page in the voip.ms customer portal.
You must also allow-list the source IP address(es) you'll be calling from on
that same page. This crate does not load credentials from the environment,
files, or any other source — pass them when you construct the [Client].
Usage
use ;
async
Every API method follows the same pattern: construct a *Params struct
(every field is Option<T> and omitted from the request when None), then
call either:
client.some_method(...)for a rawserde_json::Value, orclient.some_method_typed::<T>(...)for typed deserialization.
use ;
# async
Customizing the HTTP client
Use [Client::builder] to plug in your own reqwest::Client — for proxies,
custom timeouts, retry middleware, or anything else you'd configure on
reqwest directly.
use Duration;
use Client;
let http = builder
.timeout
.build
.unwrap;
let client = builder
.http_client
.build
.unwrap;
Typed responses
The WSDL doesn't describe response shapes (all 222 operations declare the
same generic arrayResponse), so this crate intentionally hands back
serde_json::Value by default. To reduce boilerplate, every generated method
also has a typed variant named *_typed:
use ;
# async
For methods where you only want a nested field, use
[Client::call_typed_at] with a JSON pointer:
use Deserialize;
use ;
# async
The crate also includes starter partial response types that keep unknown
fields in extra, such as GetBalanceResponse and GetDidsInfoResponse.
Running the examples
The examples/ directory contains small runnable programs that
read credentials from VOIP_MS_USERNAME and VOIP_MS_PASSWORD:
VOIP_MS_USERNAME=you@example.com \
VOIP_MS_PASSWORD=your-api-password \
Available examples: get_balance, list_dids, send_sms.
Calling methods this crate hasn't been regenerated for
If voip.ms adds an API method that isn't yet in this crate, use
[Client::call] directly with a serde-serializable parameter set:
# async
Error model
All errors surface through [voip_ms::Error]. The three variants are:
Error::Http— the request failed at the transport or HTTP-status level.Error::Api(ApiStatus)— the response was a well-formed JSON envelope but thestatusfield was something other thansuccess(e.g.invalid_credentials,missing_method,api_not_enabled). The wire string is exposed verbatim — the set of values is per-method and not stable, so consult the voip.ms documentation for the methods you use.Error::InvalidResponse— the response was not the expected JSON envelope (e.g. missingstatusfield).
Development and release
Contributor and maintainer workflows (regeneration, verification, and release) are documented in DEVELOPMENT.md.
See AGENTS.md for design decisions and project-specific guidance.
License
Licensed under the MIT license.