"Salesforce Commerce Cloud" covers two unrelated products that a connector must treat separately. B2C Commerce is the former Demandware platform, a multi-tenant hosted storefront used by large consumer brands, with its own instances, its own Business Manager admin, and two REST surfaces: the older OCAPI (Open Commerce API) and the newer SCAPI (Salesforce Commerce API). B2B Commerce and D2C Commerce run on the core Salesforce platform as Lightning web store applications, where commerce data is standard Salesforce objects and the API is the ordinary Salesforce REST, Bulk and Connect API surface. Credentials, hostnames, object models and rate limits differ completely. Always establish which product a merchant is on before writing any code.
developer.salesforce.com returned HTTP 403 to every documentation request made while preparing this page. Everything below comes from first-party Salesforce GitHub repositories (SalesforceCommerceCloud/b2c-developer-tooling, SalesforceCommerceCloud/commerce-sdk, forcedotcom/commerce-vercel) and a public mirror of the OCAPI HTML reference. Salesforce's own repository labels its request examples as illustrative. Verify endpoint contracts against the live schema before implementing: b2c scapi schemas get <family> <name> <version>.
At a glance
What it is
B2C Commerce is the enterprise storefront platform Salesforce acquired as Demandware in 2016. Merchants are large consumer brands running one or more "sites" on a shared instance (production, staging, development, plus on-demand sandboxes). It is D2C only, not a marketplace. Each instance has a tenant ID such as zzte_053 and an organization ID such as f_ecom_zzte_053, plus a region short code such as kv7kzm78 that determines the SCAPI hostname.
B2B Commerce and D2C Commerce (previously "B2B Commerce on Lightning" and "Salesforce D2C Commerce") are a different product built on the core platform. A web store is a Salesforce record, products are Product2, orders become Order and, under Order Management, OrderSummary. There is no Business Manager, no cartridge, no OCAPI, and no Account Manager. Everything runs through the normal Salesforce org, its OAuth, its API limits and its sandboxes.
API access
B2C Commerce
Credentials come from Account Manager, the shared Salesforce Commerce identity service. The documented setup is:
- Log in to Account Manager and go to API Client then Add API Client.
- Set a display name and a password, which becomes the client secret.
- Assign the organizations (instances) the client may reach.
- Give it the role "Salesforce Commerce API".
- Set Token Endpoint Auth Method to
client_secret_postand Access Token Format toJWT. - Add the allowed scopes, then copy the client ID.
OCAPI additionally requires the client ID to be allow-listed per resource in Business Manager under the Open Commerce API Settings, separately for the Shop API and the Data API. A client with a valid token still gets a 403 on OCAPI until those settings name it.
Official SDKs: commerce-sdk (Node, server side) and commerce-sdk-isomorphic (browser and Node) for SCAPI, plus a Python b2c-tooling-sdk and the b2c CLI. API versions are in the path. SCAPI admin APIs are at v1; OCAPI carries a dated version such as v23_2 or v25_6 and Salesforce retires old OCAPI versions on a published schedule.
B2B and D2C Commerce
Standard Salesforce connected app in the customer's org, with the usual OAuth flows (JWT bearer for server-to-server, web server flow for user context). API version is the org's, for example v63.0.
Authentication
B2C Commerce admin (Account Manager client credentials)
- POST to the Account Manager token endpoint with HTTP basic auth carrying the client ID and secret.
- Send
grant_type=client_credentialsas form data. - Send a
scopeparameter that contains both the tenant scope and the API scopes. This dual requirement is the single most common cause of 403s. The tenant scope isSALESFORCE_COMMERCE_API:<tenant_id>; the API scopes are values such assfcc.orders,sfcc.products.rw. - Use the returned JWT as
Authorization: Bearer <token>on every SCAPI call.
curl "https://account.demandware.com/dwsso/oauth2/access_token" \
--request 'POST' \
--user "${CLIENT_ID}:${CLIENT_SECRET}" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data "grant_type=client_credentials" \
--data-urlencode "scope=SALESFORCE_COMMERCE_API:${TENANT_ID} sfcc.orders sfcc.products"
curl "https://${SHORT_CODE}.api.commercecloud.salesforce.com/checkout/orders/v1/organizations/${ORG_ID}/orders/${ORDER_NO}?siteId=${SITE_ID}" \
--header "Authorization: Bearer ${TOKEN}" \
--header "correlation-id: $(uuidgen)"
Documented admin scopes include sfcc.orders and sfcc.orders.rw, sfcc.products and sfcc.products.rw, sfcc.catalogs and sfcc.catalogs.rw, sfcc.inventory.availability and sfcc.inventory.availability.rw, sfcc.inventory.impex-inventory, sfcc.shopper-customers and sfcc.shopper-customers.rw, sfcc.promotions and sfcc.promotions.rw. Token lifetime is not stated in the readable sources; treat it as short and refresh on 401.
Multi-merchant: one Account Manager API client can be assigned to several organizations, but each token must name one tenant in its scope. A connector serving several brands therefore mints one token per tenant and keys its credential store on tenant ID.
SLAS (Shopper Login and Session Service) credentials are a different thing entirely. They mint shopper tokens for storefront traffic through ShopperLogin.getAccessToken and cannot be used for admin APIs. If you are building a data pipeline, you want Account Manager, not SLAS.
OCAPI
The Data API uses the same Account Manager client credentials token. The Shop API accepts either an OAuth token or a customer JWT, and identifies the calling application with the client ID. Base paths are https://{hostname}/s/{site_id}/dw/shop/{version} for the Shop API and https://{hostname}/s/-/dw/data/{version} for the Data API, where {hostname} is the instance host, not the SCAPI short-code host.
B2B and D2C Commerce
Standard Salesforce OAuth against the org's login host, then Authorization: Bearer <token> against https://<my-domain>/services/data/v63.0/....
Objects we can read
Orders
SCAPI admin, single order: GET /checkout/orders/v1/organizations/{organizationId}/orders/{orderNo}?siteId={siteId}. Scope sfcc.orders.
SCAPI admin, search: POST /checkout/orders/v1/organizations/{organizationId}/order-search?siteId={siteId}. The body carries a query DSL, sort rules and a select projection. This is the endpoint to build an incremental pull on, filtering lastModified.
{
"query": {
"filteredQuery": {
"query": { "matchAllQuery": {} },
"filter": { "range": { "field": "lastModified", "from": "2026-09-01T00:00:00Z" } }
}
},
"sorts": [{ "field": "lastModified", "sortOrder": "asc" }],
"select": "(orderNo,status,lastModified,productItems,shipments,totals)"
}
OCAPI equivalents: GET /s/{site_id}/dw/shop/{version}/orders/{order_no} and POST /s/{site_id}/dw/shop/{version}/order_search. The OCAPI search request takes query, sorts, select, start and count, and the response is an order_search_result with count, total, start and hits. Over 30 fields are searchable, including customer_email, customer_name, order_no, creation_date, payment_status, shipping_status, and nested paths such as coupon_line_items.coupon_code and product_items.product_id. Custom attributes are searchable and sortable with a c_ prefix.
OCAPI order_search requires Order Incremental Indexing to be enabled on the instance. Without it the endpoint returns HTTP 500, not an empty result. Confirm the setting before building a sync on it.
Documented Order document fields (OCAPI naming): order_no, order_token, status, currency, creation_date, order_total, shipping_total, tax_total, customer_info, product_items, shipments, payment_instruments. SCAPI uses camel case for the same model: orderNo, status, lastModified, productItems, shipments, totals. A full sample response is not published in the sources readable here.
Order items and shipments
Both are embedded in the order document, not separate endpoints. product_items (SCAPI productItems) carries the line items; shipments carries the shipment records including shipping address, shipping method and shipping status. Order-level status, payment_status and shipping_status drive the state machine, and PATCH /orders/{orderNo} updates them.
Products and listings
SCAPI: GET and PATCH /product/products/v1/organizations/{organizationId}/products/{productId}, scopes sfcc.products and sfcc.products.rw. Catalog structure sits under /product/catalogs/v1/organizations/{organizationId}/catalogs and /catalogs/{catalogId}, scopes sfcc.catalogs and sfcc.catalogs.rw.
OCAPI Data API: POST /s/-/dw/data/{version}/product_search with the OCAPI query DSL, and GET /s/-/dw/data/{version}/products/{id}. The search body wraps one query type, either text_query with fields and search_phrase, or match_all_query to page everything, plus select and count.
{
"query": { "text_query": { "fields": ["id", "name"], "search_phrase": "shirt" } },
"select": "(**)",
"count": 10
}
Product fields are localized maps keyed by locale, so name comes back as {"default": "...", "en_US": "..."} rather than a string. Other documented fields on the Data API product include id, brand, price, type (a set of boolean flags such as master, variant, bundle), short_description and long_description as markup text.
Inventory
SCAPI availability, read: POST /inventory/availability/v1/organizations/{organizationId}/availability-records/search with a body naming skus and locationIds. Scope sfcc.inventory.availability.
OCAPI Data API, per inventory list: GET /s/-/dw/data/{version}/inventory_lists/{inventory_list_id}/product_inventory_records for all records, and /product_inventory_records/{product_id} for one. Documented fields: product_id, product_name, inventory_list_id, ats (available to sell), stock_level, allocation with amount and reset_date, perpetual_flag, pre_order_back_order_handling, pre_order_back_order_allocation, quantity_on_order, in_stock_date, and _resource_state, which is an ETag used for concurrency control.
Customers
POST /customer/customers/v1/organizations/{organizationId}/customer-search?siteId={siteId} with a query object, for example a textQuery over email. Scopes sfcc.shopper-customers and sfcc.shopper-customers.rw. All PII.
Returns, settlements and locations
No returns object and no settlement or payout object is exposed by either OCAPI or SCAPI in the sources read. Returns in B2C Commerce are handled through Order Management or through custom objects, and money movement lives in the payment processor. Locations appear as inventory lists (OCAPI inventory_lists) and as locationIds on SCAPI availability records.
Writing back: listings, price and stock
Inventory IMPEX is the bulk path and has three steps: POST /inventory/impex/v1/organizations/{orgId}/availability-records/imports returns an object with an uploadLink and an importStatusLink; you POST newline-delimited JSON to the upload link, one record per line with recordId, sku, locationId, onHand and effectiveDate; then you poll the status link. Documented constraints: files over 100 MB must be gzip compressed, the body must be NDJSON and not a JSON array, future quantity values must be greater than zero, imports should not run while the location graph is changing, and delta imports perform best. IMPEX activity does not appear in Log Center, so keep the correlation ID.
Price writes are not a single endpoint. B2C Commerce prices live in price books, and the readable sources cover promotions rather than price book rows, so treat price write-back as needing verification against the live schema.
Webhooks and notifications
No general outbound webhook subscription for orders, products or inventory was found in the readable sources. What B2C Commerce calls hooks are server-side extension points executed inside the instance: hooks.json registers JavaScript in a cartridge, and HookMgr invokes it during OCAPI and SCAPI requests or system events, for example dw.ocapi.shop.basket.afterPOST. They can call out to an external service, but that is code a partner deploys, not a subscription you configure. Hooks must finish inside the SCAPI timeout or the request returns HTTP 504.
The practical pattern is polling. Run order-search filtered on lastModified ascending every 5 to 15 minutes and checkpoint the last timestamp seen; run a product delta nightly; use IMPEX or availability search for stock. Send a correlation-id header on every request so failures can be traced in Log Center, and sfdc_verbose: true when debugging. Note that CDN Zones, Inventory and Shopper Context APIs do not log to Log Center at all.
Rate limits and pagination
Numeric throttle rates are published at commerce-api/throttle-rates and timeouts at commerce-api/timeouts-limits, neither of which could be fetched. What is stated in the readable sources: admin APIs have lower rate limits than shopper APIs, typical response time is under 60 seconds, a request exceeding roughly 60 seconds returns HTTP 504, and 429 means throttled. The documented guidance is to use batch endpoints where they exist, back off exponentially on 429, prefer Inventory IMPEX for large stock imports, and spread non-urgent updates over time.
Error codes to handle: 400 bad request, 401 expired token, 403 missing scope or tenant access (check the dual scope first), 404, 429 rate limited, 500, 504 timeout.
Pagination is offset based on OCAPI (start and count in the search request, with total in the response) and on SCAPI search endpoints (limit and offset, with total and hits). Always pass select to trim the projection; both APIs return very large documents by default and select=(**) on a product search is expensive.
B2B and D2C Commerce on the core platform
A different surface entirely. The Connect REST API for a web store is rooted at https://<my-domain>/services/data/v63.0/commerce/webstores/{webstoreId} and includes /products, /search/products, /pricing/products?productIds=, /product-categories/children, /carts, /carts/current, /carts/current/cart-items and /session-context. For a headless storefront these are also reachable through the Experience Cloud web runtime at /webruntime/api/services/data/v63.0/..., which needs a CSRF token from /webruntime/api/module/@app/csrfToken.
For a data pipeline the Connect API is the wrong tool. Commerce records on this platform are standard Salesforce objects, so read them with SOQL through the REST API or with Bulk API 2.0: Product2, ProductCategory, PricebookEntry, WebStore, Order, OrderItem, and under Order Management OrderSummary, OrderItemSummary, FulfillmentOrder, OrderDeliveryGroupSummary and ReturnOrder. That also gives you Change Data Capture and Platform Events as a genuine push mechanism, which B2C Commerce lacks. The object names above are from general platform knowledge and were not verified against a fetched reference; confirm them against the org's own describe call.
Mapping to the unified model
Gaps and open questions
- The official reference at
developer.salesforce.comblocks automated fetching, so every endpoint above should be confirmed against the live schema (b2c scapi schemas get) before implementation. Salesforce's own repository marks its examples as illustrative. - No full sample order or product response could be obtained. Field lists are documented; concrete values are not.
- Token lifetime for Account Manager client credentials is not stated in the readable sources.
- Numeric rate limits and timeout thresholds are behind the blocked pages.
- Price book write-back is not covered by the readable sources, only promotions.
- No settlement, payout or returns object on the B2C side.
- The B2B/D2C object list (
OrderSummaryand the rest) is general platform knowledge, not verified against a fetched reference. - OCAPI versions differ between sources: the mirrored HTML reference is
v23_2, while Salesforce's current Python tooling defaults tov25_6. Pin the version explicitly.
Sources
- SalesforceCommerceCloud/b2c-developer-tooling, SCAPI admin skill and client examples
- SalesforceCommerceCloud/b2c-developer-tooling, OCAPI Data API products notebook
- SalesforceCommerceCloud/b2c-developer-tooling, hooks skill
- SalesforceCommerceCloud/commerce-sdk and commerce-sdk-isomorphic
- OCAPI Shop API Orders and OrderSearch, mirrored reference
- OCAPI Data API ProductInventoryRecords, mirrored reference
- forcedotcom/commerce-vercel, D2C web store Connect API paths