Contactivity
Numbers and SIPMarkdown

Bring your own carrier (BYOC)

Use your own carrier's numbers and PSTN connectivity with Contactivity trunks and voice apps. Your carrier sends calls for its numbers to Contactivity; calls placed with one of its numbers as caller ID leave through it. You pay your carrier for the PSTN and Contactivity a platform fee per minute.

Paths are relative to https://api.contactivity.io/v1/projects/{project_id}; see the overview. Creating or changing carriers and numbers needs account tier 2 or higher (403 tier_required).

Method Path Scope Purpose
GET /byoc/carriers byoc:read List carriers (paged)
POST /byoc/carriers byoc:write Create a carrier
GET /byoc/carriers/{carrier_id} byoc:read Fetch a carrier
PATCH /byoc/carriers/{carrier_id} byoc:write Update a carrier
DELETE /byoc/carriers/{carrier_id} byoc:write Archive a carrier
POST /byoc/carriers/{carrier_id}/emergency byoc:write Choose how 911 calls are routed
GET /byoc/carriers/{carrier_id}/numbers byoc:read List the carrier's numbers (paged)
POST /byoc/carriers/{carrier_id}/numbers byoc:write Add a number
GET /byoc/carriers/{carrier_id}/numbers/{phone_number} byoc:read Fetch a number
PATCH /byoc/carriers/{carrier_id}/numbers/{phone_number} byoc:write Route a number
DELETE /byoc/carriers/{carrier_id}/numbers/{phone_number} byoc:write Remove a number

How it works

  • A carrier belongs to one project. It has outbound gateways (where Contactivity sends calls) and inbound source IPs (where its calls come from).
  • Inbound: a call from one of the carrier's source IPs to one of its numbers goes to that number's route, like a number bought from Contactivity. A number routed to forward is forwarded out through the same carrier. Calls from those IPs to other numbers are not accepted as carrier calls.
  • Outbound: a call from your trunks, voice apps or the Calls API whose caller ID is one of the carrier's numbers leaves through the carrier's gateways. Any other caller ID uses Contactivity's own carriers.
  • Numbers bought from Contactivity never use BYOC, in either direction, and cannot be added to a carrier.
  • The same number can be on carriers of different organizations: an inbound call is matched by source IP and number together. If both match one call, it is refused (403).
  • Contactivity does not sign outbound BYOC calls with STIR/SHAKEN; your carrier attests them.

The carrier object

Field Type Description
id string byoc_…
project_id string
name string
status string active or archived
gateways object[] Outbound gateways in failover order: id, host, port, transport, priority, username, has_password
source_ips string[] Addresses (203.0.113.7) and ranges (198.51.100.0/28) your carrier sends calls from
emergency object How 911 calls are routed; see Emergency calls
numbers_count integer Active numbers on the carrier
created_at timestamp
archived_at timestamp or null

Create or update a carrier

Field Type Rules
name string Required on create. 1-100 characters, trimmed
gateways object[] Required on create. 1-10 gateways
source_ips string[] Required on create. 1-50 public IPv4 addresses or ranges of /24 or narrower

A gateway:

Field Type Rules
host string Required. A public IPv4 address or hostname, without a port
port integer 1-65535. Default 5060 (5061 for tls)
transport string udp (default), tcp or tls
priority integer 0-100, lower first; default 0
username string Optional digest login, if your carrier challenges INVITEs
password string At most 128 characters, needs username. Never returned (has_password says if one is set)

PATCH changes only the fields you send; gateways and source_ips are replaced as a whole. A gateway sent without password keeps the stored password of the gateway with the same host, port, transport and username.

sh
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/byoc/carriers \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Acme Telecom",
    "gateways": [
      {"host": "sip1.acme.example", "port": 5060, "transport": "udp", "priority": 0},
      {"host": "sip2.acme.example", "port": 5060, "transport": "udp", "priority": 1}
    ],
    "source_ips": ["198.51.100.0/28", "203.0.113.7"]
  }'

