Contactivity
Voice.md

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, run a hosted AI agent, 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. AI agent versions, tests and analytics are in ai-agents.md; flow versions in flows.md.

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, ai_agent 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
agent object or null ai_agent apps: the agent config, with every default filled in
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.md) 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
agent object Required for, and only allowed on, ai_agent apps. instructions is required; see the AgentConfig schema in the OpenAPI spec for every field
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 agent and 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"}'

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": "ai_agent", "name": "Receptionist", "webhook_url": "https://example.com/agent-events",
       "agent": {"instructions": "You are the receptionist for Acme Dental.", "greeting": "Hi, how can I help?"}}'

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

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 and download_url (signed, valid 10 minutes, or null). DELETE …/messages/{id} archives the message (the audio is kept; ?archived=true lists archived messages).

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, 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, ai_rates_per_min and ai. 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).

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

Query Rules
to Required. 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, 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 invalid_parameter (param to, direction, duration).

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