releases.shpreview
Shopify/Developer Changelog

Developer Changelog

Mon
Wed
Fri
JunJulAugSepOctNovDecJanFebMarAprMay
Less
More
Releases98Avg30/moVersionsv4.0 to v11.5

Next Generation Events are now available in developer preview, with field-level control over when events fire, what data they carry, and what triggered each delivery.

  • Subscribe to exactly what you care about. Field-level triggers pre-qualify deliveries before they reach your endpoint. A subscription scoped to product.variants.price won't fire on title edits, tag updates, or status changes. Only when the price changes.
  • Get the payload your app needs, not a fixed schema. You define the delivery payload with a standard Admin GraphQL query. No over-fetching fields you'll discard. No extra API call to get the data you actually wanted after the webhook lands.
  • Know what fired for each delivery. Every delivery includes fields_changed: an explicit list of the fields that triggered the event, with full entity paths and IDs. That means you don't need to infer, or diff against prior state.
  • Filter on current state. query_filter narrows deliveries based on the current state of your query output. Use it to skip events that don't meet your conditions, like only delivering for active products.
  • Configure in code. Subscriptions live in shopify.app.toml alongside your other app configuration. Version-controlled, reviewable, and deployable.

As a developer preview, Events are available in the unstable API version and APIs may change. Product and Customer topics are live today.

Configuration and payload
[events]
api_version = "unstable"

[[events.subscription]]
handle = "price_sync"
topic = "Product"
actions = ["update"]
triggers = ["product.variants.price", "product.variants.compareAtPrice"]

uri = "/api/events"

query = """
	query priceSync($productId: ID!, $variantsId: ID!) {
		productVariant(id: $variantsId) {
			id
			price
			compareAtPrice
			sku
		}
		product(id: $productId) {
			id
			title
			status
		}
	}
"""
query_filter = "product.status:'ACTIVE'"

Every delivery includes fields_changed, data from your query, and query_variables used to fetch it:

{
  "topic": "Product",
  "action": "update",
  "handle": "price_sync",
  "data": {
    "productVariant": {
      "id": "gid://shopify/ProductVariant/456",
      "price": "24.99",
      "compareAtPrice": "34.99",
      "sku": "SIGNAL-NOT-NOISE"
    },
    "product": {
      "id": "gid://shopify/Product/123",
      "title": "Peace & Quiet Tee",
      "status": "ACTIVE"
    }
  },
  "fields_changed": [
    "product[id: 'gid://shopify/Product/123'].variants[id: 'gid://shopify/ProductVariant/456'].price"
  ],
  "query_variables": {
    "productId": "gid://shopify/Product/123",
    "variantsId": "gid://shopify/ProductVariant/456"
  }
}
Learn more

Learn more about how Events relate to Webhooks: https://shopify.dev/docs/apps/build/events-webhooks Get started by Creating an Events subscription.

Shopify CLI 4.0: SemVer, auto-updates, removing deprecated flags and commands

The release of Shopify CLI 4.0 today brings clarity to CLI versioning, the introduction of automatic updates, and the announced removal of the deprecated --force flag from shopify app deploy.

Semantic Versioning

Shopify CLI is now following semantic versioning practices. Releases with new features will be minor versions, and bug fixes will be patch versions. When required, major version releases will be used to communicate breaking changes to CLI command structure or behavior.

For more information, see our announcement of the move to SemVer.

Automatic Upgrades

Starting with Shopify CLI 4.0, Shopify CLI upgrades itself automatically using the package manager you installed it with. Auto-upgrade is skipped in CI, project-local installs, and for major version releases. Automatic upgrades can be disabled with the shopify config autoupgrade off command.

For more information, see Shopify CLI documentation.

Removal of the --force flag for app releases

The --force flag on the app deploy and app release commands didn’t distinguish between low-risk operations (adding or updating extensions) and high-risk ones (deleting them). This flag has been removed in Shopify CLI 4.0. The previously released --allow-updates and --allow-deletes flags give you granular control, so your CI/CD pipelines can run unattended without the risk of accidental, irreversible deletions.

