Skip to main content
OZAN ALTINBAS
Architecture

SCAPI vs OCAPI: SFCC APIs, SLAS and Migration Explained

By 12 min read

Salesforce B2C Commerce exposes its commerce engine through two REST API families: the Open Commerce API (OCAPI), part of the platform since 2014, and the B2C Commerce API (SCAPI), introduced in 2020 for headless commerce. They overlap but are not interchangeable: hosts, authentication, permissions and caching all differ, and only SCAPI is still being developed. OCAPI is deprecated as of April 2026, and new implementations should use SCAPI exclusively.

The name is often wrong, so first: Salesforce calls SCAPI the B2C Commerce API and says it also goes by Salesforce Commerce API (Get Started with the B2C Commerce API). "Shopper Commerce API" is not a Salesforce name; "Shopper" is the prefix of one of SCAPI's two API groups. For the wider platform picture, start with the SFCC guide.


SCAPI vs OCAPI at a glance

SCAPI (B2C Commerce API)OCAPI (Open Commerce API)
StatusActive; new features ship here onlyDeprecated since April 2026; fixes and security updates only
API groupsShopper APIs, Admin APIs, plus your own Custom APIsShop API, Data API, Meta API
Host{shortCode}.api.commercecloud.salesforce.com, one per realmThe instance or storefront domain, under /dw/shop, /dw/data, /dw/meta
Versioningv1 (Shopper Baskets also v2); additive changes keep the versionYear-based (v24_5); each new version deprecates the previous one
Shopper authSLAS: OAuth 2.1 JWTs with refresh tokensClient ID plus a shopper JWT; SLAS tokens also accepted
Back-office authAccount Manager client, client_credentials, scopesAccount Manager client credentials or a Business Manager user grant
PermissionsScopes on the SLAS or Account Manager clientOCAPI Settings JSON in Business Manager
CachingSCAPI CDN plus web-tier cache, TTL per API and expansionPer-resource cache_time in OCAPI Settings
Traffic protectionLoad shedding (503) and rate limits (429)None equivalent, according to Salesforce
ExtensibilityHooks and Custom APIsHooks only

What each API family is

SCAPI: Shopper APIs and Admin APIs

The split between the two SCAPI groups drives everything else (Get Started with the B2C Commerce API):

Shopper APIsAdmin APIs
PurposeCustomer-facing: browsing, cart, checkoutMerchant-facing: product, order, inventory management
AuthenticationSLAS (OAuth 2.1)Account Manager
ScaleHigh scale, anonymous usersModerate usage
AccessRead-only, except Baskets and OrdersRead and write

Custom APIs sit on top: endpoints you write in a cartridge and expose through the SCAPI gateway as Shopper or Admin endpoints. Salesforce ships commerce-sdk, commerce-sdk-isomorphic and @salesforce/commerce-sdk-react, and the Composable Storefront (PWA Kit) uses the Shopper APIs exclusively; see SFRA vs PWA Kit for that decision.

OCAPI: Shop, Data and Meta APIs

The Shop API acts as a shopper, or as a Business Manager user shopping on a customer's behalf. The Data API reads and writes merchant resources such as coupons, customer lists and content. The Meta API describes the API itself (Get Started With OCAPI). Every OCAPI request must identify a client ID, through the bearer token, a client_id parameter or an x-dw-client-id header, or it gets HTTP 400 (OCAPI Client Application Identification).


Endpoint and URL structure

SCAPI uses one hostname per realm, built from a short code, and identifies the instance by organization ID in the path (Base URL and Request Formation). OCAPI lives on the instance itself, with site and version in the path (OCAPI URL Syntax):

SCAPI: https://{shortCode}.api.commercecloud.salesforce.com/{apiFamily}/{apiName}/v1/organizations/{organizationId}/...
e.g.   https://kv7kzm78.api.commercecloud.salesforce.com/product/shopper-products/v1/organizations/f_ecom_zzte_053/products/25695327M?siteId=RefArchGlobal

