Calls

Telegram supports E2E-encrypted group and one-to-one calls.

This page describes the API methods used to work with calls.

One-to-one calls

phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector<string> = PhoneCallProtocol;

phoneConnectionWebrtc#635fe375 flags:# turn:flags.0?true stun:flags.1?true id:long ip:string ipv6:string port:int username:string password:string = PhoneConnection;

phoneCallWaiting#c5226f17 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall;
phoneCallRequested#14b0ed0c flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall;
phoneCallAccepted#3660c311 flags:# video:flags.6?true id:long access_hash:long date:int admin_id:long participant_id:long g_b:bytes protocol:PhoneCallProtocol = PhoneCall;
phoneCall#30535af5 flags:# p2p_allowed:flags.5?true video:flags.6?true conference_supported:flags.8?true id:long access_hash:long date:int admin_id:long participant_id:long g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connections:Vector<PhoneConnection> start_date:int custom_parameters:flags.7?DataJSON = PhoneCall;
phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true video:flags.6?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;

phone.phoneCall#ec82e140 phone_call:PhoneCall users:Vector<User> = phone.PhoneCall;
updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;

phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
phoneCallDiscardReasonHangup#57adc690 = PhoneCallDiscardReason;
phoneCallDiscardReasonBusy#faf7e8c9 = PhoneCallDiscardReason;
phoneCallDiscardReasonMigrateConferenceCall#9fbbf1f7 slug:string = PhoneCallDiscardReason;

---functions---

phone.requestCall#42ff96ed flags:# video:flags.0?true user_id:InputUser random_id:int g_a_hash:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtocol = phone.PhoneCall;
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;

phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool;

phone.discardCall#b2cbc1c0 flags:# video:flags.0?true peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;

See here » for a detailed description of the end-to-end encryption used for one-to-one calls, briefly re-explained below with some additional, API-level details:

  1. User A invokes phone.requestCall to call user B, passing the initial DH parameters and phone call protocol information ». Set the video flag to request a video call, otherwise an audio call is requested.
    The method will return a phone.phoneCall with a phoneCallWaiting
  2. The Server S performs privacy checks and sends an updatePhoneCall update with a phoneCallRequested constructor to all of B's active devices.
  3. Optionally, when B receives the phoneCallRequested, B may choose to invoke phone.receivedCall: this will prevent other users from calling B until the current call is discarded (either refused, or accepted, confirmed and then discarded), as the server will emit an RPC error when invoking phone.requestCall.
    This step is not strictly necessary, and may be omitted for example by userbot webradios: this will allow multiple users to establish multiple parallel calls with the same user.
  4. User B accepts the call on one of their devices, performs all the required security checks, and invokes the phone.acceptCall method passing DH parameters and phone call protocol information »: this method will return a phone.phoneCall with a phoneCallAccepted.
    User B may also choose to refuse the call instead, using phone.discardCall with phoneCallDiscardReasonMissed (user A can do the same as well).
  5. The Server S sends an updatePhoneCall with the phoneCallDiscarded constructor to all other devices B has authorized, to prevent accepting the same call on any of the other devices. From this point on, the server S works only with the specific device owned by B that invoked phone.acceptCall first.
  6. The Server S sends to A an updatePhoneCall update with a phoneCallAccepted constructor: A performs all the usual security checks, then invokes the phone.confirmCall method passing DH parameters and phone call protocol information »: the method will return a phone.phoneCall with a phoneCall.
    Then, A hands off the call to the tgcalls library using the phoneCallProtocol and phoneConnectionWebrtc constructors contained in the phoneCall constructor.
  7. The Server S sends to B an updatePhoneCall update with the phoneCall constructor: at this point B performs all the required security checks, and hands off the call to the tgcalls library as above.
  8. To hang up the call, invoke phone.discardCall, which will emit an updatePhoneCall with a phoneCallDiscarded for both users.
    Set the video flag if at least one of the two sides of the discarded call has a video stream enabled (regardless of whether initially the call was a video call or a voice call), or when migrating to a conference call.
    If the phoneCallDiscarded.need_rating flag is set, the client must invite the user to rate the call » when it ends.
    The user may still rate the call manually by right-clicking on the call service message: in this case, the user_initiative flag must be set when invoking phone.setCallRating.

