Why time to first sentence matters
Retell starts speaking as soon as it has your first complete sentence. So the caller’s wait is your time to first sentence (time to first token plus however long the model takes to finish that sentence), not the time to generate the whole response.Build the prompt from the transcript
Retell sends the full conversation so far intranscript, as a list of utterances with role set to agent or user. Map agent to your provider’s assistant role and everything else to user.
A voice prompt needs a few things a chat prompt doesn’t. Tell the model to write speech (short sentences, no markdown, no lists), because anything else gets read aloud literally. Tell it the transcript comes from speech recognition and may contain errors, so it infers meaning instead of asking the caller to repeat themselves. And give it the current date and time in the appropriate timezone.
When interaction_type is reminder_required, the caller has gone quiet. Append a short instruction so the model nudges rather than answering a question nobody asked.
Stream the response
Handle discarded responses
Retell asks for a response as soon as the caller sounds finished, which keeps the silence short by starting your generation before a response is expected. The cost is that some requests get discarded: when the caller was only pausing mid-thought and keeps going, Retell drops the pending response and asks again with a higherresponse_id.
Retell accepts content only for the response_id it most recently requested, and only until you mark that response complete. Content sent under an older response_id is dropped without an error, so a discarded response can never reach the caller.
Stopping a discarded generation early is still worth doing, because it costs tokens and provider capacity for audio nobody hears. Track the newest response_id per connection and stop as soon as yours is superseded.
Use call context
Setcall_details: true in your config event and Retell sends the whole call object as soon as the socket opens. It has what you need to personalize the first turn without an API round trip:
from_number,to_number,direction— who’s calling and which way. Phone calls only; these fields are absent on web calls.retell_llm_dynamic_variables— the dynamic variables passed when the call was created, such as a customer name pulled from your CRMmetadata— anything you attached at call creationcall_id,agent_id,call_type— for your own logging and for branching on web versus phone
call_details arrives, then greet by name. Drop the begin message from your connection setup and add this case to the message handler above.
Node.js
llm_websocket_url itself supports dynamic variables, you can also route by call. A URL of wss://your-domain.com/llm-websocket?tenant={{tenant_id}} reaches your server with the tenant already resolved.
Try it
Restart your server and start a call from the agent’s Test Audio panel. The agent should hold a real conversation, start speaking within a beat of you finishing, and stop cleanly when you interrupt. Check the call in Call History afterward. The transcript and the latency breakdown tell you whether your time to first sentence is where it needs to be. Thellm field covers your generation including the WebSocket round trip, and llm_websocket_network_rtt isolates that round trip, so the difference is your own generation time.
FAQ
Do I have to answer every response_required event?
Do I have to answer every response_required event?
Yes, unless a newer one has already arrived. Retell waits for content until you send
content_complete: true, so an ignored request leaves the agent silent. Answering with an empty string plus content_complete: true is a valid way to say nothing.What happens if my LLM provider errors or times out?
What happens if my LLM provider errors or times out?
Nothing on Retell’s side; it just keeps waiting. Always send a final event with
content_complete: true in a finally block, as the code above does. For provider outages, consider a fallback model or a spoken apology so the call degrades instead of going dead.Can I keep conversation state on my server instead of using the transcript?
Can I keep conversation state on my server instead of using the transcript?
Yes, and it’s often better. Retell sends the full transcript every time, but nothing stops you from keeping your own per-call state keyed on the call ID from the URL: retrieved documents, tool results, a state machine position. Use the transcript as the source of truth for what was said, and your own state for everything else.
Why does the agent talk over the caller, or wait too long?
Why does the agent talk over the caller, or wait too long?
That’s turn-taking, which Retell controls, not your LLM. Tune
responsiveness and interruption_sensitivity in interaction settings. You can also change them mid-call by sending an update_agent event, which is useful for making the agent less eager while it waits on a slow lookup.
