TL;DR: Connect Retell AI to n8n with two webhooks pointing in opposite directions. Retell fires inbound webhooks to n8n on call events (started, ended, analyzed) and on mid-call custom functions; n8n calls back out to Retell's REST API to place outbound calls or update agents. Keep every piece of business logic — booking, CRM writes, SMS — in n8n, not in the voice prompt. That's the pattern I use in production, and here's exactly how to wire it.
I build AI voice agents for US clients, and the question I get most from other developers is "how does the voice platform actually do anything?" The agent answers the phone, but who checks the calendar, writes the CRM contact, sends the confirmation text? The answer, in my stack, is always n8n. Retell handles the conversation; n8n handles everything the conversation triggers. This guide is the exact integration I ship, including the parts that break in production.
Why n8n sits behind the voice agent
A voice platform like Retell is very good at one thing: turning a phone call into a structured conversation. It is not where your booking logic, your CRM, or your SMS should live. I explained the full reasoning in how I build production AI voice agents with Retell, but the short version is this: if you bake business logic into the voice prompt, you have married the platform. Keep that logic in n8n and the voice layer becomes swappable. Switching or A/B-testing platforms later is a change to one webhook, not a rebuild.
So the mental model is two arrows:
- Retell → n8n: the agent tells n8n what happened (call events) and asks n8n questions mid-call (custom functions).
- n8n → Retell: n8n tells Retell to do something (place an outbound call, update an agent, fetch a transcript).
Get those two directions right and everything else is detail. I standardized on n8n as that glue layer for reasons I laid out in n8n vs Make vs Zapier.
Direction 1: Retell to n8n (the inbound webhook)
This is where most of the work happens. Retell can call your webhook on lifecycle events and on custom functions you define inside the agent.
Set up the n8n side first
- In n8n, add a Webhook node. Set the method to POST and copy the production URL it generates. If you are self-hosting, make sure the URL is publicly reachable — Retell's servers have to hit it, so
localhostwill not work. For local development I tunnel with a service like ngrok and swap the URL for the real one before going live. - Add a Respond to Webhook node so Retell gets a clean
200back. For mid-call functions this response is the answer the agent speaks, so it matters.
Point Retell at it
In the Retell dashboard, open your agent and set the webhook URL to the n8n production URL. Retell will now POST a JSON body on each event. The payload includes an event field — call_started, call_ended, call_analyzed — and the full call object with the transcript, metadata, and any dynamic variables you passed in.
A useful habit: add a Switch node right after the webhook that branches on {{ $json.event }}. Each event wants different handling. call_ended is usually where I write the contact and transcript to the CRM; call_analyzed is where I read Retell's post-call analysis (sentiment, whether a booking happened, custom extraction fields) and route accordingly.
The mid-call custom function
This is the powerful part. Inside the Retell agent you can define a custom function — for example check_availability or book_appointment. When the caller asks for a Tuesday slot, the agent calls that function, which hits your n8n webhook mid-conversation, waits for the response, and speaks the result back.
The flow for a booking function looks like this:
Caller: "Do you have anything Tuesday afternoon?"
→ Retell fires custom function `check_availability` → n8n webhook
→ n8n queries Google Calendar / GHL calendar
→ n8n responds { "slots": ["2pm", "3:30pm"] }
→ Agent: "I have 2pm or 3:30 on Tuesday — which works?"
The critical constraint here is latency. The caller is sitting in silence while n8n runs. Keep mid-call function workflows lean: one lookup, a fast response, no chained API calls that take four seconds. If a step is genuinely slow, have the agent say "give me one moment" and design for it. I keep anything non-blocking — sending the confirmation SMS, writing the CRM note — out of the mid-call path and move it to the call_ended branch instead.
Direction 2: n8n to Retell (the outbound call)
The reverse direction uses Retell's REST API. The most common use is placing an outbound call — the backbone of the missed-call callback system I build, which I documented fully in missed-call textback with GoHighLevel + Retell.
In n8n, use an HTTP Request node:
- Method POST to Retell's create-phone-call endpoint.
- Authorization header with your Retell API key. Store it in n8n credentials, never inline in the node — this is the mistake I see most often in shared workflows.
- Body: the
from_number(your registered Twilio number), theto_number(the lead), theagent_id, and aretell_llm_dynamic_variablesobject carrying anything the agent should know before it dials — the caller's name, what they enquired about, the business name.
Those dynamic variables are how you personalize the call without editing the agent. The agent prompt references {{customer_name}}; n8n fills it at call time. One agent, infinitely reusable across contacts.
The full loop, end to end
Here is how the two directions combine in a real missed-call callback:
Missed call in GHL → GHL webhook → n8n
→ n8n HTTP Request → Retell create-phone-call (with lead's name + context)
→ Retell dials the lead, agent talks
→ mid-call: agent calls `book_appointment` → n8n webhook → GHL calendar → responds
→ call_ended → Retell webhook → n8n
→ n8n writes transcript + outcome to the GHL contact, sends SMS confirmation
Every arrow in that diagram is one of the two webhook directions above. Nothing exotic, just discipline about where each piece of logic lives.
Production gotchas I have hit
- A2P 10DLC first. If your flow sends SMS through Twilio, unregistered traffic gets silently filtered. Register the brand and campaign before you test, or you will spend a day debugging texts that never arrive.
- Idempotency on
call_ended. Retell can, in rare cases, deliver an event more than once. Guard CRM writes with thecall_idso you do not create duplicate contacts. - Secure the webhook. Verify Retell's signature (or at minimum a shared secret in the URL or a header) so random POSTs cannot trigger your workflow. An open n8n webhook that places outbound calls is a liability.
- Separate test and production webhooks. n8n gives you a test URL and a production URL; they are not the same. I have watched people wire the test URL into a live agent and wonder why nothing fires after they close the editor.
- Log the raw payload. Early in a build I write every incoming Retell payload to a Google Sheet or a database node. When something misbehaves in week two, the raw event history is the fastest way to see what the agent actually sent.
Where to go from here
Once the two directions are solid, everything else is composition: swap the CRM node, add a Slack alert when a caller is angry, branch on the post-call analysis to tag hot leads. The voice agent stays simple and the intelligence lives in n8n where you can see it, test it, and change it without touching the prompt.
If you want the wider picture — platform choice, the testing pipeline, what an agent like this costs to run — I have written those up in Retell vs Vapi vs Bland and how much an AI receptionist costs in 2026.
I build production AI voice agents and the n8n automation systems behind them for US clients through Null Studio. If you want yours wired up properly — webhooks, compliance, and all — book a call.