Populating phoneCallProtocol

phoneCallProtocol#fc878fc8 flags:# udp_p2p:flags.0?true udp_reflector:flags.1?true min_layer:int max_layer:int library_versions:Vector<string> = PhoneCallProtocol;

phoneCallProtocol describes the tgcalls protocol versions supported by the local build of tgcalls, populated as follows:

  • fill library_versions with the ordered list of supported tgcalls protocol versions (order matters, the preferred tgcalls protocol version must come first)
  • set both udp_p2p and udp_reflector to true (deprecated, previously used to allow or disallow the use of direct peer-to-peer networking for libtgvoip calls)
  • set min_layer to 65 (deprecated, was previously used to describe the oldest supported libtgvoip protocol)
  • set max_layer to 92 (deprecated, was previously used to describe the oldest supported libtgvoip protocol)

u2p_p2p, udp_reflector, min_layer and max_layer were previously used by the deprecated libtgvoip library, and should not be passed to tgcalls: the values listed here are hardcoded and immutable, fixed to the last values supported by libtgvoip and are only used if the other end still uses libtgvoip.

The use of peer-to-peer networking is now controlled by the the privacyKeyPhoneP2P privacy setting », which is used by the server to choose whether to also return P2P STUN phoneConnectionWebrtc connection options, along with the usual reflector TURN phoneConnectionWebrtc connection options.

The same local capability set is re-sent in all three outbound handshake methods: the protocol embedded in phoneCallWaiting, phoneCallRequested and phoneCallAccepted does not stop either client from advertising its own local capabilities again in the next outgoing RPC.

Signaling data

updatePhoneCallSignalingData#2661bf09 phone_call_id:long data:bytes = Update;

---functions---

phone.sendSignalingData#ff7a9383 peer:InputPhoneCall data:bytes = Bool;

phone.sendSignalingData is only used after the DH handshake is over and both sides have the final phoneCall with verified key material and connection info: at this point, tgcalls should be initialized from the final phoneCall constructor's protocol and connections fields, and only then tgcalls's signaling callback can be wired to invoke phone.sendSignalingData.

Invoke this method whenever the tgcalls emits an opaque signaling packet that must be delivered to the peer: this will emit an updatePhoneCallSignalingData for the other peer in the call, who should then pass updatePhoneCallSignalingData.data to their own instance of tgcalls.

Call rating

---functions---

phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;

Invoke phone.setCallRating after the call ends to rate the call (sending a message to the official VoIP rating bot), passing the following parameters:

  • peer: The inputPhoneCall generated from the phoneCallDiscarded

  • user_initiative: Set this flag if the rating was initiated by the user by right-clicking on the call service message; must NOT be set if rating was requested by the server with phoneCallDiscarded.need_rating.

  • rating: A score from 1 to 5, chosen by the user.

  • comment: A user-specified comment (only for ratings not equal to 5).

    The user may also choose to report additional problems from the following fixed list, in the form of #hashtags appended to the comment.

    These hashtags may be appended even for ratings equal to 5, using this algorithm:

    • If the accumulated comment is not empty, append a whitespace
    • Append the ID of the chosen problem as a hashtag (i.e. #echo)
    • Repeat for all problems chosen by the user, from the following available options:
Call rating problem types
echo

The user heard their own voice (echo).

noise

The user heard background noise.

interruptions

The other side kept disappearing.

distorted_speech

The speech was distorted.

silent_local

The user couldn't hear the other side.

silent_remote

The other side couldn't hear the user.

dropped

The call ended unexpectedly.

distorted_video

The video was distorted.

pixelated_video

The video was pixelated.

Call debug

---functions---

phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;

If the final phoneCallDiscarded.need_debug flag is set, the client should upload implementation-specific debug information for the finished call, by calling phone.saveCallDebug with the JSON string returned by the active tgcalls instance.