Skip to main content

Standard product review metaobject definition

The standard product review metaobject is a restricted definition available to approved product review apps. This guide covers implementation requirements, the approval process, and how to syndicate reviews using the standard definition. Any partner can participate in the program by meeting the requirements and signing the review-specific amendment.


Anchor to Applying for the standard product review syndication programApplying for the standard product review syndication program

The standard product review metaobject allows review apps to store and manage product reviews in a consistent format across Shopify. When you syndicate reviews to this metaobject, they become eligible for display in the Shop app (subject to eligibility requirements). To implement the standard product review metaobject in your app, follow these steps:

Note

If you join the standard product review syndication program, all valid reviews must be syndicated to product review metaobjects and all total review counts and average star ratings must be syndicated to product review metafields.

  1. Request test access through Partner Dashboard

    • From your development app, go to Partners Internal
    • Navigate to API Access
    • Click on the card to request access to the scope for standard product reviews
    • This will enable the scopes on your dev test store only
    • The Shop app channel will be enabled for your dev shop so you can test functionality in the Shop app
  2. Test your implementation

    • Implement the review syndication with your dev app
    • Test the implementation thoroughly in your dev store
    • Verify functionality in the Shop app
  3. Submit for review

    • Submit your app for review
    • If you already have a production app you want to opt in, include that production app ID in your review request
    • The app review team will review your implementation
    • If approved, you'll need to sign an updated agreement
    • After signing:
      • Your dev app will either be promoted to a live app with the necessary scopes, OR
      • If you included a production app ID in your review request, that app will be granted the necessary scopes

Anchor to Requirements for the standard product review syndication programRequirements for the standard product review syndication program

Apps must meet the following requirements before being accepted into the standard product review syndication program:

Anchor to Requirement 1: review standard metaobject syndicationRequirement 1: review standard metaobject syndication

All reviews must be synchronized as standard metaobject entries. Reviews must be properly validated according to the metaobject field requirements:

  • Rating field must be a valid JSON object matching the schema: {"scale_min":"1.0","scale_max":"5.0","value":"5.0"}.
  • Required fields must be populated: rating, submitted_at, source, product, and app_verification_status.
  • Reference fields (author, order, product, product_variant) must reference valid resources in the merchant's store.
  • Language field must use valid ISO 639-1 standard language codes.

Anchor to Requirement 2: aggregate count updatesRequirement 2: aggregate count updates

Apps must maintain accurate aggregate review data by updating the following standard metafields on each product:

  1. Product Rating (reviews.rating): The average star rating for the product
  2. Product Rating Count (reviews.rating_count): The total number of reviews for the product

These metafields must be kept in sync with the metaobject reviews and updated whenever:

  • A new review is created
  • An existing review is updated
  • A review is deleted
  • A review's published status changes

Anchor to Additional Implementation RequirementsAdditional Implementation Requirements

When importing/exporting reviews via CSV:

  • Reviews with existing metaobject handles must be sourced from the metaobjects API.

  • Reviews must be enriched with data from CSV imports to support all merchant imported fields.

  • Metaobject webhook events must be handled for all merchants with the app installed.

    For bulk operations:

  • JSONL files must not exceed 20MB.

  • Consider partitioning JSONL files into batches of 10,000 reviews.

  • Handle validation errors and retry logic for failed operations.

    For review publication status:

  • Published reviews must have publishable status set to ACTIVE and a valid published_at timestamp.

  • Unpublished reviews must have publishable status set to DRAFT and published_at set to null.

    When handling webhooks:

  • Subscribe to all metaobject topics: METAOBJECTS_CREATE, METAOBJECTS_UPDATE, METAOBJECTS_DELETE.

  • Include the subTopic field in the webhook subscription payload.

  • Handle webhook requests asynchronously to prevent API throttling.

  • Implement retry/backoff strategy for API requests.

    For review syndication:

  • All valid reviews must be syndicated to metaobjects.

  • Reviews must be eligible for display in the Shop app (subject to eligibility requirements).

  • Maintain proper attribution through the createdByAppId field.

  • Handle cases where imported reviews may not have correct order, product, or customer references.

