Contactivity
AccountMarkdown

Billing: usage, call records, balance, ledger

Read-only views of what the project spends. Everything is the project in the path only. Top-ups and payment methods stay in the portal. Paths are relative to https://api.contactivity.io/v1/projects/{project_id}; see the overview. Scope: billing:read.

Method Path Purpose
GET /usage Usage and cost by product over a period
GET /cdrs Call detail records (paged)
GET /balance Current balance
GET /ledger Balance changes (paged)

from and to (on /usage and /cdrs) take RFC 3339 or YYYY-MM-DD (00:00 UTC). from defaults to the start of the current UTC month, to to one month after from; the range is [from, to) and at most 366 days (400 invalid_parameter, param from or to).

Usage

sh
curl "https://api.contactivity.io/v1/projects/$PROJECT_ID/usage?from=2026-10-01&to=2026-11-01" \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY"
json
{
  "from": "2026-10-01T00:00:00.000Z", "to": "2026-11-01T00:00:00.000Z",
  "products": [
    {"product": "programmable_voice_outbound", "count": 412, "duration_secs": 30215, "cost": {"amount": "4.1230", "currency": "USD"}}
  ],
  "total_cost": {"amount": "4.1230", "currency": "USD"}
}

Call lines are <product>_inbound and <product>_outbound for each product, for example sip_trunking_outbound (from the call records); other lines are the ledger types of other charges (number fees, features). Errors: 400 invalid_parameter, 503 usage_unavailable.

Products

Each call leg is billed on its own, as one product:

Product Legs
sip_trunking Between your SIP trunk or PBX and the phone network
programmable_voice Phone network legs handled by a voice app (CoXML, Call Control, flow) or the Calls API
programmable_voice_sip Voice app legs that never reach the phone network: SIP addresses, in-app clients, your own trunks. One rate for every destination
byoc_sip_trunking Trunk legs through your own carrier. Platform fee only, the same both ways
byoc_programmable_voice Voice app legs through your own carrier. Platform fee only, the same both ways

A call that uses several of them is billed for each leg: a number routed to a voice app that dials your trunk is one programmable_voice leg and one programmable_voice_sip leg, and a number forwarded to a phone number is two sip_trunking legs: inbound at the number's inbound rate and outbound at the forwarding destination's rate, both for the whole call (a BYOC number forwarded through your carrier: two byoc_sip_trunking legs, the fee each way). Legs are billed per 60 seconds, rounded up, unless your agreement says otherwise; each rate's increment is under GET /pricing.

