Skip to content

How to Connect AI Agents to Read and Write Data in Salesforce and HubSpot

Architect a unified execution layer that allows your AI agents to securely read and write data across Salesforce and HubSpot APIs while handling OAuth and rate limits.

Uday Gajavalli Uday Gajavalli · · 13 min read
How to Connect AI Agents to Read and Write Data in Salesforce and HubSpot

To connect an AI agent to read and write data in Salesforce and HubSpot, do not hand the Large Language Model (LLM) two raw vendor APIs and hope prompt engineering compensates for the gap. You must place a unified execution layer between your agent framework and the CRMs. Exposing a narrow set of business-level tools—like find_contact, upsert_account, or list_open_deals—allows that middle layer to absorb the underlying boilerplate of proprietary query languages, OAuth token refreshes, pagination, and rate limits, freeing the agent to reason entirely about intent rather than API mechanics.

The pressure to ship autonomous workflows is not merely academic. 88% of B2B organizations are adopting or planning to adopt AI agents, according to Forrester's State of Customer Obsession Survey, 2025. Furthermore, 91% of customer service leaders report executive pressure to implement AI-driven solutions. But giving an LLM write access to an enterprise CRM is a massive architectural risk. If your agent cannot reliably pull a deal from Salesforce, update the pipeline stage, and log a meeting note in HubSpot without hallucinating field names or dropping the OAuth context, your project will not survive production.

The failure rate is just as real as the demand: Gartner predicts that over 40% of agentic AI projects will be canceled by the end of 2027 due to escalating costs, unclear business value, or inadequate risk controls. The uncomfortable truth is that most of those escalating costs are not model spend or prompt tuning. They are the connector tax—the endless engineering hours spent keeping OAuth refreshes, schema drift, and rate-limit handling from breaking your agent in production.

Why Read-Only AI Agents Aren't Enough for Modern CRM

Read-only AI agents retrieve context to answer questions, while read/write AI agents autonomously execute workflows by creating, updating, and deleting records in downstream systems.

If your agent can only summarize a deal, it is essentially a dashboard or a chatbot with extra steps. Read-only assistants are useful for basic summarization and internal knowledge retrieval, but read/write assistants are where the business case gets serious. An agent that can read a deal, negotiate via email, and update the CRM stage autonomously is a scalable revenue engine. The business case for autonomous CRM agents sits squarely on write operations: logging engagements, updating pipeline stages, creating tasks, and enriching accounts.

The economic pull is aggressive. Salesforce reports that 83% of sales teams using AI saw revenue growth in the past year, compared to 66% of teams not using AI. Gartner projects that by 2029, agentic AI will autonomously resolve 80% of common customer service issues without human intervention, leading to a 30% reduction in operational costs.

Meanwhile, native offerings pitch themselves as the shortest path because they operate inside a single platform's data model with inherited governance. That is genuinely useful if your product lives entirely inside one CRM. However, if your B2B SaaS product needs to write into whichever CRM the customer happens to use, you cannot pick a side. You need a portable execution layer that speaks both dialects. The most interesting agent workflows are cross-CRM by definition, requiring reliable, bidirectional access. See our deeper breakdown in How to Connect AI Agents to Read and Write Data in Salesforce and HubSpot for the wider architectural framing.

The Integration Bottleneck: Connecting AI Agents to Salesforce and HubSpot

When you ask an LLM to "find all enterprise accounts in the software industry," the model has to translate that natural language intent into a specific API request. Handing an LLM the raw Salesforce REST API and HubSpot CRM API sounds elegant in a demo, but in production, it becomes a maintenance disaster. You are forcing the model to understand the distinct architectural philosophies of two entirely different platforms.

Consider what a single "find all open opportunities for Acme Corp" query requires across both systems:

  • Salesforce: Construct a SOQL (Salesforce Object Query Language) query with proper escaping, respect the 100-record default paging limit, follow nextRecordsUrl cursors, handle field-level security errors, and map custom fields with __c suffixes. The LLM must generate a valid string like: SELECT Id, Name FROM Account WHERE Industry = 'Software'.
  • HubSpot: Build a complex JSON payload containing filterGroups with AND/OR logic sent via a POST request to a search endpoint. It must respect the 100-record hard cap on /crm/v3/objects/deals/search, follow paging.next.after cursors, and reconcile associations via a separate /associations call.

