When to use it
- Support inside your app. Add a “Talk to us” button to a help or order screen, so users reach the agent without leaving the app or dialing a number.
- Voice-driven flows. Let users book, reschedule, or check a status by voice, with the call controls and visuals in your own native UI.
How does a native web call work?
- Your app asks your backend for a call.
- Your backend calls Create Web Call with your API key and returns the connection details to the app.
- The app creates a WebRTC peer connection, adds the microphone, and opens a data channel named
control. - The app posts its SDP offer to Retell and applies the SDP answer it gets back.
- Audio flows directly between the app and Retell. The
controlchannel reports when it opens and when the call ends. - The app ends the call by sending a
DELETErequest, or the agent ends it.
api.retellai.com over HTTPS. Media is Opus audio over DTLS-SRTP, relayed through the TURN servers in ice_servers when the network requires it.
Prerequisites
- A voice agent and its agent ID.
- A Retell API key on your backend.
-
A WebRTC library for your platform. The examples on this page use:
-
On iOS,
NSMicrophoneUsageDescriptioninInfo.plist. -
On Android, these permissions in
AndroidManifest.xml:INTERNET,RECORD_AUDIO,MODIFY_AUDIO_SETTINGS, andACCESS_NETWORK_STATE. React Native apps that usereact-native-incall-manageralso needWAKE_LOCK. WithoutACCESS_NETWORK_STATEorWAKE_LOCK, the app crashes when the call starts.
Connect a native app
1
Create the call on your server
Add an endpoint your app can call. It creates the web call and returns the fields the app needs.Create Web Call returns these fields. The example forwards only the three the app needs.
server.js
Connect right after creating the call. If the app doesn’t connect within 5 minutes, the call ends with the disconnection reason
error_user_not_joined (as of October 2026).2
Create the peer connection
Configure the peer connection with the
ice_servers from your backend, add a microphone audio track, and create a data channel labeled control before you create the offer. The data channel must exist in the first offer, or the call has no event channel.Put the device in voice-call audio mode: playAndRecord with the voiceChat mode on iOS, and MODE_IN_COMMUNICATION on Android. This turns on the device’s echo cancellation, so the agent doesn’t hear itself.3
Send the offer
Create an offer and set it as the local description. Wait 1 second for ICE gathering so the offer contains your candidates, then post the current local description. Don’t wait for gathering to finish without a limit: in our tests on the iOS Simulator and Android Emulator over Wi-Fi, every candidate arrived within 200 ms, but gathering didn’t report complete until 5 seconds later. To connect even sooner, use trickle ICE.Set
identity must be client, the identity the access token was issued for. A successful response is 201 Created:sdp as the remote description with type answer. The answer already contains all of Retell’s candidates, so there are none to wait for. Keep session_id; you need it to end the call.If the peer connection doesn’t reach connected within 15 seconds of the POST, Retell closes the session (as of October 2026).4
Handle call events
Read JSON messages from the
control data channel:Also watch the peer connection state, since some endings send no message. If it reaches
failed or closed, the call is over. If Retell’s side of the connection stays disconnected for 5 seconds, Retell ends the call (as of October 2026); see reconnect after a network change.You can send these messages on the same channel:To mute, you can also disable the local audio track; either way the call stays connected.
5
End the call
204 No Content, including when the call has already ended, or 401 if the access token has expired. Close the peer connection and release the microphone too. The examples close locally before sending the DELETE, so the audio stops right away.Platform examples
Each example shows the core of a call:connect() takes the call_id, access_token, and ice_servers from your backend endpoint, and hangUp() ends the call. App plumbing, such as calling your backend and converting ice_servers into the library’s type, is left out.
The examples are trimmed from apps tested on the iOS Simulator and Android Emulator with stasel/WebRTC 156.0.0, stream-webrtc-android 1.3.10, react-native-webrtc 155.0.1 on React Native 0.88, and flutter_webrtc 1.6.3 (as of October 2026).
Common pitfalls
urlscan be a string or an array. When you convertice_serversfor the iOS or Android SDK, handle both forms. React Native and Flutter accept either.- Cap the wait for ICE gathering. Gathering can take 5 seconds to report complete even though the candidates arrive within 200 ms. Wait 1 second, or use trickle ICE. If calls fail on networks that block UDP, switch to trickle ICE, since the TURN-over-TLS candidate can arrive after the wait.
- Make
close()safe to call twice. Closing the peer connection fires its state callback, which callsclose()again. Clear your reference before you close it. - Clean up when
connect()fails or the user hangs up mid-connect. Close the peer connection on any error. After each wait, such as the microphone prompt, the ICE wait, or thePOST, check that the user didn’t hang up in the meantime, and release what you just acquired if they did. - On Android, don’t close the peer connection on a WebRTC callback thread. It can deadlock. Hand the close off to another thread, as the Kotlin example does.
Signaling reference
All signaling requests go tohttps://api.retellai.com/webrtc-proxy/{call_id}/v1/webrtc/sessions and carry Authorization: Bearer {access_token}.
Trickle ICE
Waiting briefly for ICE gathering before thePOST is simplest. To connect as fast as possible, post the offer right away and send each later candidate with PATCH once you have the session_id; in the same tests, this connected in under 100 ms. Queue candidates that arrive before the POST returns, then send them in order. The fields are candidate, sdpMid, and sdpMLineIndex, as in the standard RTCIceCandidateInit.
Errors
Reconnect after a network change
When the app switches networks, the peer connection goesdisconnected. Reconnect right away: create a new peer connection, POST a new offer with the same call_id and access_token, then close the old peer connection. The new connection takes over the call. If the old connection is still reachable, it also receives {"type":"status","state":"replaced"}.
If Retell’s side of the connection stays disconnected for 5 seconds (as of October 2026), Retell ends the call, and a later reconnect fails. Create a new call instead.
After the call
- Register a webhook to receive
call_started,call_ended, andcall_analyzedon your server. - Use the
call_idwith Get Call for the transcript, recording, and analysis, or review the call in session history. - For live captions in the app, have your backend connect to the monitoring WebSocket with your API key and relay the transcript to the app.
FAQ
Is there a Retell SDK for iOS or Android?
Is there a Retell SDK for iOS or Android?
Not a dedicated one (as of October 2026). Native apps use a standard WebRTC library with the signaling described on this page. The browser Web SDK uses the same protocol.
Can I use a public key from my app?
Can I use a public key from my app?
No. Public keys limit calls to your allowed domains through the
Origin or Referer header. A browser sets those headers itself, but a native app can send any value, so the check doesn’t protect calls from an app. Create the call on your backend with an API key and send the app only the call_id, access_token, and ice_servers.Why does my Android app crash when the call starts?
Why does my Android app crash when the call starts?
Check
AndroidManifest.xml for ACCESS_NETWORK_STATE, which WebRTC needs to monitor the network, and for WAKE_LOCK if you use react-native-incall-manager. Logcat shows a SecurityException naming the missing permission just before the crash.Why can't the user hear the agent?
Why can't the user hear the agent?
Check that the audio session is active in a voice-call mode (
playAndRecord with voiceChat on iOS, MODE_IN_COMMUNICATION on Android) and that the output route is the speaker or headset you expect.Why does the agent hear itself and keep interrupting?
Why does the agent hear itself and keep interrupting?
Echo cancellation is off. Use the voice-call audio modes above, which turn on the device’s echo cancellation.
Why does the connection fail on corporate or mobile networks?
Why does the connection fail on corporate or mobile networks?
Make sure you pass all of
ice_servers to the peer connection, including the TURN entries and their username and credential. TURN over TLS on port 443 reaches networks that block UDP.Do native web calls count toward my concurrency limit?
Do native web calls count toward my concurrency limit?
Yes. They are web calls and draw from the same concurrency pool as other calls.

