Contactivity

Voice apps, PBX apps, clients and pricing

A voice app decides what happens on a call: fetch CoXML, send events to your call-control webhook, or run a flow. PBX apps are ready-made IVRs, ring groups and voicemail boxes. Client tokens let the browser and mobile voice SDKs make and take calls. Pricing shows what the project pays.

All paths are relative to https://api.contactivity.io/v1/projects/{project_id}. Authentication, errors, paging and idempotency work as described in the overview. Flow versions are in Flows.

Method Path Scope Purpose
GET /voice-apps apps:read List voice apps (paged; filter kind, comma-separated)
POST /voice-apps apps:write Create a voice app
GET /voice-apps/{app_id} apps:read Fetch a voice app
PATCH /voice-apps/{app_id} apps:write Update a voice app
DELETE /voice-apps/{app_id} apps:write Archive a voice app
GET /pbx-apps pbx:read List PBX apps (paged)
POST /pbx-apps pbx:write Create a PBX app
GET /pbx-apps/{app_id} pbx:read Fetch a PBX app
PATCH /pbx-apps/{app_id} pbx:write Update a PBX app
DELETE /pbx-apps/{app_id} pbx:write Archive a PBX app
GET /pbx-apps/{app_id}/messages pbx:read List voicemail messages (paged)
DELETE /pbx-apps/{app_id}/messages/{message_id} pbx:write Archive a voicemail message
POST /clients/tokens clients:write Create a client access token
DELETE /clients/{identity}/tokens clients:write Revoke an identity's tokens
GET /clients/settings clients:read Fetch client settings
PATCH /clients/settings clients:write Update client settings
GET /push-credentials clients:read List push credentials (paged)
POST /push-credentials clients:write Create a push credential
DELETE /push-credentials/{credential_id} clients:write Archive a push credential
GET /pricing pricing:read The project's price list
GET /pricing/check pricing:read Price one call

PATCH requests change only the fields you send; the result is checked like a create. Request bodies refuse unknown fields (400 unknown_parameter).

Voice apps

The voice app object

Field Type Description
id string app_… (AP… for apps made through the compatibility API)
kind string coxml, call_control or flow
name string
markup string coxml, plivo or auto (detect)
param_style string contactivity or twilio: the callback dialect (see below)
voice_url, voice_method string Where CoXML is fetched for each call (GET or POST)
fallback_url string or null Fetched when voice_url fails
status_callback string or null Receives call status changes
hangup_url string or null Called when the call ends
webhook_url string or null Call-control events
webhook_failover_url string or null Tried when webhook_url fails
flow object or null flow apps: the draft flow graph
flow_version integer or null flow apps: the published version that runs
enabled boolean A disabled app never runs: calls routed to it are rejected (logged in /errors, category app) and POST /calls with it returns 409 app_disabled
created_at timestamp

Create or update a voice app

Field Type Rules
kind string Required on create. Cannot be changed
name string Up to 100 characters
enabled boolean true (default) or false to disable the app
markup string coxml (default), plivo or auto
param_style string contactivity (default for new apps) or twilio. contactivity: call-control apps get the native event envelope (Events and webhooks) and CoXML requests carry snake_case params; twilio: Twilio/Telnyx-compatible names. An update keeps the stored value
voice_url string Required for coxml apps
voice_method string GET or POST (default)
webhook_url string Required for call_control apps
fallback_url, status_callback, hangup_url, webhook_failover_url string Optional; "" clears
flow object Required for, and only allowed on, flow apps. Validated on save (see POST /flows/validate)

Every URL must be a public http(s) URL.

Errors: 400 invalid_parameter (param names the field, including flow fields), 400 unknown_parameter, 400 invalid_json, 404 not_found. Deleting an app that a route or a PBX app uses returns 409 conflict.

sh
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/voice-apps \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
  -d '{"kind": "coxml", "name": "Main menu", "voice_url": "https://example.com/voice"}'

PBX apps

The PBX app object

Field Type Description
id string pa_…
name string
kind string ivr, ring_group or voicemail
config object The config of the kind, with defaults filled in
created_at, updated_at timestamp

Create or update a PBX app

name (1-100 characters) and kind are required on create; kind cannot be changed. A config you send replaces the stored one whole. Unknown config fields are refused.

An action is {"type", "target"}. type is extension (an extension or SIP username of this project), trunk, number (E.164, not blocked), sip_uri, pbx_app, voice_app, voicemail (a voicemail PBX app) or hangup (empty target). Ring group members take only extension, trunk, number or sip_uri.

A greeting is {"say"?, "voice"?, "play_url"?}: text up to 1000 characters, a voice id (1-64 letters, digits, _, ., -) and/or a public https audio URL.

Kind Config field Rules
ivr greeting Greeting
timeout_s 1-30, default 5
max_digits 1-6, default 1
retries 0-5, default 2
invalid_message Up to 1000 characters
options Up to 20 {"digits", "action"}; digits are 1-6 of 0-9*#, unique, at most max_digits long
default_action Action
ring_group strategy Required: simultaneous or sequential
ring_timeout_s 5-120, default 25
members Required: 1-20 member actions
no_answer_action Action
voicemail greeting Greeting
max_length_s 5-300, default 120
email_to Up to 10 email addresses
transcribe true transcribes each message, default false
transcribe_language Language code such as en-US (default) or es-ES

