---
title: "AI Voice Agent + Salesforce Integration: Enterprise Developer Guide"
description: "A developer guide to integrating AI voice agents with Salesforce — lead push, call activity logging, and managed packages."
canonical: https://callsphere.ai/blog/ai-voice-agent-salesforce-integration-guide
category: "Technical Guides"
tags: ["AI Voice Agent", "Technical Guide", "Salesforce", "CRM", "Integration", "Enterprise", "APIs"]
author: "CallSphere Team"
published: 2026-04-08T00:00:00.000Z
updated: 2026-05-06T01:02:47.152Z
---

# AI Voice Agent + Salesforce Integration: Enterprise Developer Guide

> A developer guide to integrating AI voice agents with Salesforce — lead push, call activity logging, and managed packages.

## Why Salesforce is different

HubSpot is a REST API with sensible defaults. Salesforce is a platform with its own query language (SOQL), its own composite API batching rules, its own OAuth flavors, and dozens of permission settings that will silently block your writes. Getting an AI voice agent into Salesforce cleanly is an enterprise-grade integration task, not a weekend project.

This guide walks through the integration patterns CallSphere uses for enterprise customers — JWT Bearer OAuth, composite API writes, call activity logging, and lead capture.

```
caller → voice agent
            │
            │ tool: lookup_lead_by_phone
            ▼
         SOQL query
            │
            ▼
       Lead / Contact / Account
            │
            ▼
       Task (type=Call) inserted via composite API
```

## Architecture overview

```
┌────────────────────┐
│ Voice agent edge   │
└─────────┬──────────┘
          │ tool call
          ▼
┌──────────────────────────┐
│ /salesforce service      │
│ • JWT Bearer OAuth       │
│ • Composite API batching │
│ • Bulk API 2.0 fallback  │
└──────────┬───────────────┘
           │
           ▼
┌──────────────────────────┐
│ Salesforce org           │
└──────────────────────────┘
```

## Prerequisites

- A Salesforce org (Enterprise, Performance, or Developer edition).
- A Connected App with JWT Bearer flow enabled and a self-signed certificate.
- The `simple-salesforce` Python library or `jsforce` for Node.
- Familiarity with SOQL and the composite REST API.

## Step-by-step walkthrough

### 1. Authenticate with JWT Bearer flow

Server-to-server. No user interaction. Re-used across calls.

```mermaid
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
```

```python
import jwt, time, requests
from simple_salesforce import Salesforce

def get_access_token():
    claim = {
        "iss": SF_CLIENT_ID,
        "sub": SF_USERNAME,
        "aud": "https://login.salesforce.com",
        "exp": int(time.time()) + 300,
    }
    assertion = jwt.encode(claim, SF_PRIVATE_KEY, algorithm="RS256")
    resp = requests.post(
        "https://login.salesforce.com/services/oauth2/token",
        data={
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
            "assertion": assertion,
        },
    )
    resp.raise_for_status()
    body = resp.json()
    return body["access_token"], body["instance_url"]

token, instance = get_access_token()
sf = Salesforce(instance_url=instance, session_id=token)
```

### 2. Look up the caller

```python
async def find_lead(phone: str):
    soql = f"""
        SELECT Id, FirstName, LastName, Company, Status
        FROM Lead
        WHERE Phone = '{phone}' OR MobilePhone = '{phone}'
        LIMIT 1
    """
    rows = sf.query(soql)["records"]
    return rows[0] if rows else None
```

### 3. Log the call as a Task

Salesforce's canonical "call activity" object is a `Task` with `Type = 'Call'`. Use the composite API to insert the task and update the lead in one round trip.

```python
def log_call(lead_id: str, subject: str, description: str, duration_sec: int):
    payload = {
        "compositeRequest": [
            {
                "method": "POST",
                "url": "/services/data/v60.0/sobjects/Task",
                "referenceId": "newTask",
                "body": {
                    "Subject": subject,
                    "Description": description,
                    "Type": "Call",
                    "Status": "Completed",
                    "CallDurationInSeconds": duration_sec,
                    "WhoId": lead_id,
                    "ActivityDate": "2026-04-08",
                },
            },
            {
                "method": "PATCH",
                "url": f"/services/data/v60.0/sobjects/Lead/{lead_id}",
                "referenceId": "updateLead",
                "body": {"Status": "Working - Contacted"},
            },
        ]
    }
    return sf.restful("composite", method="POST", json=payload)
```

