Skip to main content
Place outbound calls with bidirectional audio streaming, and retrieve call records. Calls are scoped to your agent identity and span dedicated phone numbers plus shared and dedicated iMessage lines. When you place a call, Inkbox dials the to_number and opens a WebSocket media stream to the client_websocket_url you provide. Your agent connects to that URL to send and receive audio (or text, depending on the pipeline mode). See Media Stream for details on pipeline modes. Alternatively, place the call with mode="hosted_agent" and Inkbox runs Inkbox Voice AI on the call — no WebSocket required.

Place call

Initiate an outbound call. Inkbox dials the to_number and opens a bidirectional media stream to your client_websocket_url.

Request body

Request example

JSON
To place the call from your agent identity’s shared iMessage line, omit from_number — the outbound line is resolved automatically:
JSON
To have Inkbox Voice AI run the call, pass mode and a reason instead of a WebSocket URL:
JSON
A Voice AI call leaves a voicemail by default: when the call reaches voicemail, the agent waits for the beep, leaves a message, and ends the call with hangup_reason "voicemail". Pass voicemail_message to control what it says; omit it and the agent composes a short message from reason:
JSON
Set on_voicemail to "hang_up" when the call should end instead — it hangs up as soon as a voicemail beep is detected, so automated greetings and phone menus are left alone. On Voice AI calls the agent stays silent from the machine report until the no-beep verdict (roughly 25–30 seconds) while keypad presses keep working. That is the default for client_websocket calls. Set "ignore" to skip detection entirely and treat whatever answers as a person. voicemail_detection remains accepted as a deprecated alias: "enabled" is "hang_up" and "disabled" is "ignore". The MCP call tool offers the same on_voicemail and voicemail_message fields; a call placed through MCP that omits both on_voicemail and voicemail_detection leaves a message. Voice AI inherits the identity’s saved authority when hosted_agent_authority_mode is omitted or null. An identity with no saved Voice AI configuration uses yolo. Set contact_scoped to narrow one call. Set yolo when the call needs identity-wide access to contacts, email, SMS, iMessage, or historical calls:
JSON

Response (200)

JSON
The place call response includes a rate_limit object with your organization’s rolling 24-hour usage snapshot (calls_used, calls_remaining, calls_limit, minutes_used, minutes_remaining, minutes_limit). Active calls, including initiated calls, count provisionally toward the daily call quota. Canceled outbound calls still count when dialing reached the phone network. Failed placements, pre-dial outbound cancellations, and inbound calls that end unanswered are excluded. These quota and call-admission rules are server-side behavior. They apply even when you use an older SDK or CLI version. For shared_imessage_number origination, local_phone_number is null because the shared line is never surfaced. A dedicated_imessage_number call surfaces its dedicated iMessage line as local_phone_number.

Error responses

For iMessage-line originations, actionable placement rejections use these structured 422 details:

Code examples


List calls

List calls for an agent identity, newest first — spans dedicated phone numbers plus shared and dedicated iMessage lines.

Query parameters

Identity-scoped API keys never see contact-rule-blocked calls, regardless of is_blocked. Admin API keys and human sessions see blocked and non-blocked calls by default; use is_blocked=true for an admin-side blocked listing. start_datetime, end_datetime, and tz filter on created_at. Both bounds are optional and non-breaking — omit them for unchanged behavior. See Filtering by date.

Response (200)

JSON

Code examples

The number-scoped listing is deprecated. GET /numbers/{phone_number_id}/calls only returns calls on that dedicated phone number and omits iMessage-line calls. It accepts the same start_datetime/end_datetime/tz date filters for parity. Use GET /calls instead.

Get call

Get a single call by ID across all three call origins.

Path parameters

Response (200)

Returns a call object.

Error responses

Code examples

The number-scoped route is deprecated. GET /numbers/{phone_number_id}/calls/{call_id} cannot address iMessage-line calls. Use GET /calls/{call_id}.

List Voice AI tool activity

