VoicePro Plus API v3
Check numbers, order and route phone numbers, run IoT SIMs and read your whole account from your own code.Test keys that never charge, an MCP server for AI assistants, and a real response for every call.
Your first call in 10 minutes
Create a test key · 2 min
Your email, the code we send it, your key. No account needed. Already a customer? Make keys in the portal under API and webhooks.
Expected resultA key starting vpkey_test_. It never sees your account and is never charged.
Check a number · 1 min
curl -X POST "https://api.voicepro.plus/v3/dips" \
-H "Authorization: Bearer vpkey_test_YOUR_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"number":"+447700900001"}'Expected resultHTTP 201 with the range check and the last port date 12/03/2025: this test number was ported.
Go live · 1 min
With an Enterprise account, make a live key in the portal and swap it in (no account yet? talk to us). Nothing else changes. Then pick your next call from the recipes or the reference.
Expected resultThe same code, real answers. Every answer carries a request-id to quote to support.
Why build on VoicePro
Test keys, never charged
A vpkey_test_ key answers every call with real-shaped data and fixed test numbers. Build the whole integration before you spend a penny.
Built for AI assistants
Our MCP server gives Claude, ChatGPT or Cursor one tool for each of the 82 calls, with your key and your permissions.
82 calls, real responses
Every call has a worked example, checked against the contract on every release, in cURL, JavaScript and Python.
Numbers checked first
Range and port checks on any number in one call. On our carrier voice network, every number is checked before the call connects.
One-file clients
TypeScript and Python clients generated from the contract: one method per call, errors with their code. No dependencies to fight.
Safe retries built in
Every change takes an Idempotency-Key, so a timeout never orders, charges or changes anything twice.
Recipes
Common jobs, call by call. Each step links to its call in the reference, with code and a real response.
Check a number before you route a call
Stop calls to numbers that are not in service, and route ported numbers to the right network.
- GET /dips/products See the checks on sale and their prices.
- POST /dips Check the number: range (valid, line type) and last port date.
- GET /dips/usage Watch this month's checks and spend.
Buy a UK number and send its calls to your system
Add numbers from your own app without opening the portal.
- GET /number_offers Find a number and its price.
- POST /number_orders Order it, paid from your balance.
- GET /numbers See it on your account.
- PATCH /numbers/{id} Ring your app or forward to another number.
Change 1,000 IoT SIMs at once
Switch a whole fleet on or off, or change its settings, in one job.
- GET /iot/sims Pick the SIMs.
- POST /iot/bulk_jobs Start one change for up to 5,000 SIMs.
- GET /iot/bulk_jobs/{id} Follow the job (or wait for the iot.bulk_job_done webhook).
- GET /iot/bulk_jobs/{id}/results See what changed, SIM by SIM.
Send IoT SIM traffic to your own platform
Route a SIM's traffic through your connector, and check it on the SIM's network log.
- POST /iot/connectors Add your connector.
- POST /iot/connectors/{name}/test Test it: an HTTPS connector gets a labelled test POST, and you see recent delivery errors.
- PATCH /iot/sims/{id}/connector Send one SIM's traffic through it.
- GET /iot/sims/{id}/events Read that SIM's network log: attaches, data sessions and detaches.
Pull your voice rates and traffic every day
Feed your own billing and reporting from the same numbers we bill on.
- GET /trunks Your trunk settings.
- GET /rates Current and scheduled rates.
- GET /traffic Totals and a time series for the range you ask for.
- GET /traffic/destinations Traffic by destination.
Reconcile what you owe
Match your balance, spend and invoices in your finance system.
- GET /balance Your prepaid balance now.
- GET /spend This month's spend by service, the projection and your spending limit.
- GET /invoices Every invoice.
- GET /invoices/{id} One invoice with its lines.
Guides
Keys and authentication
Base URL: https://api.voicepro.plus/v3. Send the key as Authorization: Bearer vpkey_... over HTTPS only.
A key belongs to one account and can only see that account: an id from another account answers 404. Revoking a key in the portal stops it at once. A key can carry an expiry date and a list of allowed IP addresses. The portal lists every request each key made in the last 30 days, under API and webhooks, then Requests.
Lists and paging
Every list takes limit (default 50, up to 500) and cursor. Pass back next_cursor unchanged for the next page; it is null on the last page. Lists are newest first and pages are stable.
Responses are JSON with snake_case fields and public ids such as sim_... and num_.... Money is in minor units with its currency. Reads built from stored copies carry data_as_of, the time the copy was taken.
Safe retries
Every POST, PATCH and DELETE takes an Idempotency-Key header (8-128 characters). The first answer is stored for 24 hours and replayed for a retry with the same key and body, so a network timeout never orders, charges or changes anything twice.
For calls that cost money (number checks, number check files and number orders), a retry while the first attempt is still running answers 409 idempotency_key_in_use: wait and retry with the same key.
Rate limits
10 requests a second, bursts of 25 per key, and 30 requests a second, bursts of 75, shared by all of an account's keys. Every keyed answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a 429 carries Retry-After.
A service your account does not have answers 404 service_not_enabled. A data service that is briefly down answers 503, never an empty list.
Test keys
Tick Test key when you create a key and it starts vpkey_test_. A test key gets the documented example for every call, so you can build and run your whole integration before going live. It never sees your account, never changes anything and is never charged. Changes still need an Idempotency-Key and every call still needs the key's permission, exactly as live. Number checks answer from these test numbers:
Errors
Every error has the same shape. code is stable and safe to branch on; message is for people.
{
"error": {
"type": "authentication",
"code": "invalid_api_key",
"message": "This API key is not recognised.",
"request_id": "req_..."
}
}API reference
Every path is relative to https://api.voicepro.plus/v3. Each call shows its fields, a working example in cURL, JavaScript or Python, and a real response.
Account
SIMs
IoT SIMs
IoT connectors
Numbers
Voice trunk
Activity
Billing
Dips
Webhooks
Webhooks
Add webhooks by API (POST /webhooks, with a key that has the webhooks:write scope) or in the portal, under Account, then API and webhooks (owners and admins). A webhook has an HTTPS address and one or more event types (events, up to 20), and gets a signing secret shown once.
We POST JSON with X-VoicePro-Event and two signatures made with your secret. VoicePro-Signature is t=<unix seconds>,v1=<hex>, the HMAC-SHA256 of the time, a dot and the raw body: check it, and refuse a time more than 5 minutes old so an old message cannot be replayed. X-VoicePro-Signature is the HMAC-SHA256 of the raw body alone, as before.
Each delivery has a 10-second timeout. One that does not answer 2xx is tried again, after 15 minutes, 1, 3, 6, 12 and 24 hours, for up to 3 days; X-VoicePro-Delivery-Attempt says which try it is. Every event carries an id, so a receiver can ignore one it has already handled. The last 50 deliveries for each webhook are in GET /webhooks/{id}/deliveries and the portal, and 30 days of events in GET /events; POST /events/{id}/resend sends one again. POST /webhooks/{id}/test (or the portal's Test button) sends a signed test delivery to your address. A SIM suspended or reactivated, a number transfer update and events from the IoT network are sent straight away; spending, data cap, roaming, answer rate, invoice, incident and alert rule events are checked every 15 minutes.
{
"id": "<event id, evt_...>",
"event": "<event code>",
"type": "event",
"occurredAt": "<ISO date-time>",
"workspaceId": "<your workspace id>",
"data": { ... }
}const header = String(req.headers["voicepro-signature"] ?? "");
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const expected = crypto.createHmac("sha256", SECRET).update(`${parts.t}.${rawBody}`).digest("hex");
const sent = String(parts.v1 ?? "");
const ok = fresh && sent.length === expected.length && crypto.timingSafeEqual(Buffer.from(sent), Buffer.from(expected));
if (!ok) return reject();data: pct, used, limit, currency, period
data: simId, label, pct, usedBytes, capGb
data: simId, label, countries
data: Mobile SIMs: simId, label, state ("suspended" | "active"). IoT SIMs: iot: true, simId, label, activated
data: asr, asrSameTimeYesterday, calls, window
data: id, period, totalGbp, dueAt
data: id, period, totalGbp, dueAt
data: id, status, transferDate, actionNeeded
data: id, title, kind, impact, startedAt, statusPage
data: rule, name, trigger, alerts
data: simId, iccid, label, time, network, networkType
data: simId, iccid, label, time, network, networkType
data: simId, iccid, label, time, network, networkType
data: simId, iccid, label, time, network, networkType, bytes, reason, dataLimit { used, total, period, alertAt, resetsAt }
data: simId, iccid, label, time, network, networkType, bytes
data: simId, iccid, label, time, network, networkType, text
data: simId, iccid, label, time, network, networkType, status, direction ("to_device" | "from_device")
data: jobId, total, succeeded, failed, change, finishedAt
data: watch_id, phone_number, label, at, changes { field: { from, to } }
Tools
Every call with a working example. Import it into Postman, set apiKey to your key and send.
OpenAPI 3.1 specThe full contract with examples. Generate a client in any language, or load it into your API tools.
Plain text for AI toolsThe whole reference as one text file. Give it to Claude, ChatGPT or Cursor and ask it to write your integration.
TypeScript clientOne file, no dependencies: a method for every call, and errors raised with their code. new VoicePro(key).getAccount()
Python clientOne file, needs requests: a method for every call. VoicePro(key).get_account()
Connect an AI assistant (MCP)
Our MCP server at https://api.voicepro.plus/mcp gives AI assistants one tool for every call in the reference, using your own key. A tool call is the same API call: the key's permissions, rate limits and access log all apply, and a test key keeps it on example data.
# Claude Code
claude mcp add --transport http voicepro https://api.voicepro.plus/mcp \
--header "Authorization: Bearer vpkey_test_YOUR_KEY"
# Cursor (.cursor/mcp.json)
{ "mcpServers": { "voicepro": { "url": "https://api.voicepro.plus/mcp",
"headers": { "Authorization": "Bearer vpkey_test_YOUR_KEY" } } } }Versioning and uptime
The version is in the address: https://api.voicepro.plus/v3. Within v3 we only add: new calls, new optional fields and new values. Your code should ignore fields and values it does not know.
A breaking change only ever ships as a new major version, and the previous version keeps working for at least 12 months after we announce it, with a Sunset header on its responses.
Our uptime target is 99.9% each calendar month, reported on the status page.
Changelog
Follow it in any feed reader: RSS.
Cancel a number, and number watches
- POST /numbers/{id}/cancel: no further charges; the number keeps working to the end of the month already paid, then is released.
- Number watches: POST /number_watches re-checks a number daily and records every change (porting, network, line status, risk); GET /number_watches/{id}/events lists them.
- New webhook event number.watch_changed, sent when a watched number changes.
- The number check product is now named as it is everywhere else: code nrv (VPX-NRV, Number Range Verify).
- POST /dip_batches: check a file of up to 20,000 numbers, paid from your balance before it starts; anything not run is refunded.
- SIMs: POST /sims/{id}/suspend and /reactivate; IoT: POST /iot/sims/{id}/sms sends a text to the device.
- POST /port_requests: ask to transfer numbers in. POST /invoices/{id}/payment_link: a secure payment link for an open invoice. GET /invoices/{id}/pdf: the VAT invoice.
- Webhooks v2: several event types per webhook, a timestamped signature (VoicePro-Signature), retries for up to 3 days, and 30 days of events in GET /events with POST /events/{id}/resend.
- New key scopes: sms:send, porting:write and billing:write.
API v3
- The API moves to api.voicepro.plus/v3, with a new home at voicepro.plus/developers: get started, recipes, guides and a searchable reference with cURL, JavaScript and Python for every call.
- Test keys (vpkey_test_...): every call answers with example data, and number checks answer from fixed test numbers. Never charged.
- MCP server at api.voicepro.plus/mcp: AI assistants get one tool for every call, using your key.
- Downloads: OpenAPI 3.1 spec, Postman collection, TypeScript and Python clients, and the whole reference as plain text for AI tools.
- Portal: API and webhooks now lists every request each key made in the last 30 days.
- DELETE /iot/connectors/{name} is now listed in the reference (it was live but not shown).
- New calls: GET /trunks/channels (channels in use per trunk, now and at peak), GET /fraud/incidents (your fraud incidents and losses, with the previous period) and GET /risk_ranges (search the high-risk and premium-rate ranges our network screens against).
- New call: GET /number_requirements (what you must supply, per country and number type, before a number goes live).
- Webhooks by API: list the events, create, change, pause, delete and test webhooks, and read each one's deliveries (GET/POST /webhooks and /webhooks/{id}).
- IoT SIMs: change one SIM (PATCH /iot/sims/{id}: name, on or off, IMEI lock, data limit, technologies), its eSIM state and SMS history, and your network lists with a SIM put on one.
Coming next
Get your key
API keys and webhooks come with every Enterprise account. No Enterprise account yet? Talk to us.
