--- title: Buyer Journey API description: >- Intercept and validate the buyer's progress through checkout. Block navigation with field-level or page-level errors when conditions aren't met. api_version: 2026-04 api_name: checkout-ui-extensions source_url: html: >- https://shopify.dev/docs/api/checkout-ui-extensions/latest/target-apis/checkout-apis/buyer-journey-api md: >- https://shopify.dev/docs/api/checkout-ui-extensions/latest/target-apis/checkout-apis/buyer-journey-api.md --- # Buyer Journey API The Buyer Journey API lets your extension intercept and validate the buyer's progress through checkout. Use this API to block checkout navigation with targeted error messages, check the current journey step, and enforce custom validation rules. **Blocking requires a capability:** To block the buyer journey, you must declare the [`block_progress`](https://shopify.dev/docs/apps/build/checkout/capabilities#block-progress) capability in your extension's `shopify.extension.toml` configuration. ### Use cases * **Validate a checkout field**: Intercept progress and display an error underneath a specific field, such as the shipping country. * **Block progress at the page level**: Prevent the buyer from continuing and show a page-level error when a condition isn't met. * **Enforce item quantity limits**: Block progress from a cart line item extension when a product exceeds its purchase limit. ### Support Targets (31) ### Supported targets * [purchase.​checkout.​actions.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/navigation#navigation-target) * [purchase.​checkout.​block.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/block#block-target) * [purchase.​checkout.​cart-line-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#line-item-targets) * [purchase.​checkout.​cart-line-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#checkout-cart-line-list-) * purchase.​checkout.​chat.​render * [purchase.​checkout.​contact.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/information#information-target) * [purchase.​checkout.​delivery-address.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#render-after-delivery-address-) * [purchase.​checkout.​delivery-address.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#delivery-address-targets) * [purchase.​checkout.​footer.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/footer#footer-target) * [purchase.​checkout.​header.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/header#header-target) * [purchase.​checkout.​payment-method-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/payment#render-after-payment-methods-) * [purchase.​checkout.​payment-method-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/payment#payment-targets) * [purchase.​checkout.​pickup-location-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/local-pickup#render-after-pickup-locations-) * [purchase.​checkout.​pickup-location-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/local-pickup#location-list-targets) * [purchase.​checkout.​pickup-location-option-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/local-pickup#location-option-item-target) * [purchase.​checkout.​pickup-point-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/pickup-points#render-after-pickup-points-) * [purchase.​checkout.​pickup-point-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/pickup-points#pickup-points-targets) * [purchase.​checkout.​reductions.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#checkout-reductions-after-) * [purchase.​checkout.​reductions.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/order-summary#reductions-targets) * [purchase.​checkout.​shipping-option-item.​details.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#shipping-option-item-targets) * [purchase.​checkout.​shipping-option-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#render-after-shipping-option-) * [purchase.​checkout.​shipping-option-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#render-after-shipping-options-) * [purchase.​checkout.​shipping-option-list.​render-before](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/checkout/shipping#shipping-option-list-targets) * [purchase.​thank-you.​announcement.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/announcement#thank-you-announcement-) * [purchase.​thank-you.​block.​render](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/block#block-target) * [purchase.​thank-you.​cart-line-item.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/order-summary#line-item-targets) * [purchase.​thank-you.​cart-line-list.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/order-summary#thank-you-cart-line-list-) * purchase.​thank-you.​chat.​render * [purchase.​thank-you.​customer-information.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/information#information-target) * [purchase.​thank-you.​footer.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/footer#footer-target) * [purchase.​thank-you.​header.​render-after](https://shopify.dev/docs/api/checkout-ui-extensions/2026-04/targets/thank-you/header#header-target) ### Properties The [`shopify` global object](https://shopify.dev/docs/api/checkout-ui-extensions/latest#target-apis-define-what-your-extension-does) provides buyer journey data for the current checkout. Access the following properties on `shopify` to read journey state and intercept navigation. Available to `purchase` extension targets. * **buyerJourney** **BuyerJourney** **required** Provides details on the buyer's progression through the checkout and lets you intercept navigation to validate data before the buyer continues. Refer to [buyer journey](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/buyer-journey#examples) examples for more information. ### BuyerJourney Provides details on the buyer's progression through the checkout. * activeStep The step of checkout the buyer is currently on. The value is \`undefined\` if the current step can't be determined. ```ts SubscribableSignalLike ``` * completed Whether the buyer has completed submitting their order. When \`true\`, the buyer is on the order status page after submitting payment. When \`false\`, the buyer is still in the checkout flow. ```ts SubscribableSignalLike ``` * intercept Installs a function for intercepting and preventing progress on checkout. This returns a promise that resolves to a teardown function. Calling the teardown function removes the interceptor. To block checkout progress, you must set the \[block\_progress]\(/docs/api/checkout-ui-extensions/latest/configuration#block-progress) capability in your extension's configuration. If you do, then you're expected to inform the buyer why navigation was blocked, either by passing validation errors to the checkout UI or rendering the errors in your extension. If the merchant hasn't allowed your extension to block checkout progress, show a warning in the \[checkout editor]\(/docs/apps/build/checkout/test-checkout-ui-extensions#test-the-extension-in-the-checkout-editor). ```ts (interceptor: Interceptor) => Promise<() => void> ``` * steps All possible steps the buyer can take to complete checkout. These steps vary depending on whether the checkout is one-page or three-page, and on the shop's configuration. ```ts SubscribableSignalLike ``` ### SubscribableSignalLike Represents a reactive signal interface that provides both immediate value access and subscription-based updates. Enables real-time synchronization with changing data through the observer pattern. This interface extends \`ReadonlySignalLike\` with deprecated fields that are still supported for backwards compatibility. * current The current value of the signal. Equivalent to \`.value\`, accessing this property subscribes to changes when used in a reactive context. ```ts T ``` * destroy Cleans up the subscription and releases any resources held by this signal. After calling \`destroy()\`, the signal stops receiving updates from the main thread. ```ts () => Promise ``` * subscribe Subscribes to value changes and calls the provided function whenever the value updates. Returns an unsubscribe function to clean up the subscription. Use to automatically react to changes in the signal's value. ```ts (fn: (value: T) => void) => () => void ``` * value The current value of the signal. This property provides immediate access to the current value without requiring subscription setup. Use for one-time value checks or initial setup. ```ts T ``` ### BuyerJourneyStepReference What step of checkout the buyer is currently on. * handle The handle identifying which step the buyer is on, such as \`'information'\`, \`'shipping'\`, or \`'payment'\`. See \`BuyerJourneyStepHandle\` for all values. ```ts BuyerJourneyStepHandle ``` ### BuyerJourneyStepHandle \| handle | Description | |---|---| | \`cart\` | The cart page. | | \`checkout\` | A one-page checkout, including Shop Pay. | | \`information\` | The contact information step of a three-page checkout. | | \`shipping\` | The shipping step of a three-page checkout. | | \`payment\` | The payment step of a three-page checkout. | | \`review\` | The step after payment where the buyer confirms the purchase. Not all shops are configured to have a review step. | | \`thank-you\` | The page displayed after the purchase, thanking the buyer. | | \`unknown\` | An unknown step in the buyer journey. | ```ts 'cart' | 'checkout' | 'information' | 'shipping' | 'payment' | 'review' | 'thank-you' | 'unknown' ``` ### Interceptor A function for intercepting and preventing navigation on checkout. You can block navigation by returning an object with \`{behavior: 'block', reason: 'your reason here', errors?: ValidationError\[]}\`. If you do, then you're expected to also update some part of your UI to reflect the reason why navigation was blocked, either by targeting checkout UI fields, passing errors to the page level, or rendering the errors in your extension. * interceptorProps ```ts InterceptorProps ``` returns ```ts InterceptorRequest | Promise ``` ### InterceptorProps * canBlockProgress Whether the interceptor can block the buyer's progress through checkout. When \`true\`, the merchant has granted your extension the \`block\_progress\` capability. When \`false\`, you can still validate but can't prevent the buyer from continuing. ```ts boolean ``` ### InterceptorRequest ```ts InterceptorRequestAllow | InterceptorRequestBlock ``` ### InterceptorRequestAllow * behavior Indicates that the interceptor allows the buyer's journey to continue. ```ts 'allow' ``` * perform This callback is called when all interceptors finish. We recommend setting errors or reasons for blocking at this stage, so that all the errors in the UI show up at once. Runs after all intercept results are collected. Use it for local state updates such as setting an error flag. By the time it runs, the navigation decision is final, so blocking logic belongs in the intercept handler itself, not here. ```ts (result: InterceptorResult) => void | Promise ``` ### InterceptorResult ```ts InterceptorResultAllow | InterceptorResultBlock ``` ### InterceptorResultAllow * behavior Indicates that the buyer was allowed to progress through checkout. ```ts 'allow' ``` ### InterceptorResultBlock * behavior Indicates that some part of the checkout UI intercepted and prevented the buyer's progress. The buyer typically needs to take some action to resolve this issue and to move on to the next step. ```ts 'block' ``` ### InterceptorRequestBlock * behavior Indicates that the interceptor blocks the buyer's journey from continuing. ```ts 'block' ``` * errors Used to pass errors to the checkout UI, outside your extension's UI boundaries. ```ts ValidationError[] ``` * perform This callback is called when all interceptors finish. We recommend setting errors or reasons for blocking at this stage, so that all the errors in the UI show up at once. Runs after all intercept results are collected. Use it for local state updates such as setting an error flag. By the time it runs, the navigation decision is final, so blocking logic belongs in the intercept handler itself, not here. ```ts (result: InterceptorResult) => void | Promise ``` * reason The reason for blocking the interceptor request. This value isn't presented to the buyer, so it doesn't need to be localized. The value is used only for Shopify's own internal debugging and metrics. ```ts string ``` ### ValidationError * message The error message to display to the buyer. Use this to explain what went wrong and how to fix it. ```ts string ``` * target The checkout UI field that the error is associated with. When provided, checkout highlights the matching field so the buyer knows where to fix the issue. The value is \`undefined\` if the error isn't tied to a specific field. ```ts string ``` ### BuyerJourneyStep * disabled Whether this step is disabled. When \`true\`, the buyer hasn't reached this step yet and can't navigate to it. When \`false\`, the step is accessible. For example, if the buyer hasn't reached the \`shipping\` step yet, then \`shipping\` is disabled. ```ts boolean ``` * handle The handle that uniquely identifies the buyer journey step, such as \`'information'\`, \`'shipping'\`, or \`'payment'\`. ```ts BuyerJourneyStepHandle ``` * label The localized label of the buyer journey step, suitable for rendering in navigation UI. ```ts string ``` * to The URL of the buyer journey step, using the \`shopify:\` protocol. ```ts string ``` ### Available Preact hooks The following Preact hooks provide a convenience wrapper that makes it easier to perform common tasks without writing your own logic. ## use​Buyer​Journey​Intercept(**[interceptor](#usebuyerjourneyinterceptgeneratedtype-propertydetail-interceptor)**​) Installs a function for intercepting and preventing progress on checkout. To block checkout progress, you must set the [`block_progress`](https://shopify.dev/docs/apps/build/checkout/capabilities#block-progress) capability in your extension's configuration. If you do, then you're expected to inform the buyer why navigation was blocked, either by passing validation errors to the checkout UI or rendering the errors in your extension. `useBuyerJourneyIntercept()` should be called at the top level of the extension, not within an embedded or child component, to avoid errors should the child component get destroyed. It is good practice to show a warning in the checkout and accounts editor when the merchant has not given permission for your extension to block checkout progress. ### Parameters * **interceptor** **Interceptor** **required** ### Returns**void** Examples ### Examples * #### ##### Description Intercept checkout progress and display an error underneath a specific field. This example uses \`useBuyerJourneyIntercept\` to block the buyer when the shipping country isn't Canada, targeting the \`countryCode\` field with an inline error. ##### jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import { useBuyerJourneyIntercept, useExtensionCapability, } from '@shopify/ui-extensions/checkout/preact'; export default function extension() { render(, document.body); } function Extension() { const editorType = shopify.extension.editor?.type; const blockProgressGranted = useExtensionCapability('block_progress'); useBuyerJourneyIntercept( ({canBlockProgress}) => { return canBlockProgress && shopify.shippingAddress?.value ?.countryCode && shopify.shippingAddress?.value .countryCode !== 'CA' ? { behavior: 'block', reason: 'Invalid shipping country', errors: [ { message: 'Sorry, we can only ship to Canada', // Show an error underneath the country code field target: '$.cart.deliveryGroups[0].deliveryAddress.countryCode', }, { // In addition, show an error at the page level message: 'Please use a different address.', }, ], } : { behavior: 'allow', }; }, ); return ( <> {editorType === 'checkout' && !blockProgressGranted ? ( To allow this app to block checkout, enable this behavior in "Checkout behavior" settings. ) : null} ); } ``` * #### ##### Description Prevent the buyer from continuing and show a page-level error. This example uses \`useBuyerJourneyIntercept\` to enforce a Canada-only shipping restriction, displaying the error at the top of the page rather than targeting a specific field. ##### jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import { useBuyerJourneyIntercept, useExtensionCapability, } from '@shopify/ui-extensions/checkout/preact'; export default function extension() { render(, document.body); } function Extension() { const editorType = shopify.extension.editor?.type; const blockProgressGranted = useExtensionCapability('block_progress'); useBuyerJourneyIntercept( ({canBlockProgress}) => { return canBlockProgress && shopify.shippingAddress?.value ?.countryCode && shopify.shippingAddress?.value .countryCode !== 'CA' ? { behavior: 'block', reason: 'Invalid shipping country', errors: [ { // An error without a `target` property is shown at page level message: 'Sorry, we can only ship to Canada', }, ], } : { behavior: 'allow', }; }, ); return ( <> {editorType === 'checkout' && !blockProgressGranted ? ( To allow this app to block checkout, enable this behavior in "Checkout behavior" settings. ) : null} ); } ``` * #### ##### Description Block progress from a cart line item extension when a product exceeds its purchase limit. This example uses \`useBuyerJourneyIntercept\` with the \`perform\` callback to show an inline error banner when the quantity is greater than one. ##### jsx ```jsx import '@shopify/ui-extensions/preact'; import {render} from 'preact'; import {useState} from 'preact/hooks'; import { useBuyerJourneyIntercept, useExtensionCapability, } from '@shopify/ui-extensions/checkout/preact'; export default function extension() { render(, document.body); } function Extension() { const [showError, setShowError] = useState(false); const editorType = shopify.extension.editor?.type; const blockProgressGranted = useExtensionCapability('block_progress'); useBuyerJourneyIntercept( ({canBlockProgress}) => { return canBlockProgress && shopify.target.value.quantity > 1 ? { behavior: 'block', reason: 'limited stock', perform: (result) => { if (result.behavior === 'block') { setShowError(true); } }, } : { behavior: 'allow', perform: () => { setShowError(false); }, }; }, ); return ( <> {editorType === 'checkout' && !blockProgressGranted ? ( To allow this app to block checkout, enable this behavior in "Checkout behavior" settings. ) : null} {showError ? ( This item has a limit of one per customer. ) : null} ); } ``` *** ## Best practices * **Always check `canBlockProgress`**: The `canBlockProgress` parameter passed to your intercept callback indicates whether blocking is currently allowed. Respect this flag to avoid unexpected behavior. * **Target specific fields when possible**: Use the `target` property in error objects to surface errors directly underneath the relevant field, rather than displaying a page-level error. Refer to the `target` property above for information on the correct path syntax. * **Use page-level errors only for cart-wide issues**: Omit the `target` property when the error applies to the entire checkout rather than a specific field, such as an unmet order minimum or a missing cart-level requirement. * **Warn about misconfigured capabilities in the editor**: Check `useExtensionCapability('block_progress')` and `shopify.extension.editor` to display a warning banner when the capability isn't enabled in the checkout and accounts editor. *** ## Limitations * Validation errors are cleared automatically when the buyer retries. Your extension can't persist errors across navigation attempts. ***