Now multiply that by every entity—contacts, accounts, deals, notes, tasks, engagements—and every write operation. The surface area is huge.

The Problem with Raw API Tooling

The naive shortcut is to expose one tool per raw endpoint (e.g., exposing the raw LangChain or LangGraph REST tools) and let the LLM stitch calls together. When you do this, you encounter three immediate failure modes:

  1. Schema Hallucination: LLMs are trained on public documentation, much of which is outdated. They will inevitably cross-contaminate the syntax. They will try to send SOQL to HubSpot, or they will hallucinate Salesforce's __c custom field suffixes onto standard HubSpot properties. They call crm/v3/objects/contacts/search when they meant /deals.
  2. Pagination Failures: Salesforce uses offset pagination or cursor-based links. HubSpot uses an after cursor. An autonomous agent trying to aggregate 500 records will frequently drop the pagination state, resulting in incomplete data processing.
  3. Payload Complexity: Constructing a nested JSON payload for a HubSpot batch update requires strict typing. LLMs often fail to format the arrays correctly, leading to HTTP 400 Bad Request errors.

Every failure round-trips through more tokens, blowing up latency and cost. The alternative is a narrow, business-level tool interface that maps one agent intent to one deterministic call:

# Instead of exposing raw endpoints, expose intent-level tools
tools = [
    "find_contact(email: str) -> Contact",
    "upsert_account(domain: str, name: str, industry: str) -> Account",
    "list_open_deals(account_id: str, min_amount: float) -> list[Deal]",
    "log_engagement(deal_id: str, note: str, type: str) -> Engagement",
]

Each tool resolves to a unified data model, and the execution layer translates it into the correct SOQL or HubSpot search payload behind the scenes. The LLM never sees the vendor's warts.

Handling Authentication: OAuth Token Management for Autonomous Agents

OAuth token management for AI agents is the process of securely storing, refreshing, and applying tenant-specific access tokens to API requests without requiring synchronous human intervention.

OAuth is where most in-house integrations bleed out. Traditional SaaS integrations operate synchronously: a user clicks a button, the app makes an API call. If the token is expired, the app refreshes it on the fly or prompts the user.

AI agents operate asynchronously and unpredictably. A user might trigger a workflow that takes an agent 45 minutes to process a massive batch of leads, cross-referencing them against external enrichment APIs before writing them back to the CRM. Salesforce access tokens expire based on session policy (often 15 minutes to 2 hours). HubSpot access tokens expire after 30 minutes. If the token expires at minute 30, a direct integration fails, and the agent's entire thought process is lost.

An autonomous agent cannot pause and ask a user to re-authenticate at 3 a.m. Your platform needs to handle this in the background:

  1. Schedule refreshes ahead of expiry: Waiting until you receive a 401 Unauthorized is too late—you have already burned a tool call and probably confused the agent's reasoning loop. The system must monitor the Time-To-Live (TTL) of every token and refresh it proactively.
  2. Serialize refreshes per connection: Concurrent AI workers hitting refresh at the same time will invalidate each other's tokens on providers that rotate refresh tokens on use.
  3. Distinguish transient failures from revoked grants: A 401 during a refresh usually means the user disconnected the app in Salesforce Setup. That is a user-facing state change, not a retry candidate.
  4. Encrypt tokens with per-tenant keys: A single compromised secret in a multi-tenant agent platform is a catastrophic breach.

When the agent decides to execute a tool, the unified layer intercepts the request, injects the guaranteed-fresh bearer token into the header, and proxies the request. For a deeper look at the trade-offs, see our B2B SaaS guide to OAuth token management.

Warning

Never store OAuth tokens in your agent framework's memory or vector store. Frameworks like LangChain and LangGraph were not designed as credential vaults. Keep tokens in a dedicated secrets store, fetch them at call time, and never let them cross into prompt context.

