---
title: "HubSpot and Salesforce MCP: CRM updates with approvals"
description: "Let Claude or any AI agent update HubSpot or Salesforce through one MCP server. Reads and notes run at once; deal changes wait for a manager's approval."
canonical: https://actionlake.com/guides/hubspot-salesforce-mcp-crm-approvals
updated: 2026-10-24
---

Guide · CRM

# HubSpot and Salesforce MCP for sales teams: let an agent update your CRM, with approvals

**On this page**

Updated 24 October 2026 Published 22 October 2026 By the actionlake team

To let an AI agent update HubSpot or Salesforce without handing it the whole CRM, connect the CRM to actionlake, an MCP gateway, once, then give each rep's agent a key that can read the CRM and log notes freely, while deal-stage and amount changes wait for a sales manager's approval. The agent works through one remote MCP server, `https://connect.actionlake.com/mcp`, never sees the CRM token, and every change is recorded with its reason.

**Start free - 100,000 calls a month, no card.** [Create a sandbox](https://actionlake.com/console) or read the [quickstart](https://actionlake.com/docs/quickstart).

[Start free](https://actionlake.com/console?start=sandbox)

## What sales teams actually want an agent to do

| Job | Example request | Effect |
| --- | --- | --- |
| Prepare for a call | "What do we know about Acme, and what happened last quarter?" | Read |
| Log the call | "Add a note to Jane Smith: wants pricing for 40 seats, follow up Friday." | Write, low risk |
| Clean records | "Fix the job titles on these five contacts." | Write |
| Move the pipeline | "Move the Acme deal to Contract sent, amount 48,000." | Write, forecast-changing |
| Bulk changes | "Archive every lead with no activity in a year." | Destructive |

The first two should happen at once. The last two are what a sales manager wants to see before they happen. That split is the design below.

## The connectors

| Connector | Covers | Example actions |
| --- | --- | --- |
| [HubSpot CRM](https://actionlake.com/connectors/hubspot-crm) | Contacts, companies, notes | `hubspot_crm.contacts.search`, `hubspot_crm.contacts.update`, `hubspot_crm.notes.create` |
| [HubSpot Deals](https://actionlake.com/connectors/hubspot-deals) | Deals, pipelines, stages | `hubspot_deals.deals.search`, `hubspot_deals.deals.update`, `hubspot_deals.pipeline_stages.list` |
| [HubSpot Tickets](https://actionlake.com/connectors/hubspot-tickets) | Support tickets | Ticket reads and updates |
| [Salesforce Sales Cloud](https://actionlake.com/connectors/salesforce-sales-cloud) | Accounts, contacts, leads, opportunities, tasks, SOQL | `salesforce_sobjects.opportunities.update`, `salesforce_sobjects.tasks.create`, `salesforce_sobjects.query.run` |
| [Salesforce Service Cloud](https://actionlake.com/connectors/salesforce-service-cloud) | Cases and service objects | Case reads and updates |

Contact listing answers to one shared name, `crm.contact.list`, across 34 connectors, so the same agent instructions work on whichever CRM is connected. See [canonical action names](https://actionlake.com/glossary/canonical-verb).

HubSpot and Salesforce each offer MCP access of their own. HubSpot's remote MCP server, at `https://mcp.hubspot.com`, has been generally available since 13 April 2026: each user signs in with OAuth 2.1 and PKCE, and every action respects that user's HubSpot permissions ([HubSpot developer changelog](https://developers.hubspot.com/changelog/remote-hubspot-mcp-server-is-now-generally-available), checked 22 October 2026). If you need one CRM in one client with the vendor's permissions, that may be enough. A gateway adds per-agent keys, caps, approval rules, one ledger, and questions that join the CRM with billing or support.

## Step 1: Connect the CRM once

Both CRMs use OAuth. Create an app in the CRM (a HubSpot app with OAuth, or a Salesforce connected app), authorise it once as an admin, and store the resulting tokens with the app's client id and secret. actionlake renews the access token from the refresh token as it expires.

```json
{
  "op": "connections.create",
  "service": "salesforce_sobjects",
  "name": "default",
  "secrets": { "access_token": "…", "refresh_token": "…", "client_id": "…", "client_secret": "…" },
  "setup_data": { "instance_host": "https://yourcompany.my.salesforce.com" }
}
```

For HubSpot the shape is the same, with `"service": "hubspot_crm"` (and again for `hubspot_deals`) and no `setup_data`. Ask `connections.requirements` first; it returns the exact fields each service needs, free. Tokens are sealed on arrival and never returned.

The app's OAuth scopes are the outer limit. Grant the CRM app only the objects your agents need; actionlake's keys narrow it further, never wider.

## Step 2: A key per rep, with deal changes gated

Mint keys in the console or with your root key over REST. Keys are never minted through an OAuth connection.

```json
{
  "name": "rep-agent-jordan",
  "holder": "agent",
  "allowed_actions": [
    "hubspot_crm.read:*", "hubspot_deals.read:*",
    "hubspot_crm.notes.create", "hubspot_crm.contacts.update",
    "hubspot_deals.deals.update"
  ],
  "cap_calls": 2000,
  "confirm_policy": "human",
  "approver": "sales-manager@example.com",
  "approval_rules": [
    { "match": "hubspot_deals.deals.update", "approver": "sales-manager@example.com" }
  ]
}
```

What this key does:

- **Reads** anything in HubSpot CRM and Deals, at once.
- **Notes and contact fixes** go straight in. They are writes, so they are recorded, but nobody waits.
- **Deal updates** wait. The sales manager gets an email with a signed link, or decides in the console. The update runs when they approve; a decline stops it.
- **Archiving** is not in `allowed_actions`, so it is refused. If you add it, it is a destructive action: under `confirm_policy: human` it needs a person's approval, and the agent cannot confirm it itself.

For Salesforce, the same pattern uses `salesforce_sobjects.read:*`, `salesforce_sobjects.tasks.create` and a rule on `salesforce_sobjects.opportunities.update`.

## Step 3: What the rep's agent sends

The rep says: "Move the Acme deal to Contract sent, 48,000." The agent first reads the pipeline's stages, because HubSpot wants the internal stage id, not its label. Then:

```json
{
  "action": "hubspot_deals.deals.update",
  "input": { "record_id": "18734420115", "stage": "contractsent", "amount": "48000" },
  "intent": "Jordan asked to move the Acme deal to Contract sent at 48,000 after today's call."
}
```

The call is parked, not refused. The reply names the approval it is waiting on. When the manager approves, the update runs and the run records who approved it. The `intent` sentence is what the manager reads in the approval mail, so the agent's reason travels with the request.

## Before anything runs: check

For bulk or unfamiliar changes, the agent sends the same payload to `check` first. It validates the input against HubSpot's or Salesforce's schema as the connector describes it, confirms the key may run it, and says it will need an approval and roughly what it will cost. It is free and sends nothing. See [dry run](https://actionlake.com/glossary/dry-run).

## Questions across the CRM and other systems

A sales question often needs another system. With `ask`, the agent fetches from both and joins on the company, and only the answer returns:

- "Which customers in our pipeline are overdue on invoices?" HubSpot companies with [Xero](https://actionlake.com/connectors/xero) or [QuickBooks Online](https://actionlake.com/connectors/quickbooks-online).
- "Which accounts up for renewal have open support tickets?" Salesforce accounts with your help desk.

See [cross-system questions](https://actionlake.com/guides/autotask-tickets-and-xero-invoices) for how the join matches companies.

## A rollout that keeps the forecast clean

| Week | Key change | What you learn |
| --- | --- | --- |
| 1 | Reads only: `hubspot_crm.read:*`, `hubspot_deals.read:*` | Which questions reps actually ask |
| 2 | Add `hubspot_crm.notes.create` | Whether call notes improve, and how many calls it takes |
| 3 | Add `hubspot_deals.deals.update` behind an approval rule | How often the manager approves as sent |
| 4 | Keep the rule on amounts and stages; drop it on fields managers always approve | Where approval adds value, and where it only adds delay |

Each step is a `keys.update` on the rep's key. Nothing about the CRM connection changes, and no rep loses access while you adjust.

## What it costs

Each request sent to HubSpot or Salesforce that returns an answer is one upstream call. Searches for actions, schema reads, `check`, cache hits and failures you did not cause (a 401, 403, 429, 5xx or a timeout) are free. A request the CRM rejects as invalid, such as a 400 or 404, is billed. Free includes 100,000 calls a month; Starter is US$49 a month for 250,000. A rep's agent doing call prep and logging typically makes a handful of calls per request. See [pricing](https://actionlake.com/pricing).

## Frequently asked questions

**Can the agent see every record in the CRM?**

It sees what the CRM app's OAuth scopes and its actionlake key both allow. Narrow the app's scopes in HubSpot or Salesforce, and narrow the key to the objects and actions each agent needs.

**Who approves, and how fast?**

The person named on the rule, or the key's approver, or the tenant's default approver. They get an email with a signed, single-click link and can also decide in the console. The run waits until they decide.

**Will the agent create duplicate contacts?**

It can only create contacts if its key allows hubspot_crm.contacts.create. Writes are never retried automatically, so a timeout does not produce a second record. Ask the agent to search before it creates; HubSpot dedupes contacts on email.

**Can I see what each rep's agent changed?**

Yes. Every call is a row in the ledger with the key, action, connection, outcome, cost and the agent's intent, chained with SHA-256 and sealed daily. See AI agent audit logs.

See [AI agent audit logs](https://actionlake.com/guides/ai-agent-audit-logs)

**Start free - 100,000 calls a month, no card.** [Create a sandbox](https://actionlake.com/console), connect your CRM, and give your first rep a gated key.

[Start free](https://actionlake.com/console?start=sandbox)

## Related guides

- [Guide · Cross-system Cross-system questions: joining Autotask tickets with Xero invoices using an AI agent](https://actionlake.com/guides/autotask-tickets-and-xero-invoices)
- [Guide · Proof Audit logs for AI agents: what to record, and why a hash-chained ledger](https://actionlake.com/guides/ai-agent-audit-logs)
- [Guide · Xero How to connect Claude to Xero (MCP), step by step](https://actionlake.com/guides/connect-claude-to-xero)

[All guides](https://actionlake.com/guides)

## Try it on your own systems.

[Start free](https://actionlake.com/console?start=sandbox) [See pricing](https://actionlake.com/pricing)

100,000 free calls a month. No card. Hard caps on every key.

---

Source: https://actionlake.com/guides/hubspot-salesforce-mcp-crm-approvals
More: https://actionlake.com/llms.txt
