Webviews embedded in the cart of Associate App receive a live snapshot of the cart from the app and can post messages back to trigger native app actions. Use them to surface information next to the items the associate is selling — for example a loyalty enrollment prompt, available coupons from an external promotion engine, warranty validation of scanned serial numbers, or brand-specific purchase advisories.
Available from Associate App v1.83.0
Cart webview slots and the
meta.allowExpansionoption are available from Associate App version 1.83.0. Thecart.customerobject is available from version 1.85.0.
Further reading
Adding webview customizations to the product detail page — The same mechanism on the PDP, including the full postMessage reference.
Adding webview customizations using external authentication — Setting up external identity providers and reading context in any Associate App webview.
Slots
Two slots are available in the cart. Configure which slot the webview appears via the Customization Configuration API.
Slot ID | Position |
|---|---|
| Top of the cart, below the customer and address cards, above the item list |
| Bottom of the cart, above the totals summary |
One slot can host multiple customizations, and they are rendered in configuration order.
Note
Cart slots only render when the cart contains at least one item. An empty cart shows the standard empty-cart screen without any customizations. When the cart has items, an enabled inline webview always renders and reserves its configured
meta.height. Design your page to display a sensible empty or collapsed state when it has nothing to display.
Configuring a cart webview
Send a PUT request to the Customization Configuration API. The customization_id in the path is provided by you, to generate a UUID.
{
"enabled": true,
"type": "webview",
"slot": "cart_top",
"label": "Loyalty",
"title": "Loyalty",
"caption": "Open",
"url": "https://your-webview-url.com/loyalty",
"meta": {
"inline": true,
"scrollEnabled": false,
"height": 150
}
}Display modes
Webviews can be configured as:
Full-screen (default) — Opens as a new screen when the associate taps a button in the cart
Inline — Rendered directly within the cart. Set
meta.inline: truein the configuration, along withmeta.height(pixels), and optionallymeta.scrollEnabled,meta.scrollXEnabled,meta.scrollYEnabled.
Available from Associate App v1.83.0
An inline webview displays a header with the configured title and a button (labeled with caption) that opens the same page in full-screen mode. To hide that button and keep the webview inline-only, set meta.allowExpansion: false. The allowExpansion property defaults to true. Only an explicit false value hides the button.
For the full display mode reference and code examples, see Adding webview customizations to the product detail page.
{
"meta": {
"inline": true,
"height": 180,
"allowExpansion": false
}
}Delivering context
When the app renders the webview, it adds a base64-encoded JSON payload to the URL as a fragment (#):
https://your-webview-url.com/page#<base64-encoded-JSON>Decode and parse it on the client side:
const raw = decodeURIComponent(window.location.hash.slice(1))
const context = JSON.parse(atob(raw))The context object has the following payload:
{
contextProps: {
formData: {
// --- Cart snapshot ---
cart: {
items: Array<{
productId: string
itemIds: string[] // Cart line item IDs — use with the Cart API
quantity: number
unitPrice: number
itemDiscount: number
externalIdentifiers: Array<{
serialNumber: string | null
epc: string | null
}>
}>
grandTotal: number
subtotal: number
discountTotal: number
taxTotal: number
remainingAmount: number
currency: string | null
coupons: string[] // Applied coupon codes
customerId: string | null // Customer assigned to the cart, if any
// Present only when a customer is attached and their profile has loaded
customer?: {
id: string // Same value as customerId
displayId: string | null // Human-readable customer number, e.g. "C-001"
extendedAttributes: { // Profile extended attributes, flattened
[name: string]: string // e.g. { "loyalty_id": "L-123" }
}
}
}
// --- Session fields (always present) ---
associateId: string // ID of the logged-in associate
storeId: string // ID of the current store
cartId: string // Active cart ID
}
}
auth: {
accessToken: string // NewStore access token — use to call NewStore APIs
}
externalIdentities: Array<{
identityProvider: string
identity: string
}>
theme: object // App color, spacing, and typography tokens
dimensions: { top: number, bottom: number, left: number, right: number }
enums: { DocumentType: object } // Print document type constants
securityToken: string // One-time token generated per webview session
}A page that only needs the customer's identity should use customerId. It is available from the first render and is never absent when a customer is on the cart. The customer object is for pages that need the profile's extended attributes, and is only present once the customer's profile has loaded.
A customer has two identifiers. id is the internal identifier NewStore APIs expect, and it is the same value as customerId on the cart. displayId is the short, human-readable customer number the app shows to associates, for example C-001. Use id for API calls and displayId when you want to show the customer their own reference.
extendedAttributes contains the customer profile's extended attributes as a flat map of name to value. Retailers typically use these to carry integration keys such as a loyalty membership number. Reading them here saves your page a separate call to the customer profile API.
Privacy
The payload contains no customer contact details: no name, email address, phone number, or postal address. The
customerobject is limited to identifiers and the profile's extended attributes. If you need anything beyond that, usecustomer.idtogether withauth.accessTokento fetch the customer profile from NewStore APIs.
Timing
customeris populated from the customer profile, which the app may still be loading when your page first renders. When that happens, the object is absent from the initial fragment and arrives moments later in ahashchangeupdate. Drive your rendering from the newest fragment rather than reading the hash only once.customerIdis available immediately in both cases.
Reacting to cart changes
The app re-serializes the payload whenever the cart changes, such as when the associate scans another item or applies a discount, and when the attached customer's profile finishes loading. Read the fragment at load time and listen for the hashchange event:
function render() {
const context = getWebViewContext()
if (!context) return
const { cart } = context.contextProps.formData
// update your UI from the fresh snapshot
}
window.addEventListener('hashchange', render)
render()Whether an update arrives as a soft navigation or a page reload is platform-dependent. Handle both by keeping the render() property idempotent.
Making a call to NewStore APIs
Use auth.accessToken to make authenticated calls to NewStore APIs. For example, attaching a warranty add-on to an item already in the cart via the Cart Line Item API:
const { auth, contextProps: { formData } } = context
const baseUrl = `https://<tenant>.p.newstore.net/v0/d`
await fetch(`${baseUrl}/checkout/carts/${formData.cartId}/items/${formData.cart.items[0].itemIds[0]}/add-ons`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${auth.accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
product_id: '<warranty-product-id>',
fulfillment: 'IN_STORE_HANDOVER',
price: { source: 'INTERNAL', source_id: '<pricebook-id>' },
}),
})After the webview modifies the cart, trigger Associate App to reload the cart view so the associate sees the change immediately:
window.open(`com.newstore.associate-one://cart/load?cartId=${formData.cartId}`)Posting messages back to the app
The webview can trigger native app actions via window.ReactNativeWebView.postMessage. The same messages are available as on the product detail page. See the full postMessage reference:
// Close a full-screen webview
window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'close' }))
// Open an https:// URL in the system browser
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'openExternalUrl',
message: { url: 'https://...' }
}))
// Print a document
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'print',
message: { resources: [{ documentType: context.enums.DocumentType.salesReceipt, url: 'https://...' }] }
}))What cart webviews cannot do
A cart webview is informational and additive: it can display content and call NewStore APIs, but it cannot block the checkout flow or prevent the associate from completing a sale. If your use case requires enforcement, such as for legally binding quantity restrictions per brand, you must implement it in backend order validation in addition to the in-cart advisory.
Inline webviews require network connectivity and do not render meaningful content when the device is offline.
Example: loyalty enrollment banner
A minimal cart_top page that shows how many points the current purchase would earn:
function getWebViewContext() {
const hash = window.location.hash.slice(1)
if (!hash) return null
try {
return JSON.parse(atob(decodeURIComponent(hash)))
} catch {
return null
}
}
function render() {
const context = getWebViewContext()
if (!context) return
const { cart } = context.contextProps.formData
const points = Math.round((cart.grandTotal || 0) * 10)
const loyaltyId = cart.customer?.extendedAttributes?.loyalty_id
document.getElementById('root').textContent = loyaltyId
? `Member ${loyaltyId} earns ${points} points on this purchase.`
: `Add the customer to the cart to earn ${points} points on this purchase.`
}
window.addEventListener('hashchange', render)
render()The optional chaining on cart.customer keeps the page safe during the window before the customer's profile has loaded.