Start a web Call or an outbound phone Call
POST /v1/calls
| Property | Value |
|---|---|
| Operation | create_call |
| Group | Calls |
| Scope | calls:write |
| Route class | control |
| Idempotency | Idempotency-Key required: a retry with the same key replays the first answer |
| Audit | Audited call.create (mutation) |
| Released in | 1.0.0 |
| Phase 0 host | Served (the hosted api pins 1.65.0) |
Admission runs before any room or dial exists, in the order of control-plane.md section 7.1. From 1.19.0 it refuses with 402 spend_cap_reached when the Organization's spend in the current UTC calendar month is at or above its spend_cap_micros (FR-dispatch-6); from 1.23.0 that spend is the Organization's spend counter, which GET /v1/usage/spend reads. From 1.21.0 it refuses a test Call on real Providers with 429 test_daily_cap_reached once the Organization's test Calls on real Providers have used the daily allowance: 60 minutes of Calls per UTC day (target), configured in the api's environment (FR-platform-8). A test Call counts when any stage of its resolved chains names a Provider that is not a mock Provider; a Call on mock Providers only never counts and is never refused by the cap. A Call in progress is never cut, and the allowance starts again at 00:00 UTC. From 1.22.0 an outbound phone Call is checked before any room or dial exists, in this order, and refused with 403 dial_not_allowed and the detail reason named: a test key (live_environment_required); the Phone number it dials from, from_number_id, else the setting outbound.default_from, else the Organization's earliest active number with the outbound capability (number_not_outbound_capable, no_from_number), where a from_number_id of another Organization answers 404 not_found; the destination country against the dial policy's allowed_countries (country_not_allowed); a premium-rate or special-service destination while block_premium_rate is on (premium_rate); in Phase 0, the api's environment allowlist VOCAL_OUTBOUND_ALLOWLIST of workspace ADR 0070, which stays a second gate after the policy (not_on_environment_allowlist); and the policy's max_concurrent_outbound (outbound_cap_reached). A refusal places no carrier request and is recorded as a denied call.create Audit event with the destination hint, never the number. A server with no outbound dialling configured still answers 422 capability_not_available. From 1.38.0 (workspace ADR 0086 and its amendment of 2026-10-17) admission re-resolves the pinned version over the vendor price rows in force at the admission instant: a reference the version names, primary or Agent secondary, whose model has no list price above 0 for every unit of its stage refuses the Call with 409 agent_version_unavailable and a provider_not_allowed issue with the reason model_unpriced at its pointer, even when its stage keeps another entry, while a registry secondary so unpriced is dropped silently; and an outbound phone Call whose carrier minute has no list price above 0 in force is refused with 422 capability_not_available and one provider_not_allowed issue of stage telephony and reason carrier_unpriced. A web Call has no carrier leg. The Organization's own key changes neither rule. From 1.57.0 (workspace ADR 0048 decision 5) capture asks for verbose capture, and admission refuses it with 403 capture_not_allowed and one detail whose reason is a CaptureRefusalReason when the pinned Compliance profile's verbose_capture is off, or test_only for a live Call; without capture, an open capture window of the Agent that applies to the Call makes it verbose, never refusing it. From 1.61.0 (roadmap slice 2.15) the Call may name its Contact by contact_id or contact, resolved asserted in the admission transaction (a deleted Contact is 409 contact_not_active, an identity several Contacts share 409 contact_match_conflict), and an outbound Call that names one may omit to to dial its primary phone identity (contact_phone_missing). Whether or not it names a Contact, an outbound Call whose destination is opted out of voice for the Organization or the caller ID number is refused with 422 contact_opted_out (step 4a), one detail whose reason is a ConsentCheckReason; nothing is dialled and the Audit event carries the destination hint only. schedule creates the Call scheduled, admitted by its start job at scheduled_at (schedule_in_past, schedule_too_far, window_outside_platform_bound, and schedule_not_outbound for a Call that is not an outbound phone Call, each a detail of validation_failed); a window in the callee's zone with no zone known is 422 calling_window_zone_unknown. retry and voicemail apply to the Call and its retry.
Request
Set VOCAL_API to the base URL of your API and VOCAL_API_KEY to an API key with the calls:write scope (Authentication and keys). Replace each placeholder, such as agt_... or <name>, with your own value.
curl -X POST "$VOCAL_API/v1/calls" \
-H "Authorization: Bearer $VOCAL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "transport": "web", "agent_id": "agt_..." }'const response = await fetch(`${process.env.VOCAL_API}/v1/calls`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.VOCAL_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({
"transport": "web",
"agent_id": "agt_..."
}),
});
console.log(response.status, await response.text());import os
import uuid
import requests
response = requests.post(
f"{os.environ['VOCAL_API']}/v1/calls",
headers={
"Authorization": f"Bearer {os.environ['VOCAL_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"transport": "web",
"agent_id": "agt_...",
},
)
print(response.status_code, response.text)Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
idempotency-key | header | string | yes | A client-chosen key, kept 24 hours per Organization, environment and route. A replay returns the stored response. |
Request body
Content type application/json, required: CallCreateRequest.
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | AgentId | yes | |
agent_version | integer | no | A published version to pin; the active version of the Agent when omitted. |
capture | CaptureRequest | no | Added in 1.57.0 (ADR 0048 decision 5). {"mode": "verbose"} asks for verbose capture of this Call, refused with 403 capture_not_allowed when the pinned Compliance profile's verbose_capture forbids it; standard keeps a capture window of the Agent from applying. Absent, an open capture window decides. |
contact | ContactMatchInput | no | Added in 1.61.0. The Contact by an identity or an external id, in place of contact_id; one Contact must hold it (contact_match_conflict when several do, not_found when none does). |
contact_id | ContactId | no | Added in 1.61.0. The Contact the Call is for, resolved asserted. A merged Contact resolves through its tombstone; a deleted one is 409 contact_not_active. With transport: phone and no to, the Call dials its primary phone identity. |
direction | CallDirection | no | Added in 1.16.0. Absent means inbound. outbound is accepted only with transport: phone, requires to, and makes the platform dial it. |
from_number_id | PhoneNumberId | no | Added in 1.16.0. The Phone number whose number is the caller ID of an outbound Call. From 1.22.0, when it is omitted the setting outbound.default_from as resolved for the Call names it, and when that is unset too, the Organization's earliest active Phone number with the outbound capability. A number of another Organization is not found (404 not_found), and nothing is dialled; a number of the Organization that is not active or lacks the outbound capability is refused with 403 dial_not_allowed, reason number_not_outbound_capable. |
retry | RetryPolicyInput | no | Added in 1.61.0. Retries the Call once after an ended reason it lists. An outbound phone Call only (schedule_not_outbound otherwise). The retry (call.retry) runs admission again, the pre-call hook among it (workspace ADR 0049, decision 1). |
schedule | CallScheduleInput | no | Added in 1.61.0. Starts the Call later: it is created scheduled. An outbound phone Call only (schedule_not_outbound otherwise). From 1.64.0, decided: the start job (call.scheduled_start) runs admission in full at the start, the pre-call hook among it with the direction outbound, as for a Call admitted at once (workspace ADR 0049, decision 1). |
settings_overrides | AgentSettingsInput | no | Added in 1.18.0. The per-Call override, the Call layer of the settings block, which wins over the Agent version's values. Only the paths CALL_LAYER_PATHS lists may be set (the greeting's mode and wait_ms, speech.speaking_rate, listening.end_of_turn, listening.interruption, the silence timings and limits.max_duration_s); any other path is refused with 422 validation_failed and the issue setting_override_not_allowed. A path the platform or the Organization locks is refused with setting_locked, a value outside their bounds with setting_out_of_bounds, and limits.max_duration_s may only lower the value the layers above resolve to. |
to | string | no | Added in 1.16.0. The number to dial, in E.164 form such as +14155552486; required when direction is outbound and refused otherwise, except that from 1.61.0 an outbound Call that names a Contact may omit it to dial the primary phone identity of the Contact (contact_phone_missing when it has none). It is write-only: no response, list, log or Audit event repeats it, and the Call detail carries only destination_hint. |
transport | CallTransport | yes | |
variables | map of VariableValue | no | Added in 1.17.0. The values of the Agent version's declared variables, by name: at most 50. A required variable without a value is refused with 422 validation_failed and the issue variable_missing, an undeclared name with variable_unknown, and a value of the wrong type or longer than its max_length with variable_type. Values are write-only: no response, log or Audit event repeats them. |
voicemail | VoicemailOverrideInput | no | Added in 1.61.0. The answering-machine behaviour of the Call and its retry. An outbound phone Call only (schedule_not_outbound otherwise). |
Responses
| Status | Content type | Shape | Error codes |
|---|---|---|---|
201 | application/json | CallCreateResponse | |
400 | application/json | ErrorEnvelope | invalid_request |
401 | application/json | ErrorEnvelope | unauthenticated |
402 | application/json | ErrorEnvelope | spend_cap_reached |
403 | application/json | ErrorEnvelope | forbidden, org_suspended, dial_not_allowed, capture_not_allowed |
404 | application/json | ErrorEnvelope | not_found |
409 | application/json | ErrorEnvelope | agent_version_unavailable, contact_not_active, contact_match_conflict, idempotency_in_progress, idempotency_key_reused |
422 | application/json | ErrorEnvelope | validation_failed, capability_not_available, environment_not_allowed, contact_opted_out, calling_window_zone_unknown |
429 | application/json | ErrorEnvelope | concurrency_limit_reached, test_daily_cap_reached, rate_limited |
500 | application/json | ErrorEnvelope | internal_error |
503 | application/json | ErrorEnvelope | dependency_unavailable |
Fields of the 201 answer
The shape is CallCreateResponse.
| Field | Type | Required | Description |
|---|---|---|---|
agent_id | AgentId or null | yes | The Agent. Null only on an inbound phone Call refused at admission before it was resolved (1.22.0), such as a Call ended refused_no_route on a number that routes to no Agent; a reader on an older version must take 1.22.0 first. |
agent_version | integer or null | yes | The pinned Agent version. Null only on an inbound phone Call refused at admission before it was resolved (1.22.0), such as a Call ended refused_no_route on a number that routes to no Agent; a reader on an older version must take 1.22.0 first. |
answered_at | Timestamp or null | yes | |
answering_machine | AnsweringMachineResult or null | no | The answering-machine detection of an outbound phone Call (1.50.0, FR-calls-12), known once the Call is finalized, with any reviewer label; null when detection did not run (an inbound or web Call, detection off, or not finalized yet). Absent from an api before 1.50.0; a reader treats its absence as null. |
attempt | integer | no | The attempt number (1.61.0): 1, or 2 for a retry. Absent from an older api. |
campaign_attempt | integer or null | no | The enrollment's attempt the Call is (1.65.0); null otherwise. |
campaign_enrollment_id | CampaignEnrollmentId or null | no | The enrollment (1.65.0); null otherwise. Absent from an older api. |
campaign_id | CampaignId or null | no | The Campaign whose contact instance placed the Call (1.65.0); null otherwise. Absent from an api older than 1.65.0, which a reader treats as null. |
capture_mode | CaptureMode | no | What admission decided the Call captures (1.57.0): verbose when capture asked for it or an open capture window of the Agent applied. Absent from an api older than 1.57.0; a reader treats its absence as standard. |
capture_window_id | CaptureWindowId or null | no | The capture window that made the Call verbose (1.57.0); null when none did. Absent from an api older than 1.57.0; a reader treats its absence as null. |
client | CallClient or null | yes | What the browser needs to join a web Call. Null for an outbound phone Call, which has no browser client (1.16.0): the platform dials the callee. |
compliance_profile_version | integer or null | no | The version of the Organization's Compliance profile that admission read once for the Call (1.34.0; workspace ADR 0082, decision 1): 0 for the defaults, the number the Session configuration carries as compliance_profile_version. A change of the profile applies from the next Call, so this is the profile the Call ran under. Null on a Call created before the api recorded it and on a refused inbound Call that resolved none; a reader treats its absence as null. |
contact_id | ContactId or null | no | The Contact the Call resolved (1.61.0), or, for an ambiguous resolution, the candidate verification bound; null when none. Absent from an api older than 1.61.0, which a reader treats as null. |
contact_resolution | ContactResolution or null | no | How admission resolved the Contact (1.61.0); null on a Call admitted before 1.61.0. Absent from an older api. |
created_at | Timestamp | yes | |
direction | CallDirection | yes | |
duration_ms | integer or null | yes | From answer to end, known once the Call is finalized. |
end_requested_at | Timestamp or null | no | When POST /v1/calls/{call_id}/end asked the worker to end the Call; null when no end was requested, or when the Call ended at once. Added in 1.19.0: a reader treats its absence as null. |
ended_at | Timestamp or null | yes | |
ended_reason | EndedReason or null | yes | |
engine | EngineKind or null | yes | The engine of the pinned version. Null only on an inbound phone Call refused at admission before it was resolved (1.22.0), such as a Call ended refused_no_route on a number that routes to no Agent; a reader on an older version must take 1.22.0 first. |
environment | Environment | yes | |
experiment | CallExperiment or null | no | The experiment and arm dispatch assigned the Call to at admission (1.55.0, roadmap slice 2.9), identifiers only, beside the versions it ran. Null for a Call outside every experiment: one that resolves a pinned version, whose version a pre-call hook selected, or of an Agent with no running experiment. Absent from an api older than 1.55.0, which a reader treats as null. |
id | CallId | yes | |
knowledge_versions | array of KnowledgeVersionRef | no | The Knowledge base version each binding of the Call read (1.53.0, roadmap slice 2.8), resolved once at dispatch, beside the Policy versions it ran on: a Call reads exactly one version of each bound Knowledge base. Empty for a Call whose Agent version binds none; absent from an api older than 1.53.0, which a reader treats as none. |
max_duration_s | integer | yes | The duration ceiling fixed at admission. On a Call refused at admission (1.22.0) it is the ceiling admission would have fixed, or the Organization's ceiling when no Agent version was resolved. |
object | "call" | yes | |
process_id | ProcessInstanceId or null | no | The Process instance whose start_call node placed the Call (1.62.0); null otherwise. Absent from an api older than 1.62.0, which a reader treats as null. |
process_step_id | ProcessStepId or null | no | The step of that node (1.62.0); null otherwise. Absent from an older api. |
prompt_versions | CallPromptVersions or null | no | The prompt layers dispatch resolved once for the Call (1.55.0, roadmap slice 2.9): the platform base and the Organization layer version, beside the Agent layer the pinned Agent version names. Null for a Call admitted before the api resolved them; absent from an api older than 1.55.0, which a reader treats as null. |
purpose | CallPurpose | no | Why the Call exists (1.56.0): evaluation for a test Call of an evaluation suite run or a conversation test run, which runs on the headless Engine and records engine: headless, transport: web and direction: inbound, its caller being simulated or scripted. Absent from an api before 1.56.0 and for every other Call; a reader treats its absence as conversation. |
queued_at | Timestamp | yes | |
retry_of_call_id | CallId or null | no | The attempt this Call retries (1.61.0); null for a first attempt. |
ring_ms | integer or null | no | For an inbound phone Call (1.42.0, board #136): the ring-to-answer period, the milliseconds from the instant the inbound Call was received to the instant the Agent answered (answered_at), 0 to 60,000; the Call's recording begins with a ring tone for this duration, so a player marks the answer instant at this offset. Null for a web Call, an outbound Call and a Call that was not answered; absent from an api older than 1.42.0, which a reader treats as null. |
scheduled_at | Timestamp or null | no | When a scheduled Call's start job runs (1.61.0), aligned into its window, moved by a closed window; null for a Call started at once. Until the job admits the Call its queued_at is its creation instant, then the admission instant. |
status | CallStatus | yes | |
transport | CallTransport | yes | |
turn_count | integer or null | yes | The number of Turns, known once the Call is finalized. |