Managing API Rate Limits and Error Handling

AI agents are incredibly fast. Left unchecked, an agent looping through a list of 1,000 contacts to update their lead scores will instantly trigger upstream rate limits. Salesforce enforces daily API request limits based on org edition and license count, alongside concurrent request limits. HubSpot enforces a strict 10-second rolling window for API calls, plus daily limits by tier.

When an upstream API returns an HTTP 429 Too Many Requests, the worst thing you can do is let the LLM see the raw error and attempt to "reason" its way out of it. The model will likely spam the endpoint repeatedly, resulting in a hard ban from the CRM provider.

Standardizing Rate Limit Headers

A production-grade unified API layer standardizes how your application reads rate limits. Rather than silently retrying, throttling, or absorbing errors—which can break asynchronous agent architectures—the platform should pass the error straight through to the caller but normalize the wildly inconsistent vendor signals into IETF standardized headers:

  • ratelimit-limit: The maximum number of requests permitted in the current window.
  • ratelimit-remaining: The number of requests remaining in the current window.
  • ratelimit-reset: The time (in seconds) at which the current rate limit window resets.

This design is intentional. Agent workflows have very different tolerance profiles. A background reconciliation job can back off for minutes. An interactive Slack agent responding to a sales rep needs to fail fast and tell the user "try again in 30 seconds." A queue-driven enrichment worker needs to shed load and re-enqueue.

Implementation Examples: Synchronous vs. Asynchronous Backoff

If your agent is running a synchronous loop, you should implement a circuit breaker that pauses the execution loop entirely using the ratelimit-reset header. Do not burn LLM tokens asking the model to "wait and try again."

# Synchronous Python Example
import time
import requests
 
def execute_crm_tool(tool_name, payload):
    max_retries = 3
    base_delay = 2
 
    for attempt in range(max_retries):
        response = requests.post(
            f"https://api.unified-layer.com/crm/{tool_name}",
            json=payload,
            headers={"Authorization": "Bearer YOUR_TOKEN"}
        )
 
        if response.status_code == 429:
            reset_time = response.headers.get('ratelimit-reset')
            sleep_duration = max(0, int(reset_time) - int(time.time())) if reset_time else base_delay ** attempt
                
            print(f"Rate limited. Sleeping for {sleep_duration} seconds.")
            time.sleep(sleep_duration)
            continue
            
        response.raise_for_status()
        return response.json()
        
    raise Exception("Max retries exceeded after rate limit.")

If your agent is operating in an asynchronous, queue-driven environment, the pattern should defer work rather than blocking the thread:

// Asynchronous TypeScript Example
async function callTool(toolName: string, args: object) {
  const res = await unifiedApi.tools.invoke(toolName, args);
 
  if (res.status === 429) {
    const resetSeconds = Number(res.headers['ratelimit-reset']) || 30;
    // Push work back to a queue, don't block the agent loop
    await queue.enqueue({ toolName, args, delaySec: resetSeconds });
    return { status: 'deferred', retryAfter: resetSeconds };
  }
 
  if (res.status >= 500) {
    // Exponential backoff with jitter for transient upstream failures
    return retryWithBackoff(() => unifiedApi.tools.invoke(toolName, args));
  }
 
  return res.data;
}

Pair this with idempotency keys on writes so a retried upsert_contact never creates duplicates.

Building a Unified Execution Layer for LLM Function Calling

To safely connect an agent to both Salesforce and HubSpot, you must build or buy a unified execution layer. This layer exposes a normalized data model to the LLM. Instead of teaching the model about Salesforce Account objects and HubSpot Company objects, you teach it about a single, unified Company concept.

The Architecture of Unified Tool Calling

When you use a unified API, the architecture follows a strict sequence that isolates the LLM from the integration complexity.

