Calls
Place outbound calls, list and fetch calls, and control live calls with actions. Paths are relative to
https://api.contactivity.io/v1/projects/{project_id}; auth, errors, paging and idempotency are in the
overview.
| Method | Path | Scope | Purpose |
|---|---|---|---|
| POST | /calls |
calls:write |
Place a call |
| GET | /calls |
calls:read |
List calls (paged) |
| GET | /calls/{call_id} |
calls:read |
Fetch a call |
| DELETE | /calls/{call_id} |
calls:write |
Archive a finished call's record |
| POST | /calls/{call_id}/actions/{action} |
calls:write |
Control a live call |
Recording starts and stops through actions (record_start, ...); the files are under recordings.
The call object
| Field | Type | Description |
|---|---|---|
id |
string | CA… |
parent_call_id |
string or null | The call that dialed this one (<Dial>, transfer) |
app_id |
string or null | The voice app handling the call |
app_kind |
string or null | Kind of that app: coxml, call_control, ai_agent, flow |
ai_agent |
boolean | An AI agent ran on the call (an ai_agent app, or <Connect><Agent> from another app) |
mode |
string | coxml (driven by CoXML documents) or call_control (driven by actions) |
direction |
string | inbound or outbound |
from, to |
string | E.164; a carrier's 1NXXNXXXXXX (no +) is shown as +1NXXNXXXXXX |
status |
string | queued, ringing, in_progress, completed, busy, failed, no_answer, canceled |
answered_by |
string or null | Machine detection result: human, machine_start, machine_end_beep, fax, unknown |
created_at, started_at, answered_at, ended_at |
timestamp or null | |
duration_secs |
integer or null | |
price |
money or null | Set once the call is rated |
quality |
object or null | Media quality measured at hangup |
client_state |
string or null | Echoes client_state in the create response; null on other reads |
Place a call
POST /calls returns 201 and the call (status queued). Give exactly one source of call logic:
| Source | Behaviour |
|---|---|
app_id |
A voice app of the project: a coxml app with a voice URL (its URL, method, fallback and status callback apply), a call_control app (you drive the call with actions; events go to the app's webhook_url, or the webhook_url given here), or an ai_agent app (the agent talks) |
coxml |
An inline CoXML document, at most 4000 characters |
coxml_url |
CoXML fetched when the call is answered (coxml_method GET or POST, default POST) |
webhook_url (alone) |
A call-control call without an app; events go to this URL |
| Field | Type | Rules |
|---|---|---|
to |
string | Required. E.164, client:<name>, or a SIP URI with a public host |
from |
string | Required. E.164; a number of the project or a verified number |
app_id, coxml, coxml_url, coxml_method, webhook_url |
See above. webhook_url with app_id only for call_control apps |
|
status_callback_url |
string | Public https:// URL for status callbacks |
status_callback_events |
string[] | initiated, ringing, answered, completed (default ["completed"]) |
timeout_secs |
integer | Ring time, 5-600, default 60 |
time_limit_secs |
integer | 1-14400, default 14400 |
machine_detection |
string | disabled (default), detect, detect_words, detect_beep, greeting_end, premium |
machine_detection_config |
object | total_analysis_time_millis, after_greeting_silence_millis, greeting_duration_millis, initial_silence_millis (each 100-60000) |
record |
false, "single" or "dual" |
Record the call, mono or one channel per party |
client_state |
string | Base64, at most 4096 characters; echoed on events |
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/calls \
-H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
-H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
-d '{"to": "+14155550100", "from": "+12125550123",
"coxml": "<Response><Say>Your order has shipped.</Say></Response>",
"status_callback_url": "https://example.com/status", "status_callback_events": ["answered", "completed"]}'Errors: 400 invalid_parameter (param to, from, app_id, coxml, coxml_url, coxml_method,
webhook_url, status_callback_url, status_callback_events, timeout_secs, time_limit_secs,
machine_detection, record, client_state), 400 unknown_parameter, 402 insufficient_balance,
403 caller_id_not_verified (param from), 403 destination_blocked (param to), 403 account_inactive,
409 app_disabled (param app_id: the voice app is disabled), 429 rate_limit (calls per second or simultaneous calls), 503 engine_unavailable.
List and fetch
GET /calls filters: status, direction (inbound/outbound), from, to (+1NXXNXXXXXX also matches a
stored 1NXXNXXXXXX), parent_call_id, app_id (exact matches), app_kind (coxml, call_control, ai_agent,
flow; ai_agent also matches agents started from another app), created_after (inclusive) and created_before
(exclusive) as RFC 3339, plus limit and starting_after.
curl "https://api.contactivity.io/v1/projects/$PROJECT_ID/calls?status=completed&limit=20" \
-H "Authorization: Bearer $CONTACTIVITY_API_KEY"GET /calls/{call_id}: 404 not_found when the call is not in the project.
GET /calls/{call_id}/detail (scope calls:read) returns everything stored about the call in one response:
| Field | Description |
|---|---|
call |
The call object plus hangup_cause (from the carrier CDR, or null) |
legs |
Child calls (<Dial>, transfers), each a call object with its quality |
carrier_legs |
Rated carrier CDRs: sip_call_id, started_at, answered_at, duration_secs, price, hangup_cause |
routing |
number (dialed number inbound, caller ID outbound), route (id, type, target, target_name: trunk, PBX app or voice app name), route_at_call_time (false: the current route; routes are not stored per call), app (id, name, kind, param_style) |
ai_agent |
The agent session as GET /ai/calls/{call_id} plus agent_name, voice, session_sid and parts (charge per part); null when no agent ran |
requests |
Document fetches and webhooks of the call (as the call inspector); CoXML fetches carry verbs, the verbs of the returned document (Gather>Say for nested ones) |
commands |
API requests that changed the call (call actions, updates) from the access audit log: created_at, method, path, status, actor_type, command |
events |
Native events of the call (kept 7 days), oldest first |
errors |
Call errors (category, code, message, url) |
recordings, transcriptions |
Of the call; archived ones are left out |
pricing |
call, ai, ai_billed_secs, features (amd, noise_suppression with amount), transcriptions, total (money) |
An API key gets ai_agent only with apps:read, and recordings/transcriptions only with recordings:read.
Every read is written to the access audit log as sensitive (it can carry transcripts).
DELETE /calls/{call_id} archives a finished call's record: it leaves GET /calls (list archived calls with
?archived=true) and is still returned by id with archived_at set. Its webhook deliveries, errors, events,
recordings and transcriptions are kept. 204; 404 not_found; 409 archived (already archived);
422 call_state while the call is live.
Actions
POST /calls/{call_id}/actions/{action} returns 200 and the call. redirect and hangup work on every call.
The other actions need a call-control call (placed with a call_control app or a webhook_url); on a CoXML call
they return 409 call_state (use redirect to move a CoXML call to new CoXML).
Every action takes client_state (base64, at most 4096 characters). Other fields per action; anything else is
400 unknown_parameter:
| Action | Fields |
|---|---|
answer |
none |
hangup |
none |
reject |
cause |
redirect |
exactly one of coxml_url (+ coxml_method) or coxml (at most 4000 characters) |
bridge |
call_id (the other call), park_after_unbridge |
transfer |
to, from, timeout_secs, machine_detection, machine_detection_config |
refer |
sip_address |
speak |
payload, payload_type, voice, language |
play / stop_play |
audio_url, loop, overlay / stop |
gather |
minimum_digits, maximum_digits, timeout_millis, terminating_digit, valid_digits, inter_digit_timeout_millis |
gather_using_speak |
payload, payload_type, voice, language, input, hints + the gather fields |
gather_using_audio |
audio_url + the gather fields |
gather_using_ai |
parameters, greeting, voice, language, message_history, assistant, send_partial_results, timeout_secs |
stop_gather |
none |
record_start |
format, channels, play_beep, max_length, transcription, transcription_engine, transcription_language |
record_stop, record_pause, record_resume |
none |
send_dtmf |
digits, duration_millis |
enqueue / leave_queue |
queue_name, max_wait_time_secs, max_size / none |
stream_start / stream_stop |
stream_url (ws/wss), stream_track, stream_bidirectional_mode / none |
transcription_start / transcription_stop |
language, transcription_engine, transcription_tracks, interim_results / none |
ai_agent_start |
voice_app_id (an AI agent app), parameters |
noise_suppression_start / noise_suppression_stop |
direction / none |
update_client_state |
client_state (required) |
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/calls/$CALL_ID/actions/gather_using_speak \
-H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
-H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
-d '{"payload": "Press 1 for sales, 2 for support.", "maximum_digits": 1}'
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/calls/$CALL_ID/actions/redirect \
-H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
-H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
-d '{"coxml_url": "https://example.com/after-hours.xml", "coxml_method": "GET"}'The Idempotency-Key replaces Call Control's command_id: a retried action with the same key runs once.
Errors: 404 unknown_action (param action), 404 not_found, 400 unknown_parameter, 400 invalid_parameter
(param client_state, coxml_url, coxml, coxml_method, or a value the voice engine refused),
409 call_state (the action needs a call-control call), 422 call_state (the call has ended or is not in a
state that allows the action), 502 api_error, 503 engine_unavailable.
Events of call-control calls go to the app's webhook_url; see events.md.

