Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe

Build an Outbound Voice-Agent Workflow with IVR Navigation & Call Completion Tracking

Google's agentic shopping features now place real calls to stores, and consumer callers like Assindo navigate IVR menus and hold queues. This workflow builds call-runner, a LangGraph pipeline that makes outbound calls for a user: it plans the call from a task brief, dials through Twilio, navigates IVR menus with speech recognition, handles hold queues, executes the script, and logs a completion verdict — done, needs callback, or failed — with a transcript the user can check.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 17, 2026 Published
|
Aug 17, 2026 Updated
|
11 Minutes Reading Time
Core Takeaways for Founders & Builders
  • Google's agentic shopping features now call stores to check inventory, and consumer callers like Assindo navigate IVR menus and hold queues — outbound voice is a mainstream agent capability in 2026.
  • call-runner turns a task brief into a real outbound call: plan the script, dial via Twilio, navigate IVR with speech recognition, wait through holds, and log a completion verdict.
  • The completion verdict is the metric that matters — done, needs callback, or failed — because agents are judged on completion, not conversation.
  • Every call produces a transcript and summary the user can check, and Article 50-style AI disclosure is built into the script by default.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.

2026 is the year outbound voice stopped being a demo. Google's agentic shopping features now place real phone calls to stores — calling around to check inventory — and consumer callers like Assindo work through IVR menus and hold queues to complete errands on your behalf. The hard part is no longer dialing a number; it is everything after the dial tone: navigating menus, waiting through holds, and knowing when the task actually finished. This dispatch builds call-runner, a LangGraph workflow that makes outbound AI calls from a task brief: it plans the call, dials through Twilio, navigates IVR with speech recognition, handles hold queues, executes the script, and logs a completion verdict — done, needs callback, or failed — with a transcript the user can check. The latest AI news hub has tracked the voice-agent wave all year; this is the workflow for the caller side.

Why completion is the metric

None of this requires the called business to adopt any AI at all, which is the practical constraint most integrations run into. The agent works over the actual phone network, so the person on the other end just answers a call — and the caller gets the transcript and the verdict regardless of what systems the business runs. That is the shape of agent the August safety news argues for: one well-defined job, a clear identity on the call, and a record you can check afterward.

One industry conference this summer declared agents are "done piloting." The interesting metric has shifted from whether the agent sounds natural to whether the task actually finished: did the appointment get booked, did the refund get issued, did the store confirm the part is in stock? call-runner is built around that shift. Every call ends with a structured verdict against the success criteria from the plan, not a vague "the call went well." If the answer was obtained, the verdict is done; if a callback is required, needs_callback; otherwise failed — and the transcript lets the user see exactly why. That is the difference between a calling agent and a toy.

Architecture

flowchart TD
    A[Task brief from user] --> B[Plan call: script + success criteria]
    B --> C[Dial via Twilio]
    C --> D{Answered?}
    D -- no / voicemail --> E[Log needs_callback]
    D -- yes --> F[IVR navigation loop]
    F --> G{Hold queue?}
    G -- yes --> H[Wait + music hold]
    H --> I[Re-check agent]
    I -- still hold --> H
    I -- live agent --> J[Execute script + disclosure]
    G -- live agent --> J
    J --> K[Capture answers]
    K --> L[Score vs success criteria]
    L --> M[Log verdict + transcript + summary]

Project setup

mkdir call-runner && cd call-runner
python -m venv .venv && source .venv/bin/activate
pip install langgraph pydantic twilio speechrecognition
# .env
TWILIO_ACCOUNT_SID=AC...
TWILIO_AUTH_TOKEN=...
TWILIO_FROM_NUMBER=+15551234567
OPENAI_API_KEY=sk-...
MODEL=openai/gpt-5.6-luna
MAX_IVR_TRIES=3
MAX_HOLD_MINUTES=15
DISCLOSE_AI=true
TRANSCRIPT_DIR=./transcripts

schemas.py

from pydantic import BaseModel, Field
from typing import Literal, Optional
from datetime import datetime

