Skip to main content

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 for ChatGPT, Claude and other MCP clients, or call it from a Python task. The Salesforce MCP server 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.

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:

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:

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 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 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.