# developer Commerce Cloud - Full Documentation # About the Marketplace Seller Integration API This document aims to help sellers who wish to perform the management of their items within Commerce Marketplace through a custom integration. The following documentation focuses on the basic flow for the integration of the seller data, including example requests and responses. The Marketplace Seller Integration API is based on the main REST API from Salesforce. For more information, see the [REST API Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_what_is_rest_api.htm). ## Authorization To use the API, you must authenticate using the **Login** and **Get/Refresh Token** endpoints. These endpoints include an access token in the response that you can supply as a parameter to make authorized requests to the other endpoints. For step-by-step instructions, see the [authorization instructions](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/quickstart_oauth.htm) for the REST API. ## Rate Limits To ensure availability and protect from denial of service attacks, requests to the API are rate limited. For details, see the [Limits](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_limits.htm) page in the REST API Developer Guide. # Access Protected Resources with Environment Variables Managed Runtime environment variables allow you to add variables to the application process running in an environment, without having to make code changes. Environment variables are stored securely, making them ideal for use cases such as: - Calling third-party resources that are protected by API keys - Conditionally changing React.js rendering For more details, see [Environment Variables](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/environment-variables.md). Let’s use an environment variable to store an API key used to access a secure external resource. In this tutorial the resource is an imaginary third-party content management system (CMS). We'll add a handler to the Express.js server in `ssr.js` that makes requests to the CMS and responds with the protected data. We then access the data by calling the handler. For security reasons, the API key calls the CMS from server-side code, not client-side code. ## Prerequisites To complete this tutorial, first: - Get access to Managed Runtime and Runtime Admin. See [Managed Runtime Administration](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/managed-runtime-administration.html). - Have an API key to use the Managed Runtime API. To find your API key, log in to the [Runtime Admin](https://runtime.commercecloud.com/) tool and go to the [Account Settings](https://runtime.commercecloud.com/account/settings) page. - Have a developer or admin role on the project for which you’re setting environment variables. If you don't have access, ask someone in a Managed Runtime admin role to help you with that. ## On Your Local Machine Before running the commands in this tutorial, replace any placeholders with actual values. Placeholders are formatted like this: `$PLACEHOLDER`. ### Set the Environment Variable Locally 1. To set the environment variable in the current session, open a terminal and run this command: - **Windows:** `set CMS_API_KEY='my-cms-api-key'` - **Most Unix-like operating systems, including macOS:** `export CMS_API_KEY='my-cms-api-key'` 2. Check if the environment variable is set correctly by running: - **Windows:** `echo %CMS_API_KEY%` - **Most Unix-like operating systems, including macOS:** `echo $CMS_API_KEY` The command returns this output: `my-cms-api-key` 3. Add this JavaScript code for calling the CMS in `ssr.js`: ```jsx async function fetchCMSData() { const key = process.env.CMS_API_KEY; if (!key) { throw new Error("Environment variable CMS_API_KEY isn't set."); } return await fetch("https://api.example.com/", { headers: { Authorization: "Bearer " + key, }, }).json(); } async function handlerCMS(req, res) { try { const data = await fetchCMSData(); return res.json(data); } catch (err) { return res.status(500).json({ error: err.message }); } } ``` 4. Bind the handler to a route in the Express.js server. Next, start the development server using `npm start`. Finally navigate to the handler locally to verify the CMS data is returned. ## In a Non-production or Production Environment We recommend testing these steps in a non-production environment before deploying your changes to production. Before running the commands in this tutorial, replace any placeholders with actual values. Placeholders are formatted like this: `$PLACEHOLDER`. 1. Call the [projects_target_env_var_partial_update](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/references/mrt-admin?meta=projects_target_env_var_partial_update) endpoint to set the environment variable. This deploys your bundle. Wait for the environment to redeploy before using the API key in your code. You can also use Runtime Admin to manage environment variables. See [Environment Variables](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/environment-variables.md). Use the [B2C CLI](https://developer.salesforce.com/docs/commerce/b2c-commerce/guides/b2c-developer-tooling.md): ```bash export MRT_API_KEY='your-runtime-admin-api-key' npx @salesforce/b2c-cli mrt env var set 'CMS_API_KEY=your-cms-api-key' \ --project my-storefront --environment staging ``` Alternatively, use the API: ```bash curl "https://cloud.mobify.com/api/projects/$PROJECT_ID/target/$ENVIRONMENT_ID/env-var/" \ --request 'PATCH' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header "Authorization: Bearer $MOBIFY_API_KEY" \ --data '{"CMS_API_KEY": {"value": "'$YOUR_API_KEY_VALUE'"}}' ``` 2. [Deploy your bundle](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/pushing-and-deploying-bundles.html) from your local machine to your chosen environment. ## See Also - [Environment Variables](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/environment-variables.md) - [Environment Variable Tutorials](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/environment-variables.md#tutorials) - [Troubleshoot Environment Variables](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/environment-variables.md#troubleshoot-environment-variables) - [Debugging Locally](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/debugging.html#debugging-locally) # Access Site Preferences in Your Composable Storefront from the MRT Data Store If your composable storefront code requires access to the site preferences that administrators manage in Business Manager, access site preferences directly from the Managed Runtime (MRT) Data Store. Accessing site preferences from the MRT data store is faster than making a Salesforce B2C Commerce API (SCAPI) call. Enable the MRT data store by using a configuration or an environment variable. ## Prerequisites - [PWA Kit](https://github.com/SalesforceCommerceCloud/pwa-kit) v3.21 or later. ## Enable the MRT Data Store Before accessing the MRT Data Store, enable it in the app configuration file or by using an MRT environment variable. Enable the MRT Data Store in the app configuration value in `config/default.js` as shown in this example. ``` module.exports = { app: { mrtDataStore: { enabled: true // Enable MRT Data Store bootstrap }, // ... rest of config } } ``` Alternatively, use an environment variable to enable the MRT Data Store. ``` PWAKIT_MRT_DATA_STORE_ENABLED=true npm start ``` **note:** If the `PWAKIT_MRT_DATA_STORE_ENABLED` environment variable is set to a valid value, it takes precedence over the app configuration value. ## Usage in ssr.js (Server Middleware) Access preferences at the server middleware layer, before React renders. The async `getCustomSitePreferences` and `getCustomGlobalPreferences` functions from `@salesforce/pwa-kit-runtime/utils/ssr-server` let you inspect preference values and modify the HTTP response—for example, returning a 503 status during maintenance or populating an API endpoint with feature flags. Because middleware runs before rendering, this is the right place when preferences need to gate or redirect requests. To access preferences inside React components instead, use the utility functions or hooks. ``` // app/ssr.js import { getCustomSitePreferences, getCustomGlobalPreferences } from '@salesforce/pwa-kit-runtime/utils/ssr-server' export default function(app) { app.use(async (req, res, next) => { const siteId = res.locals.site?.id const [sitePrefs, globalPrefs] = await Promise.all([ getCustomSitePreferences({siteId}), getCustomGlobalPreferences() ]) if (sitePrefs.maintenanceMode) { return res.status(503).send('Maintenance') } next() }) app.get('/api/config', async (req, res) => { const siteId = res.locals.site?.id const prefs = await getCustomSitePreferences({siteId}) res.json({ enableFeature: prefs.enableFeature || false }) }) } ``` ### Usage in React Components (Utilities) Use the utility functions from `@salesforce/pwa-kit-runtime/utils/ssr-server` when you need direct control over how preferences are fetched and cached. Wrap them in `useQuery` from `@tanstack/react-query` to configure caching behavior—for example, `staleTime: Infinity` fetches data once per session, and `enabled: Boolean(siteId)` prevents the query from running until a site ID is available. Extract this logic into shared custom hooks and call them from any component that needs to conditionally render content based on preference flags. ``` // app/hooks/useStorePreferences.js import {useQuery} from '@tanstack/react-query' import { getCustomSitePreferences, getCustomGlobalPreferences } from '@salesforce/pwa-kit-runtime/utils/ssr-server' export function useSitePreferences(siteId) { return useQuery({ queryKey: ['sitePreferences', siteId], queryFn: () => getCustomSitePreferences({siteId}), staleTime: Infinity, enabled: Boolean(siteId) }) } export function useGlobalPreferences() { return useQuery({ queryKey: ['globalPreferences'], queryFn: () => getCustomGlobalPreferences(), staleTime: Infinity }) } // app/pages/home/index.jsx const Home = () => { const {data: sitePrefs} = useSitePreferences('RefArch') const {data: globalPrefs} = useGlobalPreferences() return (
{sitePrefs?.showBanner && } {globalPrefs?.enableChat && }
) } ``` ## Usage in React Components (Hooks) Use the React hooks for the most idiomatic way to access preferences in React function components. The `useCustomSitePreferences` and `useCustomGlobalPreferences` hooks from `@salesforce/pwa-kit-react-sdk/ssr/universal/hooks` integrate with React's component model, follow standard hook conventions, and can be composed into custom hooks. This is the recommended approach for React function components. ``` // app/pages/home/index.jsx import React from 'react' import { useCustomSitePreferences, useCustomGlobalPreferences } from '@salesforce/pwa-kit-react-sdk/ssr/universal/hooks' const Home = () => { const sitePrefs = useCustomSitePreferences() const globalPrefs = useCustomGlobalPreferences() return (
{sitePrefs.showBanner && } {globalPrefs.enableChat && }
) } ``` ## Local Development Use `MRT_DATA_STORE_DEFAULTS` to seed values without a real data store for local testing. You can use either a flat object or the SCAPI envelope format with `data` and `total`. Use flat objects for simplicity: ``` MRT_DATA_STORE_DEFAULTS='{ "custom-global-preferences":{ "enableChat": true, "maintenanceMode": false }, "RefArch-custom-site-preferences":{ "productsPerPage": 24, "showBanner": true } }' PWAKIT_MRT_DATA_STORE_ENABLED=true npm start ``` Or use the SCAPI envelope format to match the actual data store structure. Include both `data` (array) and `total` (number) — both fields are required to trigger envelope unwrapping: ``` MRT_DATA_STORE_DEFAULTS='{ "custom-global-preferences":{ "data": [{ "enableChat": true, "maintenanceMode": false }], "total": 1 }, "RefArch-custom-site-preferences":{ "data": [{ "productsPerPage": 24, "showBanner": true }], "total": 1 } }' PWAKIT_MRT_DATA_STORE_ENABLED=true npm start ``` For example, based on the `MRT_DATA_STORE_DEFAULTS` definition, `getCustomSitePreferences({siteId: 'RefArch'})` returns: ``` { productsPerPage: 24, showBanner: true } ``` And `getCustomGlobalPreferences()` returns: ``` { enableChat: true, maintenanceMode: false } ``` ## Data Store Structure The MRT Data Store is populated automatically when enabled. You don't need to manually configure keys. The utilities and hooks handle key resolution internally based on the site ID you provide. **For reference, data is stored under these keys:** - Global preferences: `custom-global-preferences` - Site preferences: `{siteId}-custom-site-preferences` The exact structure of the preference objects depends on the custom preferences defined in Business Manager for your B2C Commerce instance. # Access Violation Logs ## Prerequisites Before you can view Page Shield violation logs, you must: - Configure at least one Page Shield policy in the zone. - Have access to the Log Center. For details, see [Start Log Center](https://help.salesforce.com/s/articleView?id=cc.b2c_start_log_center.htm&type=5). ## View Log Center Violation Logs In the Log Center, specify a **Service Type** of **ecdn** to restrict the search. This provides violation logs and audit logs with details of when and how a policy was changed. ![Log Center Service Type ecdn](https://developer.salesforce.com/docs/media/commerce-api/ecdn-log-center-search.png '{"class": "image-sm"}') Audit logs contain both old and new policy information, for example: - Old value: ```json {"action":"allow","description":"Page shield Test Policy Allow-Non Payment", "enabled":true, "expression":"(http.host contains \"/shop.stg.bcxg.sfcc-store-internal.net\" and http.request.full_uri contains \"cart\") or (http.host contains \"shop.stg.bcxg.sfcc-store-internal.net\" and http.request.full_uri contains \"checkout\")", "value":"script-src cquotient.com _.cquotient.com paypal.com _.paypal.com salesforce.com _.salesforce.com sfcc-store-internal.net _.sfcc-store-internal.net stripe.com _.stripe.com", "id":"d01673584c044c699782bd99b453dd5b"}, ``` - New value: ```json {"action":"allow","description":"Page shield Test Policy Allow-Non Payment", "enabled":false,"expression":"(http.host contains \"/shop.stg.bcxg.sfcc-store-internal.net\" and http.request.full_uri contains \"cart\") or (http.host contains \"shop.stg.bcxg.sfcc-store-internal.net\" and http.request.full_uri contains \"checkout\")", "value":"script-src stripe.com _.stripe.com", "id":"d01673584c044c699782bd99b453dd5b"} ``` ![Log Center example log entries and details](https://developer.salesforce.com/docs/media/commerce-api/ecdn-log-center-example1.png '{"class": "image-lg"}') ![ Log Center example log entries and details](https://developer.salesforce.com/docs/media/commerce-api/ecdn-log-center-example2.png '{"class": "image-lg"}') ![ Log Center example log entries and details](https://developer.salesforce.com/docs/media/commerce-api/ecdn-log-center-example3.png '{"class": "image-lg"}') Activation Code SPI # Activation Code SPI [Download specification](https://developer.salesforce.com/static/commercecloud/retail-cloud/activation-code-spi/activation-code-spi-oas-public.yaml) Merchant SPI to implement and provide an endpoint for the POS. This endpoint must be configured in the POS system so that the POS can use it to get activation codes and details. **Version:** 2.3.4 ## Tags ### Activation Code SPI [Download specification](https://developer.salesforce.com/static/commercecloud/retail-cloud/activation-code-spi/activation-code-spi-oas-public.yaml) Merchant SPI to implement and provide an endpoint for the POS. This endpoint must be configured in the POS system so that the POS can use it to get activation codes and details. ## Operations ### POST Retrieve activation code. `POST /merchant/spi/sample/endpoint/sa/activation/code/activation/code/from/external/system` Operation ID: `getActivationCode`. Tags: Activation Code SPI. Retrieve activation code from merchant's system based on provided input. Merchants must implement this endpoint per this specification and then configure it in CMS under **CMS** > **Store Management** > **Integration** > **Custom API Connectors** > **Create**. Make sure to create the endpoint in CMS with the following details: 1. Under **API Config**: * **API Key:** `ACTIVATION_CODE` * **API Endpoint:** Provide the external endpoint to get activation code and details, for example: /merchant/spi/sample/endpoint/sa/activation/code 2. Under **JSON Config**: * **Integration Type:** `HTTP_POST` * **JSON Headers:** Provide the header for the API. Headers can be Content-Type, for example: {"contentType": "application/json"} **Invoke:** The SPI is invoked during the order checkout flow or submit order only if the order has a product that needs population of activation code from the external service (i.e., **CMS** > **Product Class** > **Requires Activation Code via External Source is enabled**). **Response:** If the SPI has an empty response or error, the order checkout or submit order flow is not completed and the associate sees an error. For more details, refer to [Point of Sale SPI Error Handling](https://developer.salesforce.com/docs/commerce/retail-cloud/guide/retail-cloud-spi-error-handling.html) #### Request body Media type: `application/json`. Schema: `ActivationCodeRequest`. #### Responses * **default** Default response. Media type: `application/json`. Schema: `ActivationCodeResponse`. ## Schemas ### ActivationCodeRequest * `orderId` (_string_, required) The order ID of the customer order. * `storeId` (_string_, required) The store ID of the store where the transaction is happening. * `transactionId` (_string_, required) Sale transaction ID. Example: `1-test-20240612131829-4-1`. * `productList` (_array of ExternalProduct_, required) List of products that need serialized-unit attributes. For example, activation code, context code, etc. ### ActivationCodeResponse * `errorMessage` (_string_, nullable) Error message shown to the user. Example: `Invalid request. Please check the request and try again.`. * `errorCode` (_string_, nullable) Error code shown to the user. Example: `INTERNAL_ERROR`. * `serializedUnitDetailList` (_array of ExternalSerializedUnitDetail_) The list of attributes about a serialized unit, for example, activation code or context code. * `error` (_boolean_) ### ExternalProduct Products. * `productId` (_string_, required) Product ID. Unique identifier of an item. All the variations of an item will have distinct product ID, hence it is equivalent to SKU. * `variantGroupId` (_string_, required) Variant Group ID. All the variants of an item are grouped together under the same variant ID. * `name` (_string_, required) Name of the item. * `price` (_number_, required) Price of the item. * `quantity` (_integer (int32)_, required) Item's Quantity. * `orderItemId` (_string_, required) Item sequence/System generated item ID assigned to this line item. Must be unique within this order and should have numerical value in string. Example: `It can have values like 1, 2, 3, etc... which represents line item number within the order.`. * `imageURL` (_string_, required) Image URL of the item. * `subTotal` (_number_, required) Item's total excluding discounts, shipping costs and taxes but inclusive of VAT. * `discount` (_number_) Total discount amount applied on the item, including all discount types (manual price override, transaction discount, coupon, rewards, and auto promotions). Required if any discounts are applied. * `total` (_number_, required) Item's total — final amount charged for this item, inclusive of tax/VAT and discounts. * `size` (_string_, required) Item's size. Example: `39-42 or M or 38 etc...`. * `color` (_string_, required) Item's color. Example: `Light Grey or Black etc...`. * `itemStatus` (_string_, required) Below is the list of statuses for in-store purchases and BORIS. The statuses are applicable for both the order and its items. The significance of each status is described in detail below. The eligibility of an item for returns is determined by its current status and the status of the order. Moreover, the **returnsEligibility** attribute at the item level (productList) can also be used to regulate the return eligibility of an item. To learn more about returns eligibility, please refer to the table below and productList.returnsEligibility | Order Status | Display Name in Order History Filter | Significance for Order | Significance for Item | Return Eligibility | | --- | --- | --- | --- | --- | | COMPLETED | Completed | Successful sale transaction. | Item sold successfully. | Both the order and item are eligible for return. | | RETURNED | Returned | Successful return or exchange of all items with refund to customer. | Successful return of an item - whole quantity and is not eligible for return. | Both the order and item are not eligible for return. | | PARTIALLY\_REFUNDED | Partially Returned | Successful return or exchange of partial items with refund to customer. | Successful return of an item - partial quantity | Further returns are allowed on order as well as on item. | | EXCHANGED | Exchanged | Successful transaction that exchanges all items with no refund to customer | Successful exchanges of an item - whole quantity | Both the order and item are not eligible for return. | | PARTIALLY\_EXCHANGED | Partially Exchanged | Successful transaction that exchanges partial items with no refund to customer | Successful exchanges of an item - partial quantity | Further returns are allowed on order as well as on item. | | FAILED | Failed | Failed sale transaction. | Not applicable | Both the order and item are not eligible for return. | | PENDING | Pending | Sale transaction awaiting payment confirmation. For example, Remote Pay By Link | Not applicable | Both the order and item are not eligible for return. | Allowed values: `COMPLETED`, `FAILED`, `RETURNED`, `PARTIALLY_REFUNDED`, `PENDING`, `CANCELLED_WITH_REFUND`, `EXCHANGED`, `PARTIALLY_EXCHANGED`, `CANCELED`. Default: `COMPLETED`. * `gtin` (_string_, required) Item's GTIN. * `fulfillmentType` (_string_, required) Item's fulfillment type. It can be either IN\_STORE (in-store purchase) or ONLINE (delivery item). Allowed values: `IN_STORE`, `ONLINE`, `MIXED_CART`. * `fulfillmentSubType` (_string_, required) 1. DEFAULT: Either item will be available from the current store for  cash & carry or for delivery to shipping address from a warehouse.   Fulfillment Type:IN\_STORE, Fulfillment Sub Type: DEFAULT : Cash & carry item 2\. TRY\_AT\_STORE: An item will be made available in the selected store for the trial.  The taxes and promotion will not be applied on the try at store items. 3\. PICKUP\_LATER: An item will be made available for pickup from the selected store.  The fulfillment will be done the current store itself, or another store. 4\. SHIP\_TO\_HOME: An item will be delivered to provided shipping address.  The fulfillment will be done by the current store itself,  or another store or a warehouse. 5\. SHIP\_TO\_STORE: An item will be delivered to a store from where it can be picked up. 6. PICKUP\_NEARBY: An item will be made available for pickup in near by store. \---------------------------------------------------------------------------------- | Fulfillment Type | Fulfillment SubType | Item | | --- | --- | --- | | IN\_STORE | DEFAULT | Cash & carry | | ONLINE | DEFAULT | Ship to home from warehouse | | ONLINE | TRY\_AT\_STORE | Ship to store from warehouse to try out | | IN\_STORE | PICKUP\_LATER | Pickup from current store | | IN\_STORE | SHIP\_TO\_HOME | Ship to home from store | | IN\_STORE | SHIP\_TO\_STORE | Ship to a store from another store for pickup | | ONLINE | SHIP\_TO\_STORE | Ship to store from warehouse for pickup (buy online ship to store) | | IN\_STORE | PICKUP\_NEARBY | Pickup from another store | . Allowed values: `DEFAULT`, `TRY_AT_STORE`, `PICKUP_LATER`, `SHIP_TO_HOME`, `SHIP_TO_STORE`, `PICKUP_NEARBY`. * `salesAssociateAttributionList` (_array of ExternalStoreAssociateAttribution_) Sales associate attribution. If unspecified, then attribute the whole sale transaction to the logged-in associate. * `epc` (_string_, nullable) Electronic product code. Mandatory for items having epc/RFID. * `masterProductId` (_string_, nullable) Outfit Identification that groups together a set of items from a cart. Commonly used in case product customizations like DYO (i.e. Customization Identification) or Alteration etc...Mandatory for alteration or DYO items. * `subProducts` (_array of ExternalProduct_, nullable) The other items from the cart related to the current item. Commonly used in case of customizations like Alteration as well as for add-on products. Mandatory for alteration or add-on items. * `productClass` (_string_, required) Product class of the item. * `serialNumber` (_string_) Serial number of the item (if applicable). * `productCondition` (_string_) Product condition. Allowed values: `NEW`, `USED`, `REFURBISHED`. Default: `NEW`. * `reasons` (_array of Reason_, nullable) List of reasons pertaining to the actions performed on the item. For example, reason for price override applied on the item or reason for returning the item. * `dateTimeSlot` (_DateTimeSlot_) * `priceCorrection` (_ExternalProductPriceCorrection_) * `deliveryGroupId` (_string_) Delivery group ID. It is an identifier to the shipping details of the item. It links the item to one of the delivery groups available under transaction. It is applicable for delivery items. Maximum length: 32. * `formAttributes` (_array of ProductFormAttribute_, nullable) New form data to be set on the product. * `pickupDetailId` (_string_) Link to pickup person detail corresponding to the product. Points to the corresponding pickup details from pickupDetailList in the transaction. Maximum length: 128. * `productTax` (_ExternalProductTax_) * `priceAdjustmentList` (_array of ExternalPriceAdjustment_, nullable) Price adjustments applied on the item. It includes all the price adjustments like transaction discount, price override etc... Mandatory if any type of price adjustments is applied. * `returnsEligibility` (_string_, nullable) Returns eligibility of the item. Allowed values: `ELIGIBLE_FOR_RETURNS`, `EXPIRED_RETURNS`, `CANNOT_BE_RETURNED`, `ELIGIBLE_FOR_CANCELS`, `INELIGIBILITY_FOR_RETURN_CROSS_CHANNEL`. Default: `ELIGIBLE_FOR_RETURNS`. * `returnsEligibilityText` (_string_) Returns eligibility information for the product, describing why it's ineligible for return. For example, BOFIS items that haven't shipped can't be returned. In that case, this field says 'Item not shipped yet' (applies when BOFIS orders are sent to PS Queue, that is, STH from CS enabled in Omni V2 settings). When an item can't be returned, this field says 'Cannot be returned'. Maximum length: 128. * `pickupStoreId` (_string_, nullable) Pickup store ID. * `fulfillmentStoreId` (_string_, nullable) Fulfillment store ID. * `totalAdditionalCharge` (_number_, nullable) Total additional charges. * `tlogCustomProductAttributes` (_string_, nullable) Custom attributes. * `externalId` (_string_, nullable) External ID of the product. Maximum length: 128. * `productBundleId` (_string_, nullable) If the product is part of outfit or a product bundle, then provide the id which groups together all the items from the bundle. * `noteList` (_array of ExternalNote_, nullable) Note associated with the product. * `customAttributeList` (_array of ProductFormAttribute_, read-only) Custom attributes related to the product. All these attributes will be available at item level that are configured in Store Setting: spiCustomProductAttributes. _Example_ ``` [ { "key": "color", "value": "red" } ] ``` * `registryId` (_string_, nullable) The unique ID of the registry. This is required when the item being purchased or returned is a gift registry item. * `requiredDeposit` (_number_) Required deposit for the product if deposit configuration is enabled for the store. * `paidDeposit` (_number_) Paid deposit for the product if deposit configuration is enabled for the store. ### ExternalSerializedUnitDetail The list of attributes about a serialized unit, for example, activation code or context code. * `activationCode` (_string_) The activation code of the serialized unit detail. * `contextCode` (_string_) The context code of the serialized unit detail. * `orderItemId` (_string_) The order item id of the serialized unit detail. ### ExternalStoreAssociateAttribution Sales associate attribution. If unspecified, then attribute the whole sale transaction to the logged-in associate. * `storeAssociateId` (_string_, required) ID of the associate that completed the sale transaction. * `contributionPercentage` (_number_, required) Associate's contribution towards sales. * `attributionNote` (_string_) Note related to attribution. * `firstName` (_string_, required) Associate's first name. * `lastName` (_string_, required) Associate's last name. ### Reason Reason for returning the item. * `reasonCodeType` (_string_, required) Type for reason codes. It can be one of TRANSACTION\_DISCOUNT, PRICE\_OVERRIDE, RETURNS, BANK\_DEPOSIT, TILL\_RECONCILIATION, TILL\_CASH\_OUT. Allowed values: `TRANSACTION_DISCOUNT`, `PRICE_OVERRIDE`, `RETURNS`, `BANK_DEPOSIT_VARIANCE`, `TILL_RECONCILIATION_VARIANCE`, `TILL_CASH_OUT`, `PRICE_CORRECTION`, `RETURN_PRICE_CORRECTION`, `EXCLUDE_ITEM_FROM_CYCLE_COUNT`, `INVENTORY_ADJUSTMENT`, `SAFE_FLOAT_UPDATE`, `TILL_FLOAT_UPDATE`, `TILL_CASH_IN`, `TILL_NO_SALE`, `VOID_CART`, `EXPENSE_UPDATE`, `MISC_EXPENSE1_UPDATE`, `MISC_EXPENSE2_UPDATE`, `INVENTORY_ITEM_RECEIVE_MISMATCH`, `OFFLINE_GHOST_TILL`, `DELETE_ITEM`, `CORP_CANCELLATION`, `FULFILLMENT_CANCELLATIONS`. Maximum length: 64. * `reasonCode` (_string_, required) Reason code. Maximum length: 64. * `reasonText` (_string_, nullable) Reason text. Maximum length: 256. ### DateTimeSlot Backup delivery date if slots are unavailable. Default can be set via 'deliveryDatePickerWillCall' in Store settings. * `slotId` (_string_) Slot ID. Example: `1234`. * `slotDate` (_integer (int64)_, required) Delivery date in epoch. * `selectedTimeSlot` (_string_, required) Time slot. Example: `09:00 - 12:00`. * `dateTimeSlotType` (_string_, nullable) Time slot type. Allowed values: `Delivery`, `Pickup`. Default: `Delivery`. ### ExternalProductPriceCorrection Price correction applied on the item with information like the approval manager ID and original price. Sale price becomes new corrected price. * `originalPrice` (_number_) Original price of the item. * `approvingManagerId` (_string_) ID of the manager who approved the price correction request. ### ProductFormAttribute Details about a product form attribute. * `key` (_string_) The identifier of the attribute. Example: `color`. * `value` (_string_) The value associated with the key. Example: `blue`. _Example_ ``` [ { "key": "color", "value": "red" } ] ``` ### ExternalProductTax Tax/Vat details. * `totalTax` (_number_, required) Total tax. Required in case of tax countries. * `taxExemption` (_number_, nullable) Tax Exemption. * `taxRateList` (_array of ProductTaxRate_, nullable) Tax Rates. * `vatRateList` (_array of ProductTaxRate_, nullable) VAT Rates. * `valueAddedTax` (_number_, nullable) Total VAT. Required in case of VAT countries. * `taxableAmount` (_number_, required) Taxable amount. ### ExternalPriceAdjustment Price adjustments applied on the item. It includes all the price adjustments like transaction discount, price override etc... Mandatory if any type of price adjustments is applied. * `description` (_string_, required) Price adjustment description. * `adjustment` (_string_, required) Price adjustment. * `id` (_string_) Promotion Id for applied coupon or auto promotion. * `price` (_number_, required) Price adjustment amount. * `priceAdjustmentType` (_string_, required) Price adjustment type. Allowed values: `PRICE_OVERRIDE`, `PROMOTION`, `FEES`, `REWARDS`, `TRANSACTION_DISCOUNT`. * `reason` (_Reason_) * `merchantPromotionId` (_string_, nullable) External promotion ID corresponding applied promotion. It is used to identify the promotion in external system and is relevant only if it is configured in CMS Applicable only for promotions. * `promoCode` (_string_, nullable) Coupon code corresponding to promotion applied. Not applicable for auto promotions, manual price adjustments like price override & transaction discount. * `auto` (_boolean_, nullable) Applied promotion is auto or not. * `discountValue` (_number_, nullable) Applicable only for PRICE\_OVERRIDE OR TRANSACTION\_DISCOUNT.It will hold discount value either in percentage or in amount format. * `discountType` (_string_, nullable) Discount types 1. Amount: Item price or transaction-total reduced by provided amount, 2. PERCENT: Item price or transaction-total reduced by amount calculated based on provided percentage and item price/transaction-total, 3. FLAT\_PRICE: Item price is reduced to the provided amount. Not applicable to transaction discounts. The discount amount is then calculated by subtracting entered flat\_price from item-price. Allowed values: `AMOUNT`, `PERCENT`, `FLAT_PRICE`. * `externalId` (_string_, nullable) This identifier is used when an order is imported from an external system. If an order is placed directly in the POS, this identifier will not be available. The external system provides this identifier, which will then be replayed to that external system during order posting for any subsequent modifications like returns. Maximum length: 128. ### ExternalNote Notes for the transaction. * `cartId` (_string_) * `noteId` (_string_) * `noteText` (_string_) * `transactionId` (_string_) ### ProductTaxRate VAT breakdown for the shipping cost. Applicable for VAT countries and if the shipping cost is taxable. * `countryCode` (_string_, required) Country Code. Maximum length: 2. Example: `US`. * `state` (_string_, nullable) State. Applicable if the tax rate is specific to state. Maximum length: 128. * `postalCode` (_string_, nullable) Postal Code. Applicable if the tax rate is specific to postal code. Maximum length: 10. * `taxType` (_string_, required) Indicates the tax type, such as COUNTRY, CITY, or STATE. The following tax types are specific to the Vertex tax engine: PROVINCE, TERRITORY, APO, BOROUGH, FPO, LOCAL\_IMPROVEMENT\_DISTRICT, PARISH, SPECIAL\_PURPOSE\_DISTRICT, TOWNSHIP, TRADE\_BLOCK, and TRANSIT\_DISTRICT. Allowed values: `CLASS`, `CITY`, `DISTRICT`, `COUNTY`, `STATE`, `COUNTRY`, `ITEM_OVERRIDE`, `STORE_OVERRIDE`, `TRANSACTION_OVERRIDE`, `SPECIAL`, `PROVINCE`, `TERRITORY`, `APO`, `BOROUGH`, `FPO`, `LOCAL_IMPROVEMENT_DISTRICT`, `PARISH`, `SPECIAL_PURPOSE_DISTRICT`, `TOWNSHIP`, `TRADE_BLOCK`, `TRANSIT_DISTRICT`. * `chargeType` (_string_) Indicates if the charge is for tax or fee - TAX/FEE. Allowed values: `TAX`, `FEE`. Default: `TAX`. * `nonRefundable` (_boolean_) Specifies whether the charge (tax or fee) is refundable. Default: `false`. * `taxRate` (_number (float)_, required) % tax/vat rate. Example: `7.25`. * `taxClass` (_string_, required) The tax engine uses tax classes to apply specific tax rates to different product categories. Each product is linked to a tax class, which determines its applicable tax rate. Example: `Tax Class for Shoes and Clothing - PS0001`. * `taxCode` (_string_, required) Unique identifier for a tax rate, unique per item. * `taxAmount` (_number_, required) Total tax amount for the item corresponding to the item's tax rate. Example: `7.25`. * `perUnitTaxAmount` (_number_, required) Per unit tax amount of the item corresponding to the tax rate. Example: `1.25`. * `taxName` (_string_, required) Tax name, for example, Jurisdiction, MTA, BTW Hoog, or BTW Laag. Maximum length: 64. * `displayTaxName` (_string_, required) Display tax name, for example, Jurisdiction - 7.25%, BTW Hoog - 21%, or BTW Laag - 9%. * `corporateTaxId` (_string_, nullable) Merchant's Tax Identification Number. Example: `HST-74252RT0001`. * `printTaxName` (_string_, nullable) Print tax name for receipts. Includes merchant's tax identification number. For example: HST-74252RT0001 - 7.25%. * `externalTaxId` (_string_, nullable) Additional Tax Identifier for external systems. * `isExempt` (_boolean_, nullable) Indicates if tax is exempted. Default: `false`. * `taxDescription` (_string_, nullable) Tax Description text. * `jurisdiction` (_string_, nullable) Jurisdiction. Maximum length: 128. * `registrationNumber` (_string_, nullable) Registration Number. Maximum length: 128. * `externalId` (_string_, nullable) Identifies the current tax record in the external system. This is a record identifier, not a tax identifier. Used when an order is imported from an external system. Orders placed directly through POS don't populate this identifier. The external system provides this identifier and POS replays it during order posting for subsequent modifications such as returns. Maximum length: 128. # Active Data You can use [Active Data](https://help.salesforce.com/s/articleView?id=cc.b2c_active_merchandising.htm&type=5) in your [Progressive Web App (PWA) Kit](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/getting-started.md) site to: - Observe product performance and order metrics. - Set automated rules for merchandising (for example, search result sorting). Active Data works by collecting engagement data from shoppers browsing your site and interacting with products. This data is then aggregated into predefined dimensions that are stored and associated with each product in your catalog. Active Data also collects order metrics. The metrics in Active Data can be used to: - Analyze shopper on-site behavior so you can enhance the shopper experience and optimize product offerings to drive sales growth. - Configure site rules - like automated merchandising - in Business Manager. **note:** When a shopper opts out of [behavior tracking](https://developer.salesforce.com/docs/commerce/commerce-api/guide/slas-tracking-preferences.html), the Active Data for that shopper’s session isn't collected or reflected in Active Data. This applies regardless of whether you enabled tracking in Business Manager. ## Prerequisites To use Active Data: - To access the product impression and product view events, build your site using PWA Kit version 3.3.0 or later. If you have an earlier version that doesn't support Active Data, upgrade your project to use PWA Kit 3.3.0 or later. - If you haven't already done so, configure an ocapi proxy that points to your B2C Commerce instance. To see the Active Data metrics or attributes in Business Manager, you must use a production environment. See [Configure Managed Runtime Environments](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/proxying-requests.md#configure-managed-runtime-environments). - [Configure Business Manager](#configure-business-manager). ### Configure Business Manager #### Enable Data Collection Open a support case asking Salesforce Customer Support to enable data collection. #### Confirm that Sites are Online 1. Log into Business Manager. 2. Click App Launcher ![App Launcher](https://developer.salesforce.com/docs/media/shared/appLauncher.png) and then select **Administration > Sites > Manage Sites**. 3. Confirm that the status is **Online** for the sites for which you want to track metrics. Active Data is collected only for live (online) sites. #### Confirm that Tracking (Default for New Storefront Session) is Enabled 1. Log into Business Manager. 2. Click App Launcher ![App Launcher](https://developer.salesforce.com/docs/media/shared/appLauncher.png) and then select **Merchant Tools > Site Preferences > Privacy**. 3. Confirm that Tracking (Default for New Storefront Session) is set to **Enabled**. ## Supported Events This table identifies the events in a PWA Kit site that Active Data tracks. | Event | Description | PWA Kit Support | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | --------------- | | Product Impression | Counts how many times a product is shown in minimal detail on a page. Examples include search results, recommendations, and banners. | Yes | | Product View | Counts how many times a product is shown in great detail on a page. Examples include product detail and comparison pages. | Yes | | Order | Counts how many times a customer placed an order. | Yes | ## Install Active Data After completing the [prerequisites](#prerequisites), install Active Data by following these steps: 1. Run `npm run build` to rebuild the PWA Kit bundle. 2. Run `npm run push` to push the rebuilt bundle to Managed Runtime. ## Verify your Data **note:** Events from storefront interactions are collated nightly, so there’s latency between when events occur and when you see data appearing on the **Active Data** tab. Data collected from events on your storefront is automatically associated with the relevant products and aggregated at various time durations. To confirm that this data is flowing correctly: 1. Log into Business Manager. 2. Open your Product Catalog. 3. Open a product. 4. Click the **Active Data** tab to confirm that the traffic and conversion metrics in the sample data shown below are incrementing based on the time durations. ![Associated screenshot](https://developer.salesforce.com/docs/media/pwa-kit-managed-runtime/product-order-data3.png) ## Debug Metrics Issues If the metrics aren’t incrementing as expected, complete these debugging steps: - Ensure that you've waited for 24 hours after a [supported event](#supported-events) happened. - Inspect the network activity for the project and confirm that Active Data requests are returning a 200 status code. The URL for an Active Data request includes "\_Analytics-Start". If you completed the debugging steps and the metrics still aren’t incrementing, open a support case. ## Configure Sorting After you [Verify your Data](#verify-your-data), you can use [Active Data attributes](https://help.salesforce.com/s/articleView?id=cc.b2c_sorting_rules.htm&type=5) to power dynamic sort and search result sorting. To configure sorting, in Business Manager, click App Launcher ![App Launcher](https://developer.salesforce.com/docs/media/shared/appLauncher.png) and then select **Merchant Tools > Search > Sorting Rules**. For more information, see [Sorting Rules for B2C Commerce](https://help.salesforce.com/s/articleView?id=cc.b2c_sorting_rules.htm&type=5). ![Associated screenshot](https://developer.salesforce.com/docs/media/pwa-kit-managed-runtime/active-data-sorting2.png) ## Limitations Currently, Active Data in PWA Kit is limited to product-based use cases. Active Data as a mechanism for customer group segmentation isn’t supported. ## See Also - [Data and Analytics](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/data-and-analytics.md) # Add a Component That Fetches Data Extend a Storefront Next page to fetch data on the server and pass it to a React component. This guide builds on [Add a Page and a Component](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-customize.md) by adding a server-side loader that retrieves products from B2C Commerce and renders them in a new component. ## Prerequisites - A Storefront Next app created in Business Manager, which includes a SLAS client set up automatically for authorization to Shopper APIs. See [Create a Storefront in Business Manager with Local Code](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-quick-start-create-bm.md) or [Explore Storefront Next Code Locally](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-quick-start-create-sf.md). - A custom page created in [Add a Page and a Component](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-customize.md). - An Integrated Development Environment (IDE), such as VS Code, Cursor, or your favorite IDE. See [Download VS Code](https://code.visualstudio.com/download) or [Download Cursor](https://cursor.com/download). ## Steps 1. Create a `FeaturedProducts` component that renders a grid of products. a. In `{project-folder}/src/components`, create a folder named `featured-products`. b. In the new `featured-products` folder, create `index.tsx` with this code. ```tsx import { type ReactElement } from 'react'; export interface Product { productId: string; productName: string; priceMax?: number; image?: { link: string; alt: string }; } export interface FeaturedProductsProps { products: Product[]; title?: string; } export default function FeaturedProducts({ products, title = 'Featured Products' }: FeaturedProductsProps): ReactElement { return (

{title}

); } ``` `FeaturedProducts` accepts a `products` array and an optional `title`. It renders a responsive grid of product cards, each showing the product image, name, and price. 2. Add a loader to the custom page route to fetch products from B2C Commerce. Open `{project-folder}/src/routes/_app.custom-page.tsx` and replace its contents with this code. ```tsx import { type ReactElement } from 'react'; import { type LoaderFunctionArgs } from 'react-router'; import { createPage, type RouteComponentProps } from '@/components/create-page'; import CustomBanner from '@/components/custom-banner'; import FeaturedProducts, { type Product } from '@/components/featured-products'; import { createApiClients } from '@/lib/api-clients.server'; export async function loader({ context }: LoaderFunctionArgs) { const clients = createApiClients(context); const { data } = await clients.shopperSearch.productSearch({ params: { query: { q: 'dress', limit: 4, allImages: true, }, }, }); const products: Product[] = (data.hits ?? []).map((hit) => ({ productId: hit.productId, productName: hit.productName ?? '', priceMax: hit.priceMax, image: hit.image ? { link: hit.image.link, alt: hit.image.alt ?? hit.productName ?? '' } : undefined, })); return { products }; } export type CustomPageData = Awaited>; function CustomPageView({ loaderData }: RouteComponentProps): ReactElement { return (
); } export default createPage({ component: CustomPageView, }); ``` The `loader` function runs on the server before the page renders. `createApiClients(context)` returns all Shopper API clients configured for the current site. The `createApiClients` function is a template wrapper around the framework's `createCommerceApiClients` function. `createApiClients` reads site configuration, credentials, and locale from context and sets up authentication automatically so you don't need to pass those values manually. The loader calls the Shopper Search API to retrieve four products matching the query `dress` and destructures `data` from the response. The results are mapped to the `Product` type and returned as `loaderData`. `CustomPageView` receives `loaderData` as a prop and passes the products to `FeaturedProducts`. `CustomPageData` is derived directly from the loader's return type, so the component stays in sync with the data shape automatically. 3. In a terminal window, go to the storefront folder. ```bash cd {project-folder} ``` 4. Build the storefront and run the development server. ```bash pnpm run build pnpm run dev ``` 5. Navigate to `http://localhost:5173/custom-page`. The page now shows the banner followed by a grid of products fetched from B2C Commerce. ![Custom page shows the featured products that are fetched with the Shopper API](https://developer.salesforce.com/docs/media/sfnext/sfnext-product-grid.png '{"class": "image-xl"}') ## Next Steps - [Deploy Storefront Next to Managed Runtime](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-push-mrt-auto.md) ## See Also - [Data Fetching](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-data-loading.md) - [SCAPI Client](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-api-integration.md) # Add a Custom Component to Your Store Add a component to the canvas of your B2B or D2C store created with an LWR template. Optionally edit its properties. **note:** Before placing a custom component, assign a custom theme layout to the page in Experience Builder. 1. In the Visual Studio Code editor, open the Command Palette by pressing Ctrl+Shift+P (Windows) or Cmd+Shift+P (macOS). 2. Enter `SFDX`. 3. Select **SFDX: Open Default Org**. Your store opens in a separate browser. 4. In the left Navigation panel, click **Components**. The custom component displays under **Components** | **Custom Components**. 5. From the Custom area of the Lightning Components list, drag your Lightning web component to the page canvas. 6. To edit the component properties, select the component on the page canvas, and enter changes in the component property editor. To include properties that are editable when the component is selected in Experience Builder, open the .js-meta.xml file and add `{lightningCommunity__Default}` in targets and then define the properties in `targetConfigs`. ![Screenshot of component on page canvas in Experience Builder.](https://developer.salesforce.com/media/d2c-commerce/place_lwc_in_builder.png) # Add a Page and a Component Customize the storefront by adding a custom page and component. ## Prerequisites - Generate a Storefront Next app by following the steps in [Create a Storefront in Business Manager with Local Code](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-quick-start-create-bm.md) or [Explore Storefront Next Code Locally](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-quick-start-create-sf.md). - An Integrated Development Environment (IDE), such as VS Code, Cursor, or your favorite IDE. See [Download VS Code](https://code.visualstudio.com/download) or [Download Cursor](https://cursor.com/download). ## Steps 1. Create a component that displays a banner. Later, add this component to a new page. a. In `{project-folder}/src/components`, create a folder with the name `custom-banner`. b. In the new `custom-banner` folder, create the `index.tsx` file with this code. ```` import { type ReactElement } from 'react'; export interface CustomBannerProps { /** The title text to display in the banner */ title: string; /** Optional subtitle text */ subtitle?: string; /** Optional CSS class name for custom styling */ className?: string; } /** * CustomBanner component that displays a prominent title banner. * * @param props - The component props * @param props.title - The title text to display (required) * @param props.subtitle - Optional subtitle text * @param props.className - Optional CSS class name for custom styling * * @returns A React element containing the banner * * @example * ```tsx * * ``` * * @example * ```tsx * * ``` */ export default function CustomBanner({ title, subtitle, className }: CustomBannerProps): ReactElement { return (

