AI Voice Agent + HubSpot CRM Integration: Complete Developer Guide
Build a production integration between an AI voice agent and HubSpot CRM — contact sync, call logging, and deal creation.
The CRM tax on voice agents
Every voice agent you ship will immediately be asked three questions by the business owner: "did it create the contact?", "did it log the call?", and "did it update the deal?" If the answer to any of those is no, the agent is not useful to their operations team, no matter how good the conversation was.
This guide walks through a production HubSpot integration for an AI voice agent, from the initial contact lookup on ring to the deal stage update at hangup.
ring → lookup contact by phone
│
▼
existing? ── yes ──► attach call to contact
│
no
│
▼
create_contact(name, phone, lifecycle=lead)
│
▼
log_call(contact_id, recording_url, transcript)
│
▼
optionally: create_deal(contact_id, amount, stage)
Architecture overview
┌───────────────────┐
│ Voice agent edge │
└─────────┬─────────┘
│ tool call
▼
┌──────────────────────────┐
│ /hubspot service │
│ • OAuth / private app │
│ • retry + idempotency │
│ • webhook consumer │
└──────┬────────────┬──────┘
│ │
▼ ▼
HubSpot API Postgres mirror
Prerequisites
- A HubSpot account with a Private App or OAuth app with the Contacts, Engagements, and Deals scopes.
- The HubSpot Node or Python SDK.
- A Postgres table to mirror contact/engagement writes for auditing.
Step-by-step walkthrough
1. Look up the contact on ring
from hubspot import HubSpot
from hubspot.crm.contacts import Filter, FilterGroup, PublicObjectSearchRequest
client = HubSpot(access_token=HS_TOKEN)
async def find_contact_by_phone(phone: str):
search = PublicObjectSearchRequest(
filter_groups=[FilterGroup(filters=[
Filter(property_name="phone", operator="EQ", value=phone),
])],
properties=["firstname", "lastname", "lifecyclestage", "email"],
limit=1,
)
resp = client.crm.contacts.search_api.do_search(public_object_search_request=search)
return resp.results[0] if resp.results else None
2. Create the contact if missing
from hubspot.crm.contacts import SimplePublicObjectInputForCreate
async def create_contact(phone: str, first: str, last: str):
payload = SimplePublicObjectInputForCreate(properties={
"phone": phone,
"firstname": first,
"lastname": last,
"lifecyclestage": "lead",
"hs_lead_status": "NEW",
})
return client.crm.contacts.basic_api.create(simple_public_object_input_for_create=payload)
3. Log the call as an engagement
HubSpot represents a logged call as a Call engagement associated with the contact. Attach the transcript and recording URL.
sequenceDiagram
autonumber
participant Caller as Caller
participant Agent as CallSphere Agent
participant API as CRM API
participant DB as CRM Database
participant Webhook as Webhook Listener
Caller->>Agent: Inbound call begins
Agent->>Agent: STT plus intent detection
Agent->>API: Lookup contact by phone
API->>DB: Read contact record
DB-->>API: Contact and history
API-->>Agent: Personalized context
Agent->>API: Create call activity
Agent->>API: Update deal stage
API->>Webhook: Outbound webhook fires
Webhook-->>Agent: Confirmed
Agent->>Caller: Spoken confirmation
CALL_ENGAGEMENT = {
"properties": {
"hs_timestamp": "2026-04-08T15:00:00Z",
"hs_call_title": "Inbound — AI receptionist",
"hs_call_body": "Caller asked about Saturday availability.",
"hs_call_duration": "185000",
"hs_call_from_number": "+14155551234",
"hs_call_to_number": "+14155550000",
"hs_call_recording_url": "https://storage.yourapp.com/rec/abc.wav",
"hs_call_status": "COMPLETED",
},
"associations": [
{
"to": {"id": "contact_id_here"},
"types": [{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 194}],
}
],
}
4. Create or update a deal
For sales verticals, create a deal on first call and move it through the pipeline as the conversation progresses.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
async def create_deal(contact_id: str, amount: float, dealname: str):
payload = {
"properties": {
"dealname": dealname,
"amount": str(amount),
"dealstage": "appointmentscheduled",
"pipeline": "default",
},
"associations": [
{"to": {"id": contact_id}, "types": [{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 3}]},
],
}
return client.crm.deals.basic_api.create(simple_public_object_input_for_create=payload)
5. Expose tools to the agent
const hubspotTools = [
{ type: "function", name: "log_call", description: "Log an AI call to HubSpot", parameters: { type: "object", properties: { contact_phone: { type: "string" }, summary: { type: "string" }, recording_url: { type: "string" } }, required: ["contact_phone", "summary"] } },
{ type: "function", name: "create_deal", description: "Create a deal for a known contact", parameters: { type: "object", properties: { contact_id: { type: "string" }, dealname: { type: "string" }, amount: { type: "number" } }, required: ["contact_id", "dealname"] } },
];
6. Consume HubSpot webhooks
HubSpot can push deal stage changes back to you. Consume them to keep your local state in sync and trigger follow-up calls.
Production considerations
- Rate limits: 100 requests per 10 seconds on Private Apps. Retry with jitter.
- Association type IDs: HubSpot uses numeric IDs for association types. Cache them.
- Idempotency: HubSpot does not de-dupe contacts by phone automatically. Search first.
- PII: call recordings may contain PHI; do not store recording URLs in HubSpot if you are under HIPAA.
- Pipeline mapping: deal stage IDs differ per portal. Fetch and cache them.
CallSphere's real implementation
CallSphere integrates with HubSpot across its sales and real estate verticals. The sales pod uses ElevenLabs TTS with 5 GPT-4 specialists coordinated through the OpenAI Agents SDK, while the real estate stack runs 10 agents including a buyer specialist, seller specialist, rental specialist, and qualification agent. Both push contact creation, call logging, and deal updates into HubSpot through the pattern above, with every write mirrored into per-vertical Postgres for auditing.
The voice layer runs on the OpenAI Realtime API (gpt-4o-realtime-preview-2025-06-03) at 24kHz PCM16 with server VAD, and post-call analytics from a GPT-4o-mini pipeline attach sentiment, intent, and lead score to the HubSpot call engagement as custom properties. CallSphere supports 57+ languages and runs under one second end-to-end on live traffic.
Common pitfalls
- Hardcoding the deal stage: stage IDs differ between portals.
- Skipping the contact search: you end up with a HubSpot full of duplicates.
- Logging recordings under HIPAA: HubSpot is not a HIPAA BAA-covered service by default.
- Ignoring the association type IDs: your engagements will not show up under the contact.
- Retrying naively: compound rate-limit errors can lock you out.
FAQ
Should I use OAuth or a Private App?
Private App for single-tenant deployments, OAuth for multi-tenant SaaS.
How fast does HubSpot reflect changes?
Writes are usually visible within 1-2 seconds, but search indices can lag 30-60 seconds.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Can I push transcripts into a custom property?
Yes — create a custom property on the Call engagement and set it during create.
How do I handle merged contacts?
Subscribe to the contact.merged webhook and update your mirror table.
Can I trigger HubSpot workflows from a call?
Yes — enrolling a contact in a workflow is a single API call.
Next steps
Want to see an AI voice agent logging calls straight into HubSpot? Book a demo, read the technology page, or see pricing.
#CallSphere #HubSpot #CRM #VoiceAI #Integration #SalesOps #AIVoiceAgents
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.