For more information, see the March 2026 changelog.

Other removed commands and flags

The following deprecated commands and flags have also been removed in Shopify CLI 4.0:

  • shopify webhook trigger (use shopify app webhook trigger)
  • shopify theme serve (use shopify theme dev)
  • shopify app generate schema (use shopify app function schema)
  • shopify app webhook trigger --shared-secret (use --client-secret)
  • shopify app generate extension --type (use --template)

New Features

Optional Consent

Users can now reject scopes and continue using your Mini. Consent is no longer all-or-nothing — if a user declines a scope, your Mini should gracefully degrade rather than block the experience. If your Mini hard-fails when a scope is rejected, please update it using the new hooks below.

useCheckScopesConsent Hook

Check at runtime which scopes a user has granted. Use this to conditionally render features that depend on a particular scope.

Usage:

import {useCheckScopesConsent} from '@shopify/shop-minis-react'

function MyMini() {
  const {scopes} = useCheckScopesConsent()

  if (scopes.includes('product_lists:write')) {
    return <SaveToListButton />
  }
  return <SignInPrompt />
}
useRequestScopesConsent Hook

Re-request consent after a user has previously declined. The hook must be invoked from a user interaction — you cannot re-prompt automatically. Use this for flows where granting consent unlocks a clear, user-initiated action.

Usage:

import {useRequestScopesConsent} from '@shopify/shop-minis-react'

function GrantAccessButton() {
  const {requestScopesConsent} = useRequestScopesConsent()

  return (
    <button onClick={() => requestScopesConsent(['product_lists:write'])}>
      Enable saving products
    </button>
  )
}
useCheckPermissions Hook

Separate from scopes, useCheckPermissions lets your Mini check OS-level permissions (camera, photo library, and so on) before invoking an action that requires them.

Usage:

import {useCheckPermissions} from '@shopify/shop-minis-react'

function CameraFeature() {
  const {permissions} = useCheckPermissions(['camera'])

  if (permissions.camera === 'granted') {
    return <CameraView />
  }
  return <RequestCameraButton />
}
request_blocked Sentinel

When a user has declined a scope or permission and the platform will not re-prompt, the relevant hooks now return a request_blocked sentinel value instead of null. This lets you distinguish "not asked yet" from "asked and blocked" and tailor your UI accordingly. See the Scopes Consent docs on shopify.dev for the full state machine.

Intents

Intents are a new communication layer between the Shop app and Shop Minis. The Shop app launches your Mini from contextually relevant moments — such as a product detail page — and passes along the context (typically a product). For partners, this opens a new path to distribution: instead of relying on the Explore tab alone, your Mini can be surfaced where buyer intent is highest.

Two intents are supported today:

| Intent | What it's for | | :

You can query the shippingLine field on FulfillmentOrderLineItem.

This field returns the ShippingLine associated with a fulfillment order line item, if available. This feature simplifies the process for order management and fulfillment apps to identify the shipping method for each line item. It is particularly useful in scenarios where fulfillment orders are merged across different delivery profiles, and the original per-line shipping service is not identified by the fulfillment order's delivery method.

For example, apps can use properties such as shippingLine.code, shippingLine.title, and shippingLine.source to accurately map line items to the correct carrier service or shipping method.

For more information, refer to the FulfillmentOrderLineItem reference.

We’ve published new migration guides to help you upgrade Checkout and Customer Account UI extensions to the latest API version and Polaris web components.

The new guides include:

  • Guidance for moving from React or JavaScript extension APIs to Preact, Polaris web components, and the global shopify object.
  • More than 60 component-specific migration pages, covering components such as Button, Checkbox, TextField, Banner and View for Checkout and Customer Account UI extensions.
  • Instructions for migrating checkout metafields to cart metafields

If your extension uses an API version earlier than 2025-10, use these guides to adopt Polaris web components, which are the default in API version 2025-10 and later.

