> ## Documentation Index
> Fetch the complete documentation index at: https://docs.serval.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Salesforce

> Salesforce is a cloud-based CRM platform - Serval connects to your org to sync Service Cloud Cases as tickets and run on-demand actions against the Salesforce API from your workflows.

## About Salesforce

Salesforce is a cloud-based CRM platform for managing customer relationships, sales pipelines, support cases, and more. The Serval Salesforce integration connects securely to your Salesforce org and does two things: it syncs **Service Cloud Cases** with Serval tickets (one-way or two-way), and it lets your workflows reach the Salesforce API on demand. The workflow surface acts as a live passthrough - each request runs only when a workflow asks for it, so accounts, contacts, cases, opportunities, custom objects, and SOQL queries are all reachable. Because every call runs as the user you assign during setup, that user's permissions determine what Serval can read or change.

**Authentication:** OAuth 2.0 Client Credentials Flow

**Data sync:** When ticket sync is enabled, Serval polls your org for Case and CaseComment changes roughly every 30 seconds. Everything else is on-demand - workflow actions call the API only when a workflow runs, and Serval stores no Salesforce record data beyond the tickets it syncs.

## What the Salesforce integration enables

| Capability                 | Description                                                                                                                                                                                                                                                                                |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Ticket sync                | Native sync between Serval tickets and Salesforce Service Cloud Cases - one-way or two-way, including comments, statuses, priorities, requesters, and assignees. Optionally scoped to a single Case Queue. See [Ticket sync](#ticket-sync).                                                |
| Salesforce API request     | A single built-in action that sends a request to your connected Salesforce org and returns the response. Workflows specify what to do (the operation, the target, and any details to send), so it can query and manage accounts, contacts, cases, opportunities, custom objects, and more. |
| Full Salesforce API access | Because the action is a generic passthrough, any part of the Salesforce API your connected app is permitted to use is reachable from workflows - including querying records with SOQL.                                                                                                     |
| Connection healthcheck     | A built-in test that confirms Serval can authenticate to your org and reports how many API versions are available.                                                                                                                                                                         |

Anything defined in the [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest.htm) can be accessed through Serval.

## Ticket sync

Connecting Salesforce also enables native ticket sync between Serval tickets and Salesforce **Service Cloud Cases**. Serval polls your org for Case changes, ingests new and updated Cases as Serval tickets, and - in two-way mode - writes Serval-side changes back as Case updates and CaseComments. Sync can cover every Case in the org or be scoped to a single Case Queue.

### What syncs

| Serval                       | Salesforce                   | Notes                                                                                                                                                                                                                                                                                                                                             |
| ---------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ticket title and description | Case Subject and Description | Descriptions are captured when the linked ticket or Case is first created; later description edits are not overwritten in either direction.                                                                                                                                                                                                       |
| Public replies               | Published CaseComments       | Published comments from Salesforce arrive as public replies on the Serval ticket, and vice versa.                                                                                                                                                                                                                                                 |
| Internal notes               | Unpublished CaseComments     | Unpublished comments stay agent-only in Salesforce, and Serval internal notes go up as unpublished comments - neither side leaks onto the customer-visible thread.                                                                                                                                                                                |
| Status                       | Case Status picklist         | Mapped per your configuration. Serval reads your org's actual Status picklist values live, so custom statuses appear in the mapping UI. A Serval status with no mapping is never written to Salesforce - the Case keeps whatever status it has.                                                                                                   |
| Priority                     | Case Priority                | High, Medium, and Low map automatically. Salesforce's standard Priority picklist has no Urgent, so Serval's Urgent is written to Salesforce as High — but the Serval ticket keeps its Urgent priority (the sync recognizes the collapse and won't downgrade it). Custom or unmapped Salesforce priorities are left alone rather than overwritten. |
| Requester                    | Case Contact                 | The Case Contact becomes the Serval requester; Web-to-Case submissions without a Contact fall back to the Case's SuppliedEmail, then to the case creator. Cases created from Serval link the requester's Salesforce Contact when exactly one Contact matches their email.                                                                         |
| Assignee                     | Case Owner                   | When the Case Owner is a User, that user becomes the Serval assignee. Queue-owned Cases arrive unassigned. Ownership flows from Salesforce to Serval only - Serval never reassigns a Case.                                                                                                                                                        |

<Note>
  The Salesforce API posts every CaseComment as the Connected App's **Run As** user - it cannot impersonate authors. Human-authored Serval messages are therefore prefixed with an attribution header, `[Comment from: Name <email>]`, so agents in Salesforce can see who actually wrote them. The header is stripped again when the comment syncs back, so it never appears on the Serval side.
</Note>

### Set up ticket sync

Ticket sync is configured per team on top of an existing Salesforce connection.

<Steps>
  <Step title="Connect the Salesforce integration">
    Follow [Get your credentials](#get-your-credentials) and [Connect in Serval](#connect-in-serval) below if you haven't already. Ticket sync uses the same Connected App connection - no extra credentials are needed.
  </Step>

  <Step title="Open your team's ticket sync settings">
    In the Salesforce app's settings in Serval, open the team's **Salesforce Case Syncing** configuration.
  </Step>

  <Step title="Choose a Salesforce queue (optional)">
    Optionally select a Case Queue - Cases owned by that queue sync to this team, and Cases Serval creates are placed in it. Leave it on **All Cases** to sync every Case in the org; Cases Serval creates are then left to your org's assignment rules.
  </Step>

  <Step title="Map statuses">
    Map each Serval status to a Salesforce Case Status. Serval fetches your org's actual Status picklist (including custom values), so the options match what your agents see. Statuses you leave unmapped are simply never written to Salesforce.
  </Step>

  <Step title="Choose a sync strategy and enable">
    Pick how tickets should sync - selectively per ticket, automatically on escalation, for all new tickets (full two-way sync), or by importing existing Cases - and whether the sync is one-way or two-way. Save to start syncing.
  </Step>
</Steps>

### Required Salesforce permissions

Ticket sync runs entirely as the Connected App's **Run As** user, so that user's profile or permission sets must cover the objects the sync touches:

| Permission                          | Why it's needed                                                                                 |
| ----------------------------------- | ----------------------------------------------------------------------------------------------- |
| **API Enabled** (system permission) | All sync traffic goes through the REST API.                                                     |
| **Case** - Read, Create, Edit       | Poll for Case changes, create Cases from Serval tickets, and write status and priority updates. |
| **CaseComment** - Read, Create      | Sync the conversation in both directions.                                                       |
| **Contact** - Read                  | Resolve the Case Contact to the Serval requester.                                               |
| **User** - Read                     | Resolve the Case Owner to the Serval assignee and attribute comments.                           |
| **Group** / **QueueSobject** - Read | List the org's Case Queues in the queue picker and scope polling to the selected queue.         |
| **CaseStatus** - Read               | Read the org's Case Status picklist values for status mapping.                                  |

### Polling cadence and API usage

Serval polls your org for changed Cases and CaseComments roughly every **30 seconds** (a SOQL query filtered on `SystemModstamp`). There are no webhooks in this version, so changes made in Salesforce can take up to about 30 seconds to appear in Serval; changes made in Serval are pushed to Salesforce as they happen.

<Warning>
  Salesforce enforces org-wide **daily API request limits**, and Serval's polling consumes part of that allocation around the clock while ticket sync is enabled. If your org runs close to its limit, account for the polling traffic when budgeting API usage.
</Warning>

### Limitations

* **No attachments.** Files attached on either side do not sync.
* **No real-time webhooks.** Sync is polling-based, with roughly 30 seconds of latency for changes originating in Salesforce.
* **No live agent handoff.** Unlike the [ServiceNow integration](/sections/integrations/servicenow), there is no bridge to a human Salesforce agent chat.
* **Single Case treatment.** All Cases are synced the same way regardless of record type - there is no per-record-type configuration.
* **Email-to-Case conversations don't sync.** Serval syncs the CaseComment thread. Orgs whose case conversations live in EmailMessage records (typical with Email-to-Case) will see only CaseComment traffic sync - the email thread itself stays in Salesforce.

## Get your credentials

To connect Salesforce, you create an app in your Salesforce org (a Connected App, or the newer External Client App) configured for OAuth with the Client Credentials Flow enabled and a Run As user assigned. From it you collect the Consumer Key (your Client ID) and Consumer Secret (your Client Secret), plus your org's instance domain. The full provider walkthrough lives in [Salesforce's Client Credentials Flow setup guide](https://help.salesforce.com/s/articleView?id=sf.connected_app_client_credentials_setup.htm\&language=en_US\&type=5).

<Steps>
  <Step title="Open the External Client App Manager">
    In Salesforce, go to **Setup**. In the left sidebar under **Platform Tools**, navigate to **Apps → External Client Apps → External Client App Manager**. (Connected Apps work the same way via **App Manager**.)
  </Step>

  <Step title="Create the app">
    Click **New External Client App** in the top right. Enter a name (for example, "Serval Integration") and fill out the required fields.
  </Step>

  <Step title="Enable OAuth">
    In the **API (Enable OAuth Settings)** section, enable OAuth. Provide a callback URL - it is not used by this flow, but Salesforce requires a value, so something like `https://serval.com` is fine. Then move the OAuth scopes your workflows need into **Selected OAuth Scopes**.

    <Tip>
      Choose scopes that cover the Salesforce objects your workflows will read or change. You can update scopes later.
    </Tip>
  </Step>

  <Step title="Turn on Client Credentials Flow">
    Under **Flow Enablement**, toggle **Enable Client Credentials Flow** on.

    <Warning>
      This toggle is required. If the Client Credentials Flow is not enabled, Serval cannot authenticate and the connection test will fail.
    </Warning>
  </Step>

  <Step title="Assign a Run As user">
    In the app's OAuth / Client Credentials policies, assign a **Run As** user for the Client Credentials Flow.

    <Warning>
      Every Serval API call runs as this user, with this user's object and field permissions. Pick a user whose access matches exactly what your workflows should be allowed to do. If you plan to use ticket sync, also grant this user the object permissions listed in [Required Salesforce permissions](#required-salesforce-permissions).
    </Warning>
  </Step>

  <Step title="Retrieve your Client ID and Client Secret">
    Save the app, then open its OAuth settings and click **Consumer Key and Secret** (you may be prompted to re-verify your identity). The **Consumer Key** is your **Client ID** and the **Consumer Secret** is your **Client Secret**.
  </Step>

  <Step title="Note your instance domain">
    Record your instance domain (for example, `mycompany.my.salesforce.com`), entered without the `https://` prefix.
  </Step>
</Steps>

## Connect in Serval

In Serval, go to **Apps → Available → Salesforce → Connect** and fill in the following fields. All three are required, and Serval will not let you save until each one is filled in.

<Steps>
  <Step title="Instance Domain">
    Enter your Salesforce instance domain without the `https://` prefix (for example, `mycompany.my.salesforce.com`).

    <Note>
      Serval checks that this looks like a valid Salesforce domain - it must end in `.salesforce.com`. If it doesn't match that shape, the form rejects it when you try to save with the message "Must be a valid Salesforce domain (e.g., mycompany.my.salesforce.com)". A well-formed but incorrect domain (the right shape but the wrong org) is accepted, and surfaces later as a failed connection test, so double-check it carefully.
    </Note>
  </Step>

  <Step title="Client ID">
    Enter the Consumer Key from your Salesforce app.
  </Step>

  <Step title="Client Secret">
    Enter the Consumer Secret from your Salesforce app.

    <Tip>
      When you reconfigure an existing connection later, the saved Client Secret is shown obfuscated, so the real value is never displayed again. Leave it as-is to keep the value you already saved, or click **Replace** to enter a new one.
    </Tip>
  </Step>

  <Step title="Save">
    Click **Save** to create the connection.
  </Step>
</Steps>

## Verifying the connection

Serval includes a built-in **Test Salesforce Connection** healthcheck. It makes an authenticated request to your org, treats the response as a list of available API versions, and reports the count. This confirms that your instance domain, Client ID, and Client Secret all work together to authenticate against the org.

On success, you will see:

> Successfully connected to Salesforce. \<N> API versions available.

On failure, you will see:

> Unable to connect to Salesforce. Please verify your instance domain, client ID, and client secret are valid.

<Tip>
  A successful save only confirms the fields were well-formed, not that the credentials actually work. Always run the connection test after connecting to confirm Serval can authenticate to your org.
</Tip>

## Gotchas and troubleshooting

<AccordionGroup>
  <Accordion title="Connection test fails right after connecting">
    The form confirms your **Instance Domain** has a valid Salesforce shape, but it cannot tell whether it points at the right org - a well-formed domain for the wrong org saves cleanly and only fails when Serval tries to authenticate. Re-check the domain (entered without `https://`, e.g. `mycompany.my.salesforce.com`), then re-run the test. If the domain is correct, confirm the Client ID and Client Secret are the Consumer Key and Consumer Secret from the same app.
  </Accordion>

  <Accordion title="Authentication fails even though the credentials look right">
    Serval uses the OAuth Client Credentials Flow, so your Salesforce app must have **Enable Client Credentials Flow** turned on and a **Run As** user assigned. If the flow is not enabled, the token request fails and the connection test returns its failure message. Open the app's settings, confirm the flow is enabled, and confirm a Run As user is configured.
  </Accordion>

  <Accordion title="Workflows can authenticate but can't read or change certain records">
    Every Serval API call runs as the **Run As** user assigned on the app. That user's profile and permission sets determine which objects and fields your workflows can read or write. If a workflow is blocked from a record or field, adjust the Run As user's permissions in Salesforce to match what your workflows need.
  </Accordion>

  <Accordion title="No dedicated action for the object I want to manage">
    This integration ships exactly one data action - the generic **Salesforce API request**. There are no separate "create contact" or "query case" actions. Workflows specify the operation and target themselves, so refer to the [Salesforce REST API Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest.htm) for what is available.
  </Accordion>

  <Accordion title="Calls to a non-Salesforce host are blocked">
    For security, Serval only allows the connected integration to make outbound calls to Salesforce hosts - domains ending in `salesforce.com` or `force.com` (the `force.com` range covers Experience Cloud, community, and custom domains). Any other host is blocked.
  </Accordion>
</AccordionGroup>

***

Need help? Contact **[support@serval.com](mailto:support@serval.com)** for assistance with your Salesforce integration.