{title}

{subtitle && (

{subtitle}

)}
); } ```` The `CustomBanner` component in `/custom-banner/index.tsx` renders a centered banner with a large title and optional subtitle. It accepts `title` (required), `subtitle` (optional), and `className` (optional) via `CustomBannerProps`. It uses Tailwind classes for responsive typography, primary theme colors, and spacing, and centers content within a max-width container. The component returns a React element with the title as an `

` and conditionally renders the subtitle as a paragraph, if provided. 2. Add a page that includes this new component. The new page is created as a route file. In `{project-folder}/src/routes`, create a route file with the name `_app.custom-page.tsx` and add this code. ``` import { type ReactElement } from 'react'; import { createPage, type RouteComponentProps } from '@/components/create-page'; import CustomBanner from '@/components/custom-banner'; /** * Data structure for the custom-page. * This page doesn't require any loader data, so we use an empty type. */ export type CustomPageData = Record; /** * Custom page view component that displays a banner with the title "Welcome to the Custom Collection!". * @returns JSX element representing the custom page */ // eslint-disable-next-line react-refresh/only-export-components function CustomPageView({}: RouteComponentProps): ReactElement { return (
); } /** * Custom page component with loading fallback. * This creates a page component that wraps the CustomPageView component with a loading fallback. * The createPage utility provides consistent loading states and error handling. * @returns Page component with custom page functionality and loading states */ // eslint-disable-next-line react-refresh/only-export-components export default createPage({ component: CustomPageView, }); ``` The `_app.custom-page.tsx` file defines a React Router page at `/custom-page`. The `_app.` prefix in the file name causes the React Router to nest your custom page route under the `_app.tsx` layout. That layout renders the header, your page content, and the footer. It exports a default page via `createPage`, which wraps `CustomPageView` with Suspense and loading handling. `CustomPageView` renders the `CustomBanner` component inside a container with bottom padding. The page uses `CustomPageData`, an empty record. It doesn't require loading data so it renders immediately without async data fetching. 3. In a terminal window, go to the storefront folder. ``` cd {project-folder} ``` 4. If you haven't already, install Storefront Next project dependencies that are specified in `package.json`. ``` pnpm install ``` 5. Build the storefront and then run the development server. ``` pnpm run build pnpm run dev ``` 6. Navigate to the new page that you created by appending `/custom-page` to the storefront URL. For example, go to this default URL: `http://localhost:5173/MarketStreet/en-US/custom-page`. The new page shows the banner with a title. ![New page in Storefront Next displays the products by category](https://developer.salesforce.com/docs/media/sfnext/sfnext-new-page2.png '{"class": "image-xl"}') ## Next Steps - [Add a Component That Fetches Data](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-customize-data.md) - [Deploy Storefront Next to Managed Runtime](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-push-mrt-auto.md) # Add to Cart The workflow begins when a user adds items to the cart from either the Product Detail Page (PDP) or the Product List Page (PLP). The system then determines whether the user is initiating a new checkout or resuming an existing one and triggers the appropriate logic. **Endpoint:** POST [`/commerce/carts/{activeCartOrId}/cart-items`](https://developer.salesforce.com/docs/commerce/salesforce-commerce/references/comm-cart-ref?meta=postCartItemCollection) # AEO and GEO Storefront Next ships built-in support for Answer Engine Optimization (AEO) and Generative Engine Optimization (GEO) on the Product Detail Page (PDP) and Product Listing Page (PLP). **AEO (Answer Engine Optimization)** makes product and category facts machine-readable and consistent so search engines and crawlers can parse and enrich results (for example, rich product snippets), and so assistants, voice search, and other answer-style surfaces can cite accurate who, what, how much, and in-stock information without guessing from unstructured HTML alone. **GEO (Generative Engine Optimization)** supplies clear entity structure (products, offers, collections, breadcrumbs) and aligned metadata (titles, descriptions, canonical URLs) so generative and retrieval-augmented systems can ground responses in your storefront's authoritative data. AEO and GEO have these benefits: - Rich results in search engines: Structured `Product` data enables Google rich snippets: price, availability, and review stars inline in search results. - AI citation and grounding: Clear entity graphs (products, offers, breadcrumbs) give Large Language Models (LLMs) and Retrieval-Augmented Generation (RAG) systems verifiable facts to cite rather than fabricated summaries. - Voice and assistant surfaces: Answer engines can resolve who, what, how much, and in-stock queries directly from your structured data. - Multisite consistency: URL helpers respect site/locale path prefixes and proxy headers, so structured data links are correct across all your storefronts without manual configuration. Managed Runtime and proxy hosts do not leak internal origins into structured data. - Zero page-breakage risk: Schema generation runs in a deferred Promise. If it fails, the page renders normally with no script tag. ## How It Works Two technical mechanisms work together: JSON-LD and `SeoMeta`. | Mechanism | Purpose | Where | | ----------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------- | | JSON-LD (` ``` **Flow:** - Allow AlphaNumerics and some Special characters - Slash escape certain illegal characters - Replace special JavaScript characters with their Hex Encoded equivalents prepended with \\x for character codes under 128 and \\u for character codes over 128 **Parameters:** - input - untrusted input to be encoded, if necessary **Returns:** - a properly encoded string for the given input --- ### forJavaScriptInHTML(String) - static forJavaScriptInHTML(input: [String](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-script-api/TopLevel.String.md)): [String](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-script-api/TopLevel.String.md) - : Encodes a given input for use in JavaScript inside an HTML context. This method takes the UNION of allowed characters among the other contexts, so may be more imprecise that the more specific contexts. Generally, this method is preferred unless you specifically understand the context in which untrusted data will be output. **Example Usage:** ```