Skip to main content

Connect Two Numbers

Initiate an outbound call that connects two phone numbers — typically an agent and a customer. Exotel first calls the From number, waits for it to answer, then dials the To number and bridges the two legs.

Using Voice v1 endpoint

Call initiation in Voice v3 uses the Voice v1 endpoint (/v1/Accounts/...). Use the Voice v3 Call Details API to fetch enhanced reporting on the call after it is placed.

Endpoint​

POST /v1/Accounts/<account_sid>/Calls/connect

Content-Type: application/x-www-form-urlencoded

Regional Base URLs​

RegionBase URL
Singaporehttps://<api_key>:<api_token>@api.exotel.com
Mumbaihttps://<api_key>:<api_token>@api.in.exotel.com

Prerequisites​

Before initiating a call, ensure the following:

  1. Agent device must be ON — The From number must have an active device set to available: true. Use the Manage Devices API to check or update device availability.
  2. Agent must not be on another call — If the agent's device is already in use, the API returns 409 with error code 10706 (User device busy). Wait for the current call to end before retrying.
  3. Phone numbers in E.164 format — Both From and To should be in E.164 format (e.g., +919900XXXXXX), or From can be a SIP URI.
Error CodeHTTPDescriptionFix
1012409User device unavailable (OFF)Ask agent to turn their device ON
10706409User device busyAgent is on another call — wait and retry

Request Parameters​

Required​

ParameterTypeDescription
FromStringThe number dialled first (typically your agent or dialler). Accepts an E.164 phone number (e.g., +919900XXXXXX) or a SIP URI (e.g., sip:agent@exotel.com).
ToStringThe customer's phone number. E.164 format only (e.g., +918800XXXXXX).
CallerIdStringYour Exotel ExoPhone / virtual number used as the outbound caller ID.

Optional​

ParameterTypeDefaultDescription
CallTypeString—Set to trans for transactional calls.
TimeLimitInteger—Maximum call duration in seconds. Maximum value: 14400 (4 hours).
TimeOutInteger—Ringing timeout in seconds before the call is considered unanswered.
WaitUrlString—Audio file URL (WAV) played to the From caller while waiting for To to answer. File should be under 2 MB. Note: URLs are cached — append a unique query parameter (e.g., ?v=2) when updating audio.
RecordBooleanfalseSet to true to record the conversation.
RecordingChannelsStringsinglesingle (merged audio) or dual (separate channels per leg).
RecordingFormatStringmp3mp3 or mp3-hq (high quality).
StreamUrlString—WebSocket URL for real-time audio streaming. See AgentStream.
StreamBeginString—When streaming starts: at Leg1Connect or at Leg2Connect.
CustomFieldString—Custom metadata string, max 128 characters. Passed through to StatusCallback and applets.
StartPlaybackToNewStringCalleeWho hears pre-call audio: Callee or Both.
StartPlaybackValueNewString—Audio URL played to the specified party before the call connects.
StatusCallbackString—Webhook URL to receive call status updates (POST).
StatusCallbackEventsArray—Events to trigger the callback: terminal, answered, or both.
StatusCallbackContentTypeStringmultipart/form-dataFormat of the callback payload: multipart/form-data or application/json.

Code Examples​

curl -X POST \
'https://<api_key>:<api_token>@api.exotel.com/v1/Accounts/<account_sid>/Calls/connect' \
-d 'From=+919900XXXXXX' \
-d 'To=+918800XXXXXX' \
-d 'CallerId=09XXXXXXXXX' \
-d 'Record=true' \
-d 'TimeLimit=600' \
-d 'StatusCallback=https://your-server.com/callback' \
-d 'StatusCallbackEvents[]=terminal'

Response​