Anchor to Verified by Shop Branding RequirementsVerified by Shop Branding Requirements

Apps that participate in the standard product review syndication program must display the "Verified by Shop" badge to indicate that their reviews are eligible for display in the Shop app. This helps merchants and customers identify trusted review sources.

  1. Badge Assets

    • Download the official "Verified by Shop" badge assets. There are purple, black, and gray options.
    • Use only the official badge assets provided by Shopify
    • Do not modify, recreate, or alter the badge in any way
  2. Badge Placement

    • Display the badge prominently in your app's review interface
    • Include the badge on any review widgets or components that display syndicated reviews
    • Ensure the badge is visible when reviews are displayed on the merchant's storefront
  3. Badge Usage

    • Use the badge only for reviews that are successfully syndicated to or from the standard product review metaobject
    • Do not use the badge for reviews that are not eligible for Shop app display
    • Ensure the badge is properly sized and legible on all devices and screen sizes

For questions about badge usage or to request access to the badge assets, contact the Shop team through the Partner Dashboard.


Anchor to Product review metaobject fieldsProduct review metaobject fields

The following table details all available fields for the product review metaobject:

Field nameField keyField typeRequiredValidation
RatingratingRating fieldYesA validated JSON object matching the following schema: {"scale_min":"1.0","scale_max":"5.0","value":"5.0"}
TitletitleSingle line text fieldNoNone
BodybodyMulti line text fieldNoNone
Submitted atsubmitted_atDate timeYesNone
Edited atedited_atDate timeNoNone
Published atpublished_atDate timeNoNone
SourcesourceSingle line text fieldYesNone
AuthorauthorCustomer referenceNoWhen this field is populated, the customer must be present in the merchant's store.
Author display nameauthor_display_nameSingle line text fieldNoNone
OrderorderOrder referenceNoWhen this field is populated, the order must be present in the merchant's store.
ProductproductProduct referenceYes*When this field is populated, the product must be present in the merchant's store.
Product variantproduct_variantProduct variant referenceNoWhen this field is populated, the product variant must be present in the merchant's store.
Merchant replymerchant_replyMulti line text fieldNoNone
Merchant replied atmerchant_replied_atDate timeNoNone
LanguagelanguageLanguage fieldNoThis should be a valid ISO 639-1 standard language code
App verification statusapp_verification_statusSingle line text fieldYes*Validates that the value is one of: verified_buyer, verified_reviewer, unverified
Media URLsmedia_urlsURL listNoNone
Note

Fields marked with * are required by implementation but not enforced by the schema. This can happen because of the deprecation and renaming of existing fields, or the introduction of new fields.


Anchor to Key concepts for building your apps integration to the standard product review metaobjectKey concepts for building your apps integration to the standard product review metaobject

Anchor to Access scopes that must be granted by the merchantAccess scopes that must be granted by the merchant

In order for your app to syndicate reviews through metaobjects, you'll need to make sure that it has the necessary access scopes.

ScopeRequiredAccess
write_product_reviewsYesRead, create, or update product review metaobjects and enable the standard metaobject definitions for product reviews
read_metaobjectsYesRead any metaobject and subscribe to metaobject webhooks
read_customersYesRead customer information
read_ordersYesRead order information
read_productsYesRead product information
Note

For existing apps that are new to syndication, required permissions can be backfilled after app review by the app review team.

The Shopify OAuth docs contain more information around how to enable access scopes. You'll need to verify that the correct scopes have been added.

Anchor to Enabling the metaobject definitionEnabling the metaobject definition

To leverage the product review standard definition on a merchant's store, you'll first need to run a mutation to enable the product_review standard metaobject definition. This is done by calling the standardMetaobjectDefinitionEnable mutation:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation ReviewStandardMetaobjectDefinitionEnable {
standardMetaobjectDefinitionEnable(type: "product_review") {
metaobjectDefinition {
id
}
userErrors {
field
code
message
}
}
}

JSON response

{
"data": {
"standardMetaobjectDefinitionEnable": {
"metaobjectDefinition": {
"id": "gid://shopify/MetaobjectDefinition/1800798496"
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1990,
"restoreRate": 100
}
}
}
}