Errors: 400 invalid_parameter (param name, gateways, gateways[i].host, gateways[i].port, gateways[i].transport, gateways[i].priority, gateways[i].username, gateways[i].password, source_ips), 400 invalid_request (a source range that overlaps a SIP trunk's allowed IPs), 400 unknown_parameter, 403 tier_required, 404 not_found, 409 archived.

DELETE archives the carrier and its numbers: its gateways and source IPs stop working at once.

Gateways and failover

An outbound call tries the gateway with the lowest priority first (equal priorities: the order sent). When a gateway does not answer, answers 408 or 5xx, or asks for a login that it does not accept, the call moves to the next one. Any other answer (busy, not found, declined) ends the call: it is the callee's answer, not a gateway failure. When every gateway fails, the caller gets 503.

Source IPs

Ask your carrier for the addresses it sends calls from. A range alone opens nothing: only calls to the carrier's numbers are accepted from it. A range that overlaps a SIP trunk's allowed IPs is refused, because those addresses place calls.

Numbers

Add your carrier's numbers to the carrier. There is no verification: you confirm you hold them.

Field Type Rules
phone_number string Required on add. E.164
route_id string or null A route of the project (routes). null unroutes
fallback boolean Default false. true: a call that fails on the route goes on to the route's fallback route

The number object: id and phone_number (the E.164 number), carrier_id, project_id, route_id, fallback, created_at, archived_at. PATCH takes route_id and/or fallback; fallback alone keeps the route. Phone numbers in paths are URL-encoded (/numbers/%2B14155550123).

sh
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/byoc/carriers/$CARRIER_ID/numbers \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
  -d '{"phone_number": "+14155550123", "route_id": "rt_us1_…"}'

Errors on add: 400 invalid_parameter (param phone_number, route_id), 403 tier_required, 404 not_found, 409 number_in_inventory (a Contactivity number: route it under numbers instead), 409 already_exists (already on a carrier of your organization), 409 archived (the carrier). On PATCH: 400 missing_parameter and 400 invalid_parameter (param route_id), 404 not_found, 409 archived. DELETE archives the number (204).

A BYOC number can be the caller ID of calls from your trunks and voice apps; those calls leave through its carrier. It is not listed under /numbers.

Emergency calls

A 911 or 933 call whose caller ID, or the calling trunk's default_caller_id, is one of the carrier's numbers is not sent to your carrier until you consent to route it there. With consent, the call goes to the carrier's gateways. Read emergency on the carrier:

Field Description
mode carrier, platform (consent withdrawn), or null (never chosen)
route carrier when consent is recorded, else platform
consent_at, consent_by, consent_text_version The recorded consent, or null

POST /byoc/carriers/{carrier_id}/emergency:

Field Type Rules
mode string Required. carrier (give consent) or platform (withdraw it)
consent boolean Required true with carrier: you confirm your carrier delivers 911 calls for these numbers, with the caller's location. Not allowed with platform
consent_text_version string Required with carrier, 1-50 characters: the version of the consent text the person accepted
sh
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/byoc/carriers/$CARRIER_ID/emergency \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" -H "Content-Type: application/json" \
  -d '{"mode": "carrier", "consent": true, "consent_text_version": "2026-10"}'

Who consented, when and from which address is kept in the project's audit log. {"mode": "platform"} clears the consent. Returns the carrier. Errors: 400 consent_required, 400 invalid_parameter (param mode, consent, consent_text_version), 403 tier_required, 404 not_found, 409 archived.

Billing

BYOC legs are billed a platform fee per minute, the same inbound and outbound and for every destination; no PSTN rate applies. The product is byoc_sip_trunking for legs to or from your trunks and byoc_programmable_voice for legs handled by a voice app. Check a fee with GET /pricing/check?product=byoc_programmable_voice (Voice apps); legs show their product in calls and call records.

Limits

Calls through your carrier count toward the organization's calls-per-second and concurrent-call limits (GET /limits).