{
"Call": {
"Sid": "b6cfaf5f5cef3ca0fc937749ef96d245",
"ParentCallSid": null,
"DateCreated": "2024-03-03 10:48:33",
"DateUpdated": "2024-03-03 10:53:33",
"AccountSid": "<account_sid>",
"To": "0XXXXX38847",
"From": "0XXXXX30240",
"PhoneNumberSid": "0XXXXXX4890",
"Status": "in-progress",
"StartTime": "2024-03-03 10:48:33",
"EndTime": null,
"Duration": null,
"Price": null,
"Direction": "outbound-api",
"AnsweredBy": null,
"RecordingUrl": null,
"Details": {
"ConversationDuration": 0,
"Leg1Status": "in-progress",
"Leg2Status": "queued",
"Legs": []
}
}
}

Response Fields​

FieldTypeDescription
Call.SidStringUnique call identifier. Use this with the Call Details API for enhanced reporting.
Call.ParentCallSidString / nullParent call identifier if this is a child call.
Call.DateCreatedDateTimeTimestamp when the call was created.
Call.DateUpdatedDateTimeTimestamp of the last status update.
Call.AccountSidStringYour Exotel account SID.
Call.ToStringThe To phone number.
Call.FromStringThe From phone number.
Call.PhoneNumberSidStringIdentifier of the ExoPhone used as CallerId.
Call.StatusStringCall status — see Call Statuses below.
Call.StartTimeDateTimeTime the call was initiated.
Call.EndTimeDateTime / nullTime the call ended (null while in progress).
Call.DurationString / nullTotal duration in seconds (updates ~2 minutes after call ends).
Call.PriceString / nullCall cost (updates ~2 minutes after call ends).
Call.DirectionStringoutbound-api for calls initiated via this endpoint.
Call.AnsweredByString / nullHuman, Machine, NotSure, or NA.
Call.RecordingUrlString / nullDirect URL to recording MP3 (populated if Record=true).
Call.Details.ConversationDurationIntegerSeconds both parties were connected.
Call.Details.Leg1StatusStringStatus of the From leg.
Call.Details.Leg2StatusStringStatus of the To leg.
Call.Details.LegsArrayPer-leg detail objects with Id and OnCallDuration.
note

Duration, Price, and EndTime are populated asynchronously — typically within 2 minutes after the call ends. Use the StatusCallback webhook to receive the final values.

Call Statuses​

StatusDescription
queuedCall is queued, waiting to be sent to the operator.
in-progressCall is currently active.
completedCall ended normally.
failedCall could not be completed.
busyTo number was busy.
no-answerCall was not answered within TimeOut.

StatusCallback Webhook​

When the call reaches a terminal state (or is answered, if configured), Exotel sends a POST to your StatusCallback URL.

Payload Fields​

ParameterDescription
CallSidUnique call identifier.
EventTypeterminal or answered.
StatusFinal call status (completed, failed, busy, no-answer).
FromFrom-leg number.
ToTo-leg number.
PhoneNumberSidExoPhone identifier.
StartTimeCall start timestamp.
EndTimeCall end timestamp.
DurationTotal call duration in seconds.
ConversationDurationTalk time in seconds.
RecordingUrlRecording URL (if Record=true).
Directionoutbound-api.
CustomFieldCustom data passed in the original request.
LegsArray of per-leg status objects (see below).

Legs Array​

{
"Legs": [
{
"OnCallDuration": 41,
"Status": "completed",
"AnsweredBy": "NA"
},
{
"OnCallDuration": 32,
"Status": "completed",
"AnsweredBy": "Human"
}
]
}

AnsweredBy values:

ValueDescription
HumanAnswered by a person.
MachineAnswered by voicemail or an IVR.
NotSureCould not determine.
NANot applicable (first / From leg).

HTTP Status Codes​

CodeDescription
200Success — call queued.
400Bad Request — missing or invalid parameters.
401Unauthorized — invalid API credentials.
429Rate limit exceeded (200 calls/minute).
500Server error.

Fetch Enhanced Call Details​

After placing a call, use the Call.Sid from the response to retrieve enhanced reporting via Voice v3:

curl -X GET \
'https://<api_key>:<api_token>@ccm-api.exotel.com/v3/accounts/<account_sid>/calls/<call_sid>' \
-H 'Content-Type: application/json'

The v3 Call Details response includes richer metadata: DTMF digits, recording objects, app/flow information, and per-leg data. See Call Details (v3).

Try It​