---
title: "Build a CallSphere-Style Multi-Agent for HVAC Dispatch"
description: "HVAC companies miss 40–60% of inbound. Build a 4-agent dispatch (intake, scheduling, parts, emergency) that integrates with ServiceTitan in 600 lines."
canonical: https://callsphere.ai/blog/vw3h-build-callsphere-style-multi-agent-for-hvac-dispatch
category: "AI Voice Agents"
tags: ["HVAC", "ServiceTitan", "Dispatch", "Multi-agent", "Tutorial"]
author: "CallSphere Team"
published: 2026-04-29T00:00:00.000Z
updated: 2026-05-07T09:59:37.142Z
---

# Build a CallSphere-Style Multi-Agent for HVAC Dispatch

> HVAC companies miss 40–60% of inbound. Build a 4-agent dispatch (intake, scheduling, parts, emergency) that integrates with ServiceTitan in 600 lines.

> **TL;DR** — At $2,500–$5,000 average ticket, missing 5 calls/week is $50–100k/year in lost revenue. Build a 4-specialist HVAC dispatcher (intake, scheduling, parts/quote, emergency) on OpenAI Realtime + ServiceTitan API. Per-minute cost: ~$0.07 vs. 0.30–0.80 for vendor stacks.

## What you'll build

A 4-agent HVAC dispatcher: triage → (new-job intake | reschedule | parts/quote | emergency). Books to live ServiceTitan capacity, SMSes confirmations with tech name + ETA window, and pages the on-call for after-hours emergencies.

## Prerequisites

1. ServiceTitan API access (Marketplace integration).
2. OpenAI Realtime + Twilio Voice + SMS.
3. Python 3.11+, `openai-agents[voice]`, `fastapi`.
4. SMS opt-in language reviewed for your state.
5. On-call rotation in Postgres.

## Architecture

```mermaid
flowchart TB
  C[Caller] --> TR[Triage]
  TR --> NEW[New Job]
  TR --> RES[Reschedule]
  TR --> PQ[Parts/Quote]
  TR --> EM[Emergency]
  NEW --> ST[(ServiceTitan)]
  RES --> ST
  PQ --> ST
  EM --> PAGE[On-call page]
```

## Step 1 — Triage

```python
triage = RealtimeAgent(
    name="triage",
    instructions="""You're dispatch for ABC Heating. Identify intent in 2
    exchanges: new-job, reschedule, parts/quote, emergency. Emergencies
    (no heat in winter, gas smell, water leak) hand off to emergency
    immediately.""",
    handoffs=[new_job, reschedule, parts_quote, emergency],
)
```

## Step 2 — New-job specialist

```python
@function_tool
async def find_open_slots(zip_code: str, day: str, urgency: str) -> list[dict]:
    techs = await st.techs.list_by_zip(zip_code, skill_set=["hvac"])
    slots = await st.dispatch.aggregate_slots(techs, day,
                                              urgency_score=URGENCY_MAP[urgency])
    return slots[:5]

@function_tool
async def create_job(name: str, phone: str, address: str,
                     slot_id: str, problem: str, system_type: str) -> dict:
    j = await st.jobs.create(customer={"name": name, "phone": phone,
                                       "address": address},
                             slot_id=slot_id, summary=problem,
                             tags=[system_type])
    ref = f"HV-{datetime.now():%Y%m%d}-{j.id:03d}"
    await sms.send(phone, f"Tech {j.tech_name} arriving {j.eta_window}. Ref {ref}")
    return {"ref": ref, "job_id": j.id, "eta": j.eta_window}
```

## Step 3 — Reschedule specialist

```python
@function_tool
async def lookup_existing_job(phone: str) -> dict:
    j = await st.jobs.find_active(phone)
    if not j: return {"found": False}
    return {"found": True, "job_id": j.id, "current_slot": j.slot_iso}

@function_tool
async def reschedule_job(job_id: str, new_slot_id: str) -> dict:
    j = await st.jobs.reschedule(job_id, new_slot_id)
    await sms.send(j.customer.phone, f"Updated to {j.eta_window}. Ref HV-{j.id}")
    return {"ok": True}
```

## Step 4 — Parts / quote

```python
@function_tool
async def get_diagnostic_quote(zip_code: str, after_hours: bool) -> dict:
    z = await st.pricing.zone(zip_code)
    return {"diagnostic_fee": z.diag_fee + (z.after_hours_premium if after_hours else 0)}

@function_tool
async def check_part_in_stock(model_number: str, part: str) -> dict:
    return await st.inventory.find(model_number, part)
```

## Step 5 — Emergency

```python
@function_tool
async def page_oncall(summary: str, address: str, phone: str) -> dict:
    oncall = await rotation.who_is_oncall()
    twilio.calls.create(to=oncall.phone, from_=BUSINESS_NUMBER,
                        url=f"[https://you/page?summary={summary}](https://you/page?summary=%7Bsummary%7D)")
    j = await st.jobs.create_emergency(summary=summary, address=address,
                                       phone=phone)
    return {"job_id": j.id, "paged": oncall.name}
```

## Step 6 — Twilio bridge

Standard Realtime bridge. Add CSAT IVR ("press 1 if booked correctly") at end-of-call to populate quality metrics.

## Step 7 — Reporting

Track: book-rate (booked / total), AHT, average revenue per booking, after-hours emergencies/night, missed-call rate (always 0 with AI). Replace your old IVR dashboard.

## Common pitfalls

- **Static schedule copies.** Always read live capacity — never overbook.
- **Generic emergency thresholds.** Per-region tuning matters (snowstorm = no heat is critical).
- **Tech name privacy.** Some companies prefer "your technician" vs. names — config flag.

## How CallSphere does this in production

OneRoof Property runs 10 specialists over WebRTC + Pion + NATS — same shape, scaled. Healthcare's FastAPI :8084 ships 14 HIPAA tools. Salon emits `GB-YYYYMMDD-###` references the way the `HV-YYYYMMDD-###` pattern above mirrors. 37 agents · 90+ tools · 115+ DB tables · 6 verticals. Pricing $149/$499/$1499 flat — no $0.30–$0.80/min surprise. Start a [14-day trial](/trial) or check [/pricing](/pricing).

## FAQ

**ServiceTitan tier needed?** "Marketing Pro" or higher for full API.

**TCPA?** Inbound is fine; outbound recall to cell phones needs PEWC.

**After-hours pricing?** Tool returns the surcharge so the AI can quote correctly.

**Multi-trade (HVAC + plumbing + electric)?** One `trade` parameter on every tool.

**Voice tone?** Use ElevenLabs for warmer voice for residential calls.

## Sources

- [ServiceTitan AI voice](https://www.servicetitan.com/blog/ai-voice-agents-in-hvac)
- [HVAC voice agent guide](https://www.famulor.io/blog/ai-voice-agent-for-hvac-and-trades-247-job-dispatch)
- [OpenAI Realtime](https://platform.openai.com/docs/guides/realtime)
- [/pricing](https://callsphere.ai/pricing)

---

Source: https://callsphere.ai/blog/vw3h-build-callsphere-style-multi-agent-for-hvac-dispatch
