Build a CallSphere-Style Multi-Agent for an MSP IT Helpdesk
By Sagar Shankaran, Founder of CallSphere
Replace a single voicemail box with 4 specialists (auth, ticketing, password reset, escalation) that talk straight to ConnectWise/Halo PSA — full code.
Key takeaways
TL;DR — Single-vendor MSP voice tools (Thread Voice, MSP Process VoiceAssist) are great products but expensive at scale. A 4-specialist setup (auth, ticketing, password reset, escalation) on OpenAI Realtime + your PSA's API does the same job and lives inside your tenant.
What you'll build
A 4-agent IT helpdesk that authenticates the caller against AD or Entra, opens a ticket in ConnectWise PSA or Halo, handles password resets via Microsoft Graph, and escalates to the on-call tech for outages.
Prerequisites
- PSA: ConnectWise Manage, Halo PSA, or Autotask.
- Identity: Microsoft Graph or Okta.
- OpenAI Realtime + Twilio.
- Python 3.11+,
msal,httpx,openai-agents[voice]. - A list of caller-id phone numbers per company for fast match.
Architecture
flowchart TB
C[Caller] --> TR[Triage]
TR --> AU[Auth]
AU --> TK[Ticketing]
AU --> PW[Password Reset]
AU --> ES[Escalation]
TK --> PSA[(PSA API)]
PW --> GRAPH[(MS Graph)]
ES --> ONCALL[Twilio voice]
Step 1 — Caller-ID auth
```python @function_tool async def identify_caller(phone: str) -> dict: contact = await psa.contacts.find_by_phone(phone) if not contact: return {"authenticated": False, "next_step": "ask_company_pin"} return {"authenticated": True, "company_id": contact.company_id, "name": contact.name, "tier": contact.support_tier}
@function_tool async def verify_pin(company_pin: str, last_four_phone: str) -> dict: return await psa.auth.verify(pin=company_pin, last4=last_four_phone) ```
Step 2 — Ticketing specialist
```python @function_tool async def open_ticket(company_id: str, contact_id: str, summary: str, severity: str, category: str) -> dict: t = await psa.tickets.create( company_id=company_id, contact_id=contact_id, summary=summary, severity=severity, category=category, source="ai_voice") return {"ticket_id": t.id, "ref": t.ref} ```
Hear it before you finish reading
Talk to a live CallSphere AI voice agent for IT support in your browser — 60 seconds, no signup.
Step 3 — Password reset (Microsoft Graph)
```python import msal, httpx
async def graph_token(): app = msal.ConfidentialClientApplication( os.environ["AZ_CLIENT_ID"], authority=f"https://login.microsoftonline.com/{os.environ['AZ_TENANT_ID']}", client_credential=os.environ["AZ_CLIENT_SECRET"]) r = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"]) return r["access_token"]
@function_tool async def reset_password(user_principal_name: str) -> dict: tok = await graph_token() new_pw = secrets.token_urlsafe(12) async with httpx.AsyncClient() as c: r = await c.patch( f"https://graph.microsoft.com/v1.0/users/{user_principal_name}", headers={"Authorization": f"Bearer {tok}"}, json={"passwordProfile": {"forceChangePasswordNextSignIn": True, "password": new_pw}}) if r.status_code in (200, 204): return {"ok": True, "temp_password": new_pw} return {"ok": False, "error": r.text} ```
Step 4 — Outage escalation
```python @function_tool async def escalate_outage(company_id: str, summary: str) -> dict: t = await open_ticket(company_id, None, summary, "P1", "outage") oncall = await rotation.who_is_oncall() twilio.calls.create(to=oncall.phone, from_=BUSINESS_NUMBER, url=f"https://you/escalation?ticket={t['ref']}") return {"ticket_ref": t["ref"], "paged": oncall.name} ```
Step 5 — Triage prompt
```md You are the IT helpdesk front desk for [MSP]. Authenticate via caller-id; if unknown, ask company PIN + last 4 of phone. Then classify: password, outage, ticket. Hand off appropriately. Never share temp passwords on the phone — always SMS. ```
Step 6 — Twilio bridge
Same Realtime bridge pattern; pass From (caller-id) into the agent context as a session metadata field.
Still reading? Stop comparing — try CallSphere live.
See the IT support AI agent handle a real call — complete, industry-specific, and live in your browser. No signup.
Step 7 — Reporting
```sql SELECT date_trunc('day', occurred_at) AS day, count() FILTER (WHERE outcome = 'ticket_opened') AS tickets, count() FILTER (WHERE outcome = 'pw_reset') AS resets, count(*) FILTER (WHERE outcome = 'escalated') AS escalations FROM call_sessions GROUP BY 1 ORDER BY 1 DESC LIMIT 30; ```
Common pitfalls
- Voice-readable temp passwords. Don't — SMS only.
- Caller-ID spoofing. Always require a second factor for password resets.
- PSA rate limits. ConnectWise caps API calls/min.
How CallSphere does this in production
Explore a live demo and compare current plans to find the right fit for your business.
FAQ
SOC2? Use SOC2-compliant infrastructure; OpenAI Enterprise has SOC2.
MFA? Always — voice auth is necessary but not sufficient.
Cost per ticket? ~$0.20–$0.50 vs. $4–$8 for a human L1 touch.
Time savings? Reports of 25–40% L1 deflection in MSP studies.
Multi-tenant? company_id in every tool; row-level filtering.
Sources

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.