sequenceDiagram
    participant LLM as LLM (OpenAI/Anthropic)
    participant Agent as Agent Framework
    participant Unified as Unified API Layer
    participant CRM as Upstream API (Salesforce)

    Agent->>LLM: Prompt with unified tools (e.g., upsert_account)
    LLM->>Agent: Call tool: upsert_account with JSON args
    Agent->>Unified: POST /unified/crm/accounts
    Unified->>Unified: Map normalized fields to upstream schema
    Unified->>Unified: Inject fresh OAuth token
    Unified->>CRM: PATCH /services/data/vXX.X/sobjects/Account
    CRM-->>Unified: 200 OK (Record ID)
    Unified-->>Agent: Normalized Account Object
    Agent-->>LLM: Return tool execution result

Instead of exposing full CRUD access to the entire CRM, define narrow, purpose-built tools for your agent. For example, define a tool called upsert_account. The LLM only needs to provide the company name and domain. The unified layer handles the mapping configuration that links these unified fields to the provider-specific fields (e.g., mapping domain to Website in Salesforce or domain in HubSpot).

{
  "type": "function",
  "function": {
    "name": "upsert_account",
    "description": "Creates a new account or updates an existing one based on the company domain.",
    "parameters": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "The legal name of the company"
        },
        "domain": {
          "type": "string",
          "description": "The primary website domain of the company (e.g., acme.com)"
        }
      },
      "required": ["name", "domain"]
    }
  }
}

Here is what binding those tools looks like in LangChain, agnostic to which CRM the tenant connected:

from langchain_openai import ChatOpenAI
from unified_api_sdk import UnifiedTools
 
# One call fetches the tool schema for whichever CRM this tenant uses
tools = UnifiedTools.for_tenant(
    tenant_id="acme-corp",
    unified_model="crm",
    allowed_tools=[
        "find_contact",
        "upsert_contact",
        "list_open_deals",
        "log_engagement",
    ],
)
 
llm = ChatOpenAI(model="gpt-4o").bind_tools(tools)
 
response = llm.invoke([
    ("system", "You are a sales ops agent. Use tools to update CRM state."),
    ("user", "Log a discovery call with jane@acme.com and move the deal to Stage 2."),
])

The exact same agent logic—and the exact same prompt—works seamlessly whether the end-user has connected their Salesforce instance or their HubSpot instance. The cross-CRM translation happens entirely within the unified layer.

Enforcing Architectural Rules at the Boundary

When exposing these tools, enforce strict rules at the boundary:

  • Scope tools per user, not per app: The token used for a call should match the user who authorized it, so audit logs and permission checks work as expected.
  • Whitelist tools per agent: A support agent should not have delete_opportunity in its toolbox.
  • Add approval gates on destructive writes: Bulk deletes, deal-stage changes above a dollar threshold, and account merges deserve a human-in-the-loop check.
  • Emit structured audit events: Every tool call should log tenant, user, tool name, arguments hash, and outcome for compliance replay.

For more architectural patterns on this, see our guide on what is LLM function calling for integrations.

Primary Agent Use Cases in Unified CRM

Once you have established a secure, normalized read/write connection, the workflows that used to require months of connector work become straightforward orchestration problems. Here are the primary use cases driving adoption in B2B SaaS:

1. AI-Powered Lead Enrichment & Routing

When an inbound web lead is captured, an agent can check for an existing Account. If none exists, it can use an external data provider (like Clearbit or ZoomInfo) to enrich the company data, create the Account and Lead records via the unified API, and generate a Task for the appropriate User based on complex territory rules. The agent handles the entire triage process autonomously through one unified tool surface.

2. Automated Meeting Logging

Sales reps despise manual data entry. An intelligent calendar integration can parse meeting transcripts, automatically generate summarized Notes, and use the unified Engagements endpoint to log the meeting against the correct Contact and Opportunity. This guarantees high-fidelity CRM data without rep intervention. The same tool call works whether the tenant is on Salesforce or HubSpot.

3. Pipeline Hygiene & Stale Deal Alerts

Agents can act as autonomous sales managers. An agent can be scheduled to periodically poll Opportunities and their associated Stages and Engagements. If a high-value deal has not had a logged interaction in 14 days, the agent can automatically alert the assigned User via Slack or Teams with a summarized recap and next-step suggestions, and update a custom "At Risk" flag on the CRM record.

