Build a cart validation UI extension
This feature is available only to approved apps and is subject to change.
This feature is available only to approved apps and is subject to change.
With POS intercepts, you can add checks before checkout or a cash payment. Your extension returns validation findings, which Shopify Point of Sale (POS) displays as required or recommended actions. A resolution target renders the interface where staff address those findings. When staff try to proceed, Shopify POS checks the cart or payment again to see whether any findings remain.
In this tutorial, you'll build a demo that requires a serial number for every item in the cart. Staff enter a serial number, and the extension checks it against a demo registry when they proceed. They can also add recommended inspection notes. The required serial check and recommended notes apply to the same cart line.
The demo calls JSONPlaceholder, a public test service. DEMO-4 maps to a fixture that passes, and every other value maps to one that fails. The entered serial isn't sent.
Anchor to What you'll learnWhat you'll learn
In this tutorial, you'll learn how to do the following tasks:
- Generate a POS intercept extension.
- Configure a background target and a resolution target.
- Return required and recommended findings for individual cart lines.
- Make an HTTP request from the resolution save handler.
- Save line item properties before Shopify POS revalidates the cart.
- Test the validation and resolution flow in Shopify POS.
Requirements
Create a Shopify app, or use an existing app. Run the generation commands from the app root, which contains shopify.app.toml. Use Shopify CLI 4.8.0 or later. The template includes @shopify/ui-extensions@2026.10.0-rc.13, preact, and @preact/signals.
For an extension-only app, see Create an extension-only app.
Your app's API client must be approved for POS intercepts before you can test with shopify app dev. Creating a new app or installing Shopify CLI and the SDK doesn't grant access.
Anchor to Generate the extensionGenerate the extension
Generate a cart intercept extension from the template. If you've already generated one, then skip this step and use its existing directory.
From your Shopify app root, download the cart intercept template catalog:
Terminal
Generate the extension using that catalog and template branch:
Terminal
The custom catalog applies only to this command. Don't pass --flavor. The template uses Preact automatically.
Shopify CLI creates extensions/authenticity-check/ with a background module, a cart resolution module, and their paired targets. The following steps replace the starter example with the authenticity check demo. All file paths in those steps are relative to this extension directory. Keep its generated uid.
Anchor to Configure interceptsConfigure intercepts
Update extensions/authenticity-check/shopify.extension.toml, or the configuration file in your existing cart intercept extension. The template already declares the paired targets and enables offline support. Keep the existing name, handle, and generated uid. Point the pos.app.ready.data entry at ./src/Background.js and the resolution entry at ./src/CartResolution.jsx. Later steps create those modules. Intercepts require api_version = "2026-10" and runs_offline = true under [extensions.supported_features].
Add the cart interceptor to the pos.app.ready.data targeting entry. Set blocking = true because the callback can return a Required finding.
Declare the matching cart resolution target. Every intercepted event needs one.
Shopify CLI checks the TOML structure. When you deploy, Shopify also checks the event names, API version, and matching resolution targets. For all supported settings, see Configuration rules.
Anchor to Define the demo rulesDefine the demo rules
Create src/authenticity.js for the rules and registry lookup shared by the interceptor and resolution target.
Define three public line item properties: Serial number, Demo checked serial, and Inspection notes.
A line needs a serial check until Demo checked serial matches its Serial number. Changing the serial reopens the check. A line needs notes when Inspection notes is blank.
Check the serial against the demo registry. DEMO-4 requests https://jsonplaceholder.typicode.com/todos/4, whose fixture has completed: true. Any other value requests /todos/1, whose fixture has completed: false. An HTTP error throws, so it never counts as a pass.
Anchor to Register the interceptorRegister the interceptor
Create src/Background.js to replace the template's src/Background.jsx. Register a callback for the cartvalidations event from the target's default entry function. After the new file is in place, remove src/Background.jsx. The configuration now points at src/Background.js.
Register the callback. It returns an empty operations array for an empty cart or a cart in which every line passes both checks. An empty array adds no findings.
The callback must return {operations: [...]} synchronously, not a Promise. The resolution target performs the asynchronous registry lookup and saves the result for the callback to read on the next validation event.
Return an ERROR finding when a line needs a serial check.
Each finding uses the line from the current event and targets $.cart.lineItems['${line.uuid}']. Handles must be unique within one result, so add the line's uuid to each handle. Shopify POS labels an ERROR finding Required and blocks checkout only when the interceptor declares blocking = true and the store allows your app to block checkout. Otherwise, Shopify POS labels it Recommended.
Return an independent WARNING finding when the same line has blank inspection notes. Shopify POS labels the warning Recommended, so it doesn't block checkout.
Each line gets its own findings, so resolving one item doesn't clear another. For the supported fields and target values, see Validation operations.
Anchor to Build the resolution interfaceBuild the resolution interface
Staff can open your interface from an item-level finding. Your extension renders the serial and notes fields. Shopify POS owns the item indicator, validation banner, and controls for saving or proceeding.
Replace the starter src/CartResolution.jsx with the interface shown in the code panel. Use signals for the selected line, serial, notes, and serial error. The resolution event contains the cart, not the finding that staff selected, so pick a line from the cart and load its saved properties. Pick the first line that needs a serial check, and then the first line missing notes, so staff reach required work before recommended work. Subscribe to the event and pick the line again each time it changes. Clear the serial error each time the target opens, not when the event changes, so an earlier rejection doesn't appear on another line and a new rejection stays visible after Shopify POS revalidates.
Register the save handler before rendering the interface. Shopify POS keeps only the most recently registered handler, so register it once when the target starts.
When staff proceed, shopify.resolution.onSave() checks the serial and saves the line properties. Shopify POS waits for the handler before it revalidates the cart.
The handler saves the serial and notes before the lookup. Because the interceptor passes a line only when Demo checked serial matches Serial number, a replaced serial can't pass on an old check, even if Shopify POS stops waiting after 10 seconds. After the lookup, a pass sets Demo checked serial to the serial and a rejection clears it. A failed lookup leaves it unchanged, so the new serial stays unchecked.
Render the line title, the serial field with any registry error, and the optional notes. Shopify POS provides the controls for proceeding.
When Shopify POS revalidates, the interceptor reads the saved line item properties. The findings, not the save handler's return value, decide whether staff can proceed. For the APIs and components that you can use, see the cart resolution target and supported components.
Anchor to Test your extensionTest your extension
Use a dev extension to test local changes. Use the dev-store app preview to test saved POS editor settings.
Anchor to Build the extensionBuild the extension
From your app root, run shopify app build. Shopify CLI bundles the extension and reports missing modules or imports. Unlike shopify app dev, this check doesn't require intercept preview approval.
Anchor to Test with a dev extensionTest with a dev extension
From your app root, run shopify app dev. Follow Connect your app and preview it in POS to open the Dev Console and load the dev extension on your device through the mobile preview link. Local code changes update the dev extension without a separate deploy.
The dev extension doesn't use your saved POS editor settings, so its Required and Recommended labels don't confirm those settings.
- Start a fresh cart and add one unit each of two products. Use a quantity of
1for this test because the demo checks one serial per cart line, not per unit. The demo checks every cart line and doesn't filter by product or quantity. Check that each line has both the serial check and notes findings. - Open the resolution interface. Check that it shows the first line, and that the serial is required and the inspection notes are optional. The interface picks the line from the cart rather than from the finding you opened, because the resolution event contains the cart but not the selected finding.
- Leave the notes blank, enter
DEMO-4, and proceed. Check that the first line's Required finding clears and its Recommended notes finding remains. - Open the interface again. Check that it shows the second line, because serial checks come before notes. Enter
DEMO-4, add inspection notes, and proceed. Check that both of the second line's findings clear. - Open the interface again. Check that it shows the first line, add inspection notes, and proceed. Check that all findings clear.
- Check that the line item properties contain
Serial numberandDemo checked serialvalues ofDEMO-4, along with the inspection notes. - In a fresh cart, enter
DEMO-1, add inspection notes, and proceed. Check that the serial field shows the rejection,Demo checked serialis empty, and the Required finding remains. - Disconnect the device from the network, enter
DEMO-4, and proceed. Check that the serial field reports the failed lookup and the Required finding remains. Reconnect before continuing.
Anchor to Configure the app preview in the POS editorConfigure the app preview in the POS editor
shopify app dev also builds and uploads an app preview on your dev store. This preview uses uploaded extension code instead of the device's live dev extension.
- Keep
shopify app devrunning, or rerun it from your app root. After your latest changes, wait for Shopify CLI to report that the dev preview is ready or updated. - On your device, open the POS Dev Console and select Remove dev extensions. Don't open the mobile preview link again during this pass because it reconnects the dev extension.
- Keep the dev-store app preview. Don't run
shopify app dev cleanor select Clean dev preview, which would remove it and restore the released app version. - From the Shopify admin, go to Sales channels > Point of Sale.
- Under Customize the in-store experience, click Edit Point of Sale. If this section isn't displayed, then use the alternate settings route.
- Click the apps icon in the editor menu bar. Under Apps for Point of Sale, find your app, and select its cart validation extension. For navigation help, see Activate a POS app extension.
- Turn on Activate on. The extension lists Cart, checkout as its intercept surface.
- Turn on Allow app to block checkout.
- Leave Block checkout if app experiences a problem off for the normal validation checks.
- Click Save, and then close and reopen the extension's configuration to check that the settings persist.
Click Save after every editor change, and reopen the settings to check persistence before testing the result in Shopify POS.
Use these configurations for the POS checks below:
- Turn on Activate on and Allow app to block checkout to test blocking.
- Keep Activate on on and turn off Allow app to block checkout to test recommendations.
- Turn off Activate on to test the inactive extension.
After testing with the extension off, turn Activate on and Allow app to block checkout back on, then click Save.
Anchor to Test the app preview in POSTest the app preview in POS
Start with the extension active and allowed to block checkout. Return to Shopify POS with the device dev extension still disconnected, and run these checks:
- Add one product with blank properties, and check that the serial finding is Required and the notes finding is Recommended.
- Use
DEMO-4to clear the Required finding. Leave the notes blank, and check that staff can proceed with the Recommended finding still present. - With Allow app to block checkout off, start a fresh cart with one product. Check that the serial finding is Recommended instead of Required and doesn't block staff from proceeding.
- With Activate on off, start another fresh cart. Check that this app doesn't return cart findings.
- With both settings back on, repeat the first check.
Troubleshooting
Anchor to The event isn't supportedThe event isn't supported
Use cartvalidations or paymentvalidations.
Anchor to The event is supported but isn't configuredThe event is supported but isn't configured
Add the event under pos.app.ready.data, and add its matching resolution target.
Anchor to An interceptor is already registeredAn interceptor is already registered
Register only one callback for that event in each extension instance, or call the existing callback's unregister function before replacing it.
Anchor to The interceptor returned a PromiseThe interceptor returned a Promise
PromiseReturn {operations: [...]} synchronously. Perform the HTTP lookup in the resolution target, and save its result before Shopify POS revalidates.
Anchor to Learn moreLearn more
Require staff to confirm a cash count before Shopify POS accepts a large cash payment.Intercept API
Explore callback types, validation operations, configuration rules, and when validation runs.Resolution targets
Explore the cart and payment resolution targets, and the APIs and components that they support.Resolution API
Read event data, save pending work, and see what Shopify POS does when staff proceed.