# How to collect feedback for a Shopify app

> For a Shopify app, send merchant feedback from your backend with the Escuta Produto REST API, and store the shop domain as metadata. Link the hosted form in onboarding emails, and use separate products for merchants and storefront shoppers when their feedback needs different handling.

Source: https://escutaproduto.com/resources/collect-feedback-shopify-app
Last updated: 2026-10-09

## Where feedback comes from in a Shopify app

A Shopify app usually has two audiences, and they ask different things. The first is the merchant who installed the app. They report bugs in the admin, request features for their store and tell you when onboarding confused them. The second is the shopper, if your app adds something to the storefront, such as a product option or a checkout block. Shoppers report problems with how the feature looks and behaves for them.

This article focuses on the merchant side, since that is where most app feedback comes from. The storefront case appears at the end, where it affects how you set up products.

## Store the shop domain and send it as metadata

Your app already stores the shop domain when a merchant installs it. That domain is the most useful context you have, because it tells you which store is affected without asking the merchant to explain. Send it with every report.

The server function below builds the request. It assumes your app keeps the product key in an environment variable:

```ts
type MerchantFeedback = {
  shop: string;
  merchantName: string;
  merchantEmail: string;
  kind: "bug" | "idea" | "praise" | "other";
  message: string;
  appVersion: string;
};

export async function sendMerchantFeedback(input: MerchantFeedback) {
  const res = await fetch("https://escutaproduto.com/api/v1/feedback", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      key: process.env.ESCUTA_PRODUTO_KEY,
      kind: input.kind,
      message: input.message,
      name: input.merchantName,
      email: input.merchantEmail,
      metadata: { shopDomain: input.shop, appVersion: input.appVersion },
    }),
  });
  if (!res.ok) throw new Error("Feedback failed with status " + res.status);
  const saved = (await res.json()) as { ok: true; id: string };
  return saved.id;
}
```

The API returns `201` for a saved message, and `fetch` treats that as `res.ok`. Errors come back as 400 for validation problems, 404 for an unknown key and 429 when a single server sends too many requests to one product in a minute. The [REST API reference](/docs/api) lists each one.

## Post from your backend, not from the admin page

It is tempting to call the API straight from the admin page. Route the request through your server instead. Three reasons matter for a Shopify app.

First, your server already knows the session. You can confirm that the request comes from an installed shop before it reaches the inbox, which keeps spam away from your team.

Second, the key stays in your configuration. It is public and safe to expose, but keeping calls on the server lets you change the key, add logging and drop obviously bad input in one place.

Third, a server request sends no `Origin` header, so the allowed origins list in your product settings does not interfere. The admin page runs inside the Shopify admin, and the origin of that page is not the one you want to allow for a public widget anyway.

## Link the hosted form in onboarding emails

Merchants who install your app and then go quiet are a useful group to hear from. Add the hosted feedback page to your onboarding emails, for example in a line that says "Something unclear? Tell us in one short message". The [hosted feedback page](/docs/hosted-page) lives at `https://escutaproduto.com/f/your-product-slug`.

You can prefill the merchant's email with `?email=`, so the form already knows who is writing. Do this only if your privacy notice covers it, because the address is personal data. Escuta Produto does not send emails, so the message comes from your own email provider, and the reply goes out from your address too.

## Separate merchants from shoppers

If your app also adds a storefront feature, create two products. Merchant feedback and shopper feedback differ in tone, urgency and fix. A bug that stops a merchant from publishing a product is an emergency, while a shopper who finds a button hard to read is a design task.

Two products mean two keys, two sets of allowed origins and two webhooks. Point the storefront product's allowed origins at the storefront domains where the feature runs, which may include the merchant's custom domain. Point the merchant product at your own app domain, and route its webhook to the team that answers support.

The inbox filters by product, so the split costs nothing in daily use. You simply open the product you want to read.

## Keep store data out of feedback

Feedback is stored for your team to read, so treat it like any other customer data. Send the shop domain, the app version and the merchant's own message. Do not send order records, customer lists, product catalogs, API tokens or session data. A tempting shortcut is to attach the store's last error log, but that log can contain customer information that you should not copy into a feedback item.

When a report needs deeper data, ask for it in the reply. The merchant can share what they are comfortable with, and you can look at the store in your own admin tools.

## Set up Escuta Produto for your Shopify app

Create a product for the merchant side and copy its key into your server configuration. Add your app's domain to the allowed origins if you also use the widget, and send a test report from a development store. Check that the shop domain and app version appear in the item.

Connect a Slack or Discord webhook to the team that handles support. The [notifications guide](/docs/notifications) explains the setup. Then close the loop: when a fix reaches the store, reply to the merchants who reported it. The [article on telling customers their request shipped](/resources/tell-customers-request-shipped) covers the wording, and the [article on collecting feedback in a WordPress plugin](/resources/collect-feedback-wordpress-plugin) shows the same server-side pattern on a different platform. For the storefront side, [adding a feedback widget to a Shopify store](/resources/feedback-widget-shopify-store) explains how the widget fits in the theme.

## Frequently asked questions

### Should a Shopify app send merchant feedback from the browser or from its server?

From the server. Your backend already knows which shop installed the app and can check the session before it accepts a message. The public key stays in your configuration, and a server request has no Origin header, so the allowed origins list does not block it.

### What should I send as metadata for a Shopify app?

The shop domain, your app version and the plan the merchant chose in your app are useful. Do not send order data, customer lists or access tokens. Keep metadata short, since it is capped at 4 KB.

### Can I use the same Escuta Produto product for merchants and storefront shoppers?

You can, but two products are easier to triage. Each product has its own inbox filter, key, allowed origins and webhook, so merchant requests and shopper messages stay in separate queues.