Anchor to Webhook subscriptionWebhook subscription

Before you can subscribe to the product review metaobject webhooks, you'll need to ensure you have the required access scopes and have the product review metaobject definition enabled.

To subscribe to product review metaobject webhooks, use the following GraphQL mutation:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation subscribeToWebhook {
webhookSubscriptionCreate(
topic: METAOBJECTS_CREATE,
webhookSubscription: {
uri: "https://example.com",
filter: "type:product_review"
}
) {
webhookSubscription {
id
format
createdAt
uri
}
userErrors {
field
message
}
}
}

JSON response

{
"data": {
"webhookSubscriptionCreate": {
"webhookSubscription": {
"id": "gid://shopify/WebhookSubscription/1716799734048",
"format": "JSON",
"createdAt": "2025-05-01T14:56:37Z",
"uri": : "https://example.com/"
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1990,
"restoreRate": 100
}
}
}
}

Shopify expects your app to subscribe to all metaobject topics when handling webhooks for product review metaobjects. The available topics are:

  • METAOBJECTS_CREATE

  • METAOBJECTS_UPDATE

  • METAOBJECTS_DELETE

    When you subscribe to metaobject topics, you must provide a filter field in the payload to specify the type of metaobject you want to receive webhooks for.

    To unsubscribe your app from receiving webhooks, use this GraphQL mutation:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation webhookSubscriptionDelete {
webhookSubscriptionDelete(id: "gid://shopify/WebhookSubscription/525699895") {
userErrors {
field
message
}
deletedWebhookSubscriptionId
}
}

JSON response

{
"data": {
"webhookSubscriptionDelete": {
"userErrors": [],
"deletedWebhookSubscriptionId": "gid://shopify/WebhookSubscription/1716799734048"
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1990,
"restoreRate": 100
}
}
}
}

For METAOBJECTS_CREATE and METAOBJECTS_UPDATE lifecycle events, your app receives the payload for the product review metaobject, as shown below. Note that empty fields may not be populated.

{
"type": "product_review",
"handle": "product-review-1",
"created_at": "2022-11-28T14:23:11+00:00",
"updated_at": "2023-09-20T23:02:09+01:00",
"id": "gid://shopify/Metaobject/1",
"definition_id": "gid://shopify/MetaobjectDefinition/2",
"fields": {
"app_verification_status": "verified_buyer",
"author": "gid://shopify/Customer/1",
"author_display_name": "John",
"body": "Great shirt!",
"edited_at": "2023-06-30T14:39:13Z",
"language": "en",
"media_urls": "[\"http://example.com/image.jpg\"]",
"merchant_replied_at": "2023-06-30T14:39:13Z",
"merchant_reply": "Thank you for buying our product!",
"order": "gid://shopify/Order/3",
"product": "gid://shopify/Product/4",
"product_variant": "gid://shopify/ProductVariant/5",
"published_at": "2022-11-28T14:22:52Z",
"rating": "{\"scale_min\":\"1.0\",\"scale_max\":\"5.0\",\"value\":\"5.0\"}",
"reviewed_order": "gid://shopify/Order/3",
"reviewed_product": "gid://shopify/Product/4",
"reviewed_variant": "gid://shopify/ProductVariant/5",
"source": "email",
"submitted_at": "2022-11-28T14:22:52Z",
"title": "Love it!"
},
"created_by_staff_id": null,
"created_by_app_id": "gid://shopify/ApiClient/1",
"capabilities": {
"publishable": {
"status": "active"
}
}
}

For METAOBJECTS_DELETE, your app receives:

{
"id": "gid://shopify/Metaobject/1",
"type": "product_review",
"handle": "prouct-review-1",
"created_by_app_id": "gid://shopify/ApiClient/1"
}

Anchor to Managing reviews with the GraphQL Admin APIManaging reviews with the GraphQL Admin API

Anchor to Getting a review metaobjectGetting a review metaobject

You can read metaobjects by id using the metaobject query. Alternatively, you can read metaobjects by their handle using the metaobjectByHandle query.

Example GraphQL:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query Metaobject {
metaobject(id: "gid://shopify/Metaobject/35225622") {
id
createdBy {
id
}
rating: field(key: "rating") {
value
}
title: field(key: "title") {
value
}
body: field(key: "body") {
value
}
product: field(key: "product") {
value
}
productVariant: field(key: "product_variant") {
value
}
order: field(key: "order") {
value
}
author: field(key: "author") {
value
}
authorDisplayName: field(key: "author_display_name") {
value
}
submittedAt: field(key: "submitted_at") {
value
}
editedAt: field(key: "edited_at") {
value
}
merchantReply: field(key: "merchant_reply") {
value
}
merchantRepliedAt: field(key: "merchant_replied_at") {
value
}
mediaUrls: field(key: "media_urls") {
value
}
language: field(key: "language") {
value
}
appVerificationStatus: field(key: "app_verification_status") {
value
}
handle
type
capabilities {
publishable {
status
}
}
updatedAt
}
}

JSON response

{
"data": {
"metaobject": {
"id": "gid://shopify/Metaobject/35225622",
"createdBy": {
"id": "gid://shopify/App/12345"
},
"rating": {
"value": "{\"scale_min\":\"1.0\",\"scale_max\":\"5.0\",\"value\":\"5.0\"}"
},
"title": {
"value": "Awesome"
},
"body": {
"value": "Great product!"
},
"product": {
"value": "gid://shopify/Product/1234567"
},
"productVariant": {
"value": null
},
"order": {
"value": "gid://shopify/Order/1234567"
},
"author": {
"value": "gid://shopify/Customer/1234567"
},
"authorDisplayName": {
"value": "John"
},
"submittedAt": {
"value": "2022-11-30T15:18:27Z"
},
"editedAt": {
"value": "2023-06-30T15:22:09Z"
},
"merchantReply": {
"value": "Thank you for purchasing our product!"
},
"merchantRepliedAt": {
"value": "2023-06-30T15:22:09Z"
},
"mediaUrls": {
"value": "[\"https://example.com/images/image1.jpg\"]"
},
"language": {
"value": "en-US"
},
"appVerificationStatus": {
"value": "verified_buyer"
},
"handle": "review-ef1d9f1e-d0f6-40cd-97c8-1234567",
"type": "product_review",
"capabilities": {
"publishable": {
"status": "ACTIVE"
}
},
"updatedAt": "2023-08-04T09:38:59Z"
}
},
"extensions": {
"cost": {
"requestedQueryCost": 14,
"actualQueryCost": 14
}
}
}

Anchor to Getting multiple review metaobjects in bulkGetting multiple review metaobjects in bulk

Use a bulk query to get all product review metaobjects for a shop. You can select as many fields as your app requires, as shown in the previous example.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation BulkQuery {
bulkOperationRunQuery(query: "{ metaobjects(type: \"product_review\") { edges { node { id createdBy { id } rating: field(key: \"rating\") { value } title: field(key: \"title\") { value } body: field(key: \"body\") { value } reviewedProduct: field(key: \"product\") { value } productVariant: field(key: \"product_variant\") { value } order: field(key: \"order\") { value } author: field(key: \"author\") { value } authorDisplayName: field(key: \"author_display_name\") { value } submittedAt: field(key: \"submitted_at\") { value } editedAt: field(key: \"edited_at\") { value } handle type capabilities { publishable { status } } updatedAt } } }}") {
bulkOperation {
id
url
}

userErrors {
field
message
}
}
}

JSON response

{
"data": {
"bulkOperationRunQuery": {
"bulkOperation": {
"id": "gid://shopify/BulkOperation/1234567",
"url": null
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1990,
"restoreRate": 100
}
}
}
}

The following example fetches data asynchronously and stores it in a JSONL file. When it finishes, you can fetch the file with the currentBulkOperation query. You can only run one bulk operation (query or mutation) at a time, and you can only fetch the most recent operation. Previous bulk operation results are not available.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query CheckBulkOperation {
currentBulkOperation {
id
partialDataUrl
fileSize
objectCount
url
status
}
}

JSON response