4. Natural Language CRM Querying (RAG)

Enable sales leaders to ask questions like, "What deals are closing this month over $50K that have not had a meeting in two weeks?" The prompt engine translates this into unified API calls, fetching Opportunities filtered by close date and checking related Engagements. It then presents the structured data as a conversational summary, effectively replacing complex CRM reporting dashboards with a natural language interface. This pattern is the easiest way to pull real-time CRM context into an LLM prompt without relying on stale caches. For a complete walkthrough, see Connect HubSpot to AI Agents: Sync invoices, orders, and workflows.

None of these require the agent to know the difference between a Salesforce Opportunity.StageName and a HubSpot deal.dealstage. The unified model collapses that distinction.

Where This Approach Stops Being Enough

Radical honesty: a unified execution layer is not a free lunch. There are cases where it will hurt more than help.

  • Deep custom object graphs: If your customer has a heavily customized Salesforce org with hundreds of custom objects and complex triggers, the unified model will cover the standard entities and leave you to add passthrough calls for the rest.
  • Latency-critical single-CRM workflows: If you only ever integrate with one CRM and every millisecond counts, a hand-tuned direct client may edge out any abstraction layer.
  • Ultra-specific vendor features: Salesforce Flow triggers, HubSpot workflow enrollments, and other vendor-native primitives may require raw API access alongside the unified surface.

The practical pattern is hybrid: use the unified layer for 90% of standard CRUD and orchestration, and drop to a passthrough call for the remaining vendor-specific edge cases. That way you avoid the connector tax for everything portable while retaining full control where it matters.

Strategic Next Steps for Engineering Teams

Connecting an AI agent to read and write data in Salesforce and HubSpot is not a prompt engineering challenge; it is an infrastructure challenge. If you rely on direct API calls, your team will drown in OAuth token lifecycle management, undocumented vendor edge cases, and rate limit bans.

If you are staring down the roadmap of building this yourself, follow this sequence to survive production:

  1. Define a narrow tool surface: Identify the smallest set of business-level tools your agent needs. Ten well-scoped tools beat fifty raw endpoints.
  2. Automate OAuth background refresh: Wire OAuth token storage and schedule access token refreshes before expiry. Do this before you write a single agent prompt.
  3. Define your rate-limit contract: Decide how the agent responds to HTTP 429. Read the standardized IETF rate limit headers and implement a circuit breaker or queue deferral based on workflow tolerance.
  4. Add idempotency and audit logging: Attach idempotency keys to every write call so retries do not create duplicates.
  5. Gate destructive operations: Require human approval for bulk deletes and high-value deal-stage changes.

Stop building custom CRM connectors for your AI agents. Focus your engineering cycles on improving the agent's core reasoning capabilities, and let a unified execution layer absorb the complexities of SOQL, HubSpot search payloads, and HTTP 429 backoff requirements.

FAQ

How do I handle Salesforce and HubSpot rate limits with AI agents?
Use a unified API that normalizes vendor rate-limit signals into IETF standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset). When receiving an HTTP 429 error, pause your agent's execution loop or defer the task to a queue based on the reset time.
Should I let my LLM write SOQL or HubSpot filterGroups directly?
No. Models frequently hallucinate SOQL syntax, forget URL encoding, and struggle with HubSpot's filterGroups structure. It is safer to expose narrow, business-level tools like find_contact to the LLM instead of providing raw database query access.
How do I manage OAuth tokens for background AI tasks?
Offload token lifecycle management to an infrastructure layer that automatically schedules and refreshes OAuth tokens ahead of their expiry. Serialize refreshes per connection to avoid stampedes, ensuring long-running agents never fail due to expired credentials.
Can the same agent code work against both Salesforce and HubSpot?
Yes, if you bind to a unified CRM data model. The same find_contact or upsert_account tool call resolves to a SOQL query for Salesforce tenants and a filterGroups search for HubSpot tenants. Your agent framework never learns the vendor differences.

More from our Blog