An automated, zero-human-intervention outbound dialing system designed to call Pakistani insurance policyholders in native Urdu,and English negotiate renewal terms, and update centralized CRM records.
VID_20260621_054558_646_bsl.1.mp4
In the Pakistani insurance sector, policy lapse rates spike due to manual, delayed follow-up phone calls. Monika automates the retention pipeline entirely.
Driven by a scheduled n8n cron-job, the system scans a master database for expiring policies, translates the raw expiration timestamps into spoken Urdu phonetics, triggers a Twilio outbound call via Retell AI, and captures the policyholder's legally binding verbal intent.
Stack: Retell AI (voice) + n8n (orchestration) + Twilio (telephony) + Google Sheets (data) + Gmail (notifications)
Insurance companies rely on manual follow-up calls to remind customers about expiring policies. This process is slow, inconsistent, and entirely dependent on human availability β leading to missed renewals, lost revenue, and poor customer experience. Calling customers in their native language (Urdu) adds another layer of complexity that most automation tools can't handle.
An outbound AI voice agent that automatically dials customers before their policy expires, speaks to them in fluent Urdu, collects their renewal decision in real time, and logs the outcome back to a Google Sheet β with zero human involvement.
[Google Sheet] β [n8n reads blank rows] β [Retell API: create-phone-call] β [Twilio dials customer] β [Monika speaks in Urdu] β [Outcome function fires] β [n8n updates Sheet + sends Gmail]
| Metric | Result |
|---|---|
| Average call duration | ~2 minutes |
| Renewal confirmation rate | ~65% of answered calls |
| Calls handled simultaneously | Unlimited (scales with Twilio) |
| Manual follow-up hours saved | ~5β8 hours/day |
| Sheet update latency after call | Under 5 seconds |
| Language | Urdu (native speaker-level fluency) |
Because an outbound phone call can last anywhere from 10 seconds to 10 minutes, the architecture is strictly split into two decoupled Webhook processes to keep server memory consumption near zero:
STAGE 1: THE DISPATCHER STAGE 2: THE RESOLVER
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β G-Sheet (Policy DB) β β Retell AI Call Hook β
ββββββββββββββ¬βββββββββββββ ββββββββββββββ¬βββββββββββββ
β (Scan blank rows) β (Webhook payload)
βΌ βΌ
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β n8n Code: Urdu Formatterβ β n8n Webhook Listener β
ββββββββββββββ¬βββββββββββββ ββββββββββββββ¬βββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββββββ βββββββββββββββββββββββββββ
β POST /v2/create-call β β Switch: Intent Parser β
ββββββββββββββ¬βββββββββββββ ββββββββ¬ββββββββββββ¬βββββββ
β β β
(Twilio Outbound) (Accepted) (Rejected)
β βΌ βΌ
βΌ βββββββββββββ βββββββββββββ
[ Customer Phone ] βG-Sheet: OKβ βG-Sheet: NOβ
β + Gmail β βββββββββββββ
βββββββββββββ
The sheet acts as both the trigger queue and the source of truth:
full_name |
mobile_number |
policy_number |
expiry_date |
renewal_status (Target) |
|---|---|---|---|---|
| Tariq Jameel | +923001234567 |
POL-88192 |
2026-06-28 | (Blank - Pending Call) |
| Fatima Noor | +923339876543 |
POL-44102 |
2026-06-29 | renewal_accepted |
A major point of failure in Urdu voice bots is the LLM reading an English string "2026-06-25" as "Two thousand twenty-six dash zero six..." To bypass this, Stage 1 passes the date through a JavaScript transformation node before sending it to Retell's dynamic variables:
// Input: "2026-06-25"
// Spoken output injected into LLM Prompt: "Pachees June, Do Hazaar Chhabees"
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const parts = input.expiry_date.split('-');
const urduFormatted = `${parseInt(parts[2])} ${months[parseInt(parts[1]) - 1]}, Do Hazaar ${Chhabees}`; During the call, Monika listens specifically to trigger one of three hardcoded boolean tool states:
renewal_accepted: Triggers if the user says words aligning with "Jee haan karein" (Yes, do it) or "Theek hai".renewal_rejected: Triggers on explicit refusal "Nahi karwana" or "Band kar dein".no_response: Fired automatically by Twilio if the line returns aBUSY,FAILED, orNO_ANSWERcarrier code.
-
Wrong Match Column β Lost Call Outcomes
- The Issue: After a call ended, the n8n outcome webhook was trying to match the result back to the Google Sheet using the wrong column. Records weren't being found, so Renewal Status was never updating.
- The Fix: Changed the lookup key to
policy_number, which is the only guaranteed unique identifier per customer. Name and phone number can have duplicates; policy number cannot.
-
The Silent Switch Node Failure (Trailing Whitespace)
- The Issue: The n8n Switch node routing on
renewal_accepted,renewal_rejected, andno_responsewas silently failing to match β sending every call outcome to the fallback branch regardless of what Retell returned. - The Fix: Discovered invisible trailing spaces in the Switch condition strings.
"renewal_accepted "does not equal"renewal_accepted"β n8n performs strict string comparison. Trimmed all condition values. This is the kind of bug that produces zero error logs, making it particularly dangerous.
- The Issue: The n8n Switch node routing on
-
Missing Gmail Node in Accepted Branch
- The Issue: The
renewal_acceptedbranch had no Gmail node connected. Customers who agreed to renew were never receiving a confirmation email, and there was no error thrown β the workflow simply completed silently without sending anything. - The Fix: Added the Gmail node into the correct position after the
renewal_acceptedSwitch branch, with the customer's email pulled dynamically from theEmail Addresscolumn in the Google Sheet.
- The Issue: The