{
"data": {
"currentBulkOperation": {
"id": "gid://shopify/BulkOperation/5885752410400",
"partialDataUrl": null,
"fileSize": "1423",
"objectCount": "2",
"url": "https://example.com",
"status": "COMPLETED"
}
},
"extensions": {
"cost": {
"requestedQueryCost": 1,
"actualQueryCost": 1,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1999,
"restoreRate": 100
}
}
}
}

The response from the currentBulkOperation contains a URL link to a file with the response. To ensure the creation of the corresponding metaobject, it's important to inspect the file for any userErrors that occurred during the operation. If any userErrors are found, the metaobject is not created.

Anchor to Creating a review metaobjectCreating a review metaobject

You can create reviews by issuing a metaobjectUpsert mutation.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation MetaobjectUpsert {
metaobjectUpsert(
handle: {
handle: "review-1",
type: "product_review"
},
metaobject: {
fields: [
{
key: "rating",
value: "{\"scale_min\":\"1\", \"scale_max\": \"5\", \"value\": \"4.0\"}"
}
]
}
) {
metaobject {
type
handle
rating: field(key: "rating") {
value
}
}
userErrors {
code
field
message
}
}
}

JSON response

{
"data": {
"metaobjectUpsert": {
"metaobject": {
"type": "product_review",
"handle": "review-1",
"rating": {
"value": "{\"scale_min\":\"1.0\",\"scale_max\":\"5.0\",\"value\":\"4.0\"}"
}
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 11,
"actualQueryCost": 11,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1989,
"restoreRate": 100
}
}
}
}

An example MetaobjectUpsertInput. Note that this example is incomplete, with only one field. You must supply all available and required fields.

{
"handle": {
"handle": "review-1",
"type": "product_review"
},
"input": {
"fields": [
{
"key": "rating",
"value": "{\"scale_min\":\"1\", \"scale_max\": \"5\", \"value\": \"4.0\"}"
}
]
}
}

Anchor to Updating a review metaobjectUpdating a review metaobject

To update a review, use the same metaobjectUpsert mutation used for creating a new review. This mutation updates an existing review or creates a new review if it doesn't already exist. When you provide a list of fields to update, those fields are overwritten and the final metaobject is merged with the remaining fields left intact.

Anchor to Bulk upserting multiple review metaobjectsBulk upserting multiple review metaobjects

Upload a JSONL file containing newline-separated JSON objects with the mutation parameters with the stagedUploadCreate mutation. You can provide as many fields you wish to update. Refer to all available and required fields. There is a hard limit of 20MB on the JSONL file. You can partition the JSONL file into batches of 10,000 reviews to ensure it does not exceed this limit.

{
"handle": {
"handle": "1234-fe28285a-4b39-4993-8f57-db3cfbf53a95",
"type": "product_review"
},
"input": {
"type": "product_review",
"handle": "1234-fe28285a-4b39-4993-8f57-db3cfbf53a95",
"fields": [
{
"key": "title",
"value": "this is an awesome product"
},
{
"key": "rating",
"value": "{\"scale_min\":\"1\", \"scale_max\": \"5\", \"value\": \"5.0\"}"
},
{
"key": "body",
"value": "will buy this again"
}
]
}
}
{
"handle": {
"handle": "1234-3f5b7d20-4a6b-4b7e-8a8e-3b2cd8d1f3e1",
"type": "product_review"
},
"input": {
"type": "product_review",
"handle": "1234-3f5b7d20-4a6b-4b7e-8a8e-3b2cd8d1f3e1",
"fields": [
{
"key": "title",
"value": "loved it"
},
{
"key": "rating",
"value": "{\"scale_min\":\"1\", \"scale_max\": \"5\", \"value\": \"4.0\"}"
},
{
"key": "body",
"value": "Exactly what I needed"
}
]
}
}

Pass the resulting path to the staged upload to a bulkOperationRunMutation that performs the mutations asynchronously.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation {
bulkOperationRunMutation(
mutation: "mutation MetaobjectUpsert($input: MetaobjectUpsertInput!) { metaobjectUpsert(metaobject: $input) { metaobject { type handle } } userErrors { message field } }",
stagedUploadPath: "tmp/21759409/bulk/89e620e1-0252-43b0-8f3b-3b7075ba4a23/bulk_op_vars") {
bulkOperation {
id
url
status
}
userErrors {
message
field
}
}
}