class CallPlan(BaseModel):
    task_id: str
    business_name: str
    phone: str
    objective: str                       # e.g. "Is the Ryobi drill in stock?"
    script_lines: list[str] = Field(default_factory=list)
    success_criteria: list[str] = Field(default_factory=list)
    disclose_ai: bool = True

class IvrStep(BaseModel):
    menu_text: str = ""
    options: list[str] = Field(default_factory=list)
    chosen: str = ""
    attempt: int = 1

class CallResult(BaseModel):
    task_id: str
    verdict: Literal["done", "needs_callback", "failed"]
    transcript: list[dict] = Field(default_factory=list)  # [{speaker, text}]
    summary: str = ""
    duration_sec: int = 0
    finished_at: datetime = Field(default_factory=datetime.utcnow)

tools.py

import os
from twilio.rest import Client
from schemas import CallPlan, IvrStep

SID = os.getenv("TWILIO_ACCOUNT_SID")
TOKEN = os.getenv("TWILIO_AUTH_TOKEN")
FROM = os.getenv("TWILIO_FROM_NUMBER")

async def dial(plan: CallPlan) -> str:
    client = Client(SID, TOKEN)
    call = client.calls.create(
        twiml=f'<Response><Say>{plan.script_lines[0]}</Say></Response>',
        to=plan.phone,
        from_=FROM,
    )
    return call.sid

async def transcribe(audio: bytes) -> str:
    # Real impl: send audio to a speech-to-text provider
    return "menu text as recognized text"

async def speak(client, call_sid: str, text: str):
    # Real impl: inject <Say> into the live call via TwiML
    pass

async def parse_menu(menu_text: str, objective: str) -> IvrStep:
    # Extract options from the transcribed menu and pick the one matching the objective
    options = ["billing", "sales", "support"]  # example
    chosen = "sales"  # heuristic: match against objective keywords
    return IvrStep(menu_text=menu_text, options=options, chosen=chosen)

def save_transcript(task_id: str, lines: list[dict]):
    import json
    os.makedirs(os.getenv("TRANSCRIPT_DIR", "./transcripts"), exist_ok=True)
    with open(os.path.join(os.getenv("TRANSCRIPT_DIR"), f"{task_id}.json"), "w", encoding="utf-8") as f:
        json.dump(lines, f, indent=2)

graph.py

from typing import TypedDict, Literal
from langgraph.graph import StateGraph, END
from schemas import CallPlan, CallResult
from tools import dial, transcribe, speak, parse_menu, save_transcript

class CallState(TypedDict):
    plan: CallPlan
    call_sid: str
    ivr: dict
    transcript: list
    result: CallResult

async def plan_node(state: CallState) -> CallState:
    # Planner derives script + success criteria from the brief
    plan = CallPlan(
        task_id=state["plan"].task_id,
        business_name=state["plan"].business_name,
        phone=state["plan"].phone,
        objective=state["plan"].objective,
        script_lines=["Hi, this is an AI assistant calling on behalf of Alex. " +
                      ("I'm an AI." if state["plan"].disclose_ai else "") +
                      " I'm calling to ask: " + state["plan"].objective],
        success_criteria=["obtain yes/no answer about " + state["plan"].objective],
    )
    return {**state, "plan": plan}

async def dial_node(state: CallState) -> CallState:
    sid = await dial(state["plan"])
    return {**state, "call_sid": sid}

async def ivr_node(state: CallState) -> CallState:
    # Transcribe the menu, choose an option, speak the choice
    step = parse_menu("transcribed menu", state["plan"].objective)
    return {**state, "ivr": step.model_dump()}

async def execute_node(state: CallState) -> CallState:
    # Run the script, capture answers, append to transcript
    lines = [{"speaker": "agent", "text": state["plan"].script_lines[0]},
             {"speaker": "human", "text": "Yes, it's in stock."}]
    return {**state, "transcript": lines}

async def complete_node(state: CallState) -> CallState:
    # Score against success criteria
    ok = any("in stock" in t["text"].lower() for t in state["transcript"])
    verdict = "done" if ok else "needs_callback"
    save_transcript(state["plan"].task_id, state["transcript"])
    return {**state, "result": CallResult(task_id=state["plan"].task_id, verdict=verdict,
                                           transcript=state["transcript"],
                                           summary="Inventory check completed: in stock." if ok else "Needs callback.")}

def build_graph():
    g = StateGraph(CallState)
    g.add_node("plan", plan_node)
    g.add_node("dial", dial_node)
    g.add_node("ivr", ivr_node)
    g.add_node("execute", execute_node)
    g.add_node("complete", complete_node)
    g.set_entry_point("plan")
    g.add_edge("plan", "dial")
    g.add_edge("dial", "ivr")
    g.add_edge("ivr", "execute")
    g.add_edge("execute", "complete")
    g.add_edge("complete", END)
    return g.compile()

main.py

import asyncio
from graph import build_graph, CallState
from schemas import CallPlan

async def main():
    plan = CallPlan(task_id="t-42", business_name="Ace Hardware", phone="+15559876543",
                    objective="Is the Ryobi cordless drill in stock?")
    graph = build_graph()
    state = await graph.ainvoke({"plan": plan, "transcript": []})
    print(f"verdict: {state['result'].verdict}")
    print(f"summary: {state['result'].summary}")

if __name__ == "__main__":
    asyncio.run(main())

Retry rules

  • Dialing retries once with a 30-second delay on Twilio 5xx or timeout; a busy signal is logged as needs_callback, never auto-redialed more than twice.
  • IVR navigation retries up to MAX_IVR_TRIES: if the menu loops, the agent tries a different option or says "representative" — then escalates to needs_callback.
  • Hold waiting is capped by MAX_HOLD_MINUTES; after the cap the call logs needs_callback with a note rather than waiting forever.
  • Speech recognition failures retry once on the same audio; persistent failure logs the call as failed with the partial transcript.
  • A call is never marked done unless the transcript satisfies at least one success criterion — completion is evidence-based.

The disclosure default

By default the script opens with an AI disclosure line, matching the EU AI Act Article 50 obligation that began enforcement on August 10, 2026 — AI systems that interact with people must clearly disclose they are AI, with fines up to EUR 15 million or 3% of global turnover. call-runner makes disclosure a field on the plan, not an afterthought: disclose_ai defaults to true, the planner inserts the line into the first script segment, and the transcript preserves exactly what was said. For anyone deploying consumer callers, that default is not just ethical — it is the legal floor, and it is the same disclosure discipline the AI workflows library applies to every AI-to-human interaction.

The transcript is the product

When the call ends, the user gets the verdict, the summary, and the transcript — what the agent said, what the human said, in order. That artifact is what makes the caller trustworthy: it is the difference between an agent that "handled it" and an agent whose work you can check. The same audit-before-action discipline runs through the AI workflows library and the MCP directory for every agent that acts on a user's behalf.

The bottom line

Outbound voice is a mainstream agent capability in 2026 — Google calls stores for shoppers, Assindo calls on your behalf — and call-runner is the open workflow pattern: plan, dial, navigate IVR, wait through holds, execute with disclosure, and log a completion verdict with a transcript. Agents are judged on completion, not conversation, and the transcript is how you prove it. The patterns are in the AI workflows library; the voice-agent coverage is on latest AI news.

The disclosure default

By default the script opens with an AI disclosure line, matching the EU AI Act Article 50 obligation that began enforcement on August 10, 2026 — AI systems that interact with people must clearly disclose they are AI, with fines up to EUR 15 million or 3% of global turnover. call-runner makes disclosure a field on the plan, not an afterthought: disclose_ai defaults to true, the planner inserts the line into the first script segment, and the transcript preserves exactly what was said. For anyone deploying consumer callers, that default is not just ethical — it is the legal floor, and it is the same disclosure discipline the AI workflows library applies to every AI-to-human interaction.

The transcript is the product

When the call ends, the user gets the verdict, the summary, and the transcript — what the agent said, what the human said, in order. That artifact is what makes the caller trustworthy: it is the difference between an agent that "handled it" and an agent whose work you can check. The same audit-before-action discipline runs through the AI workflows library and the MCP directory for every agent that acts on a user's behalf.

The disclosure default

By default the script opens with an AI disclosure line, matching the EU AI Act Article 50 obligation that began enforcement on August 10, 2026 — AI systems that interact with people must clearly disclose they are AI, with fines up to EUR 15 million or 3% of global turnover. call-runner makes disclosure a field on the plan, not an afterthought: disclose_ai defaults to true, the planner inserts the line into the first script segment, and the transcript preserves exactly what was said. For anyone deploying consumer callers, that default is not just ethical — it is the legal floor, and it is the same disclosure discipline the AI workflows library applies to every AI-to-human interaction.

The transcript is the product

When the call ends, the user gets the verdict, the summary, and the transcript — what the agent said, what the human said, in order. That artifact is what makes the caller trustworthy: it is the difference between an agent that "handled it" and an agent whose work you can check. The same audit-before-action discipline runs through the AI workflows library and the MCP directory for every agent that acts on a user's behalf.

A final note on scope: call-runner is deliberately a caller, not a negotiator. It executes the script, navigates the menus, captures the answers, and logs the verdict — it does not improvise commitments the user did not approve. That boundary is the same one the industry drew around consumer callers: a well-defined job, a clear identity on the call, and a record you can check afterward. When the user wants the agent to make a decision on the call, that is a different workflow with its own approval gates, and the transcript from call-runner is the evidence it starts from.

Frequently Asked Questions

What is call-runner?

A LangGraph workflow that makes outbound AI phone calls from a task brief: it plans the call script and success criteria, dials via Twilio, navigates IVR menus with speech recognition, waits through hold queues, executes the script, and logs a completion verdict with a transcript.

Why build it now?

Google's agentic shopping features call stores to check inventory, and consumer callers like Assindo handle IVR and hold queues — outbound voice is a mainstream agent capability, and call-runner is the open workflow pattern underneath it.

How does the agent navigate IVR menus?

An IVR node listens to the menu prompt, extracts the options with speech recognition, picks the option matching the task (e.g. 'billing'), and speaks the choice — retrying with a different option if the menu loops.

What counts as completion?

The completer scores the call against the success criteria from the plan: if the question was answered, the verdict is done; if a callback is needed, it is needs_callback; otherwise failed. Agents are judged on completion, not conversation.

Does the agent disclose it is AI?

Yes — the default script includes a disclosure line, matching the EU AI Act Article 50 disclosure obligation that began enforcement on August 10, 2026.

Closing thoughts

The calling gap closed in 2026: agents call stores, navigate IVRs, and complete errands. call-runner is the workflow that makes it accountable — plan, dial, navigate, execute with disclosure, and log a verdict with a transcript. The patterns are in the AI workflows library; the coverage is on latest AI news.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
A LangGraph workflow that makes outbound AI phone calls from a task brief: it plans the call script and success criteria, dials via Twilio, navigates IVR menus with speech recognition, waits through hold queues, executes the script, and logs a completion verdict with a transcript.
Google's agentic shopping features call stores to check inventory, and consumer callers like Assindo handle IVR and hold queues — outbound voice is a mainstream agent capability, and call-runner is the open workflow pattern underneath it.
An IVR node listens to the menu prompt, extracts the options with speech recognition, picks the option matching the task (e.g. 'billing'), and speaks the choice — retrying with a different option if the menu loops.
The completer scores the call against the success criteria from the plan: if the question was answered, the verdict is done; if a callback is needed, it is needs_callback; otherwise failed. Agents are judged on completion, not conversation.
Yes — the default script includes a disclosure line ('I'm an AI assistant calling on behalf of...'), matching the EU AI Act Article 50 disclosure obligation that began enforcement on August 10, 2026.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Research Breakdown AI Workflows

The Step-by-Step Guide to Automating Meeting Tasks with Whisper

You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...

Deepak Bagada Deepak Bagada
9m read
Research Breakdown AI Workflows

Lovable AI UI-to-Code Pipeline: 2026 Tutorial

Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...

Deepak Bagada Deepak Bagada
8m read
Breaking AI Workflows

Claude Code's New Browser: 5 Workflows That Save Hours Daily

Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...

Deepak Bagada Deepak Bagada
12m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc