> For a complete page index of the Captain API documentation, fetch https://docs.captain.dev/llms.txt?excludeSpec=true

# Salesforce Commerce Cloud

> Sync a Salesforce B2C Commerce site's catalog into Captain Product Search and put Captain's search box on your storefront.

Connect a Salesforce B2C Commerce (Commerce Cloud) site, and Captain imports its online products and the orders it needs to measure search revenue. Then put Captain's search box on the storefront.

> **Note**
>
> The Salesforce Commerce Cloud integration is in beta. Captain tested it end to end against a recorded copy of Salesforce's documented Commerce API, not yet against a live instance. Captain switches it on per tenant: ask the Captain team on Slack.

## Quick start

#### [1. Make an API client](#make-a-read-only-api-client)

Create a read-only API client in Account Manager.

#### [2. Connect](#connect-the-site)

Paste the short code, instance ID, site ID and client credentials into the Captain console.

#### [3. Turn on search](#turn-on-storefront-search)

Create a storefront key and add the snippet to your SFRA or PWA Kit storefront.

## How it works

* **The connection** reads one site's catalog and orders through the Salesforce Commerce API (SCAPI) Admin APIs, with an Account Manager API client that can only read. It never changes anything in Commerce Cloud.
* **Hourly sync, no webhooks.** Salesforce offers no product or order webhooks to an API client, so Captain imports what changed on the schedule you choose (every hour at most), plus a full import daily and on **Sync now**.
* **The search box** is Captain's [search widget](/guides/product-search/search-widget). Your developers add it to the storefront code. It searches your Captain catalog with a public, scoped storefront key.

## Before you start

* A B2C Commerce instance with the Salesforce Commerce API enabled, and an Account Manager administrator for its organization.
* Business Manager access to read the short code and organization ID.
* A Captain Product Search tenant with Salesforce Commerce Cloud switched on.
* An owner of your Captain organization, to create the storefront key later.

## Make a read-only API client

### Find the short code and organization ID

In Business Manager, open **Administration**, **Site Development**, **Salesforce Commerce API Settings**. Copy the **Short Code** and the **Organization ID**, for example `f_ecom_zzxy_prd`. The instance ID is the part after `f_ecom_`: `zzxy_prd`.

### Add an API client

In Account Manager, open **API Client** and select **Add API Client**. Give it a display name such as `Captain search` and a password. The password is the client secret.

### Give it access

* **Organizations:** your organization.
* **Roles:** **Salesforce Commerce API**, with this instance in its filter.
* **Allowed Scopes:** `sfcc.products sfcc.catalogs sfcc.orders`. These are the read-only scopes. Don't add the `.rw` ones: Captain refuses a client that can change the store.
* **Token Endpoint Auth Method:** `client_secret_basic`.
* **Access Token Format:** JWT.

### Save

Select **Save** and copy the **API Client ID**.

| Scope           | Why Captain needs it                                                      |
| --------------- | ------------------------------------------------------------------------- |
| `sfcc.products` | Search products with their variants, site prices, stock and online flags. |
| `sfcc.catalogs` | Read category names.                                                      |
| `sfcc.orders`   | Measure revenue from search.                                              |

## Connect the site

### Open the Salesforce Commerce card

In the Captain console, open **Developers**, then **Integrations**. On the **Salesforce Commerce Cloud** card, select **Connect**.

### Name the connection and the catalog

In **Name**, enter a name such as the site's name. In **New catalog name**, enter a name no other catalog uses, such as `sfcc-products`. Captain creates this catalog.

### Choose how often to resync

In **Sync every**, choose 1, 6, 12 or 24 hours.

### Paste the details

* **Short code**, **Instance ID** and **Site ID** (as **Administration**, **Sites**, **Manage Sites** shows it, such as `RefArch`).
* **Client ID** and **Client secret** from Account Manager.
* **Product page address** (optional): your product page address with `{id}` where the product ID goes, so search results link to the product. For PWA Kit, `https://www.example.com/product/{id}`. For SFRA, `https://www.example.com/on/demandware.store/Sites-RefArch-Site/default/Product-Show?pid={id}`.

Select **Connect**.

Captain tests the client first: it gets a token, checks its scopes, and reads the site's orders and one product. It refuses a client with a write scope, a missing scope, or an unknown site, in plain words. The client ID and secret are kept in AWS Secrets Manager and never shown again, logged or sent to the browser.

## What syncs

### Products

Captain imports the site's products that are online and searchable on that site. A master product's variants become one record each; an offline variant stays out. Simple products, sets and bundles are one record each.