List a call’s built-in and remote MCP tool activity in execution order. Activity is recorded for both contact_scoped and yolo Voice AI calls. Calls without recorded activity, including older calls and calls driven by your own WebSocket, return an empty page. The response includes the tool name, execution status, timestamps, and a small result summary for built-in tools. source is builtin or external_mcp. MCP records also include connection_id, config_version, connection_label, and server_url. Full arguments and results are retained when supplied by the provider, but are not returned by this endpoint; arguments_available and result_available indicate whether an external payload was captured. Credentials are never returned.

Path parameters

Query parameters

Response (200)

JSON
status is "started", "succeeded", "failed", or "unknown". For MCP calls, unknown means the outcome could not be confirmed; check the external service before repeating an action. started_at can be null when only a completion was observed; observed_at records when Inkbox first observed it. Page-level incomplete: true indicates missing external observations, including when no individual tool record is available. On yolo calls, an accepted write can remain "started" after the call ends while Inkbox finishes it. Poll this endpoint until it reaches a terminal status when completion matters.

Error responses

Code example


Hang up call

End a live call from outside it. The agent on the call can always end it in-band over the media WebSocket; this endpoint is the lever for everything else — tests, operator tooling, or any process that only holds the call ID. It works identically on Inkbox Voice AI calls, where it’s the operator kill switch. The carrier confirms the teardown asynchronously, so the returned call may still show its live status for a moment; the final status, ended_at, and transcript land through the normal call lifecycle (including the call.ended webhook).

Path parameters

Response (200)

Returns the call object as of the hangup request, normally with hangup_reason set to "local". If the call ended on its own at the same moment — the carrier reports it already over before the hangup lands — the request still returns 200, but the call closes with hangup_reason "remote". Don’t branch on hangup_reason to confirm the hangup took effect.

Error responses

Code examples


Call object

Call statuses

An outbound call rings for up to 60 seconds. If nobody answers in that window, Inkbox stops the dial and the call ends unanswered — a Voice AI call reports it as the no_answer outcome on call.ended. A call that reaches voicemail counts as answered: its hangup_reason is "voicemail" when on_voicemail ended it, whether after leaving a message or at the beep. A destination connection does not replace the original call’s status. For example, a call can be completed while a successful connection retains status: "connected" and has an ended_at timestamp.

Connection history

Each item in connections records one attempt to connect another destination. Records are ordered from oldest to newest. The listed kind, trigger, and status values can grow. Preserve unfamiliar strings and display an unknown value neutrally rather than assuming a handoff or a successful connection. "requested" and "dialing" do not mean the destination is connected. For a conference, answering the destination phone is not enough: the guest must join the conversation. A successful connection remains "connected" after it ends; ended_at records that later event. A failed attempt does not necessarily end the original call. A later termination reason can set failure_code without changing a successfully connected attempt to "failed". Read it alongside status and ended_at, and treat unfamiliar reason strings as opaque values; the set of reasons is not exhaustive. Automatic forwarding is kind: "handoff" with trigger: "incoming_action". A live transfer is kind: "handoff" with trigger: "agent_tool". A group call is kind: "conference" with trigger: "agent_tool". Transfers and conferences are actions the Voice AI agent takes during a conversation. This history is read-only; it does not add SDK or CLI transfer or conference commands.

Forwarding history

forwardings contains only automatic incoming-call forwarding attempts with trigger: "incoming_action". Live transfers and conference guests appear only in connections, so existing clients can continue reading calls and typed webhooks without accepting new forwarding trigger values. For an automatic forwarding attempt, both lists use the same id, destination, and request, dialing, ending, and failure fields, with this legacy field mapping: Use status, not the presence of failure_code, to determine whether forwarding succeeded. A "forwarded" record can also carry a later termination reason. Other status values are unchanged. Read connections when the field is present, even if it is empty. Fall back to forwardings only for older responses that omit connections; do not combine the two lists or count each attempt twice.