Errors: 400 invalid_parameter (param names the field, e.g. name, kind, strategy, options[0].digits, members[1].target), 400 unknown_parameter, 400 invalid_json, 404 not_found. Deleting an app that a route or another PBX app points at returns 409 conflict.

sh
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/pbx-apps \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name": "Main IVR", "kind": "ivr", "config": {
        "greeting": {"say": "Press 1 for sales, 2 for support."},
        "options": [{"digits": "1", "action": {"type": "extension", "target": "101"}},
                    {"digits": "2", "action": {"type": "number", "target": "+15551234567"}}]}}'

Voicemail messages

GET /pbx-apps/{app_id}/messages lists messages newest first: id (a recording id), from, to, duration_secs, created_at, download_url (signed, valid 10 minutes, or null) and transcription (id, status, text; null when the box does not transcribe). DELETE …/messages/{id} archives the message (the audio is kept; ?archived=true lists archived messages).

With transcribe on, each message is transcribed like a recording with transcription: true and charged per audio minute at the transcription rate, on top of the recording. The email_to email then waits for the transcript and includes it; after 2 minutes it goes out without it ("Transcript unavailable"). One email per message. Projects in restricted data mode get the short "sign in to listen" email, never the transcript.

Clients (in-app calling)

Access tokens

Mint a token on your server and hand it to the browser or mobile SDK. It signs in as client:<identity>, so calls to client:<identity> ring it.

Field Type Rules
identity string Required. 1-64 letters, digits, _, . or -
ttl integer 60-3600 seconds, default 3600
voice_app_id string Voice app for calls the client places (default: client settings)
push_credential_id string Push credential the mobile SDK registers its push token with

Response 201: {"token": "ctk1.…", "identity", "expires_at"}. Errors: 400 invalid_parameter (param identity, ttl, voice_app_id, push_credential_id).

DELETE /clients/{identity}/tokens ends every unexpired token of the identity and returns {"revoked": <count>}. Devices already registered stay until their registration is refreshed.

sh
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/clients/tokens \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
  -d '{"identity": "alice", "ttl": 600}'

Client settings

GET/PATCH /clients/settings: ring_time (10-180 seconds a call to a client rings) and voice_app_id (default app for calls clients place; "" clears it). Errors: 400 invalid_parameter (param ring_time, voice_app_id).

Push credentials

Your APNs key or Firebase service account, used to wake the mobile SDK for incoming calls. Secrets are stored encrypted and never returned.

Field Type Rules
kind string Required: apns or fcm
name string Required. 1-100 characters
team_id, key_id string apns: 10 characters, A-Z and 0-9
bundle_id string apns: your app's bundle identifier
private_key string apns: contents of the .p8 key (PKCS#8 P-256)
sandbox boolean apns: development builds
service_account object fcm: the Firebase service-account JSON file

The object returned is {"id": "pcr_…", "kind", "name", "team_id", "key_id", "bundle_id", "project_id", "sandbox", "created_at"}. Errors: 400 invalid_parameter (param name, kind, private_key, service_account, or no param for a bad APNs id), 404 not_found (delete). DELETE archives the credential: it is no longer used to wake devices.

Pricing

GET /pricing returns the project's price list: currency, billing_increment, products, then the sip_trunking prices at the top level: regions (voice rates for +1 numbers by region and direction), international (prefix rates; the longest matching prefix applies), custom_rates (your organization's rates, which replace standard prices for matching prefixes), number_fees, number_rates and number_rates_source (standard or custom), blocked_prefixes, and recording (recording per minute, recording_storage per minute-month, recording_storage_free_days, live_transcription per minute, increment, and custom when your organization has its own price; see Recording and storage). Amounts are money objects ({"amount": "0.009000", "currency": "USD"}, 6 decimals); each rate has its own increment (e.g. 60/60: per 60 seconds, rounded up).

products has one entry per product. sip_trunking and programmable_voice each have regions, international and custom_rates; programmable_voice_sip, byoc_sip_trunking and byoc_programmable_voice have one rate_per_min for every destination and direction, with increment and custom (true when your organization has its own rate).

GET /pricing/check?to=+442071234567&direction=outbound&duration=120 prices one call:

Query Rules
product A product; default sip_trunking
to Required, except for programmable_voice_sip and the BYOC products (one rate for every destination). E.164 (a missing + is added)
direction outbound (default) or inbound (a carrier calling your number to)
duration 1-86400 seconds, default 60

The response has to, direction, duration, product, then either blocked: true (outbound to a blocked destination), or custom, custom_prefix, billing_increment, billed_seconds and available. When available is true it also has rate_per_min and cost as money objects. Errors: 400 when product, to, direction or duration is not valid.

sh
curl "https://api.contactivity.io/v1/projects/$PROJECT_ID/pricing/check?to=%2B442071234567&duration=120&product=programmable_voice" \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY"