Add Phone Calls to Your CrewAI Agent

Add Phone Calls to Your CrewAI Agent

6 min read
Yanis Mellata
Integrations

What You're Building

One of your crew's tasks requires calling a business by phone. Book an appointment, verify insurance, get a quote, follow up on a delivery.

This post gives you a PhoneCallTool for CrewAI and a dedicated "caller" agent. The caller handles phone interactions while other agents focus on research, analysis, or coordination.

Prerequisites:

  • Python 3.10+
  • pip install crewai httpx
  • An AgentPhone API key (get one free — 5 calls, no credit card)

The Phone Tool

CrewAI tools extend BaseTool from crewai.tools. Here's the complete implementation:

import os
import time
import httpx
from crewai.tools import BaseTool
from pydantic import BaseModel, Field

AGENTPHONE_API_KEY = os.environ["AGENTPHONE_API_KEY"]
AGENTPHONE_BASE = "https://agentphone.app/api/v1"
AGENTPHONE_HEADERS = {
    "x-api-key": AGENTPHONE_API_KEY,
    "Content-Type": "application/json",
}


class PhoneCallInput(BaseModel):
    to_phone_number: str = Field(
        description="Phone number in E.164 format (e.g. +14155551234)"
    )
    objective: str = Field(
        description="What the call should accomplish. Be specific — include "
        "names, dates, reference numbers, and relevant details."
    )
    business_name: str = Field(
        default="",
        description="Name of the business being called."
    )


class PhoneCallTool(BaseTool):
    name: str = "Place Phone Call"
    description: str = (
        "Place a real phone call to a business or person. Use for booking "
        "appointments, verifying information, following up on orders, getting "
        "quotes, or checking availability. Returns the call outcome, a "
        "summary of what happened, and the full transcript."
    )
    args_schema: type[BaseModel] = PhoneCallInput

    def _run(
        self,
        to_phone_number: str,
        objective: str,
        business_name: str = "",
    ) -> str:
        # Create the call
        response = httpx.post(
            f"{AGENTPHONE_BASE}/calls",
            headers=AGENTPHONE_HEADERS,
            json={
                "to_phone_number": to_phone_number,
                "objective": objective,
                "business_name": business_name,
            },
        )

        if response.status_code != 202:
            return f"Failed to create call: {response.json()}"

        call_id = response.json()["data"]["call_id"]

        # Poll until done
        while True:
            time.sleep(4)
            result = httpx.get(
                f"{AGENTPHONE_BASE}/calls/{call_id}",
                headers=AGENTPHONE_HEADERS,
            ).json()["data"]

            if result["status"] in ("completed", "failed", "canceled"):
                break

        return (
            f"Call Status: {result['status']}\n"
            f"Outcome: {result.get('outcome', 'unknown')}\n"
            f"Details: {result.get('outcome_details', '')}\n"
            f"Summary: {result.get('summary', '')}\n"
            f"Duration: {result.get('duration_seconds', 0)}s\n"
            f"Transcript:\n{result.get('transcript', 'N/A')}"
        )

CrewAI tools return strings. The formatted result gives the agent all the information it needs to continue the workflow.

The Caller Agent

CrewAI's strength is role specialization. Create a dedicated caller agent:

from crewai import Agent

phone_tool = PhoneCallTool()

caller = Agent(
    role="Phone Coordinator",
    goal="Place phone calls to businesses and extract the information or "
         "confirmations the crew needs. Report outcomes clearly.",
    backstory=(
        "You're the crew member who handles all phone interactions. When "
        "someone in the crew needs to call a business — to book, verify, "
        "follow up, or gather information — you handle it. You're direct, "
        "efficient, and always report back with specific details."
    ),
    tools=[phone_tool],
    verbose=True,
)

The role, goal, and backstory prime the agent for phone-specific tasks. It knows it's the phone specialist in the crew.

A Complete Crew: Appointment Scheduling

A researcher finds the business, a caller books the appointment, and a reporter compiles the results. The context parameter chains task outputs — the caller gets the researcher's findings, and the reporter gets everything.

from crewai import Agent, Task, Crew
from crewai_tools import SerperDevTool

search_tool = SerperDevTool()
phone_tool = PhoneCallTool()

researcher = Agent(
    role="Business Researcher",
    goal="Find business phone numbers, hours, and relevant details.",
    backstory="You research local businesses to find accurate contact "
              "information for the crew.",
    tools=[search_tool],
    verbose=True,
)

caller = Agent(
    role="Phone Coordinator",
    goal="Place phone calls to book appointments and gather information.",
    backstory="You handle all phone calls for the crew. You're efficient "
              "and always confirm key details before ending a call.",
    tools=[phone_tool],
    verbose=True,
)

reporter = Agent(
    role="Results Reporter",
    goal="Compile research and call results into a clear summary.",
    backstory="You take the outputs from the researcher and caller and "
              "present a clear, organized summary for the user.",
    verbose=True,
)

research_task = Task(
    description=(
        "Find the phone number and business hours for {business_name} "
        "in {location}. Get the closest location."
    ),
    expected_output="Phone number in E.164 format and business hours.",
    agent=researcher,
)

call_task = Task(
    description=(
        "Call {business_name} using the phone number from the researcher. "
        "Objective: {objective}. Context: {context}."
    ),
    expected_output="Call outcome with confirmed details.",
    agent=caller,
    context=[research_task],
)

summary_task = Task(
    description="Compile research and call results into a clear summary.",
    expected_output="Organized summary of what was accomplished.",
    agent=reporter,
    context=[research_task, call_task],
)

crew = Crew(
    agents=[researcher, caller, reporter],
    tasks=[research_task, call_task, summary_task],
    verbose=True,
)

result = crew.kickoff(inputs={
    "business_name": "Sunset Veterinary Clinic",
    "location": "San Francisco",
    "objective": "Schedule a wellness check for a 3-year-old Labrador",
    "context": "Dog's name is Max. Owner name is Jamie. "
               "Prefer next Tuesday or Wednesday afternoon.",
})

Multi-Call Crew: Insurance + Scheduling

CrewAI shines when the crew needs to make multiple sequential calls where each builds on the last:

verify_task = Task(
    description=(
        "Call the insurance company at {insurance_phone} to verify coverage "
        "for {patient_name}. Check:\n"
        "1. Is the plan active?\n"
        "2. What's the copay for {procedure}?\n"
        "3. Is pre-authorization required?\n"
        "4. Get a reference number for the verification."
    ),
    expected_output=(
        "Coverage status, copay amount, pre-auth requirement, "
        "and reference number."
    ),
    agent=caller,
)

schedule_task = Task(
    description=(
        "Based on the insurance verification, call {provider_phone} to "
        "schedule {procedure} for {patient_name}. Mention that insurance "
        "was verified and provide the reference number from the previous call. "
        "Preferred dates: {preferred_dates}."
    ),
    expected_output=(
        "Confirmed appointment date, time, provider name, "
        "and any prep instructions."
    ),
    agent=caller,
    context=[verify_task],
)

The caller makes two calls in sequence. The second call uses information from the first — the reference number, coverage status, copay amount. The crew handles the dependency automatically through the context parameter.

What Crews Can Build

  • Healthcare coordination — Verify insurance → schedule appointment → confirm with patient. Three sequential calls, each depending on the last.
  • Vendor procurement — Research suppliers → call 3 for quotes → compare → call winner to place order.
  • Event planning — Find venues → call for availability → negotiate pricing → book.
  • Lead qualification — Pull leads from CRM → call to verify interest → update CRM with status.
  • Property management — Identify maintenance needs → call contractors for quotes → schedule repairs.

Each workflow uses the same building blocks: a researcher, a caller, and a reporter (or coordinator). The caller makes calls; the other agents handle everything else.

Getting Started

Get an API key at agentphone.app (5 free calls, no credit card). API reference: agentphone.app/docs.

Other frameworks: OpenAI Agents SDK | LangChain | MCP

Ready to give your agent a phone?

Get Your API Key →

Written by Yanis Mellata, Founder & CEO