--- title: Contextual product feeds description: >- Learn how to use product feeds to synchronize contextualized product listings to your platform. source_url: html: 'https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds' md: >- https://shopify.dev/docs/apps/build/sales-channels/contextual-product-feeds.md --- # Contextual product feeds Contextual product feeds allow [sales channels](https://shopify.dev/docs/apps/build/sales-channels) to sync merchant catalogs to their platforms. You can configure distinct feeds for the different language and country pairs that a merchant supports. *** ## What you'll learn In this tutorial, you'll learn how to do the following tasks: * Describe your channel to Shopify * Establish a new channel connection * Subscribe to product feed webhooks * Initiate a full sync *** ## Requirements * Your app has the `read_product_listings` [access scope](https://shopify.dev/docs/api/usage/access-scopes). Learn more about requesting access scopes when your app is installed using [authorization code grant](https://shopify.dev/docs/apps/build/authentication-authorization/access-tokens/authorization-code-grant). *** ## Step 1: Describe your channel When you create a sales channel, you need to describe its properties to Shopify. Channel descriptions include capabilities, categories, fields, validation rules, and more. This channel description enables Shopify to automatically manage a large part of the merchant's channel experience. Describing a channel requires the following steps: 1. Add the [channel config extension](https://shopify.dev/docs/apps/build/sales-channels/channel-config-extension) to your app 2. Create a channel specification `example-us-channel.toml` for that channel **Note:** A sales channel app can have many target channels. Each target channel can have a different taxonomy, product type set, and field definitions. See [Channel config extension](https://shopify.dev/docs/apps/build/sales-channels/channel-config-extension#channel-specifications) for more details. ## example-us-channel.toml ```toml handle = "example-us" label = "example.com" icon = "example-channel-icon.svg" productFeedManagement = "automatic" [capabilities] bundles = true digitalProducts = true [requirements] expectsOnlineStoreParity = false merchantOfRecord = "channel" [[countries]] code = "US" languages = [ "en" ] currency = "USD" ``` Then deploy your channel targets with Shopify CLI: ## Terminal ```terminal shopify app deploy ``` *** ## Step 2: Establish channel connections Before subscribing to product feeds, you need to establish at least one channel connection. Channel connections link your channel specification to a merchant's external account and automatically create the product feeds for the channel's configured regions. See [Managing channel connections](https://shopify.dev/docs/apps/build/sales-channels/channel-connections) for the full guide on creating, updating, and querying channel connections using the [`channelCreate`](https://shopify.dev/docs/api/admin-graphql/unstable/mutations/channelCreate), [`channelUpdate`](https://shopify.dev/docs/api/admin-graphql/unstable/mutations/channelUpdate), [`channelDelete`](https://shopify.dev/docs/api/admin-graphql/unstable/mutations/channelDelete) mutations and `channels` query. *** ## Step 3: Subscribe to product feed webhooks Subscribe to product feed webhooks to receive initial [full sync payloads](#step-5-initiate-a-full-sync) and [incremental sync events](#subscribe-to-incremental-sync-events) whenever products are updated. **Note:** If you set `productFeedManagement` to `"automatic"` (recommended) in your channel specification, product feeds are created automatically when channel connections are established. If you set it to `"manual"`, you'll need to create feeds using the `productFeedCreate` mutation before initiating a full sync. See [Step 4](#step-4-optional-create-feeds-manually) below. ### Subscribe to full sync events Full sync events are triggered when a product feed full sync is initiated. Full sync events contain individual product payloads and allow you to sync a merchant's entire catalog during channel setup. Use the [`webhookSubscriptionCreate` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/webhookSubscriptionCreate) to subscribe to the [`PRODUCT_FEEDS_FULL_SYNC`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsfullsync) webhook topic. ## Subscribe to full sync topic ```graphql mutation { webhookSubscriptionCreate( topic: PRODUCT_FEEDS_FULL_SYNC webhookSubscription: { uri: "{your-callback-uri}" format: JSON } ) { webhookSubscription { id topic uri createdAt } userErrors { field message } } } ``` Individual product listing payloads are sent when a [full sync is initiated](#step-5-initiate-a-full-sync). ## product\_feeds/full\_sync ```json { "metadata": { "action": "CREATE", "type": "FULL", "resource": "PRODUCT", "fullSyncId": "gid://shopify/ProductFullSync/1123511235", "truncatedFields": [ ], "occurred_at": "2024-01-01T00:00:00.000Z" }, "productFeed": { "id": "gid://shopify/ProductFeed/12345", "shop_id": "gid://shopify/Shop/12345", "country": "CA", "language": "EN" }, "product": { "id": "gid://shopify/Product/12345", "title": "Coffee", "description": "The best coffee in the world", "onlineStoreUrl": "https://example.com/products/coffee", "createdAt": "2024-12-31T19:00:00-05:00", "updatedAt": "2024-12-31T19:00:00-05:00", "isPublished": true, "publishedAt": "2024-12-31T19:00:00-05:00", "productType": "Coffee", "vendor": "Cawfee Inc", "handle": "", "images": { "edges": [ { "node": { "id": "gid://shopify/ProductImage/394", "url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331", "height": 3024, "width": 4032 } } ] }, "options": [ { "name": "Title", "values": [ "151cm", "155cm", "158cm" ] } ], "seo": { "title": "seo title", "description": "seo description" }, "tags": [ "tag1", "tag2" ], "variants": { "edges": [ { "node": { "id": "gid://shopify/ProductVariant/1", "title": "151cm", "price": { "amount": "100.00", "currencyCode": "CAD" }, "compareAtPrice": null, "sku": "12345", "barcode": null, "quantityAvailable": 10, "availableForSale": true, "weight": 2.3, "weightUnit": "KILOGRAMS", "requireShipping": true, "inventoryPolicy": "DENY", "createdAt": "2024-12-31T19:00:00-05:00", "updatedAt": "2024-12-31T19:00:00-05:00", "image": { "id": "gid://shopify/ProductImage/394", "url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331", "height": 3024, "width": 4032 }, "selectedOptions": [ { "name": "Title", "value": "151cm" } ] } } ] } }, "products": null } ``` ### (Optional): Subscribe to full sync completion events You can subscribe to the [`PRODUCT_FEEDS_FULL_SYNC_FINISH`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsfullsyncfinish) webhook topic to be notified whenever a full sync finishes. This is a useful signal for scenarios where the merchant hasn't published any products to the channel. ```json { "metadata": { "action": "CREATE", "type": "FULL", "resource": "PRODUCT", "fullSyncId": "gid://shopify/ProductFullSync/1123511235", "truncatedFields": [ ], "occurred_at": "2024-01-01T00:00:00.000Z" }, "productFeed": { "id": "gid://shopify/ProductFeed/12345", "shop_id": "gid://shopify/Shop/12345", "country": "CA", "language": "EN" }, "fullSync": { "createdAt": "2024-12-31 19:00:00 -0500", "errorCode": null, "status": "completed", "count": 12, "url": null } } ``` ### Subscribe to incremental sync events Incremental sync events are triggered whenever changes occur relative to app's feeds, such as when: * Product fields are updated. * Product variant fields are added, updated, or deleted. * Product translations are updated. * Product market price is updated. * Products are published to the app. * Products are unpublished from the app. Use the [`webhookSubscriptionCreate` mutation](https://shopify.dev/docs/api/admin-graphql/latest/mutations/webhookSubscriptionCreate) to subscribe to the [`PRODUCT_FEEDS_INCREMENTAL_SYNC`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsincrementalsync) webhook topic. ## Subscribe to incremental sync topic ```graphql mutation { webhookSubscriptionCreate( topic: PRODUCT_FEEDS_INCREMENTAL_SYNC webhookSubscription: { uri format: JSON } ) { webhookSubscription { id topic uri createdAt } userErrors { field message } } } ``` ## product\_feeds/incremental\_sync ```json { "metadata": { "action": "CREATE", "type": "INCREMENTAL", "resource": "PRODUCT", "truncatedFields": [ ], "occured_at": "2024-12-31T19:00:00-05:00" }, "productFeed": { "id": "gid://shopify/ProductFeed/12345", "shop_id": "gid://shopify/Shop/12345", "country": "CA", "language": "EN" }, "product": { "id": "gid://shopify/Product/12345", "title": "Coffee", "description": "The best coffee in the world", "onlineStoreUrl": "https://example.com/products/coffee", "createdAt": "2024-12-31T19:00:00-05:00", "updatedAt": "2024-12-31T19:00:00-05:00", "isPublished": true, "publishedAt": "2024-12-31T19:00:00-05:00", "productType": "Coffee", "vendor": "Cawfee Inc", "handle": "", "images": { "edges": [ { "node": { "id": "gid://shopify/ProductImage/394", "url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331", "height": 3024, "width": 4032 } } ] }, "options": [ { "name": "Title", "values": [ "151cm", "155cm", "158cm" ] } ], "seo": { "title": "seo title", "description": "seo description" }, "tags": [ "tag1", "tag2" ], "variants": { "edges": [ { "node": { "id": "gid://shopify/ProductVariant/1", "title": "151cm", "price": { "amount": "100.00", "currencyCode": "CAD" }, "compareAtPrice": null, "sku": "12345", "barcode": null, "quantityAvailable": 10, "availableForSale": true, "weight": 2.3, "weightUnit": "KILOGRAMS", "requireShipping": true, "inventoryPolicy": "DENY", "createdAt": "2024-12-31T19:00:00-05:00", "updatedAt": "2024-12-31T19:00:00-05:00", "image": { "id": "gid://shopify/ProductImage/394", "url": "https://cdn.shopify.com/s/files/1/0262/9117/5446/products/IMG_0022.jpg?v=1675101331", "height": 3024, "width": 4032 }, "selectedOptions": [ { "name": "Title", "value": "151cm" } ] } } ] } }, "products": null } ``` ### Subscribe to product feed updates Product feeds might change as the merchant modifies their market settings. For example, if a merchant un-publishes a language, then any dependant feeds will become inactive. Subscribe to the [`PRODUCT_FEEDS_UPDATE`](https://shopify.dev/docs/api/admin-graphql/latest/enums/WebhookSubscriptionTopic#value-productfeedsupdate) webhook topic to stay updated on [feed `statuses`](https://shopify.dev/docs/api/admin-graphql/latest/objects/ProductFeed#field-status). ## product\_feeds/update ```json { "id": "gid://shopify/ProductFeed/1285521464", "country": "CA", "language": "FR", "status": "inactive" } ``` *** ## Step 4 (optional): Create feeds manually If you set `productFeedManagement` to `"manual"` in your channel specification, you need to create product feeds for each country and language pair before initiating a full sync. **Note:** Skip this step if your channel specification uses `productFeedManagement = "automatic"`. Feeds are created automatically when you establish a channel connection. Use the `productFeedCreate` mutation to create a feed: ## Create a product feed ```graphql mutation { productFeedCreate(input: { country: US language: EN }) { productFeed { id country language status } userErrors { field message } } } ``` Repeat this for each country and language pair defined in your channel specification. *** ## Step 5: Initiate a full sync After your app has subscribed to the necessary webhook topics, trigger a full sync to fetch the merchant's published catalog. Use the full sync mutation that matches how your channel manages feeds: * If your channel uses `productFeedManagement = "automatic"`, use [`channelFullSync`](https://shopify.dev/docs/api/admin-graphql/unstable/mutations/channelFullSync) to trigger a full sync for a channel connection. Shopify syncs all relevant feeds for that channel, or the feeds matching the optional `country` and `language` filters. See [Managing channel connections](https://shopify.dev/docs/apps/build/sales-channels/channel-connections#trigger-a-full-sync-for-a-channel). * If your channel uses `productFeedManagement = "manual"`, use [`productFullSync`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productFullSync) to trigger a full sync for each individual product feed that you created in [Step 4](#step-4-optional-create-feeds-manually). The returned [`id`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/productFullSync#field-productfullsyncpayload-id) can be used to attribute the webhooks and identify the sync completion event. Your app should trigger a full sync once during initialization to fetch the store's entire catalog. You can trigger additional full syncs on a regular schedule for [reconciliation](https://shopify.dev/docs/apps/build/webhooks/best-practices#implement-reconciliation-jobs). When a full sync is initiated, individual [`PRODUCT_FEEDS_FULL_SYNC` webhook payloads](#subscribe-to-full-sync-events) are fired for each of the merchant's published products. The webhook payloads contain localized data according to the feed's context. A confirmation event is sent to the [`PRODUCT_FEEDS_FULL_SYNC_FINISH` subscription](#optional-subscribe-to-full-sync-completion-events) once the full sync has completed. The payload's `fullSyncId` identifies the sync operation. An initial full sync should be triggered during app onboarding, and regular reconciliation syncs should be scheduled after. The optional `beforeUpdatedAt` and `updatedAtSince` parameters allow you to specify a time range for the sync. ## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json ## start a feed-scoped product full sync ```graphql mutation { productFullSync(id: "gid://shopify/ProductFeed/1164017720") { id userErrors { field message } } } ``` ## JSON response ```json { "data": { "productFullSync": { "id": "gid://shopify/ProductFullSync/2523324778944713391", "userErrors": [] } } } ``` ### (Optional): Full Sync by download **Developer preview:** Full sync by download is in an invite-only developer preview. Access to this functionality is currently invite-only. To learn more, contact your Shopify solutions representative or [Shopify Partner Support](https://help.shopify.com/en/support/partners) An alternative solution is to download a single [`JSONL` file](https://jsonlines.org/) containing all products that would normally be sent through individual [`PRODUCT_FEEDS_FULL_SYNC`](#subscribe-to-full-sync-events) payloads. This can be useful in scenarios where the amount of webhook traffic is difficult to manage. When full sync by download is enabled, the `PRODUCT_FEEDS_FULL_SYNC_FINISH` webhook payload contains a `JSONL` file URL. This functionality is similar to [bulk operations](https://shopify.dev/docs/api/usage/bulk-operations/queries). ## product\_feeds/full\_sync\_finish ```json { "metadata": { "action": "CREATE", "type": "FULL", "resource": "FULL_SYNC", "fullSyncId": "gid://shopify/ProductFullSync/23104717021717166290741", "truncatedFields": [] }, "productFeed": { "id": "gid://shopify/ProductFeed/2310471702", "shop_id": "gid://shopify/Shop/59810447382", "country": "US", "language": "EN" }, "fullSync": { "createdAt": "2024-05-31T14:38:10Z", "errorCode": "", "status": "completed", "count": 16, "url": "https://storage.googleapis.com/shopify-tiers-assets-prod-us-east1/bulk-operation-outputs/dyfkl3g72empyyoenvmtidlm9o4g?bulk-4258087174166.jsonl&response-content-type=application%2Fjsonl" } } ``` *** ## Next steps * For other channel product sync solutions, refer to the [Options to sync product data](https://shopify.dev/docs/apps/build/sales-channels/product-sync) guide. ***