<!-- Generated by scripts/generate-llms.mjs from docs/connectors/salesforce.md. Do not edit. -->

Source: https://qbash.com/docs/connectors/salesforce

# Connect Salesforce

Tasks reach Salesforce through `qbash.integrations.salesforce`, which calls the Salesforce REST API. The connector runs SOQL queries, SOSL searches and saved reports. It also reads single records and object definitions, and it creates and updates records. It has no delete method.

After you connect Salesforce, you can add its tools to a [qbash MCP server](https://qbash.com/docs-markdown/mcp-servers/overview.md) for ChatGPT, Claude and other MCP clients, or call it from a Python task. The [Salesforce MCP server](https://qbash.com/connectors/salesforce) page lists what the connector can do.

The org needs API access. Enterprise, Unlimited, Performance and Developer editions include it. Professional Edition needs Salesforce's API add-on, and Essentials has no API access.

## Choose how qbash signs in

The credential form offers two methods:

- **Client credentials (recommended).** An admin creates an External Client App in your org and names a Run As user. qbash gets its own access token with the app's consumer key and secret, so scheduled tasks never wait for anyone to sign in. Sandboxes connect this way.
- **Sign in with Salesforce.** You sign in, and tasks act as you. Your Salesforce admin first installs the qbash package in the org. That package is coming soon, so connect with client credentials for now. A sign-in that no task uses for 30 days expires, and you connect again.

The rest of this guide sets up client credentials.

## Create an integration user

Every call runs as the Run As user, so that user's profile and permission sets decide what tasks can read and write. Salesforce recommends one dedicated integration user for each system that connects. Records a task creates or changes then name that user as the last to modify them, which sets them apart from people's edits.

1. In Setup, enter **Users** in the Quick Find box, select **Users**, and click **New User**.
2. Set **User License** to **Salesforce Integration** and **Profile** to **Minimum Access - API Only Integrations**. That profile turns on API access and keeps the user out of the Salesforce app. Save the user.
3. Enter **Permission Sets** in Quick Find, click **New**, name the set qbash, and choose **Salesforce API Integration** as its license.
4. In the permission set, give **Read** on each object your tasks read, and **Create** and **Edit** on each object they write. Grant field permissions for the fields they use. For `run_report`, add **Run Reports**, and share the report's folder with the user.
5. Assign the permission set to the integration user.

Enterprise, Unlimited and Performance orgs come with five Salesforce Integration licenses. **Company Information** in Setup shows how many are free. Any user with the **API Enabled** permission can also be the Run As user. In a Developer Edition org that you use for testing, your own admin user works.

## Create the External Client App

1. In Setup, enter **External Client App Manager** in Quick Find and select it.
2. Click **New External Client App**.
3. Under **Basic Information**, name the app qbash, enter a contact email and leave **Distribution State** at **Local**.
4. Expand **API (Enable OAuth Settings)** and check **Enable OAuth**.
5. The form requires a **Callback URL**, though the client credentials flow never uses it. Enter `https://login.salesforce.com/services/oauth2/success`.
6. Under **OAuth Scopes**, add **Manage user data via APIs (api)**.
7. Under **Flow Enablement**, check **Enable Client Credentials Flow** and accept the warning.
8. Click **Create**.

## Set the Run As user

1. On the app's page, open the **Policies** tab and click **Edit**.
2. Under **OAuth Policies**, find **OAuth Flows and External Client App Enhancements**. Check **Enable Client Credentials Flow** if it is clear, and enter the integration user's username in **Run As (Username)**.
3. Click **Save**.

## Copy the consumer key and secret

1. Open the app's **Settings** tab, expand **OAuth Settings** and click **Consumer Key and Secret**.
2. Salesforce asks you to verify your identity, then shows both values. Copy the **Consumer Key** and the **Consumer Secret**.

Together, the key and secret can call Salesforce as the integration user. Store them like a password. Salesforce says a new External Client App can take up to 30 minutes before it works, so a first attempt in qbash may fail until then.

## Find your My Domain

In Setup, enter **My Domain** in Quick Find. qbash needs the domain that ends in `my.salesforce.com`:

- production: `qbash.my.salesforce.com`
- sandbox: `qbash--uat.sandbox.my.salesforce.com`
- Developer Edition: `qbash-dev-ed.develop.my.salesforce.com`

Salesforce issues client credentials tokens only at the org's own domain, so `login.salesforce.com` and `test.salesforce.com` are refused. You can also paste the address of any Lightning or Setup page in the org, and qbash converts it.

## Add the credential in qbash

1. Open **Connectors**, then **Salesforce**, and click **Connect**.
2. Enter a credential name. **Client credentials (recommended)** is already selected.
3. Enter the My Domain in **Salesforce domain**, then paste the **Consumer key** and **Consumer secret**.
4. Click **Add Credential**. qbash asks Salesforce for an access token before saving, so a wrong value shows up in the form, before any task runs. If the error says the app has no Run As user, go back to the app's **Policies** tab.
5. In the task editor, open **Resources**, then **Integrations**, and select the credential. See [Resources](https://qbash.com/docs-markdown/tasks/task-editor.md#resources).

To replace the secret later, edit the credential and paste the new one. A blank secret field keeps the saved value.

## Call Salesforce from a task

`run_soql` runs a SOQL query and returns one page of records. Related fields use dot notation, and the result nests them:

```python
result = qbash.integrations.salesforce.run_soql(
    query=(
        "SELECT Id, Name, Account.Name, StageName, Amount, CloseDate, LastModifiedDate "
        "FROM Opportunity WHERE IsClosed = false AND CloseDate < TODAY ORDER BY CloseDate LIMIT 100"
    ),
)

for opp in result["records"]:
    account = opp["Account"]["Name"] if opp["Account"] else None
    print(opp["Name"], account, opp["StageName"], opp["CloseDate"])
```

`update_record` changes only the fields in `values`. With `if_last_modified`, qbash compares the record's `LastModifiedDate` just before the update and refuses it if someone changed the opportunity after the query read it:

```python
for opp in result["records"]:
    qbash.integrations.salesforce.update_record(
        object="Opportunity",
        record_id=opp["Id"],
        values={"NextStep": "Confirm a new close date with the buyer"},
        if_last_modified=opp["LastModifiedDate"],
    )
```

Each write reads the record back and returns it as `record`. If that read fails, the write still stands. `record` comes back as None and `warnings` says why.

`create_record` makes a new record on every call, so a task that runs again creates a second copy. For records that come from another system, `upsert_record` matches on a field marked External ID and updates the record it finds. To add an External ID field, open **Object Manager** in Setup, pick the object, and create a text field under **Fields & Relationships** with **External ID** and **Unique** checked.

`list_objects` and `describe_object` return the API names of the org's objects and fields, including custom ones that end in `__c`. The [SDK Reference](https://qbash.com/docs-markdown/tasks/sdk-reference.md) lists every method.

## Details that change results

- Every call counts against the org's API request limit, which covers a rolling 24 hours and is shared with every other integration in the org. A write uses two requests because of the read back, and three with `if_last_modified`. A Developer Edition org allows 15,000 requests a day. **System Overview** in Setup shows the requests made in the last 24 hours.
- `run_soql` returns one page. When `done` is False, pass `next_cursor` back as `cursor` for the next page. `batch_size` (200 to 2,000) only sets the page size, and Salesforce can send fewer rows. Cap the total with `LIMIT` in the query.
- A query that names a field the Run As user cannot see fails. `describe_object` lists only the fields that user can see.
- Datetimes come back in UTC, as in `2026-10-22T03:04:05.000+0000`. Date fields are `YYYY-MM-DD`.
- `search` uses Salesforce's search index, which can take a few minutes to include a new or changed record. Use `run_soql` to read a record a task just wrote.
- `run_report` runs the report with its saved filters and returns at most 2,000 rows. `all_data` is False when the report had more. For the full set, or other filters, query the objects with `run_soql`.
- `upsert_record` writes nothing when more than one record holds the external ID value, and the error says so.
- `if_last_modified` is checked just before the update. An edit made between the check and the update still goes through.
- Validation rules, flows and Apex triggers run on every write. When one stops a write, the error names the rule or field.

## Add test data

A free [Developer Edition](https://developer.salesforce.com/signup) org keeps tests away from customer records. A sandbox works too, with its sandbox domain in the credential.

To see whether the org came with sample records, run `SELECT COUNT() FROM Opportunity` with `run_soql` and read `total_size`. If it has none, add a few accounts, contacts and opportunities in the Salesforce app, with opportunities in more than one stage and close dates on both sides of today.

When you test writes, start each test record's name or subject with `TEST-`. The connector cannot delete records, so remove them in Salesforce after the test.

Salesforce is a trademark of Salesforce, Inc.