JSON response

{
"data": {
"bulkOperationRunMutation": {
"bulkOperation": {
"id": "gid://shopify/BulkOperation/123456",
"url": null,
"status": "CREATED"
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 10,
"actualQueryCost": 10,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1990,
"restoreRate": 100
}
}
}
}

The guidelines for bulk importing data provide more detailed information around bulk operations.

Anchor to Deleting a single review metaobjectDeleting a single review metaobject

Deleting a single review metaobject can be done through the metaobjectDelete mutation.

Anchor to Bulk deleting multiple review metaobjectsBulk deleting multiple review metaobjects

Multiple reviews can be deleted through the metaobjectBulkDelete mutation. There's a limit of 250 review IDs per request.

Anchor to Mark a review metaobject as published or unpublishedMark a review metaobject as published or unpublished

In order to mark a review as published or unpublished in its metaobject, supply two things in the metaobjectUpdate payload:

  • Publishable status capability:
    • {"capabilities": {"publishable": {"status": "ACTIVE"}} when published
    • {"capabilities": {"publishable": {"status": "DRAFT"}} when unpublished
  • published_at:
    • A UTC timestamp when published
    • null when unpublished

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation PublishMetaobject {
metaobjectUpdate(id: "gid://shopify/Metaobject/4", metaobject: {
capabilities: {
publishable: {
status: ACTIVE
}
},
fields: [
{
key: "published_at",
value: "2022-08-26T12:09:46+00:00"
}
]
}) {
metaobject {
id
fields {
key
value
}
capabilities {
publishable {
status
}
}
}
userErrors {
code
field
message
}
}
}

JSON response

{
"data": {
"metaobjectUpdate": {
"metaobject": {
"id": "gid://shopify/Metaobject/123456",
"fields": [
{
"key": "rating",
"value": "{\"scale_min\":\"1.0\",\"scale_max\":\"5.0\",\"value\":\"5.0\"}"
},
{
"key": "title",
"value": "This is an awesome product!"
},
{
"key": "body",
"value": "Will buy this again!"
},
{
"key": "submitted_at",
"value": "2023-08-04T09:38:59Z"
},
{
"key": "edited_at",
"value": null
},
{
"key": "published_at",
"value": "2022-08-26T12:09:46+00:00"
},
{
"key": "source",
"value": "email"
},
{
"key": "author",
"value": null
},
{
"key": "author_display_name",
"value": "John"
},
{
"key": "order",
"value": null
},
{
"key": "product",
"value": "gid://shopify/Product/123456"
},
{
"key": "product_variant",
"value": null
},
{
"key": "merchant_reply",
"value": null
},
{
"key": "merchant_replied_at",
"value": null
},
{
"key": "media_urls",
"value": null
},
{
"key": "language",
"value": null
},
{
"key": "app_verification_status",
"value": null
}
],
"capabilities": {
"publishable": {
"status": "ACTIVE"
}
}
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 13,
"actualQueryCost": 13,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1987,
"restoreRate": 100
}
}
}
}

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL mutation

mutation UnpublishMetaobject {
metaobjectUpdate(id: "gid://shopify/Metaobject/4", metaobject: {
capabilities: {
publishable: {
status: DRAFT
}
},
fields: [
{
key: "published_at",
value: ""
}
]
}) {
metaobject {
id
fields {
key
value
}
capabilities {
publishable {
status
}
}
}
userErrors {
code
field
message
}
}
}

JSON response

{
"data": {
"metaobjectUpdate": {
"metaobject": {
"id": "gid://shopify/Metaobject/123456",
"fields": [
{
"key": "rating",
"value": "{\"scale_min\":\"1.0\",\"scale_max\":\"5.0\",\"value\":\"5.0\"}"
},
{
"key": "title",
"value": "This is an awesome product!"
},
{
"key": "body",
"value": "Will buy this again!"
},
{
"key": "submitted_at",
"value": "2023-08-04T09:38:59Z"
},
{
"key": "edited_at",
"value": null
},
{
"key": "published_at",
"value": null
},
{
"key": "source",
"value": "email"
},
{
"key": "author",
"value": null
},
{
"key": "author_display_name",
"value": "John"
},
{
"key": "order",
"value": null
},
{
"key": "product",
"value": "gid://shopify/Product/123456"
},
{
"key": "product_variant",
"value": null
},
{
"key": "merchant_reply",
"value": null
},
{
"key": "merchant_replied_at",
"value": null
},
{
"key": "media_urls",
"value": null
},
{
"key": "language",
"value": null
},
{
"key": "app_verification_status",
"value": null
}
],
"capabilities": {
"publishable": {
"status": "DRAFT"
}
}
},
"userErrors": []
}
},
"extensions": {
"cost": {
"requestedQueryCost": 13,
"actualQueryCost": 13,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1987,
"restoreRate": 100
}
}
}
}

Anchor to Importing and exporting reviews with handlesImporting and exporting reviews with handles

If you support importing reviews from another partner via CSV import, you should avoid duplicating reviews in metaobjects. When a metaobject handle is present for an imported review, source the review from the metaobjects API.

To support review importing, do the following things:

  • Update your review export flow to include the review's metaobject handle.

  • Update your review import flows to support reading metaobject handles.

  • Shop reviews from the CSV import should be sourced from the metaobjects API.

  • Partner reviews from the CSV import with handles should be sourced from the metaobjects API.

  • To support all merchant imported fields, review data sourced from the metaobjects API should be enriched with the data from CSV imports. For example, custom questions.

  • To prevent outdated reviews in your app, handle all metaobject webhook events for merchants with your app installed regardless of which app ID has created the metaobject.

    The partner app responsible for creating the metaobject can be identified by reading the createdByAppId field. It is important to note that this field is immutable and is always attributed to the app that created the metaobject. This means that not all metaobjects for your active merchants will show the createdByAppId field, populated with your app ID.

    Furthermore, if your application supports exporting and importing reviews from one Shopify store to another, you should recreate the metaobjects using the same handle. Shop may not be able to ingest such reviews, since the imported reviews will not have correct order, product or customer references for the new store.

Before you can test the integration, you'll need a test store that is configured to be visible in the Shop App.

Your test store must meet the following conditions:

  • The Shop channel is installed.

  • The app for which you are testing the integration is installed.

  • The store has at least one test product that can be purchased.

  • Test payments have been enabled, or there's a 100% order discount you can use to complete the checkout.

    To test your development shop with the Shop app integration:

  1. Install the Shop app on your development store from the Shopify App Store
  2. From the Shop app in your store admin, toggle on development mode to enable testing functionality

When your test store meets these conditions, let the Shop team know, so they can enable your store. The steps outlined in the test plan may vary depending on the features available in your app.

Anchor to Handling validation errorsHandling validation errors

GraphQL mutations return validation errors in the userErrors field. These errors can be a good first indication of how well your app's syndication of reviews to metaobjects is working.

When syndicating reviews to metaobjects, common integration errors usually have to do with incorrectly provided values, especially around reference fields. You should review the validation errors you receive and make the required adjustments to your integration, to ensure as many reviews can be syndicated to metaobjects as possible. That being said, reference fields are more complicated to handle and fix, as your app may have a resource ID that's not present on the merchant's store at the time of review syndication. In such a scenario, retry the review syndication without the resource ID.

Anchor to Encountering internal server errorsEncountering internal server errors

To account for intermittent internal server errors from Shopify's APIs, your app should use retry logic to ensure recovery and eventual success. For major outages, refer to Shopify's status page.

Anchor to Encountering rate limitsEncountering rate limits

Shopify API rate limits are applicable to all requests for metaobjects, and handling large volumes of incoming webhooks may throttle the API requests.

Consider the following strategies for handling webhook requests in your app:

  • Handling incoming webhook requests asynchronously
  • Having a retry/backoff strategy when making requests to Shopify APIs
  • Implementing rate limiting internally within your app to reduce the chance of your app getting throttled by Shopify

Was this page helpful?