OCAPI: https://{domain}/s/{site_id}/dw/{api_type}/{version}/{resource}
e.g.   https://{domain}/s/RefArch/dw/shop/v24_5/products/25695327M
  • Short code: eight characters, shared by the whole realm. Request it in Business Manager under Administration > Site Development > Salesforce Commerce API Settings; propagation can take up to 24 hours.
  • Organization ID: one per instance. In the demo value f_ecom_zzte_053, zzte is the realm and 053 the instance (prd, stg or dev on primary instances).
  • Site: a siteId query parameter, not a path segment.

The difference is architectural. Salesforce describes SCAPI as a decoupled, multi-service architecture behind a CDN gateway that handles authentication and routing, while OCAPI "runs on the application tier, competing with Business Manager and storefront logic for resources" (The Advantages of SCAPI).


Authentication

Shopper APIs: SLAS

The Shopper Login and API Access Service (SLAS) secures the Shopper APIs of SCAPI and OCAPI (SLAS overview). Each SLAS client belongs to one tenant, and each tenant maps to one instance. Clients are managed in the SLAS Admin UI (https://{shortCode}.api.commercecloud.salesforce.com/shopper/auth-admin/v1/ui/) or the SLAS Admin API, with the SLAS Organization Administrator role in Account Manager.

The deciding question is whether the client can keep a secret. Use a private client for full-stack web apps and anything with a backend-for-frontend (BFF); use a public client only when no secret can be stored, such as a mobile app calling SLAS directly. The SLAS best practices recommend a private client in production, with Strict Client Auth turned on, except for clients used by PWA Kit or Storefront Next: those don't send the required x-slas-client-auth header yet, so their /login and /authorize requests would return 401.

ClientShopperGrant type
PublicGuestauthorization_code_pkce (/authorize with hint=guest)
PublicRegisteredauthorization_code_pkce
PrivateGuestclient_credentials
PrivateRegistered, federated loginauthorization_code
PrivateRegistered, B2C Commerce loginauthorization_code or authorization_code_pkce

The token rules that shape session design:

  • Access tokens last 30 minutes and work only for the instance and site they were issued for.
  • On production tenants, refresh tokens last 90 days for registered shoppers and 30 days for guests; on non-production tenants, 9 days. Public-client refresh tokens are single use.
  • One refresh token can be exchanged at most 3 times within 60 seconds; beyond that, HTTP 429.
  • Guest token requests must send channel_id, enforced on all production tenants since 25 March 2025.
  • OCAPI accepts SLAS tokens for the resources the client is allowed to call, which is what makes a phased migration possible.

A guest token from a private client, followed by a Shopper API call:

curl "https://{shortCode}.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/{organizationId}/oauth2/token" \
  --user "{client_id}:{client_secret}" \
  --data "grant_type=client_credentials" \
  --data "channel_id=RefArch"

curl "https://{shortCode}.api.commercecloud.salesforce.com/product/shopper-products/v1/organizations/{organizationId}/products/25695327M?siteId=RefArch" \
  --header "Authorization: Bearer {access_token}"

Avoid three mistakes: admin scopes on SLAS clients (or shopper scopes on Account Manager clients), which bloats tokens and fails; sfcc.shopper-context.rw on a public client; and one USID reused across many guests, because Shopper API requests are routed by USID.

Admin APIs: Account Manager

Admin APIs need an Account Manager API client with the Salesforce Commerce API role, filtered to its instances, the required sfcc.* scopes under Allowed Scopes, client_secret_post as the token endpoint auth method and JWT as the token format (Authorization for SCAPI Admin APIs). Only client_credentials is supported, and each token covers one instance:

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:{realm}_{instance} sfcc.catalogs"

A SCAPI client cannot be used with OCAPI, or the other way round, and an OCAPI client must not carry the Salesforce Commerce API role. Plan separate clients from the start.

OCAPI authentication, for comparison

Shop API catalogue data needs only an allowed client ID; shopper calls carry a customer JWT from the /customers/auth resource; agent calls use a Business Manager user's OAuth token. The Data API takes an Account Manager client-credentials token (30 minutes) or a Business Manager user grant (15 minutes) (OCAPI OAuth 2.0). Salesforce sums up OCAPI's shopper authentication as proprietary, with no native refresh tokens.


Permissions: OCAPI Settings vs OAuth scopes

OCAPI grants nothing by default. Each client ID needs an entry in the OCAPI Settings JSON, under Administration > Site Development > Open Commerce API Settings, per API type (Shop or Data) and context (Global or a site). Anything else returns HTTP 401, edits can take up to three minutes to apply, and most Data API resources allow only global permissions (OCAPI Settings):

{
  "_v": "24.5",
  "clients": [
    {
      "client_id": "[your_own_client_id]",
      "allowed_origins": ["https://www.example.com"],
      "resources": [
        {
          "resource_id": "/product_search",
          "methods": ["get"],
          "read_attributes": "(**)",
          "write_attributes": "(**)",
          "cache_time": 900
        },
        {
          "resource_id": "/baskets/*/items",
          "methods": ["post"],
          "read_attributes": "(**)",
          "write_attributes": "(product_id, quantity)"
        }
      ]
    }
  ]
}

_v versions the settings format, not the OCAPI version you call, and resource_id accepts patterns such as /products/*/images. In a migration this document is your most useful artefact: an exact list of what each client may call (the Business Manager guide covers the surrounding admin work). Since 1 September 2026, realms provisioned for new customers no longer get OCAPI settings; existing customers provisioning a new realm must request them through support.

SCAPI has no such document: Salesforce's mapping marks OcapiConfigs as not applicable, because permissions are the scopes on the client. A scope ending in .rw grants read-write, so sfcc.catalogs.rw sfcc.products means read-write catalogues and read-only products. Grant the minimum.


Rate limits, load shedding and timeouts

SCAPI protects the platform two ways (Load Shedding and Rate Limiting):

  • Load shedding: at the load threshold, after scaling to maximum capacity, endpoints return HTTP 503. Checkout APIs (Shopper Baskets, Shopper Orders) are exempt. Protected responses carry sfdc_load (0–100) and sfdc_load_status: WARN at 80%, THROTTLE at 90%.
  • Rate limits: HTTP 429; SLAS adds a Retry-After header in seconds. SLAS allows 24,000 requests per minute per production instance, 500 per non-production instance, and 25 for getJwksUri and getWellknownOpenidConfiguration. SLAS and the Omnichannel Inventory APIs carry documented limits; all other endpoints are no longer rate limited, and limits can be adjusted through your Customer Success Manager or Account Executive.

The timeouts and size limits that matter most (Timeouts and Limits):

ConstraintDocumented valueStatus if exceeded
Shopper API response10 s504
Admin API response60 s504
Hook execution on a Shopper API, per hook10 s500
Request payload5 MB413
Response payload10 MB500
Hook circuit breakerMore than 50 failures in the last 100 calls503

When the hook circuit breaker opens, that extension point returns 503 for 60 seconds before it tests recovery. Salesforce states that OCAPI has nothing like load shedding or rate limiting, so OCAPI-era clients rarely handle 429 or 503 well. Build in Retry-After handling and backoff before go-live.


Caching: what changes

SCAPI caches eligible GET responses in two tiers, a SCAPI CDN at the edge and a web-tier cache at the origin, provided Enable Page Caching is on for the site (SCAPI Caching). Products, Search, Availability, Promotions, SEO URL mapping, Stores and Configurations are cached; Baskets, Orders, Customers, SLAS and Shopper Context are not. Four rules decide the hit rate:

  • The lowest TTL among requested expansions wins, and omitting expand selects them all, including availability at 60 seconds. Always send an explicit expand.
  • The cache key is computed before hooks run, so personalisation applied in a hook yields wrong or missed entries. Use the Shopper Context API instead; personalized=none marks content that is identical for every shopper.
  • Custom API responses are cached at the web tier only, and only when your script sets a cache time with response.setExpires() (1 to 86,400 seconds).
  • A CDN hit can serve a cached 200 after logout, so keep personal data out of cacheable responses.

OCAPI instead takes a per-resource cache_time from OCAPI Settings (default 60 seconds, maximum 86,400) and caches personalised responses by default when a customer JWT is present. The SFCC caching guide covers both alongside page caching and the eCDN.


Extensibility: hooks and Custom APIs

Hooks: shared by both APIs

Extension points keep their dw.ocapi.shop.* names, and the same hooks fire for SCAPI and OCAPI requests (Customization with Hooks). Turn on API hook execution under Administration > Global Preferences > Feature Switches; registration still runs from the cartridge's package.json to a hooks.json:

{
  "hooks": [
    {
      "name": "dw.ocapi.shop.basket.billing_address.beforePUT",
      "script": "./basket_hook_scripts.js"
    }
  ]
}
var Status = require('dw/system/Status');

// Runs for SCAPI Shopper Baskets and OCAPI Shop API requests alike.
exports.beforePUT = function (basket, doc) {
    var isValidAddress = true; // replace with real validation
    if (!isValidAddress) {
        var status = new Status(Status.ERROR);
        status.addDetail('validation_error', 'Bad billing address');
        return status;
    }
    // Return nothing on success: a returned value skips the system
    // implementation and any later hooks registered for this extension point.
};

Use request.isSCAPI() to branch, especially where a calculate hook shared with controllers opens transactions, which breaks SCAPI. Fields added in modifyResponse must be c_ properties, and Shopper Experience, Shopper Login, Shopper Payments, Shopper Custom Objects and Shopper Consents support no hooks. The SFCC hooks guide goes deeper.

Custom APIs: SCAPI only

Custom APIs are what OCAPI never had: your own REST endpoints behind the SCAPI gateway, at https://{shortCode}.api.commercecloud.salesforce.com/custom/{apiName}/{apiVersion}/organizations/{organizationId}/{endpointPath} (Custom APIs). Each API is a folder under cartridge/rest-apis/ with three files. The contract is an OAS 3.0 schema.yaml, shown with optional parts removed:

openapi: 3.0.0
info:
  version: 1.0.1
  title: Loyalty Information API
components:
  securitySchemes:
    ShopperToken:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://my-shortcode.api.commercecloud.salesforce.com/shopper/auth/v1/organizations/my-org-id/oauth2/token
          scopes:
            c_loyaltyInfo_r: Read loyalty information of a customer
paths:
  /customers:
    get:
      operationId: getLoyaltyInfo
      parameters:
        - in: query
          name: c_customer_id
          required: true
          schema:
            type: string
        - in: query
          name: siteId
          required: true
          schema:
            type: string
            minLength: 1
      responses:
        "200":
          description: Shows loyalty information for the customer
security:
  - ShopperToken: ["c_loyaltyInfo_r"]

script.js exports a function named after the operationId, and api.json maps the two:

var RESTResponseMgr = require('dw/system/RESTResponseMgr');

exports.getLoyaltyInfo = function () {
    var customerId = request.getHttpParameterMap().get('c_customer_id').getStringValue();
    // ... look up loyalty data for customerId ...
    if (customerId === 'customer1') {
        RESTResponseMgr.createSuccess({ tier: 'silver', points: 14275 }).render();
    } else {
        RESTResponseMgr.createError(404, 'customer-not-found', 'Customer Unknown', 'You provided an unknown customer ID.').render();
    }
};
exports.getLoyaltyInfo.public = true;
{
  "endpoints": [
    { "endpoint": "getLoyaltyInfo", "schema": "schema.yaml", "implementation": "script" }
  ]
}

Know these rules before designing one:

  • Registration happens when the code version containing the API is activated, and the cartridge must be on the site's cartridge path (the Business Manager site's for Admin endpoints); see the cartridge development guide.
  • Security: exactly one scheme per endpoint, ShopperToken (SLAS) or AmOAuth2 (Account Manager), with exactly one custom scope starting c_, at most 25 characters (Custom API authentication).
  • Validation: every parameter must be declared, custom ones prefixed c_, and additionalProperties is not allowed in request bodies. A request with siteId is a Shopper request; without it, an Admin request.
  • Limits: Shopper endpoints default to a 60-second runtime (configurable to 120, 10 recommended) and 5 MiB request bodies under storefront quotas; Admin endpoints accept 20 MB.

Where OCAPI is still needed

As of May 2026, Salesforce lists two areas still best handled through OCAPI: system information (low-level server status and maintenance indicators) and certain niche Data API tasks without a released Admin API (The Advantages of SCAPI). OCAPI also persists wherever an existing integration, third-party cartridge or tool still calls it. That is a migration backlog, not a reason to build on it. An excerpt of Salesforce's mapping:

OCAPI resourceSCAPI equivalent
Shop BasketsShopper Baskets v1 / v2
Shop Products, Shop CategoriesShopper Products
Shop ProductSearch, Shop SearchSuggestionShopper Search
Shop Content, Shop FoldersShopper Experience
Shop Orders, Shop OrderSearchShopper Orders
Shop SessionsCovered by Hybrid Auth
Data Products, Data CatalogsAdmin Products, Admin Catalogs
Data Jobs, Data CodeVersionsAdmin Jobs, Admin Scripts
Data OcapiConfigsNot applicable: scopes in Account Manager and SLAS

The parity gap now runs the other way: Shopper Context, Shopper SEO, Custom APIs, CDN Zones and the Omnichannel Inventory APIs exist only in SCAPI. Personalisation shows why it matters. As Salesforce puts it, on OCAPI a personalised price usually needed a modify-response hook; on SCAPI you send shopper attributes to Shopper Context and the platform resolves prices and promotions natively.


Salesforce's stated direction for OCAPI

The OCAPI versioning and deprecation policy is explicit:

  • OCAPI is deprecated as of April 2026. Support requests, bug fixes and security updates continue where applicable; innovation does not.
  • No end-of-life date is set. The decision is reviewed annually, and any EOL date will fall at least two years after Salesforce formally decides to retire OCAPI.
  • New implementations must use SCAPI exclusively; existing OCAPI users should start planning their migration (The Advantages of SCAPI).

There is no cliff edge today, but OCAPI is frozen. Treat it as a dependency with a removal plan, not a platform choice.


OCAPI to SCAPI migration checklist

#StepWhat to do
1InventoryExport OCAPI Settings for every client ID (Shop and Data, global and per site) and compare with real traffic.
2MapMap each resource with Salesforce's table; mark the documented gaps that stay on OCAPI for now.
3ProvisionRequest the realm's short code (allow 24 hours) and record each instance's organization ID.
4SLAS firstCreate SLAS clients (private wherever there is a BFF) with channels, scopes and redirect URIs. OCAPI accepts SLAS tokens, so authentication can move before endpoints do.
5Admin clientsCreate new least-privilege Account Manager clients; OCAPI clients cannot be reused.
6HooksEnable API hook execution, test every dw.ocapi.shop.* hook through SCAPI, branch with request.isSCAPI(), and return nothing on success.
7PersonalisationMove price and promotion logic out of hooks into Shopper Context so it is part of the cache key.
8Custom APIsReplace logic bent into hooks or controllers to act as an endpoint with a Custom API.
9CachingEnable page caching, send explicit expand values, and keep personal data out of cacheable responses.
10ResilienceHandle 429 with Retry-After, back off on 503, budget for the 10-second timeout, and send a correlation-id header for tracing.
11Hybrid storefrontsUse Hybrid Auth, which replaced Plugin SLAS from B2C Commerce 25.3: add the sfcc.session_bridge scope and remove plugin_slas.
12DecommissionRemove migrated resources from OCAPI Settings and disable unused OCAPI client IDs.

Key takeaways

  • SCAPI is the B2C Commerce API, also called the Salesforce Commerce API: Shopper APIs, Admin APIs and Custom APIs.
  • OCAPI has been deprecated since April 2026, with no EOL date set. New builds use SCAPI only.
  • Shoppers authenticate through SLAS (a private client behind a BFF in production); back-office systems through Account Manager clients. SCAPI and OCAPI need separate clients.
  • Permissions move from the OCAPI Settings JSON to OAuth scopes.
  • Design for a 10-second Shopper API timeout, 429 with Retry-After and 503 under load shedding.
  • Existing hooks run on both APIs; Custom APIs replace the workarounds OCAPI forced. For the business case, see headless Commerce Cloud.

Planning a headless build or an OCAPI-to-SCAPI migration? An architect's review of the authentication model, hooks and caching before you commit is part of the SFCC consulting services; get in touch to discuss it.

Further reading