| Commerce Cloud field                                                                                 | Captain field                               |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| `name` (default locale)                                                                              | `title`                                     |
| `longDescription`, else `shortDescription` (HTML removed)                                            | `body`                                      |
| `brand`                                                                                              | `vendor`                                    |
| assigned category names (`assignedCategories`)                                                       | `collections`                               |
| classification category name                                                                         | `product_type`                              |
| `pageKeywords` (comma separated)                                                                     | `tags`                                      |
| variation attribute display names and values                                                         | `options`, `option_values`, `variant_title` |
| variant `productId`                                                                                  | the record ID and `sku`                     |
| `upc`, else `ean` (simple products)                                                                  | `barcode`                                   |
| site `price` and `priceCurrency`                                                                     | `price`, `currency`                         |
| `ats` (available to sell)                                                                            | `inventory`                                 |
| `orderable`, else `inStock`                                                                          | `available`                                 |
| `image`, then the `large` image group                                                                | `image`, `images`                           |
| the product page address with the product ID                                                         | `url`                                       |
| `creationDate`, `lastModified`                                                                       | `created_at`, `updated_at`                  |
| `manufacturerName`, `manufacturerSku`, `ean`, `upc`, `unit`, `taxClassId`, custom attributes (`c_*`) | `attributes`                                |

The Admin APIs give one effective price per site. Captain doesn't receive a list price next to a sale price, so records carry no compare-at price.

### Orders

Captain reads the site's orders by when they last changed. It records one purchase event for each order with status **new** or **completed**: the variants, quantities and prices after discounts, the product total (before shipping, and before tax on net-taxation sites), and the currency. Created, cancelled and failed orders count zero. Captain stores no customer names, emails or addresses.

## Live updates

Commerce Cloud has no webhooks for an API client, so there are no live updates. Captain imports products changed since the last import on your **Sync every** schedule, and a full import at least daily.

* A price-book or inventory change that doesn't change the product's modified date reaches Captain at the daily full import, or right away with **Sync now**.
* A product removed from the site leaves the catalog at the next full import.

## Turn on storefront search

### Create a storefront key

Open the connection's **Manage** panel. Under **Storefront search**, add your storefront's domains and select **Create storefront key**. The panel shows a snippet with the key filled in, once. Copy it.

```html
<script type="module"
  src="https://cdn.jsdelivr.net/gh/runcaptain/captain-search@1.8.0/captain-search.js"
  integrity="sha384-JE4C0r/iXcnqRePQ+XK/wsFWEWRqxIR8gTfYjlnOTnd2gUaq+Y+BYHC/IRNFy/xp"
  crossorigin="anonymous"></script>

<captain-search
  endpoint="https://acme.captain.dev"
  index="sfcc-products"
  key="YOUR_SCOPED_KEY"
  placeholder="Search products"
></captain-search>
```

### Add it to the storefront

Commerce Cloud storefronts are code, so a developer adds the snippet.

#### SFRA

In your storefront cartridge, render the `<script>` tag in the page head, through the `app.template.htmlHead` hook or `htmlHead.isml`. Put the `<captain-search>` element in the header in place of the search form (`headerSearch.isml`). Keep the cartridge first on the cartridge path (**Administration**, **Sites**, **Manage Sites**, your site, **Settings**), upload it and activate the code version.

#### Composable Storefront (PWA Kit)

Add the `<script>` tag to the head in `app/components/_app/index.jsx` and the `<captain-search>` element to your header component. In `app/ssr.js`, add `https://cdn.jsdelivr.net` to `script-src` and your Captain endpoint to `connect-src` in the content security policy. Then push the bundle to production.

Search on the storefront in a private window to test.

## Sync now and Disconnect

* **Sync now** starts a full import at any time. It also removes products the site no longer has.
* **Disconnect** stops syncing and deletes the stored client credentials. The catalog stays until you delete it. To revoke access in Salesforce too, disable or delete the API client in Account Manager.

## Troubleshooting

#### Salesforce didn't issue a token, or refused the credentials

Check the client ID and password, that the client's **Allowed Scopes** hold `sfcc.products sfcc.catalogs sfcc.orders`, and that its **Salesforce Commerce API** role includes this instance. A token without that role is issued but every call answers 403.

#### The API client can change the store

Captain only reads. Remove the `.rw` scopes from the client's **Allowed Scopes** and connect again.

#### 404: check the store or account id

The site ID or instance ID is wrong. Copy the site ID from **Manage Sites** and the organization ID from **Salesforce Commerce API Settings**.

#### A product is missing

Check that it's online and searchable on this site, and that at least one of its variants is online. Then select **Sync now**.