Start with:

You no longer need to ask merchants to share function run logs with you. These logs are now automatically available in the Dev Dashboard for any function your app has the necessary access scopes to view.

What's Changed

Function run logs in the Dev Dashboard are now accessible based on the access scopes granted to your app by the merchant. The required scopes to view a log are determined by the function's input query. If your app has the necessary scopes to read these fields via the GraphQL Admin API, you will automatically see the run details without needing any additional action from the merchant.

What You Need to Do

If you expect to see function run details but don't, ensure your app has the scopes required by the input query. Here's how you can check:

  1. Request the scopes during your app's installation/authentication process: This is ideal for scopes your app consistently needs. Refer to Access scopes for instructions on declaring and requesting access scopes.

  2. Request protected customer data scopes when accessing customer data: Some fields, like customer details and addresses, require additional protected customer data access. Consult Protected customer data for the approval process and necessary data-level scopes.

  3. Use optional scopes for temporary or debugging access: If a scope is needed occasionally, such as for debugging, request it as an optional scope. Merchants can grant or revoke it without reinstalling the app.

Once the required scopes are granted, the run details will automatically be visible the next time you access the log.

Starting with the 2026-07 version of the Storefront API, the Cart emits a PRODUCT_UNAVAILABLE_IN_BUYER_LOCATION warning when a cart line contains a product that isn't available in the buyer's location.

Each affected cart line returns its own warning. The warning's target is set to the CartLine ID so you can map it back to the line in your UI.

For background on cart warnings and an example of handling them, refer to our docs on cart warnings.

The App Events API lets you send any event from your app to Shopify. App event data appears in your Dev Dashboard Logs alongside webhooks, Function executions, and API calls.

How it works:

1. Send app events to a single API endpoint: Define the event_handle and attributes you want to track and send them to the App Events API, including:

  • Feature usage: bulk_edit_completed, report_generated, automation_created
  • Workflows: onboarding_completed, campaign_sent, export_finished
  • Performance: sync_failed, api_timeout, rate_limit_hit
  • Conversion signals: limit_hit, premium_viewed, milestone_achieved
  • Billable activities: order_processed, email_sent, label_printed

2. View events in Dev Dashboard: All app events flow into Dev Dashboard Logs automatically for monitoring, alongside data Shopify provides about your app, i.e: Webhooks, Functions executions, and API calls.

3. Optional: Turn app events into billing: On Shopify App Pricing, any app event can become a usage-based charge. Define a meter in the Partner Dashboard, match it to an event_handle, and Shopify handles metering and invoicing. No additional code required.

App Events is available now for all apps, regardless of billing method. Learn more

Shopify App Pricing supports subscriptions, usage-based charges, or combined models, configured in the Partner Dashboard.

What's new:

Managed Pricing is now Shopify App Pricing Shopify App Pricing replaces Managed Pricing as Shopify’s default billing solution that gets configured during app submission in the Partner Dashboard. Apps previously on Managed Pricing will now see “Shopify App Pricing” as their selected billing solution.

Usage-based billing now possible with App Events API Charge based on merchant usage using the App Events API. Send events from your app, define meters in the Partner Dashboard, and Shopify handles aggregation, calculation, and invoicing. Three pricing structures supported: fixed, graduated, and volume. Supports negative reporting for automatic charge corrections.

