Skip to content

Place a voice call

Use PlaceCall to place a voice call, give the agent a task, and read what happened. This guide asks a business what time it closes today.

Before you start

You need a PlaceCall API key with available credits, a terminal with curl and Python 3, and a US phone number you own or are authorized to call. Get an API key if you do not have one, then set it as PLACECALL_API_KEY in your terminal or server environment. Keep it out of browser code and public repositories. See Authentication for key setup.

Submitting the request with a valid number places a real outbound call and can use credits. The +1XXXXXXXXXX placeholder below cannot be dialed; replace it with the authorized number in E.164 format.

1. Describe the task

Give the agent one clear objective in brief: what to ask, any information it needs, and when to finish. For this example, it should ask for today’s closing time, thank the person, and end the call.

FieldWhat to send
target_phoneThe authorized US number, including the +1 country code.
briefThe instructions the agent should follow during the call.
languageen for this English-language example.

2. Place the call

Create a unique request ID once for this intended call. This command uses Python 3 to generate one:

CALL_REQUEST_ID=$(python3 -c 'import uuid; print(uuid.uuid4())')

Send the request, including that ID in the Idempotency-Key header:

curl --include --request POST https://api.voygr.tech/calls \
  --header "X-API-Key: $PLACECALL_API_KEY" \
  --header "Idempotency-Key: $CALL_REQUEST_ID" \
  --header "Content-Type: application/json" \
  --data '{
    "target_phone": "+1XXXXXXXXXX",
    "brief": "Ask what time the business closes today. Thank them and end the call.",
    "language": "en"
  }'

--include shows the HTTP status alongside the response. For the freeform request above, save the returned call ID:

HTTP statusWhere to find the IDWhat it means
201 Createdcall.call_idThe call was created.
202 Acceptedcall_idThe call is queued and waiting to start.

Structured calls (intent + slots) instead return top-level call_id for both 201 and 202. If your application supports both request shapes, extract the ID from the parsed JSON as response.get("call", response)["call_id"] in Python.

Both responses mean the request was accepted. Use the returned ID to follow progress; do not submit another call just because this one is queued.

If the connection drops before you receive a response, retry the same POST with the same request ID and unchanged body. Do not rerun the ID-generation command for that retry. A replay returns the stored response with Idempotent-Replayed: true. Use a new ID only when you intend to place another call; reusing an ID with a different body returns 409.

3. Follow progress

Set CALL_ID to the ID returned in step 2, then look up the call:

CALL_ID="REPLACE_WITH_RETURNED_CALL_ID"

curl "https://api.voygr.tech/calls/$CALL_ID" \
  --header "X-API-Key: $PLACECALL_API_KEY"

This response contains the call fields directly, so read status, not call.status. Repeat the GET while the call is in progress. A queued call uses this same lookup.

The call has finished when status is completed, failed, or cancelled. If a completed call still has a null outcome_type, check again: its outcome can be available shortly after the call ends.

4. Read the outcome

FieldWhat it tells you
statusWhether the call is still running or has ended.
outcome_typeThe classified result of the conversation.
outcome_summaryA short explanation of what happened.
transcript_fullThe conversation transcript, when available.

For the closing-time task, read the summary and transcript for the business’s answer. completed means the call ended; it does not by itself mean the agent obtained the information you wanted. Handle missing results in your application rather than treating them as an answer.

Understand outcome values

status tracks the call lifecycle; outcome_type explains the result. These are the currently produced outcome values:

OutcomeMeaning
success_bookedA booking was confirmed.
success_refusedThe business answered but declined the request.
success_no_bookingA conversation completed without a booking, including information requests.
failed_no_answerNobody answered.
failed_voicemailThe call reached voicemail.
failed_busyThe line was busy.
failed_short_hangupThe call ended before a substantive conversation.
failed_technicalA technical failure prevented completion.
failed_no_agent_availableNo person answered within the hold-time limit.
failed_no_disclosureThe required AI/recording disclosure could not be delivered.
failed_call_droppedThe connection dropped during the conversation.
failed_wrong_numberThe number did not reach the intended business.
failed_cancelledThe customer cancelled the call.
failed_no_engagementThe person answered but did not engage with the task.
failed_agent_muteThe agent failed to speak when required.

A refusal is different from a technical failure: the business may decline even though the call reached it successfully. Preserve unfamiliar outcome values for troubleshooting rather than discarding the entire response.

Receive live events

For live updates and mid-call questions, create the call with ask_user_mode: "stream" and consume its event stream incrementally:

curl --no-buffer "https://api.voygr.tech/calls/$CALL_ID/events?after_event_id=0" \
  --header "X-API-Key: $PLACECALL_API_KEY"

Parse each SSE event as it arrives; do not wait for the whole response to finish. Ignore heartbeat events and SSE comments, preserving the last event ID when a heartbeat has no id. After a disconnect while the call is active, reconnect with after_event_id set to the last received id. The Last-Event-ID header is not supported. Answer ask_user events promptly through the answer endpoint.

Handle errors and retries

Read the error response before retrying:

HTTP statusWhat to check
401The API key is missing or invalid.
402The key does not have enough credits to place the call.
403The key cannot use this calling operation.
404 on lookupCheck the call ID and use the same API key that created it.
409Check the error: the concurrent-call limit may be reached, or the idempotency key may have been reused with a different request.
422Check the phone number and request fields, including brief.
429Wait before retrying; follow Retry-After when returned.
503The service is temporarily unavailable; follow any retry guidance in the response.

For an uncertain POST response, keep the same request ID and body as described above. For access help, contact support@voygr.tech.

See Place a call for the full request and response reference, including its interactive playground.