Calls between Contactivity numbers stay on our network and are billed at the programmable_voice_sip rate, never at phone network rates. A number forwarded to another Contactivity number (yours or another customer's) pays its inbound leg as usual and a programmable_voice_sip leg to that number; the number it reaches pays a programmable_voice_sip inbound leg on its own project. In a chain of forwards (A to B to C) each number pays for its own legs: A its programmable_voice_sip leg out, B one in and one out, C one in; a chain that ends at a phone number pays that outbound leg at its sip_trunking rate. When the project a call would be billed to on the receiving side has no credit, the caller hears that the number is not available (480); your own calls out of a project without credit get 402. A call that goes on to a route's fallback route is checked against your balance again before it rings, at the fallback's price; without enough credit it ends with 480.

Transactions show amounts to 4 decimals, rounded up: a charge below $0.0001 shows as $0.0001. Your balance and the amounts we store keep 6 decimals. A call from your trunk or a voice app to a Contactivity number is a programmable_voice_sip leg for you and one for the number's project. More than 3 numbers forwarding in a row is a loop: the call is refused with 482 Loop Detected and nothing is charged.

Standard prices

GET https://api.contactivity.io/v1/public/pricing returns the platform's standard prices, with no API key: voice per product (sip_trunking and programmable_voice per +1 region and direction, the others one rate per minute), and US and Canada local and toll-free numbers. It never includes your own terms; your project's prices are GET /pricing.

sh
curl "https://api.contactivity.io/v1/public/pricing"
json
{
  "currency": "USD", "billing_increment": "60/60",
  "voice": {
    "sip_trunking": {"regions": [{"id": "us48", "name": "US48",
      "outbound": {"rate_per_min": {"amount": "0.009000", "currency": "USD"}, "increment": "60/60"},
      "inbound": {"rate_per_min": {"amount": "0.005000", "currency": "USD"}, "increment": "60/60"}}]},
    "programmable_voice_sip": {"rate_per_min": {"amount": "0.004000", "currency": "USD"}, "increment": "60/60"},
    "byoc_programmable_voice": {"rate_per_min": null, "increment": "60/60"}
  },
  "numbers": [{"country": "US", "type": "local", "available": true,
    "setup_fee": {"amount": "1.000000", "currency": "USD"}, "monthly_fee": {"amount": "1.150000", "currency": "USD"}}]
}

A price that is not set is null; a number with "available": false is not sold. Responses are cached for 5 minutes (Cache-Control: public, max-age=300) and rate limited per IP address (429 with Retry-After).

Each region also lists its prefixes, the +1 area codes it covers (+1 holds every area code not in another region). features has the call feature prices: transcription and noise_suppression per minute, machine_detection and premium_machine_detection per attempt (price_per_attempt), recording per recorded minute and recording_storage per stored minute per month (rate_per_min_month) after free_days (see Recording and storage), live_transcription per minute (see Live transcription), and speech_recognition per 15 seconds (price_per_15_secs) only when it is priced. A detection attempt is charged each time detection runs on a call, whatever the result.

International prices

GET https://api.contactivity.io/v1/public/pricing/international?q= searches the standard international outbound prices of sip_trunking and programmable_voice, also with no API key. q is a destination name (2-60 characters, matched anywhere in the name), a prefix such as +44, or a whole number, which returns every prefix of it. Up to 100 destinations, by name; has_more is true when more match. A product without a standard price for a destination is null. Destinations under a blocked prefix are left out, and your own terms are never included.

sh
curl "https://api.contactivity.io/v1/public/pricing/international?q=united%20kingdom"
json
{
  "currency": "USD", "billing_increment": "60/60", "has_more": false,
  "destinations": [{"prefix": "+44", "destination": "United Kingdom",
    "sip_trunking": {"rate_per_min": {"amount": "0.020000", "currency": "USD"}, "increment": "60/60"},
    "programmable_voice": {"rate_per_min": {"amount": "0.025000", "currency": "USD"}, "increment": "60/60"}}]
}

Calling these destinations needs account tier 3 and an approved international calling request (POST /international-requests). Errors: 400 invalid_parameter (q missing, too short or too long), 429 rate_limit. Cached for 5 minutes, like the standard prices.

Recording and storage

Call recordings are a software charge, separate from call minutes (ledger types recording and recording_storage):

  • Recording: charged once when a recording completes, whatever started it (<Record>, <Dial record>, record_start, conference and trunk recording, voicemail): its duration rounded up to the billing increment (60/60) at the recording price per minute. A dual-channel recording counts its minutes once. The charge is the recording's price and shows in the call's pricing.
  • Storage: free for free_days (90) from the recording's creation. After that, each calendar month (UTC) a stored recording is charged for the whole month: its billed minutes times the storage price per minute-month, once per recording and month, at the first hourly run after its free period ends. Archived recordings are charged too: recordings are never deleted. Billing contacts get one notice per project and month.

Amounts are rounded to 4 decimals; a charge that rounds to $0 is not booked. Organizations can have their own recording and storage prices; GET /pricing shows them under recording.

Live transcription

Live transcription (<Start><Transcription>, transcription_start) is charged per minute of streamed audio, rounded up to the billing increment, once per session and track (ledger type live_transcription). Dual-track transcription counts each track. Organizations can have their own price, shown under recording.live_transcription in GET /pricing. Speech recognition in a speech <Gather> (ledger type speech_recognition) is charged per 15 seconds, at least one block per gather, when priced.

Call detail records

GET /cdrs lists rated call legs, newest first. Filters: from, to, direction (inbound, outbound), product, status (all, answered, missed), number (digits matched anywhere in the caller or destination), min_sec, min_cost, limit, starting_after.

Field Type Description
id string
sip_call_id string The leg's SIP Call-ID
direction string inbound or outbound
from, to string
started_at, answered_at timestamp answered_at null when unanswered
answered boolean
duration_secs integer
price money or null
product string See Products
hangup_cause string
quality object or null Media quality per leg

Errors: 400 invalid_parameter, 503 cdrs_unavailable.

Balance

GET /balance returns {"balance": {"amount": "125.40", "currency": "USD"}} (2 decimals). Errors: 503 balance_unavailable.

Ledger

GET /ledger lists balance changes, newest first; filter type (for example topup, refund, adjustment, number_monthly, recording, recording_storage, live_transcription, speech_recognition).

Field Type Description
id string
type string
amount money Always positive
direction string credit or debit
description string or null
status string succeeded, or pending until applied to the balance
created_at timestamp

Prices per destination are under GET /pricing (Voice apps).