New APIs make billing data more accurate Active Subscription API: Real-time subscription status (active, pending, cancelled, frozen) that persists beyond uninstall [Historical API:](https://shopify.dev/docs/apps/launch/billing/shopify-app-pricing#app

We're changing how public apps handle offline access tokens to enhance merchant data protection. Starting January 1, 2027, all public apps must use expiring offline access tokens when calling the Admin API. After that date, public apps still using non-expiring tokens will receive authentication errors.

This extends the April 1, 2026 change, which applied only to newly created public apps, to all public apps, including those created before April 1, 2026.

What apps are affected

Public apps making Admin API requests using non-expiring offline access tokens, including apps created before April 1, 2026

What apps are unaffected

  • Custom apps
  • Apps created by merchants either in the Dev Dashboard or in the admin

Why we're making this change

Non-expiring tokens, if leaked, remain valid indefinitely. Expiring tokens close that window in 60 minutes and rotate automatically, dramatically reducing the impact of a credential leak. This aligns with modern OAuth best practices, and as a developer it gives your app a predictable refresh flow.

Action required

Existing public apps: Migrate from non-expiring to expiring offline access tokens.

Merchants don't need to reinstall, as your app exchanges existing tokens through code. Follow the migration guide for the step-by-step path. If you use Shopify's app templates and official API libraries, refresh handling is already implemented; you only need to handle the token exchange and storage updates.

Need help? Engage with the dev platform community for support and questions.

Seven new Settings intents let apps open editors for notifications, payment capture, gift cards, delivery profiles, and business details. This builds on the initial Settings intents release from March.

With a single API call, your app opens the relevant Settings section as a contextual overlay and scrolls the merchant directly to the field they need to edit.

New intents

Notifications
  • edit:settings/NotificationsSenderEmail
  • edit:settings/NotificationsStaff
Payments and gift cards
  • edit:settings/PaymentCaptureMethod
  • edit:settings/GiftCardExpiration
Delivery
  • create:shopify/DeliveryProfile
  • edit:shopify/DeliveryProfile
Business
  • edit:settings/BusinessDetails

Each intent opens the relevant Settings page in a page stack and scrolls the merchant directly to the targeted card, following the pattern established in the initial release.

Resources

We've introduced markets as a new option in DiscountContextInput, enabling you to target discounts to specific regional markets, retail locations, or B2B company locations. This option can be used alongside existing eligibility options such as all, customerSegments, and customers.

You can now set market eligibility for all discount types, including:

  • Basic, BXGY, App, and Free Shipping discounts (both automatic and code-based).
  • Note that eligibility types are mutually exclusive—you can target either markets OR customer segments, but not both simultaneously.

What you can do:

  • Assign market eligibility to a discount by using markets in DiscountContextInput when creating or updating a discount.
  • Query the discounts and discountsCount fields on a Market to view the list of discount customizations for that market.
  • Filter discounts by any market eligibility using context:market or by specific markets using market_ids in discountNodes.

What you need to know:

  • Discounts do not inherit across different market types (e.g., from regional to B2B or retail). When you assign a discount to a regional market, it automatically applies to sub-markets of the same type (e.g., from "North America" to "Canada").
  • If you are using API versions prior to 2026-07, discounts with market eligibility will be filtered out, as these versions cannot represent them (both in node queries and specific discounts by ID).

For more information, refer to the Admin GraphQL API documentation.

What's changing

Shopify now applies stricter rate limits to bots and agents that access the Storefront API and Shopify-hosted online store pages. Bots and agents that don't sign their requests are subject to the strictest limits. To qualify for higher rate limits, operators should sign their requests with Web Bot Auth. For more details, see Storefront rate limits.

What you should do

If you operate a bot or agent accessing Shopify storefronts, sign your requests using Web Bot Auth. To get started, review the Web Bot Auth architecture and Cloudflare's implementation guide — for context only, you do not need to enroll with Cloudflare.

Higher access tiers

If you require higher rate limits than those provided to Web Bot Auth traffic, please contact us through this form.

Shopify Merchants

Shopify merchants who want to crawl their own stores can find ready-to-use Web Bot Auth signatures in the Shopify admin.

ProductVariant is now a Publishable. Variants can be published or unpublished per publication (channel or catalog) in API version 2026-07, giving merchants — and your apps — fine-grained control over where each variant is visible without deleting variants, duplicating products, or hiding them via storefront code.

This is a non-breaking, additive change:

  • Product-level publishing is unchanged and still takes precedence. A product must be active and published to a channel for any of its variants to render there.
  • Variants default to published (opt-out model). Existing apps that publish at the product level continue to work without modification.
  • Variants can be created in an unpublished state in order to prevent early exposure to buyers.
What's new in the Admin GraphQL API
  • publishablePublish and publishableUnpublish now accept ProductVariant IDs.
  • ProductVariant now adheres to the Publishable interface, similar to Product and Collection, which includes resourcePublicationv2 and publishedOnPublication
  • Product feed webhooks fire with a product update for variant added and deleted when variants are published to the feed’s channel.
  • variant_publication/create/update/delete webhooks are still under development and will be shipped imminently.
What this means for your app
  • Channel apps using product feeds should not require any changes to work - the product feed adds and removes variants to the product and an incremental sync webhook is triggered.
  • Channels or pseudo channels that do not use product feeds and rely on reading publication state via the admin graphql API should implement support by reading the respective publication state of variants via ProductVariant.resourcePublicationsv2.
  • Apps that create variants after product publication occurs: new variants default to published in all of the parent product's publications. If your app plans to create variants in an unpublished state, productSet and productVariantBulkCreate both include a variant.published: false field to create variants as unpublished.
  • Apps that publish products: no changes required. Continue calling publishablePublish on products as before. Consider implementing variant publishing support if unpublishing variants is relevant to your apps workflows.

Read the developer guide →

App deployment in CI/CD is now available for all apps through app automation tokens on the Dev Dashboard. These tokens offer app-scoped authentication, allowing you to use the latest Shopify CLI to automate app releases in GitHub Workflows and similar tools. App-scoped authentication ensures that each token is specific to an individual app, enhancing security and control.

To deploy your app using an app automation token, set the token as an environment variable and execute the deployment command:

export SHOPIFY_APP_AUTOMATION_TOKEN="your-app-automation-token"
shopify app deploy --config production --allow-updates

App automation tokens replace the CLI tokens previously generated on the Partner Dashboard. While existing CLI tokens will remain functional until they expire, transitioning to app automation tokens is recommended for enhanced security and functionality. Learn more about migrating to app automation tokens.

For further information on app automation tokens, visit Shopify.dev:

The default value of appliesOnSubscription has been changed from false to true on the DiscountCodeAppInput and DiscountAutomaticAppInput input types in GraphQL Admin API.

No action is required. This default value change has no effect on how discounts are applied at checkout. If your app explicitly sets appliesOnSubscription when creating or updating app discounts, your behavior is unchanged.

This change applies across all active API versions. The appliesOnOneTimePurchase field already defaults to true and is unchanged.

We have introduced a new actor field in subscription mutations to help you track who initiated an action - whether it was the customer, the merchant, or the partner app's automated system.

You can now include an actor argument when creating billing attempts or editing subscription contracts. This field accepts the following values:

  • customer: The buyer initiated the action (e.g., clicking "pay now" in a customer portal).
  • merchant: A merchant or their staff manually initiated the action.
  • partner: The partner app's automated system initiated the action (e.g., scheduled billing or retry logic).

This field is available in the following Admin API mutations:

Billing Attempts:

  • subscriptionBillingAttemptCreate (via subscriptionBillingAttemptInput.actor)
  • subscriptionBillingCycleCharge
  • subscriptionBillingCycleBulkCharge

Contract Edits:

  • subscriptionContractCreate
  • subscriptionContractUpdate
  • subscriptionContractAtomicCreate
  • subscriptionContractProductChange
  • subscriptionBillingCycleContractEdit

Status Updates:

  • subscriptionContractActivate
  • subscriptionContractPause
  • subscriptionContractCancel
  • subscriptionContractFail
  • subscriptionContractExpire

App-owned metaobjects, identified by types such as $app:example, including those created using declarative metaobject definitions, can now be utilized by their owning app without requiring any access scopes. This change simplifies the process for developers by eliminating the need to request additional access scopes, thereby reducing potential merchant friction.

If you are considering using declarative metaobjects in your app, you can now proceed without worrying about additional access scope requests. Ensure you are using Admin API version 2026-04 or later to read or write app-owned metaobjects without the need for additional access scopes.

Working with merchant-owned metaobject types still requires specific access scopes, such as read_metaobjects or write_metaobject_definitions, to be granted.

You can now create and manage metric targets for merchants using four new GraphQL Admin API operations: analyticsTargets, analyticsTargetCreate, analyticsTargetUpdate, and analyticsTargetsDelete.

With targets, merchants can set numeric goals for analytics metrics, such as "achieve $50K in gross sales this quarter," and track their progress with a visual gauge. The API has been available to apps from the start, enabling you to build on the same foundation that supports targets in the Shopify admin.

Whether you're developing a goal-setting tool, a reporting dashboard, or a planning app, you can now read and write the same targets that merchants view in their analytics, using the same API, rules, and validation.

What you can do
  • Create: Specify a metric, target amount, time period, and an optional filter (for example, limit to a specific sales channel or product).
  • Read: Retrieve targets for a shop using analyticsTargets, with support for pagination.
  • Update: Modify the metric, name, amount, time period, or filters.
  • Delete: Remove targets using analyticsTargetsDelete. Deleted targets can't be recovered.
Key details
  • Scope: Requires read_reports and write_reports permissions.
  • Deduplication: The API uniquely identifies each target by its metric, date range, and filters. Attempting to create a duplicate results in a userError with guidance on necessary changes.
  • Status computation: The API automatically derives the status field (In progress, Achieved, Not achieved, Upcoming) from the target's date range and current metric value at query time.
  • Filters: Each target can have one dimension-based filter in the WHERE statement of ShopifyQL using = or IN operators.
Use cases
  • Goal-setting apps: Create targets for merchants based on historical performance or industry benchmarks, providing smart defaults.
  • Reporting and BI tools: Access merchant targets and display progress alongside your visualizations, ensuring consistency with Shopify.
  • Planning and forecasting apps: Integrate external goals into Shopify Analytics, offering merchants a unified place to track all targets.
  • Agency dashboards: Programmatically set and monitor targets across multiple client stores.

Targets created by your app appear alongside merchant-created targets in the Shopify admin, on the Targets index page with their own target gauge, and on merchant dashboards. Merchants can manage them consistently, regardless of their origin.

View the API reference →

Related: new ColumnDataType enum values

API version 2026-04 adds two new enum values to ColumnDataType in ShopifyqlTableDataColumn:

  • UNITLESS_SCALAR: Represents a dimensionless numeric score with no associated unit. Applied to web performance metrics: p50_cls, p75_cls, p90_cls, p99_cls (Cumulative Layout Shift percentiles).
  • MULTIPLIER: Represents a ratio or multiplier value (for example, 3.2x). Applied to shop_campaign_return_on_ad_spend (Return on ad spend).

These metrics previously returned FLOAT. This change doesn't affect callers on API versions prior to 2026-04.

Minor rounding change for custom line item discounts in POS 11.5

Starting with POS version 11.5, we are updating the internal calculation method for custom fixed-amount line item discounts. These discounts will now be applied on a per-unit basis rather than across the entire line. Note that this change only affects fixed-amount discounts; percentage discounts remain unchanged.

If your app uses setLineItemDiscount or bulkSetLineItemDiscounts from the Cart API with a FixedAmount discount type, you can continue to pass the total discount amount for the line item as you currently do. The POS system will automatically handle the conversion.

In most cases, the total discount will remain the same. However, for amounts that don't divide evenly by quantity, there might be a rounding difference of up to one cent, the smallest denomination in the local currency. For example, a $5.00 discount applied to 3 items becomes $1.67 per unit, totaling $5.01 instead of $5.00.

No action is required for extensions using API version 2026-04 or earlier. In a future API version, extensions will need to pass per-unit discount amounts directly. We will provide more details as the release approaches.

For more information, refer to the Cart API reference.

Last Checked
58m ago
Latest
May 22, 2026
Tracking since Oct 9, 2025