Type: Developer Guide / Fix · Stage: Scale · Topic: Storefront API / Sales Channels / Headless Commerce
Quick answer
If one Shopify app owns multiple sales channels, a Storefront API query can return the wrong channel’s product visibility or pricing unless you select the intended channel context. In Storefront API 2026-10, the @inContext directive accepts channelId. Pass the GID of the channel created by the same API client that is making the request.
This is not a new requirement for every storefront. If the app owns only one channel, Shopify can use its default. The explicit argument matters when the same app serves more than one channel and the query must target a particular one.
On this page
Why country and language are not enough
Older Storefront API examples usually show @inContext(country: CA) or @inContext(language: FR). That is still correct for international pricing and translations. But a country is not a sales channel. Two channels owned by the same app can serve the same country while having different product publication or merchandising context.
Shopify announced channelId on July 15, 2026, for Storefront API version 2026-10. When omitted, the API uses the first channel owned by the requesting API client. That implicit default is easy to miss when a headless app grows from one storefront to a marketplace plus a second storefront.
Symptoms can look like missing products, unexpected availability, or a price that matches the wrong channel’s context. Before blaming a theme, cache, or ERP feed, inspect the API request context.
Set the channel explicitly in the GraphQL query
Replace the example channel GID with one owned by the requesting app:
query ChannelCatalog
@inContext(
country: CA
language: EN
channelId: "gid://shopify/Channel/123456789"
) {
products(first: 10) {
nodes {
id
handle
title
availableForSale
variants(first: 1) {
nodes {
id
availableForSale
price { amount currencyCode }
}
}
}
}
}
Send this to the shop’s /api/2026-10/graphql.json endpoint using the appropriate Storefront API access token. The directive applies to the entire query. Keep country and language if those contexts matter too; channelId does not replace them.
Ownership matters. Shopify only accepts a channel created by the same API client making the request and belonging to the same store. Passing another app’s channel GID can return Channel not found for the given id. A channel ID is not a generic way to impersonate any installed sales channel.
Do not confuse channel context with buyer identity
These are separate dimensions:
| Context | Controls | Where to set it |
|---|---|---|
| Country | Market pricing, availability, currency | @inContext(country: ...); persisted cart buyer identity can take precedence |
| Language | Translated storefront content | @inContext(language: ...) |
| Channel | Channel-scoped publication and other channel context | @inContext(channelId: "...") |
| B2B buyer | Customer/company-location pricing and permissions | Authenticated buyer context and cart buyer identity |
For cart queries and mutations, Shopify documents that persisted cart buyer identity takes precedence over conflicting @inContext country values. Language, visitor consent, and channel context continue to apply. This is why a product query can appear correct while an existing cart still uses another country: inspect the cart’s buyer identity, not only the query directive.
A B2B catalog price mismatch is a different problem if the buyer’s company location is missing. See Shopify Dude’s purchasing-entity guide for that separate context.
Build a channel-aware request boundary
- Identify the caller. Confirm which app credentials make the Storefront API request and which channels that app owns.
- Choose the channel on the server. Resolve a trusted channel mapping for the storefront or marketplace. Do not accept an arbitrary browser-supplied channel ID as proof of authorization.
- Set context on every relevant operation. Use the same channel context for product listings, product details, and cart operations. Inconsistent requests can produce confusing availability differences.
- Separate cache keys. If product responses are cached, include channel, country, language, and any buyer context that changes the result. A global cache keyed only by product handle can leak the wrong experience between channels.
- Preserve fallbacks deliberately. If a channel ID is missing, do not silently use the first channel when that would expose an incorrect catalog. Log the configuration problem and return a controlled error.
Older guides about international pricing are not obsolete for ordinary Markets localization. Their missing piece is channel selection in a multi-channel app. Avoid turning this targeted feature into a blanket migration task for every theme.
Common misunderstanding
“Setting country: CA chooses the Canadian sales channel.” Country and channel are distinct. “Any channel ID from the shop will work.” Shopify restricts the argument to channels created by the requesting API client. “Changing @inContext will always override cart pricing.” Existing cart buyer identity can override conflicting country context. “All storefronts must add channelId.” Single-channel apps generally do not need to.
How to test this
- Use a development shop and an app that owns two test channels with intentionally different publication or pricing context.
- Query the same product without
channelIdand record which default channel is applied. - Repeat with each owned channel GID; compare product visibility, availability, and variant price/currency.
- Pass an ID belonging to another app and verify the documented channel-not-found error is handled safely.
- Create carts in each channel and confirm their lines, costs, and checkout behavior remain consistent with the intended channel.
- Set cart buyer identity to a different country and verify which country and channel context Shopify applies.
- Warm the cache for channel A, then request channel B. Confirm no catalog or price response crosses between them.
Sources and further reading
Documentation checked October 24, 2026.

