Docs

AI agent playbooks

Voice calls through an assistant

Place or answer a 1:1 Telegram voice call from an AI assistant, with the audio in a ready browser tab and the person confirming before anything rings.

View Markdown

Goal

You start, answer, decline or hang up a 1:1 Telegram voice call on the user's behalf. You do not carry the audio: it flows between the worker and one of the user's browser tabs.

Permissions needed

  • Scope calls for calls.start, calls.answer, calls.decline, calls.hangup, calls.endpoints.
  • messages.read for calls.readiness, calls.list, calls.get.
  • The account must be within the connection's reach. The calls scope is not part of the Operations or Setup presets: the user has to switch it on or choose Full access.

Ask the person before

A call makes a real person's phone ring. Ask the user whom to call, from which account and why. Never call someone just because they are on a list.

Check first

  1. Call whoami to make sure you have the calls scope.
  2. Call calls.endpoints. An empty list means no tab has Ready for calls placed by an agent switched on: ask the user to open the app and switch it on (on the AI connections page or in Call settings). Without a tab, calls.start answers audio_endpoint_required and nothing rings.
  3. Call calls.readiness with conversationId. When ready is false, read the reason code in the table below and stop.

Steps

  1. Call calls.start with conversationId. The first answer is always confirmation_required with a confirm_token.
  1. Take the callId and follow the dialling with operations_get, then the call itself with calls.get (or the calls.* events): ringing → exchanging_keys → active → ended.
  2. To answer an incoming call that is ringing, call calls.answer with callId; the audio goes to the ready tab. If someone already answered, you get already_answered.
  3. Call calls.hangup to end, or calls.decline for an incoming call nobody answered. Both always run and never count against limits.

Check afterwards

Read calls.get: state = ended and end_reason tell the outcome (hangup, remote_hangup, declined, missed, busy, failed, media_unreachable…). Only tell the user "you talked" when the call was active and has a duration.

When it fails

Code Meaning What to do
proxy_no_udp The account's proxy cannot carry audio (HTTP, or SOCKS5 without confirmed UDP) Tell the user a SOCKS5 proxy with UDP is needed; do not retry
audio_endpoint_required No ready tab Ask the user to switch on Ready for calls placed by an agent
account_unavailable The account is not logged in, paused, its proxy fails or the worker is off Stop and tell the user
worker_outdated The worker is older than 1.0.49 Wait for it to update itself
not_private_chat Not a private chat with one person Pick another private chat
peer_opted_out The person asked not to be contacted Do not call
quota_exceeded A Safe account already made 10 calls today, or calls are too close together Wait; never switch accounts to get around it
account_busy, node_calls_full The account or machine already carries its calls Try again later
calls_disabled Calls are switched off system-wide Wait
already_answered, call_ended, call_not_found Someone answered, the call ended, or it is out of reach Read calls.get again

An end of media_unreachable means the call was picked up but the audio could not get through the proxy. Do not call again right away; ask the user to check the proxy.

Stop conditions

Stop when the call is ended, when the user does not confirm, or on any error code above other than account_busy / node_calls_full.