### 4. Create new leads from the call

```python
def create_lead(first: str, last: str, phone: str, company: str, source: str = "AI Voice Agent"):
    return sf.Lead.create({
        "FirstName": first,
        "LastName": last,
        "Phone": phone,
        "Company": company or "Unknown",
        "LeadSource": source,
        "Status": "New",
    })
```

### 5. Expose the tools to the agent

```typescript
const sfTools = [
  { type: "function", name: "find_lead_by_phone", description: "Look up a Salesforce lead by phone", parameters: { type: "object", properties: { phone: { type: "string" } }, required: ["phone"] } },
  { type: "function", name: "create_lead", description: "Create a new Salesforce lead", parameters: { type: "object", properties: { first: { type: "string" }, last: { type: "string" }, phone: { type: "string" }, company: { type: "string" } }, required: ["last", "phone"] } },
  { type: "function", name: "log_call_task", description: "Log a completed call as a Task", parameters: { type: "object", properties: { lead_id: { type: "string" }, subject: { type: "string" }, description: { type: "string" }, duration_sec: { type: "number" } }, required: ["lead_id", "subject"] } },
];
```

### 6. Handle errors like an enterprise integrator

Salesforce will return `REQUIRED_FIELD_MISSING`, `INVALID_SESSION_ID`, and `DUPLICATES_DETECTED`. Map each to a clean tool response the LLM can act on.

## Production considerations

- **API limits**: orgs get 15k-100k API calls per 24h depending on edition. Monitor `Sforce-Limit-Info`.
- **Session expiry**: JWT tokens last ~30 minutes. Cache them and refresh proactively.
- **Duplicate rules**: they will block `Lead.create`. Handle the `DUPLICATES_DETECTED` error by surfacing the existing record.
- **Field-level security**: the service user needs explicit field permissions, not just object permissions.
- **Governor limits on triggers**: an insert can fire Apex triggers that fail silently if your payload is too large.

## CallSphere's real implementation

CallSphere connects to Salesforce for enterprise sales and real estate customers. The real estate stack runs 10 agents (buyer specialist, seller specialist, rental specialist, tour coordinator, qualification agent, and more) coordinated through the OpenAI Agents SDK, and the sales pod pairs ElevenLabs TTS with 5 GPT-4 specialists for discovery, qualification, demo scheduling, objection handling, and close.

The voice plane runs on the OpenAI Realtime API with `gpt-4o-realtime-preview-2025-06-03`, PCM16 at 24kHz, and server VAD. Salesforce writes flow through a dedicated service that batches composite requests, mirrors every write to per-vertical Postgres for auditing, and attaches sentiment and lead score from the GPT-4o-mini post-call pipeline as custom fields on the Task. CallSphere runs 57+ languages with under one second end-to-end response time.

## Common pitfalls

- **Per-call OAuth**: re-authenticating on every call burns your API quota. Cache the token.
- **Ignoring duplicate rules**: your agent will hallucinate "I added you" while nothing was saved.
- **Skipping composite API**: individual writes blow through API limits under load.
- **Not handling REQUIRED_FIELD_MISSING**: required fields vary by org; surface them as tool errors.
- **Hardcoding the API version**: pin it, but plan to bump every year.

## FAQ

### Should I use Bulk API or REST?

REST for single-record writes, Bulk API 2.0 for backfills. Voice agents almost always want REST.

### Can I use a managed package instead?

Yes, but the ROI is only there if you are selling to many Salesforce customers. For a single deployment, direct API is simpler.

### How do I handle Person Accounts?

Check `Account.IsPersonAccount`. The field layout differs.

### What about sandboxes?

Use a separate Connected App pointed at `https://test.salesforce.com` for sandbox JWT auth.

### How do I test without burning API calls?

Use the `cometd` streaming API + simulator, or a Salesforce DX scratch org.

## Next steps

Looking to integrate Salesforce with an AI voice agent in your org? [Book a demo](https://callsphere.tech/contact), see the [technology page](https://callsphere.tech/technology), or check [pricing](https://callsphere.tech/pricing).

#CallSphere #Salesforce #CRM #VoiceAI #EnterpriseIntegration #SOQL #AIVoiceAgents

---

Source: https://callsphere.ai/blog/ai-voice-agent-salesforce-integration-guide
