Phone numbers
Buy, configure, and release the DIDs your calls flow through.
All paths live under
/v1/. Authentication isAuthorization: Bearer <api-key>on every request.
Number types
| Type | Notes |
|---|---|
local | A normal geographic number (e.g. +1 512 …). Cheapest, available in most countries. |
toll_free | US/CA toll-free (+1 800/833/844/855/866/877/888 …). Free for the caller; you pay both directions. |
national | A non-geographic national-coverage number. Availability varies by country. |
mobile | A mobile-prefix DID. Required in some countries before SMS works. |
The number_type you pick at order time affects pricing (see the
quote in the order response). Some types are unavailable in some
countries — check GET /v1/numbers/available before you commit.
Search the inventory
GET /v1/numbers/available
?country_code=US
&number_type=local
&national_destination_code=512 # area code; optional
&locality=Austin # city name; optional
&features=voice,sms # comma list
&limit=25
{
"data": [
{
"phone_number": "+15125550100",
"country_code": "US",
"region": "TX",
"locality": "Austin",
"number_type": "local",
"capabilities": ["voice", "sms"],
"pricing": {
"activation_fee_cents": 100,
"monthly_fee_cents": 150
}
}
],
"meta": { "count": 25 }
}
Pricing is your customer price (after markup), so what you see is what we'll debit.
Reserve before you buy (optional)
If you want to hold a candidate set while a user picks one in your UI, reserve them first. Reservations expire automatically.
POST /v1/number-reservations
Content-Type: application/json
{
"phone_numbers": ["+15125550100", "+15125550101"]
}
Response includes expires_at. The numbers can't be bought by anyone
else (including other voepy tenants) until then.
Order a number
POST /v1/numbers/order
Content-Type: application/json
Idempotency-Key: <uuid>
{
"phone_numbers": ["+15125550100"],
"country_code": "US",
"number_type": "local"
}
What happens:
- We quote the order against the current pricing tier (NRC + MRC).
- We debit your balance for the NRC up front. If you can't cover it,
you get
402 balance_insufficientand nothing is ordered. - We submit the order to the upstream carrier. The number row appears
in
pendingstatus immediately. - When activation completes (usually seconds, sometimes minutes), the
row flips to
activeand you'll receive anumber_order.completedwebhook.
The response is a list of PhoneNumber resources:
[
{
"id": "f3b0a9b7-…",
"e164": "+15125550100",
"country_code": "US",
"number_type": "local",
"capabilities": ["voice", "sms"],
"status": "pending",
"monthly_price_cents_customer": 150,
"activation_price_cents_customer": 100,
"connection_id": null,
"ordered_at": "2026-05-12T14:32:18.000Z",
"activated_at": null,
"released_at": null
}
]
Always send
Idempotency-Keyon/numbers/order. Retrying without it can charge you twice for the same number.
List your numbers
GET /v1/numbers?status=active&limit=50
Filter by status (pending, active, released), number_type,
or connection_id.
Configure a number
PATCH /v1/numbers/{id} updates voice / messaging settings. Only
include the fields you want to change.
PATCH /v1/numbers/f3b0a9b7-…
Content-Type: application/json
{
"customer_reference": "primary-support-line",
"cnam_lookup_enabled": true,
"cnam_display_name": "Yourco Sales",
"messaging_enabled": true,
"connection_id": "9a8d1c…"
}
| Field | What it does |
|---|---|
customer_reference | Free-form tag (≤120 chars). Shown back to you on the resource and in admin views. |
cnam_lookup_enabled | Look up caller-id name on inbound. Adds per-call cost (see pricing). |
cnam_display_name | The 15-char name displayed to callees on outbound when CNAM is supported. |
tech_prefix_enabled | Add the technical prefix on SIP signaling. Most tenants leave this off. |
inbound_call_screening | disabled, flag_calls (flag suspicious in headers), or reject_calls (refuse them outright). |
messaging_enabled | Attach the tenant's messaging profile so the DID can send/receive SMS. |
connection_id | Reassign the DID to a different connection (sub-customer scope). null to detach. |
You'll get 409 number_not_yet_active if you try to patch a pending
number — retry once it activates.
Release a number
DELETE /v1/numbers/{id}
We release the DID upstream and stop billing the monthly fee at end of
the current cycle (no refund for partial months). The row stays
queryable in released state for audit / dispute purposes.
Number lookup
Look up metadata for any number (yours or not). Useful for caller-id enrichment.
GET /v1/numbers/lookup?phone_number=+15125550100&type=carrier
type is carrier (carrier name + network type) or caller-name
(CNAM). Both are billed per lookup.
Porting numbers in
Bringing a number you already own at another carrier:
# 1. Pre-flight: are these numbers portable?
GET /v1/numbers/portability-check?phone_numbers=+15125550100,+15125550101
# 2. Pull the CSR (Customer Service Record) to confirm losing-carrier data
POST /v1/numbers/csr-coverage
{ "phone_numbers": ["+15125550100"] }
# 3. Submit the porting order
POST /v1/numbers/porting-orders
{
"phone_numbers": ["+15125550100"],
"user_feedback": { "comment": "Moving from CarrierX" },
"documents": ["<doc-id-from-step-4>"]
}
# 4. Upload supporting docs (LOA, recent bill)
POST /v1/numbers/porting-documents
Content-Type: multipart/form-data
... <file uploads>
Porting takes business days, not seconds. Status transitions fire
webhooks (porting_order.*). Track progress at GET /v1/numbers/porting-orders.
Emergency 911 (US/CA)
If you originate emergency-capable calls, register an address per DID:
POST /v1/emergency-addresses
{
"street_address": "1 Example St",
"extended_address": "Suite 200",
"locality": "Austin",
"administrative_area": "TX",
"postal_code": "78701",
"country_code": "US"
}
Then attach it via an emergency endpoint that maps DID → address. Required for compliance — uncovered numbers will reject 911 traffic.
Connections (sub-customer scope)
A connection groups one or more numbers under a shared webhook /
configuration scope. Default tenants have one connection. Multi-brand
tenants can create more and assign DIDs to them via PATCH /v1/numbers/{id} with connection_id.
GET /v1/connection # the tenant's default connection
GET /v1/connections # all connections you own
Webhook subscriptions can be scoped to a single connection (see Webhooks).
