# Escuta Produto

> Escuta Produto collects bugs, ideas and praise from every product you ship into one inbox, through an embeddable feedback widget, a hosted feedback page and a REST API.

## Frequently asked questions

### What is Escuta Produto?

Escuta Produto is a customer feedback tool for people who run several software products. It collects bugs, ideas and praise through an embeddable widget, a hosted feedback page and a REST API, and puts everything in one inbox with a filter per product.

### How do I add a feedback widget to my website?

Create a product in the dashboard, copy its public key and paste one script tag before the closing body tag: <script src="https://escutaproduto.com/widget.js" data-key="pk_your_key" defer></script>. A Feedback button appears and messages arrive in your inbox.

### Does it work with Next.js, React, WordPress or plain HTML?

Yes. The widget is a single dependency-free script, so it works on any site that can include a script tag. Next.js apps load it with next/script in the root layout.

### What information is saved with each piece of feedback?

The message, its type (bug, idea, praise or other), an optional 1–5 rating, the page URL, browser, country and any metadata you attach, such as user id, email, plan or app version.

### Can I get notified in Slack or Discord?

Yes. Add an incoming webhook URL to a product and every new feedback posts a message with its type, rating, sender, excerpt and a link to the dashboard.

### Where is the data stored?

Escuta Produto runs on Cloudflare Workers and stores feedback in Cloudflare D1, a SQLite database on Cloudflare's network. You can export any product's feedback to CSV at any time.

---

# Install the feedback widget

> Paste one `<script>` tag with your product key before `</body>`. A Feedback button appears and every message lands in your Escuta Produto inbox with the page URL, browser and country attached.

Source: https://escutaproduto.com/docs/widget
Last updated: 2026-10-09

## Quick start

1. Sign in to the [dashboard](https://escutaproduto.com/dashboard) and create a product.
2. Open **Settings & install** and copy the product key (`pk_…`).
3. Paste the snippet before `</body>` on every page where customers should be able to send feedback.

```html
<script
  src="https://escutaproduto.com/widget.js"
  data-key="pk_your_product_key"
  defer></script>
```

The script is about 5 KB compressed, has no dependencies and renders inside a Shadow DOM, so your CSS can't break the widget and the widget's CSS can't leak into your site. It loads with `defer` and never blocks rendering.

## What does the customer see?

A floating **Feedback** button in the bottom corner. Clicking it opens a small form where the customer picks a type (bug, idea, praise or other), optionally rates the product from 1 to 5 stars, writes a message and, if you haven't identified them, leaves an email. The form shows English or Portuguese based on the browser language.

## Script attributes

| Attribute | Values | Default |
| --- | --- | --- |
| `data-key` | The product's public key. Required. | none |
| `data-color` | Any CSS color for the button and highlights. | `#4f46e5` |
| `data-position` | `right` or `left` | `right` |
| `data-locale` | `pt-BR` or `en` | browser language |
| `data-trigger` | `button` shows the floating button, `none` hides it | `button` |

## How do I open the widget from my own button?

Set `data-trigger="none"` to hide the floating button, then add `data-escuta-open` to any element. Its value preselects the feedback type.

```html
<button data-escuta-open="bug">Report a bug</button>
<button data-escuta-open="idea">Suggest a feature</button>
<a href="#" data-escuta-open>Send feedback</a>
```

## How do I attach the signed-in user?

Push an `identify` call onto the queue. It works before or after `widget.js` loads. `email` and `name` fill the matching fields, so the customer isn't asked for an email. Every other key (`id`, `plan`, `company`…) is stored as metadata on each message.

```html
<script>
  window.EscutaProduto = window.EscutaProduto || { q: [] };
  (window.EscutaProduto.q ||= []).push(["identify", [{
    email: user.email,
    name: user.name,
    id: user.id,
    plan: user.plan
  }]]);
</script>
```

## JavaScript API

Once the script has loaded, `window.EscutaProduto` exposes:

| Method | What it does |
| --- | --- |
| `EscutaProduto.open({ kind })` | Opens the widget. `kind` is optional: `bug`, `idea`, `praise` or `other`. |
| `EscutaProduto.close()` | Closes the widget. |
| `EscutaProduto.identify({...})` | Sets the user for every message sent afterwards. |
| `EscutaProduto.setMetadata({...})` | Attaches extra context, such as the current screen or a feature flag. Up to 4 KB of JSON. |

## Restrict which sites can use your key

The product key is public by design: it ships in your HTML. To stop other sites from sending feedback with it, list your domains under **Allowed origins** in the product settings (for example `https://app.example.com`). Requests from any other site are rejected with `403`.

## Limits

- 10 messages per minute per visitor and product.
- Messages up to 5,000 characters, metadata up to 4 KB.
- A hidden honeypot field silently drops simple bots.

## Frequently asked questions

### Does the feedback widget slow down my website?

No. The script is about 5 KB compressed, has no dependencies, loads with defer and renders inside a Shadow DOM, so it doesn't block rendering or interfere with your CSS.

### Is it safe to put the product key in my HTML?

Yes. The key only allows sending feedback, never reading it. Add your domains to Allowed origins so other sites can't use it, and rotate the key from product settings if needed.

### Can I use my own button instead of the floating one?

Yes. Set data-trigger="none" on the script tag and add the data-escuta-open attribute to any element, optionally with a type such as data-escuta-open="bug".

---

# Add a feedback widget to a Next.js app

> Load `https://escutaproduto.com/widget.js` once in `app/layout.tsx` with `<Script strategy="afterInteractive" data-key="pk_…" />`, then push an `identify` call from a client component to attach the signed-in user.

Source: https://escutaproduto.com/docs/nextjs
Last updated: 2026-10-09

## 1. Load the script in the root layout

`next/script` forwards `data-*` props to the script tag, so the widget reads its options as usual.

```tsx
// app/layout.tsx
import Script from "next/script";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://escutaproduto.com/widget.js"
          data-key="pk_your_product_key"
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}
```

Loading it in the root layout keeps one widget instance across client-side navigations. The widget records `location.href` at the moment of sending, so each message has the right page URL even in a single-page app.

## 2. Identify the signed-in user

Render this client component anywhere inside your authenticated layout. The queue form works whether or not the widget has finished loading.

```tsx
// components/feedback-identity.tsx
"use client";
import { useEffect } from "react";

type User = { id: string; email: string; name?: string; plan?: string };

export function FeedbackIdentity({ user }: { user: User }) {
  useEffect(() => {
    const w = window as typeof window & { EscutaProduto?: { q?: unknown[][] } };
    w.EscutaProduto = w.EscutaProduto || { q: [] };
    (w.EscutaProduto.q ||= []).push(["identify", [user]]);
  }, [user]);
  return null;
}
```

## 3. Open the widget from your own UI (optional)

Add `data-trigger="none"` to the `<Script>` to hide the floating button, then use the `data-escuta-open` attribute on any element, including inside Server Components, because it needs no JavaScript of its own:

```tsx
<button data-escuta-open="bug">Report a bug</button>
```

## Content Security Policy

If your app sends a CSP header, allow the script and the API:

```text
script-src  'self' https://escutaproduto.com;
connect-src 'self' https://escutaproduto.com;
```

## Sending feedback from the server

For Server Actions, Route Handlers or background jobs, call the [REST API](/docs/api) directly instead of the widget. Requests without an `Origin` header are accepted from any server.

## Frequently asked questions

### Should I use next/script or a plain script tag in Next.js?

Use next/script with strategy="afterInteractive" in app/layout.tsx. It loads the widget once after hydration and keeps it across client-side navigations.

### Does the widget work with the Next.js App Router and Server Components?

Yes. The script is loaded once in the root layout, and the data-escuta-open attribute works on elements rendered by Server Components because it needs no client-side React code.

---

# Feedback REST API

> `POST https://escutaproduto.com/api/v1/feedback` with a JSON body containing your product `key` and a `message`. A success returns `201` with the new feedback `id`. The full contract is published as OpenAPI 3.1 at `/openapi.json`.

Source: https://escutaproduto.com/docs/api
Last updated: 2026-10-09

## Endpoint

```text
POST https://escutaproduto.com/api/v1/feedback
Content-Type: application/json
```

No API secret is needed: the product key identifies where the feedback goes and can only be used to send feedback, never to read it. The machine-readable spec lives at [/openapi.json](https://escutaproduto.com/openapi.json).

## Example

```bash
curl -X POST https://escutaproduto.com/api/v1/feedback \
  -H "content-type: application/json" \
  -d '{
    "key": "pk_your_product_key",
    "kind": "idea",
    "message": "Let me export invoices as PDF",
    "rating": 4,
    "email": "customer@example.com",
    "metadata": { "plan": "pro", "appVersion": "2.3.1" }
  }'
```

```json
{ "ok": true, "id": "3f0c6a8e-7d1b-4c55-9a43-1f2e8b7c9d10" }
```

## Request body

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `key` | string | yes | The product's public key (`pk_…`). |
| `message` | string | yes | At least 2 characters. Text beyond 5,000 characters is cut off. |
| `kind` | string | no | `bug`, `idea`, `praise` or `other`. Defaults to `other`. |
| `rating` | integer | no | 1 to 5. |
| `name` | string | no | Up to 120 characters (longer values are cut off). |
| `email` | string | no | A valid email address, up to 254 characters. |
| `pageUrl` | string | no | An http(s) URL, up to 2,048 characters. Other values are ignored. |
| `metadata` | object | no | Any JSON object up to 4 KB, such as user id, plan or app version. |

## Responses

| Status | Meaning |
| --- | --- |
| `201` | Saved. Body: `{ "ok": true, "id": "…" }`. |
| `400` | Invalid JSON or a field failed validation. Body: `{ "error": "…" }`. |
| `403` | The request came from a browser origin that isn't in the product's allowed origins. |
| `404` | Unknown product key. |
| `413` | Body larger than 16 KB. |
| `429` | More than 10 submissions per minute from the same IP to the same product. |

## Calling it from a browser

Browsers may call the endpoint directly. It answers CORS preflights, and the widget sends `text/plain` to skip the preflight entirely. If the product has **Allowed origins** configured, the `Origin` header must match one of them. Server-side requests (no `Origin` header) are always accepted.

## Calling it from a backend

```ts
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: "bug",
    message: "Checkout failed with card_declined",
    metadata: { orderId: "ord_123" },
  }),
});
```

## Frequently asked questions

### Do I need an API key or secret to send feedback?

No. Send the product's public key in the request body. It can only create feedback, never read it, so it's safe to ship in apps and websites.

### What is the rate limit of the feedback API?

10 submissions per minute per IP address and product. Requests over the limit get HTTP 429.

### Is there an OpenAPI specification?

Yes. The OpenAPI 3.1 document is published at https://escutaproduto.com/openapi.json and can be imported into Postman, Insomnia or any code generator.

---

# Hosted feedback page

> Each product has a ready-made feedback form at `https://escutaproduto.com/f/<slug>`. It needs no install, works on any device and switches between English and Portuguese automatically.

Source: https://escutaproduto.com/docs/hosted-page
Last updated: 2026-10-09

## Where do I find the link?

Open the product in the [dashboard](https://escutaproduto.com/dashboard) and go to **Settings & install**. The link looks like this:

```text
https://escutaproduto.com/f/your-product-slug
```

## When should I use it instead of the widget?

Use the hosted page anywhere you can't run a script:

- Onboarding and churn emails ("Tell us what's missing").
- App Store and Google Play listings.
- Support replies and help center articles.
- Printed QR codes at events or in packaging.

## Language

The form follows the visitor's browser language. Force one with a query parameter:

```text
https://escutaproduto.com/f/your-product-slug?lang=pt
https://escutaproduto.com/f/your-product-slug?lang=en
```

## Prefill the email

Pass `email` when you send the link to a known customer so they don't have to type it:

```text
https://escutaproduto.com/f/your-product-slug?email=customer%40example.com
```

## Look and privacy

The page uses the product's accent color from settings. It is excluded from search engines (`noindex`), so your customers' feedback forms don't show up in search results.

---

# Slack and Discord notifications

> Paste a Slack or Discord incoming webhook URL into the product's **Notification webhook** setting. Every new feedback posts the type, rating, sender, message excerpt and a link to the dashboard.

Source: https://escutaproduto.com/docs/notifications
Last updated: 2026-10-09

## Set it up

1. Create an incoming webhook:
   - **Slack:** *Apps → Incoming Webhooks → Add to Slack*, then pick a channel.
   - **Discord:** *Channel settings → Integrations → Webhooks → New Webhook → Copy Webhook URL*.
2. In Escuta Produto, open the product's **Settings & install** page.
3. Paste the URL into **Notification webhook** and save.

## What the message looks like

```text
💡 Idea on Acme ★★★★☆ from customer@example.com
> Let me export invoices as PDF
https://escutaproduto.com/dashboard/feedback/3f0c6a8e-…
```

The excerpt is cut at 500 characters. The link opens the full message with its page URL, browser, country and metadata.

## Payload

Escuta Produto sends one JSON body that both services understand:

```json
{ "text": "💡 Idea on Acme …", "content": "💡 Idea on Acme …" }
```

Slack reads `text`; Discord reads `content`. Any other service that accepts one of those fields works too.

## Delivery

Notifications are sent in the background after the feedback is saved, so a slow or failing webhook never blocks or loses a submission.

---

# How to collect customer feedback for a software product

> Ask inside the product at the moment of use, keep the form to one open question plus a type and optional rating, capture context automatically (page, device, user, plan), and send every channel into a single inbox you review weekly.

Source: https://escutaproduto.com/resources/how-to-collect-customer-feedback
Last updated: 2026-10-09

## Why ask inside the product?

Customers remember friction for seconds, not days. A question asked while they're looking at the broken screen gets a specific answer ("the export button does nothing on Safari"). The same question in a survey a week later gets a vague one ("exporting is buggy"), or none at all. An always-available feedback button inside the product catches those moments without interrupting anyone.

Use other channels to reach the people the in-app button can't:

| Channel | Best for | Example |
| --- | --- | --- |
| In-app widget | Active users, bugs and ideas in context | A Feedback button on every page |
| Hosted feedback link | Places without your code | Onboarding emails, app store listings, QR codes |
| API from your backend | Moments your server knows about | Cancellations, failed payments, support tickets |

## What should the form ask?

Keep it short. Every extra field lowers the number of people who finish.

1. **One open question.** "What's on your mind?" works better than a list of leading questions.
2. **A type.** Bug, idea, praise or other. Customers classify their own message, which saves you triage time.
3. **An optional 1–5 rating.** Cheap to answer, and a falling average is an early warning.
4. **Email only when you don't know who they are.** If the user is signed in, attach their identity automatically instead of asking.

## What context should you capture automatically?

The message is half of the story. Capture the rest without asking:

- The **page URL** where the feedback was sent.
- **Browser and device** (user agent) and **country**.
- **Who sent it**: user id, email, plan, company.
- **App state**: version, feature flags, current screen.

With Escuta Produto, the widget records the page, browser and country by itself, and `identify()` / `setMetadata()` attach the rest. See [Install the feedback widget](/docs/widget).

## Put every channel in one inbox

Feedback spread across email threads, chat messages and support tools gets lost. Send the widget, the hosted link and backend events to the same place, tagged by product. If you run several products, one inbox with a filter per product beats one tool per product.

Turn on [Slack or Discord notifications](/docs/notifications) so new messages are seen within minutes, but do the actual review in the inbox, in batches.

## Which moments are worth a dedicated prompt?

An always-on button covers most feedback, but a few moments deserve their own entry point because the customer's intent is unusually clear:

- **Onboarding.** New users notice friction that long-time users have learned to ignore. See [How to ask for feedback during onboarding](/resources/onboarding-feedback).
- **Right after a new feature ships.** Put a "Tell us what you think" link next to the feature for its first weeks. See [How to collect feedback on a new feature launch](/resources/feature-launch-feedback).
- **Cancellation.** The last chance to learn why someone is leaving. Send the reason from your backend with the API so it lands in the same inbox. See [How to collect cancellation and churn feedback](/resources/cancellation-feedback).
- **Empty states and errors.** A screen with nothing on it, or an error message, is where expectations and reality meet. A small "Was this what you expected?" link there gets unusually specific answers.

Keep each of these prompts optional and quiet. A modal that blocks the screen gets closed, not answered.

## Close the loop

Customers keep sending feedback when they see it go somewhere:

- Reply to people who left an email, especially for bugs.
- Mark items as **planned**, **in progress** and **done** so you can tell them when it ships.
- Mention fixes in your changelog and credit the request.

Read [How to triage customer feedback](/resources/how-to-triage-customer-feedback) for a weekly routine and [How to reply to customer feedback](/resources/reply-to-customer-feedback) for reply templates.

## How do you do this with Escuta Produto?

Create one product per app or site in the [dashboard](/dashboard), paste the [widget snippet](/docs/widget), share the [hosted feedback page](/docs/hosted-page) in emails and store listings, and send backend events through the [REST API](/docs/api). Everything lands in one inbox with the page, browser, country and your metadata attached, and [Slack or Discord alerts](/docs/notifications) tell you when something new arrives.

## Checklist

- [ ] Feedback button available on every page of the product.
- [ ] Form: open question, type, optional rating, email only for anonymous users.
- [ ] Signed-in users identified automatically.
- [ ] Page URL, device, country and plan attached to every message.
- [ ] Hosted link in onboarding emails and store listings.
- [ ] All channels land in one inbox, with real-time alerts.
- [ ] A weekly review slot on the calendar.

## Frequently asked questions

### What is the best way to collect customer feedback for a SaaS product?

An always-available in-app feedback button, because it captures specific problems at the moment they happen. Complement it with a hosted feedback link for emails and app stores, and API calls for events your backend sees, all landing in one inbox.

### How many questions should a feedback form have?

As few as possible: one open question, a feedback type and an optional 1–5 rating. Ask for an email only when the user isn't signed in; otherwise attach their identity automatically.

### What context should be saved with each piece of feedback?

The page URL, browser and device, country, the user's id or email, their plan, and app state such as version or feature flags. Capture it automatically so customers only write the message.

---

# How to triage customer feedback

> Review new feedback in one weekly batch. Give every item a status (new, planned, in progress, done or closed), group repeats by type and page, act on bugs first, and tell customers when their request ships.

Source: https://escutaproduto.com/resources/how-to-triage-customer-feedback
Last updated: 2026-10-09

## Use a small, fixed set of statuses

A status answers one question: what happens next with this item? Five are enough:

| Status | Meaning |
| --- | --- |
| **New** | Nobody has read it yet. Your inbox-zero target. |
| **Planned** | You decided to act on it. |
| **In progress** | Someone is working on it now. |
| **Done** | Shipped or fixed. Time to tell the customer. |
| **Closed** | Read and decided not to act, a duplicate, or spam. |

Everything outside **New** has been read. Everything in **Planned** or **In progress** is a promise.

## The weekly routine

Set aside 30 minutes once a week. Real-time alerts tell you that something arrived; the weekly batch is where you decide.

1. **Filter to New.** Read every item.
2. **Bugs first.** Reproduce with the page URL and browser saved on the item. Fix or move to Planned.
3. **Ideas: look for repeats.** Search the inbox for the same words. Five customers asking for the same export format is a signal; one is an anecdote.
4. **Praise: note what to protect.** Praise tells you what not to break and makes good copy for your landing page (with permission).
5. **Close the rest** with a short internal note explaining why, so you don't re-decide it next month.
6. **Check the 30-day chart and rating.** A spike in bugs after a release or a falling average rating is worth a look even when no single message is alarming.

## How do you prioritize feature requests?

Weigh each idea by:

- **Frequency**: how many distinct customers asked.
- **Who asked**: paying customers on the plan you want to grow count more.
- **Effort**: small requests that many people want are the easy wins.
- **Fit**: does it move the product in the direction you already chose?

Feedback is input, not a vote. Customers describe problems well and solutions badly; look for the problem behind the request.

## Close the loop

When an item moves to **Done**, reply to the customers who asked. A one-line "this shipped today, thanks for the idea" turns a customer into someone who sends feedback again. Use the internal notes on each item to record who to tell.

## Common triage mistakes

- **Letting New pile up.** An inbox with 300 unread items stops being read at all. Close aggressively; you can always reopen.
- **Treating every request as a vote.** Ten requests from free trials that never converted weigh less than two from your best customers.
- **Deciding without context.** Read the page URL, plan and metadata before judging a bug as "can't reproduce".
- **Never saying no.** Closing an idea with a note is a decision. Leaving it open forever is not.

## Export for deeper analysis

A CSV export of a product's feedback lets you count themes in a spreadsheet, share a quarterly summary or feed an AI model to cluster requests. Escuta Produto exports UTF-8 CSV that opens correctly in Excel and Google Sheets.

## Who should own triage?

On a small team, one person owns the weekly pass and everyone else can read the inbox. Ownership rotates monthly so nobody becomes the only person who knows what customers are asking for. On a solo product, the owner is you; protect the slot on your calendar the way you would a customer call.

Whoever owns it makes three kinds of decision and nothing else: what's a bug to fix now, what's an idea worth planning, and what gets closed. Building, designing and replying can be handed to others. If you run several products, see [A feedback routine for running five products](/resources/feedback-routine-multiple-products).

## A worked example

Suppose a week brings in 18 new items for one product (an example, not a benchmark):

| Type | Count | Decision |
| --- | --- | --- |
| Bug | 5 | 3 reproduced and fixed, 1 planned, 1 closed as a duplicate with a note |
| Idea | 9 | 2 repeats of an existing planned item (noted), 1 new item planned, 6 closed with reasons |
| Praise | 3 | Noted what customers liked; asked one for permission to quote them |
| Other | 1 | A billing question forwarded to support, then closed |

The inbox ends the week at zero new items, and every decision has a short note explaining it.

## Related

- [How to collect customer feedback](/resources/how-to-collect-customer-feedback)
- [How to deduplicate feature requests](/resources/deduplicate-feature-requests)
- [How to design a feedback status workflow](/resources/feedback-status-workflow)
- [Slack and Discord notifications](/docs/notifications)

## Frequently asked questions

### How often should a team review customer feedback?

Once a week in a fixed 30-minute batch, with real-time Slack or Discord alerts only for awareness. Batching lets you spot repeats and decide consistently.

### What statuses should a feedback workflow use?

Five are enough: New (unread), Planned (decided to act), In progress (being worked on), Done (shipped, tell the customer) and Closed (won't act, duplicate or spam).

### How do you prioritize feature requests from customers?

Weigh how many distinct customers asked, which customers asked, how much effort it takes and whether it fits the product direction. Look for the underlying problem rather than the exact solution requested.

---

# Feedback widget best practices

> Keep the button visible but quiet, open a short form instead of a survey, load the script deferred and isolated, identify signed-in users, restrict the key to your domains and rate-limit submissions.

Source: https://escutaproduto.com/resources/feedback-widget-best-practices
Last updated: 2026-10-09

## Placement: visible, never in the way

- Put a small floating button in a bottom corner, on the side your chat or cookie banner **isn't** on.
- Add a contextual entry point where problems happen: "Report a problem" next to an export, "Suggest an improvement" at the end of a flow. Preselect the type so the form opens ready.
- Don't pop the form open on its own. Feedback that customers start themselves is more specific and more honest than feedback you interrupt them for.

With Escuta Produto, `data-position` moves the button and any element with `data-escuta-open="bug"` opens the form with the type preselected.

## Keep the form short

One open question, a type (bug, idea, praise, other) and an optional rating. Skip required fields you could fill yourself: if the user is signed in, attach their name and email automatically. A long form turns a 20-second report into a chore customers abandon.

## Performance

A feedback widget should never cost you speed:

- **Load it with `defer`** (or `next/script` with `afterInteractive`) so it never blocks rendering.
- **Keep it small** and free of dependencies like React or a CSS framework.
- **Isolate it in a Shadow DOM** so your styles and the widget's styles can't collide.
- **Avoid CORS preflights**: sending `text/plain` saves a round trip on every submission.

## Accessibility

- The trigger is a real `<button>` with a text label, not an icon-only div.
- The panel is a dialog that closes with **Escape** and returns focus to the trigger.
- Inputs have labels, and errors are announced in text, not only in color.
- Colors meet contrast on both light and dark sites.

## Language

Show the form in the visitor's language. Detect it from the browser and let the site override it (Escuta Produto's `data-locale`). A Portuguese-speaking customer writes more, and more clearly, in Portuguese.

## Spam and abuse

A public form will be found by bots. Layer cheap defenses:

1. **Allowed origins**: only your domains may submit with your key.
2. **Rate limiting** per visitor and product.
3. **A honeypot field** that humans never see; bots fill it and are silently dropped.
4. **Size limits** on the message and metadata.

## Privacy

Collect only what helps you act: page, browser, country and the user's identity in your own product. Don't capture keystrokes, screenshots or form contents without asking. Mention the widget in your privacy policy.

## How do you know the widget is working?

Watch three numbers in the first month:

1. **Volume per product.** A widget that gets nothing is usually hidden, broken by a Content Security Policy, or loaded on the wrong pages.
2. **Share of messages with an identified user.** If it's low on a logged-in product, `identify()` isn't being called.
3. **Type mix.** Mostly bugs after a release is normal; mostly "other" means the type buttons aren't clear to your customers.

If volume is low, add a contextual entry point next to your most-used feature before redesigning anything.

## Mobile

On phones the floating button competes with bottom navigation bars, cookie banners and chat bubbles. Check it on a real device:

- The button shouldn't cover navigation or the main call to action. Move it to the other side with `data-position="left"` if needed.
- When the keyboard opens, the message field must stay visible above it.
- Tap targets need to be comfortable for a thumb, not just a mouse pointer.

More detail in [Designing a feedback widget for mobile screens](/resources/feedback-widget-mobile-design).

## Test it before customers see it

Before rolling the widget out, walk through it as a customer would:

1. Open it with the mouse, then again with only the keyboard (Tab to the button, Enter to open, Escape to close).
2. Send one message of each type and confirm each arrives in the inbox with the right page URL.
3. Sign in as a test user and check that the email field disappears because `identify()` filled it.
4. Load the page with your Content Security Policy enabled and look for blocked requests in the browser console.
5. Check it on a dark page and a light page, on a phone and on a desktop.

Ten minutes of testing catches the problems that otherwise show up as an empty inbox.

## What should the button say?

"Feedback" is short and understood in most products. Contextual entry points can be more specific, such as "Report a problem" or "Suggest an improvement", because the customer already knows what they want to say. Keep labels in the customer's language. See [What should a feedback button say?](/resources/feedback-button-copy) for options.

## Related

- [Install the feedback widget](/docs/widget)
- [Add a feedback widget to a Next.js app](/docs/nextjs)
- [How to make a feedback widget accessible](/resources/accessible-feedback-widget)
- [How to protect a feedback form from spam](/resources/feedback-form-spam-protection)

## Frequently asked questions

### Where should a feedback button be placed on a website?

As a small floating button in a bottom corner that doesn't collide with chat or cookie banners, plus contextual links such as 'Report a problem' next to features where issues happen.

### How do you stop spam in a feedback widget?

Combine an allowed-origins list for your public key, rate limiting per visitor, a hidden honeypot field and size limits on the message and metadata.

### Will a feedback widget slow down my site?

Not if it loads with defer, has no dependencies, stays small and renders in a Shadow DOM. Escuta Produto's widget is about 5 KB compressed and follows all four rules.

---

# What is in-app feedback?

> In-app feedback is a form inside a product that lets customers send a message without leaving the screen they are on. The form attaches the page URL, browser and any user details you pass, so each message arrives with the context needed to act on it.

Source: https://escutaproduto.com/resources/what-is-in-app-feedback
Last updated: 2026-10-09

## What does in-app feedback look like?

In-app feedback is a short form that lives inside your product. A customer clicks a button labeled "Feedback", picks a type such as bug, idea, praise or other, types a message and sends it. The message reaches your team without an email client, a support address or a separate web page.

The button can be a floating element in a corner of the screen, a link in a settings menu or a button placed next to one feature. Escuta Produto's widget adds the floating button by default. Any element with a `data-escuta-open` attribute opens the same form, so you can place entry points wherever a customer is most likely to have something to say.

## How does it differ from email and surveys?

Three channels compete for the same customer attention. Each answers a different question.

| Channel | Who starts it | Context you get | Best for |
| --- | --- | --- | --- |
| In-app feedback | The customer, when they have something to say | Page, browser and any user details you send | Bugs, ideas and praise tied to a screen |
| Support email | The customer, often after the problem has grown | Whatever the customer types | Account problems that need a conversation |
| Survey | You, on a schedule you choose | Only the answers to the questions you wrote | Comparing the same question over time |

Email is the default for most small teams, and it works for one-off questions. Its weakness is missing context. A message that says "the export is broken" arrives without the page, the browser or the plan, so someone has to ask follow-up questions before the item is useful.

A survey asks the questions you chose. It answers "what do you think of this?" but misses the message you did not think to ask about, such as "this button did nothing on my phone". In-app feedback is open by default, so customers can raise the problem you did not expect. Escuta Produto does not run surveys or NPS campaigns. If you need scheduled questions, run them with another tool and post the results through the REST API with metadata.

## When does in-app feedback work best?

Show the entry point at a moment when the customer has something concrete to say:

1. **Right after a task.** Exporting a report, inviting a teammate or finishing a setup step are good moments to ask how it went.
2. **Next to a new feature.** A button beside a feature you just shipped can preselect the idea or bug type, so the feedback is about that feature.
3. **On error screens.** A "something went wrong" page is where the most useful bug reports begin. A feedback link there turns a dead end into a report.
4. **In settings or help menus.** Customers who want to suggest something larger will look there.

Keep the form away from onboarding steps and checkout. A customer who is trying to pay should not be stopped by a feedback prompt, and a new user who has not yet reached the core feature has nothing to report about it.

## What should the form ask for?

A good in-app form asks for very little. A typical form has four parts:

- **Type**: bug, idea, praise or other. Customers pick the type themselves, and you can preselect it from the entry point.
- **Rating**: an optional 1 to 5 star rating. It gives you a quick trend number alongside the written message.
- **Message**: the only required field in practice. Ask for the message itself, not for a long template.
- **Email**: shown only when the user is not identified. If you pass the email with `identify()`, customers never see the field.

Every extra field lowers the number of people who finish the form. Let the page context do the work that a long form would otherwise do.

## What context does an in-app message carry?

Context is what separates in-app feedback from a contact form. The Escuta Produto widget saves the following automatically or from your code:

- **Page URL**: the screen the customer was on when they sent the message.
- **Browser**: the user agent string, which names the browser and operating system. It helps when a layout bug only appears in one browser.
- **Country**: added by the server when the message arrives.
- **User details**: the email and name you pass to `identify()`, plus any other keys, such as an account id or plan name, stored as metadata on every message.

With the page URL, a message about a broken export becomes a link you can open. You can load the same page, try the same action and see the failure. Without it, the first reply is usually a question about where the customer was.

## What are the limits of in-app feedback?

In-app feedback has real limits, so plan around them:

- **It only reaches people who use the product.** Prospects, trial visitors and churned customers will not see your button. Keep a support address or a hosted feedback page linked from your site for them.
- **A broken product can block the button.** If a bug stops the page from loading, the customer cannot send the report. Link the hosted feedback page from your help center as a fallback.
- **It creates volume.** A busy product can produce dozens of messages a week. You need a routine to read and decide on them, which is covered in [what feedback triage is](/resources/what-is-feedback-triage).
- **It does not capture the screen.** Escuta Produto does not record sessions or take screenshots. Ask customers to describe what they see when the bug is visual, or use a dedicated visual bug tool for that job.

## How do you start with in-app feedback in Escuta Produto?

Copy the script tag from your product settings into your app, with your product key in the `data-key` attribute. For logged-in users, queue an `identify` call so every message carries who sent it:

```js
window.EscutaProduto = window.EscutaProduto || { q: [] };
(window.EscutaProduto.q ||= []).push(["identify", [{ email: user.email, name: user.name, id: user.id }]]);
```

Then read the [widget setup guide](/docs/widget) for placement options such as `data-position`, `data-color` and `data-trigger`. If some customers need a link instead of a button, the [hosted feedback page](/docs/hosted-page) gives them one. For a deeper definition of the context that makes messages useful, see [what feedback metadata is and why it matters](/resources/what-is-feedback-metadata), and for the difference between the button and the wider collection process, read [what a feedback widget is](/resources/what-is-a-feedback-widget).

## Frequently asked questions

### Is in-app feedback the same as a survey?

No. A survey asks the questions you wrote, on a schedule you choose. In-app feedback is a form the customer opens when they want to say something, so it captures bugs and ideas you did not think to ask about.

### Does in-app feedback require the customer to log in?

No. Anyone who can see the button can send a message. If you identify logged-in users with their email and id, the team knows who wrote in and the form hides the email field.

### Where should an in-app feedback button go?

Use a floating button for general feedback and place buttons next to specific features when you want a preselected type. Keep the form out of checkout and onboarding steps, where it would slow the customer down.

### Can customers send feedback if the app is broken?

Only if they can reach the button. Link a hosted feedback page from your help center or support pages as a fallback, so customers can still send a report when the product will not load.

---

# What is a feedback widget?

> A feedback widget is a small script that adds a feedback button and form to a web page. It has three parts: a trigger that opens it, a form that collects the message, and context capture that attaches the page URL and browser without asking the customer.

Source: https://escutaproduto.com/resources/what-is-a-feedback-widget
Last updated: 2026-10-09

## Which parts make up a feedback widget?

A feedback widget has three parts that work together:

1. **The trigger.** The element a customer clicks to open the widget. Usually this is a floating button in a corner, but it can be any link or button you mark up yourself.
2. **The form.** The panel that opens. It asks for the type of message, the message itself and, optionally, a rating and an email address.
3. **Context capture.** The data the widget attaches without asking. The page URL, the browser string and any user details your code passes in.

Remove any one part and the widget becomes something else. A form with no trigger is a page nobody finds. A trigger with no context is a contact link that produces unanswerable messages.

## What does the trigger need to do?

The trigger has one job: make it obvious where feedback goes, without taking attention from the page. Good triggers are small, labeled and placed where they do not cover content, such as the bottom corner of the screen.

Most embeddable widgets, including Escuta Produto's, let you choose the side and color of the floating button. You can also hide the floating button and open the form from your own elements. In the Escuta Produto widget, `data-trigger="none"` hides the floating button, and any element with `data-escuta-open` opens the form. The value of that attribute, such as `bug`, preselects the message type.

## How does the form collect a message?

A widget form should be short enough to finish in under a minute. The fields that matter most are the type and the message. Everything else is optional, which keeps the completion rate high for the messages you care about.

Customers choose a type from buttons such as bug, idea, praise and other. Some forms add a star rating, and the Escuta Produto form offers an optional 1 to 5 star rating. An email field is useful for people you cannot identify, but hide it when your code already knows who is signed in.

## What is context capture and why does it matter?

Context capture is the difference between a useful report and a vague one. A widget that saves the page URL tells you where the customer stood. A widget that saves the browser string tells you which environment they used.

The Escuta Produto widget saves the page URL and the user agent on every message. The server adds the country. Your code adds the rest through the JS API, for example:

- `EscutaProduto.identify({ email, name, id, plan })` sets who the customer is for every message sent after the call.
- `EscutaProduto.setMetadata({...})` attaches extra JSON, up to 4 KB, such as the feature flag a customer is testing.

For a full explanation of what each field is for, read [what feedback metadata is and why it matters](/resources/what-is-feedback-metadata).

## What are the main types of feedback widget?

Widgets differ mainly in how they are opened and where they sit:

| Type | How it opens | Best for |
| --- | --- | --- |
| Floating button | Always visible in a corner | General feedback on any page |
| Inline trigger | A link or button you place next to a feature | Feedback about one feature or screen |
| Code-triggered form | Your code calls the open method after an event | Error screens and post-task moments |
| Hosted page | A link to a separate page, not a widget | Customers who cannot load the app, and sharing in emails |

A hosted page is not a widget because it lives on its own address. It is useful as a fallback, and the [hosted feedback page](/docs/hosted-page) shows how to link to one.

## How do you keep a widget from slowing the page?

Load the script with the `defer` attribute so the page renders first. The Escuta Produto widget is about 5 KB compressed, has no dependencies and renders inside a Shadow DOM, so its styles do not leak into your site and your styles do not leak into it. Those choices keep the impact on the rest of the page small.

Check the widget on a slow connection before you publish it. If the page is slower after adding the script, move the script lower in the body or load it only on pages where customers can act on it.

## How do you add a feedback widget with Escuta Produto?

Copy the script tag from your product settings into the pages where feedback makes sense. The tag carries your public product key in `data-key`, which can only create feedback and never read it. Add `data-color` to match the button to your brand and `data-position` to move it to the left.

Set the allowed origins in product settings so only your own sites can submit through the widget. That single setting blocks most abuse from copied keys. The full list of attributes is in the [widget documentation](/docs/widget).

If you are not sure whether to build your own widget or use a ready one, the [build versus buy](/resources/build-vs-buy-feedback-widget) comparison covers the work a widget hides. For the wider picture of how feedback arrives, read [what in-app feedback is](/resources/what-is-in-app-feedback).

## Frequently asked questions

### What is a feedback widget in simple terms?

A feedback widget is a small script that adds a feedback button and a form to your website or app. Customers open it, write a message and send it, and the message arrives with the page they were on.

### Do feedback widgets slow down a website?

They can, if they are large or load early. Load the script deferred, keep it small and check the page speed before and after. Widgets that run in a Shadow DOM and have no dependencies add very little weight.

### Can I hide the floating button and open the widget from my own button?

Yes. Set the trigger option to none to hide the floating button, then add the open attribute to any element you want. The value of that attribute can also preselect the type of message.

### Does a feedback widget need a secret key?

No. The public product key can only create feedback, never read it. Set allowed origins so only your own sites can submit messages with the key.

---

# What is feedback triage?

> Feedback triage is the process of reading new customer messages and deciding what happens next. Each item gets one decision: act on it, plan it, close it, or ask the customer for more detail. Teams usually run triage on a fixed weekly schedule.

Source: https://escutaproduto.com/resources/what-is-feedback-triage
Last updated: 2026-10-09

## What does triage mean for customer feedback?

Triage comes from emergency medicine, where staff sort incoming patients by urgency before treating them. Applied to feedback, triage means sorting incoming messages by what they need from you. Each message gets a decision, and the decision is recorded so nobody has to re-read the message to know its status.

Triage is not the same as fixing. A triage pass can end with a bug fixed, a feature planned, a message closed with a reason or a question sent back to the customer. The point is that every item leaves the unread pile with a clear next step.

## Which decision does each item need?

Every item needs exactly one of four decisions:

- **Act.** The item is a bug you can reproduce, or a request that clearly fits the product. It moves to planned or to in progress.
- **Plan.** The item is valid but not for now. You record it so it can be found later, and you tell the customer if you want them to know it is recorded.
- **Close.** The item is a duplicate, spam, out of scope or already fixed. A short internal note explains why, so the next person does not reopen the same debate.
- **Ask.** The item cannot be acted on yet. A bug report with no steps, or a vague idea, needs one more question before a decision is possible.

The fourth decision is the one teams skip most often. Asking takes a minute and often turns a vague message into a bug you can fix.

## Which statuses does triage use?

Triage needs a short, shared vocabulary for where an item stands. Escuta Produto uses five statuses: new, planned, in progress, done and closed. Each status answers a single question:

- **New** means nobody has decided yet.
- **Planned** means you decided to act, but the work has not started.
- **In progress** means someone is working on it.
- **Done** means the change shipped, and it is time to tell the customers who asked.
- **Closed** means you read it and chose not to act.

Keep the list short. Each extra status adds a decision that someone must make on every item, and the team stops agreeing on what each one means.

## How often should triage run?

Triage works best as a fixed batch rather than a constant trickle. A weekly session is a good starting point for most small teams, with real-time alerts used only to tell people that something arrived.

A batch has two benefits. You can see repeats, such as three customers describing the same confusing step, which is hard to notice one message at a time. You also make decisions with the same standard for every item, because you judge them in one sitting.

If volume grows, shorten the cadence before you add people. A daily ten-minute pass keeps the new pile small. A weekly hour of reading two hundred items is where triage quietly stops.

## What goes wrong when triage is skipped?

Without triage, messages pile up in an unread state and the inbox becomes a list of things nobody remembers. Three problems follow:

1. **Customers stop writing.** When they see no response for weeks, they assume nobody reads the messages.
2. **Old bugs resurface as new reports.** Each customer who hits a known bug writes again, and the same item gets reported many times.
3. **Decisions get made by whoever shouts loudest.** Without a routine, the most recent or most persistent message wins.

Triage fixes all three by giving every item a status and a reason, so the record shows what happened and why.

## How do you run a triage session in Escuta Produto?

Open the product's inbox and filter by the new status. Read each bug with the page URL and browser saved on the item, because that information is often enough to start reproducing it. For each item, set the status and add an internal note with the reason. The note matters more than the status, because it stops the same decision being made twice.

Use the 30-day chart and the average rating on the same screen to notice spikes. A jump in bug messages after a release is a signal that a single status change will not show. For the full routine, including how to order the work, see [how to triage customer feedback](/resources/how-to-triage-customer-feedback). For the rules that decide what happens to each item, read [how to design a feedback status workflow](/resources/feedback-status-workflow).

Escuta Produto has no tags, so keep categories in the status and type, and use internal notes for anything more specific. If you need deeper analysis, export the product's feedback as UTF-8 CSV and count themes in a spreadsheet. The [widget documentation](/docs/widget) covers the entry points that send messages into the inbox in the first place.

## Frequently asked questions

### What is feedback triage in one sentence?

Feedback triage is the regular step where you read each new customer message and make one decision about it: act, plan, close, or ask the customer for more detail. Every item leaves the unread pile with a status.

### How often should a team triage feedback?

A weekly batch of about thirty minutes suits most small teams. A product with high volume may need a short daily pass so the new pile stays manageable. Real-time alerts should only tell people something arrived.

### What is the difference between closing and planning a feedback item?

Planning means you decided to act on the item later. Closing means you read it and chose not to act, because it is a duplicate, out of scope or spam. Always add a short internal note explaining a close decision.

### Who should do feedback triage?

Whoever owns product decisions should own the routine, and the same person should make the final call on each item. Others can read the inbox, but one owner keeps the statuses consistent across weeks.

---

# Bug report vs feature request: what's the difference?

> A bug report describes behavior that contradicts what the product is supposed to do. A feature request asks for behavior the product does not have yet. The test is whether the expected behavior already exists, and the label decides whether the item goes to a fix or to a decision.

Source: https://escutaproduto.com/resources/bug-report-vs-feature-request
Last updated: 2026-10-09

## What makes something a bug report?

A bug report describes behavior that is wrong by the product's own standard. The standard can be written in the docs, promised in the interface or obvious from how the rest of the product works. The customer expected one result, got another, and the gap is a defect.

Typical bug reports look like this: a button saves nothing, a total is off by one day, a page shows another customer's name, or a file export drops the last column. Each one has an expected behavior that the product already claims, even if nobody wrote it down.

## What makes something a feature request?

A feature request asks for behavior the product does not have. Nothing is broken. The customer wants the product to do something new, or to do an existing thing in a way it has never done.

Examples include a request to export to a spreadsheet format the product never supported, a request for a dark theme, or a request to schedule a report. The product is working as designed, and the question is whether the design should change.

The distinction is simple to state and harder to apply. A bug is a gap between what exists and what was promised. A feature request is a gap between what exists and what would be useful.

## What about missing behavior and friction?

Gray areas appear when something is not broken but feels wrong. Two cases come up most often.

**Missing behavior** sits between the two. A customer says "the export does not include archived projects". If the docs say the export covers all projects, it is a bug. If the docs are silent, it is a feature request with a bug-like tone.

**UX friction** is usually a feature request in disguise. "The save button is hard to find" does not describe a defect. It describes a design that could be better. Treat it as an idea, and read the message for the real problem before deciding what to change.

When you cannot decide, ask. A short question such as "what did you expect to happen?" usually answers the question for you.

## Why does the label change the response?

The label decides the path an item takes. A bug goes to reproduction, a fix and a release note, and it usually has a short deadline. A feature request goes to a decision about fit, cost and direction, and it can wait for a planned cycle.

The labels also shape what you tell the customer. For a bug, you can say you are reproducing it and will confirm the fix. For an idea, you can say you have recorded it, without implying a date. Mixing the two causes trouble. Calling a bug an idea sends a broken feature into a backlog, and calling an idea a bug sets an expectation you cannot meet.

## Should customers pick the type?

Customers pick the type when they send a message, and that choice is useful even when it is wrong. The Escuta Produto widget offers four types: bug, idea, praise and other. You can preselect one from the entry point, such as a "report a problem" link that opens with bug selected.

Treat the customer's choice as a hint, not a decision. Read the message, check the page URL and decide yourself. Keep the type the customer chose as the record of what they reported, and write your reading of the item in an internal note and in the status you set.

## How do you handle a message that is both?

Many messages are a bug and a request at once. "The chart does not show last month, and I wish I could compare two months" contains a defect and an idea. Split them into two internal notes, handle the bug first and record the idea as a separate decision.

If you cannot split a message cleanly, handle the defect and file the rest as an idea. Customers rarely mind when you fix the broken part first.

## How do you sort both types in Escuta Produto?

Filter the product inbox by type to see bugs and ideas separately, then work the bug list first. Use the page URL and browser saved on each bug to reproduce it. For ideas, search the inbox for the same words to find repeats, and read the [triage guide](/resources/how-to-triage-customer-feedback) for how to rank them.

If a bug turns out to be a missing feature, change your internal note, not the type the customer chose. Keep the record of what the customer reported, so the history still makes sense later. For the widget options that preselect a type, see the [widget documentation](/docs/widget). For a fuller definition of the form itself, read [what a feedback widget is](/resources/what-is-a-feedback-widget).

## Frequently asked questions

### What is the simplest test for a bug versus a feature request?

Ask whether the product already promises the behavior the customer expected. If it does and the product fails to deliver it, it is a bug. If the behavior is new, it is a feature request.

### What should I do with a message that describes a confusing design?

Treat it as an idea about design, not as a bug. Read the message for the underlying problem, then decide whether the change is worth making. Ask the customer what they expected if the message is unclear.

### Should I trust the type a customer selects in the feedback form?

Use it as a hint, but read the message and the page URL before you decide. Customers often choose the wrong type, so set the status and internal note yourself after reading the item.

### Why does it matter whether something is a bug or a request?

Bugs usually need a fix and a short deadline, while feature requests need a decision about fit and cost. Mixing them up sends broken behavior into a backlog or promises a date for an idea you have not chosen.

---

# Qualitative vs quantitative customer feedback

> Quantitative feedback is feedback you can count, such as star ratings, the number of messages by type or the number of customers who asked for something. Qualitative feedback is the text customers write. Counts show how big a problem is; the words show what it is.

Source: https://escutaproduto.com/resources/qualitative-vs-quantitative-feedback
Last updated: 2026-10-09

## What is quantitative customer feedback?

Quantitative feedback is anything you can count or put on a scale. A star rating is quantitative: a 4 is a 4 regardless of who gave it. So is the number of bug messages this week, the number of customers who asked for an export format or the average rating across a product.

Counts are good at answering "how many?" and "how much?". They make trends visible, and they let you compare one period with another. They also flatten meaning, because two customers can give the same 2 rating for completely different reasons.

## What is qualitative customer feedback?

Qualitative feedback is the open text customers write in their own words. "The invoice PDF shows the wrong tax rate for Portugal" is qualitative. It contains a product, a place, a problem and an implied expectation that a number could never carry.

Qualitative feedback is good at answering "why?" and "what exactly?". It shows the words customers use for a problem, which helps you name features the way they do. It also surfaces problems you did not know existed, because customers describe what matters to them rather than answering the questions you chose.

## What is each one good for?

Use each type for the job it does best:

| Question | Quantitative helps with | Qualitative helps with |
| --- | --- | --- |
| How big is this problem? | Counting distinct customers who report it | Checking that the reports describe the same problem |
| Is the product getting better? | Average rating and bug counts over time | Reading what praise and complaints say now |
| What should we fix first? | Ranking by how many people are affected | Understanding how severe the problem feels |
| What words should we use? | Rarely helpful | Finding the vocabulary customers use |

Counts without text tell you that something is wrong, but not what to change. Text without counts can make one loud customer look like a trend.

## Why combine a rating with a written message?

The most useful feedback form pairs a number with a sentence. The Escuta Produto form offers an optional 1 to 5 star rating and a required message, so every item has both. The rating gives you a quick trend, and the message explains it.

Read the two together. For example, if 12 of 40 customers gave a rating of 2 this month, the count tells you the problem is wide. Reading those 12 messages tells you whether they describe one broken step or several unrelated irritations. In this example the numbers are invented to show the method, not a real result.

## How do you read counts without losing the story?

Counts can hide meaning, so use a simple routine:

1. **Start with the count.** Note how many items arrived and how many describe the same thing.
2. **Read a sample of the text.** Open the messages behind the biggest count, not just the first few.
3. **Name the pattern in your own words.** Write a short internal note such as "export drops archived projects" on the item.
4. **Count again.** Use the internal notes to count distinct customers, not repeated messages from one person.

Counting the notes rather than the raw messages is the step most teams skip, and it is what keeps one enthusiastic customer from distorting the numbers.

## What are the traps with each type?

Each type has a characteristic mistake:

- **Quantitative traps.** Averages hide spread. A rating average of 3 can mean everyone was lukewarm or that half loved the product and half hated it. Check the distribution, not just the mean.
- **Qualitative traps.** Vivid messages feel more representative than they are. A single detailed complaint can dominate a meeting, so tie every quote to a count before you act on it.
- **Mixed traps.** Treating the rating as a measure of the product, when it is a measure of one moment. A customer who rates a product after a failed export is reacting to that export.

## How do you use both in Escuta Produto?

Escuta Produto keeps the rating and the message on each item, and the inbox shows counts by type, the average rating and a 30-day chart. Use those counts to decide where to look, then read the messages behind them. The inbox has text search, so you can find every message that mentions the same word.

For analysis beyond the screen, export the product's feedback as UTF-8 CSV and count themes in a spreadsheet. Escuta Produto does not cluster or score sentiment for you, so the tagging and grouping are work you do with internal notes or the export. For how to turn those notes into decisions, read [how to triage customer feedback](/resources/how-to-triage-customer-feedback). For the rating and message fields in detail, see [what a feedback widget is](/resources/what-is-a-feedback-widget) and the [widget documentation](/docs/widget).

## Frequently asked questions

### What is the main difference between qualitative and quantitative feedback?

Quantitative feedback can be counted or scored, such as ratings and the number of customers who asked for something. Qualitative feedback is the open text customers write, which explains why and what exactly is wrong.

### Which kind of feedback should a small team collect first?

Collect both at once with a short form. A star rating gives you a quick number, and a required written message explains it. Adding a rating costs customers almost nothing and makes the messages easier to rank.

### Can you turn written feedback into numbers?

Yes, by counting distinct customers who describe the same problem. Write a short internal note for each theme, then count the notes. Count customers rather than messages so repeat writers do not inflate the total.

### Is a low average rating enough to decide what to fix?

No. An average can hide a split between very happy and very unhappy customers. Read the distribution and the messages behind the low ratings before you decide what to change.

---

# NPS vs open-ended feedback: which tells you more?

> Net Promoter Score (NPS) asks how likely customers are to recommend a product on a 0 to 10 scale and turns the answers into one number. Open-ended feedback asks what the customer thinks in their own words. NPS tracks loyalty over time, while open text explains what is driving it.

Source: https://escutaproduto.com/resources/nps-vs-open-ended-feedback
Last updated: 2026-10-09

## What does NPS actually measure?

Net Promoter Score asks one question: how likely is the customer to recommend the product to a friend or colleague, on a scale from 0 to 10? Customers who answer 9 or 10 are promoters, those who answer 7 or 8 are passives, and those who answer 0 to 6 are detractors.

The score is the share of promoters minus the share of detractors. It ranges from minus 100, when every customer is a detractor, to 100, when every customer is a promoter. Passives count in the total but not in the result, so a product with many passives scores lower than the raw ratings suggest.

NPS measures willingness to recommend. That is a useful signal of loyalty, and it is easy to track over time because the question never changes.

## What does NPS miss?

A single score cannot say why a customer answered the way they did. A 7 from a customer who loves the product but found one bug looks the same as a 7 from a customer who tolerates it and is looking for a replacement. Both land in the passive group, and the number gives you no way to tell them apart.

NPS also reflects the customers who answer. If the people who never reply are the unhappy ones, the score can look healthier than the product really is. Timing matters too. A score taken right after a support problem is not the same measurement as one taken during a quiet month.

Finally, NPS does not tell you what to build next. It is a headline, not a plan.

## Why does open text explain the score?

Open-ended feedback fills the gap. A customer who writes "the reports take a minute to load on my phone" tells you what is wrong, where it happens and how much it matters to them. That sentence is much harder to get from a number.

Open text also surfaces problems you did not ask about. A fixed question can only ask what you already suspect. An open message can say "the invoice is confusing for my accountant", which may point to a feature you never considered.

The cost is effort. Open text has to be read, grouped and counted by a person, which is why teams often skip it in favor of the score.

## When should you use NPS?

NPS works best as a periodic check on loyalty across the same group of customers. It answers a question that open text answers poorly: is the overall trend moving up or down? Use it when you want one number to compare over quarters, and when you have enough customers that a single unhappy message will not swing it.

Avoid using NPS as a verdict on a single release or a single customer. A low score after one bad week is noise until the open comments confirm a pattern.

## When is open-ended feedback the better tool?

Open-ended feedback is better when you need to act. Use it when you are deciding what to fix, when you are checking whether a new feature landed, or when you want to learn the vocabulary customers use for their problems. It is also the right tool for small products, where a handful of messages can be read in a sitting.

Open text is also the right channel for bugs. A score cannot reproduce a broken export. A message with the page URL and browser can.

## How do you read NPS and open text together?

Treat the two as a pair. Use the score to decide when to look, and use the comments to decide what the score means. If the score falls, read the detractor comments first, then group them by theme. If the score rises, read the promoter comments to learn what to protect.

Keep the two in separate places but record the link. A simple way is to store the score as metadata on the feedback item, so each message carries the score the customer gave.

## How do you collect open feedback with Escuta Produto?

Escuta Produto does not run NPS surveys, scheduled questions or score dashboards. Its feedback form is open by default: customers choose a type, write a message and can add an optional 1 to 5 star rating. That rating is not NPS, so do not treat the two as the same measure.

If you run NPS with another tool, you can still bring the two together. Send the result to the REST API at `/api/v1/feedback` with your public key, the customer's comment as the message and the score in metadata, for example `{ "nps": 9 }`. The item then appears in the inbox next to the open text. Read the [API documentation](/docs/api) for the request format and error codes, and the [widget documentation](/docs/widget) for the in-app form. For the difference between a rating and free text, see [qualitative vs quantitative feedback](/resources/qualitative-vs-quantitative-feedback). To decide what the comments mean for your roadmap, read [what feedback triage is](/resources/what-is-feedback-triage).

## Frequently asked questions

### What is a good NPS score?

A score only means something against your own history and your market. Track the same question over time and compare the trend, rather than judging a single number against a published benchmark you cannot check.

### Can NPS replace open-ended feedback?

No. NPS shows how loyal customers feel in one number, but it cannot say why. Open text explains the reasons, names the problems and shows what to change. Use both together.

### How often should you ask the NPS question?

Ask on a regular schedule, such as quarterly, to the same kind of customer each time. Asking after every support ticket or release makes the score reflect those moments rather than overall loyalty.

### Does Escuta Produto send NPS surveys?

No. Escuta Produto does not run NPS or survey campaigns. Its form collects open messages with an optional star rating. You can run NPS in another tool and send the comment and score to the REST API as metadata.

---

# What is a product feedback loop?

> A product feedback loop is the repeating cycle of collecting customer feedback, deciding what to do, building the change and telling the customers who asked. A loop is complete only when the last step happens, because customers who never hear back stop sending feedback.

Source: https://escutaproduto.com/resources/what-is-a-product-feedback-loop
Last updated: 2026-10-09

## What are the four steps of a feedback loop?

A product feedback loop has four steps that repeat:

1. **Collect.** Customers send messages through a widget, a hosted page, an API or email. Each message arrives with enough context to act on.
2. **Decide.** Someone reads the message and chooses what happens: fix it, plan it, close it or ask a question.
3. **Build.** The team ships the change, or confirms that an idea has been recorded for later.
4. **Tell.** The customers who asked hear what happened. This is the step that closes the loop.

The loop is a cycle, not a line. Each release produces new messages, and each message restarts the collect step. The loop works when all four steps happen regularly, not when one of them happens once.

## Where does the loop usually break?

Most loops break at one of two points. The first is between collect and decide. Messages arrive, nobody reads them, and the inbox grows until the team stops looking. The second is between build and tell. The change ships, the release notes go out and the people who asked for it never hear that their request was answered.

The third break is less visible. Teams decide without the context they need, so the build step starts from a guess. A bug decided from a message with no page URL can waste a week of reproduction work.

Customers notice all three. When nothing comes back, they stop writing, and the loop has nothing left to run on.

## What does a healthy decide step look like?

A healthy decide step gives every new item a status and a reason. The reason can be short, but it should exist. It stops the same question being asked again next month and tells a new team member why an idea was closed.

Set a fixed time for the decision, such as a weekly batch, so items do not wait for a mood. Keep the list of statuses short. The full routine is in [how to triage customer feedback](/resources/how-to-triage-customer-feedback), and the meaning of the step itself is in [what feedback triage is](/resources/what-is-feedback-triage).

## Why does the tell step matter most?

The tell step is the one that turns customers into repeat senders. A customer who hears "this shipped, thanks for the idea" learns that their message had an effect. A customer who never hears back learns that feedback goes nowhere, and most of them will not try again.

Telling customers does not need a campaign. Find the people who asked, using the internal notes and the inbox search, and write a short personal reply. Escuta Produto does not send automatic emails to customers, so you reply from your own email account. Use the internal notes on each item to record who asked and whether they were told, so the step is not forgotten when the release gets busy.

## How long should a loop take?

The loop should be as fast as your release rhythm allows, and no faster than your ability to decide well. There is no universal number of days, because a team that ships daily and a team that ships quarterly run different loops. A reasonable test is whether a customer who sends a message can hear back within the same cycle as the next release.

If the loop takes longer than your release cycle, most messages will wait a long time before any change. Shorten the decide step first, because that step is usually the one under the team's control.

## How do you keep the loop running in Escuta Produto?

Escuta Produto covers the collect and decide steps in one place. Messages arrive in each product's inbox with the page URL, browser and any user details you pass in. Statuses run from new through planned, in progress and done to closed, so the decide and build steps have a shared record. Use the inbox filters and text search to find the people who asked for a change.

The tell step happens in your own email, because Escuta Produto does not send customer emails. Record the decision in the internal notes, then reply to the people who asked when the status moves to done. For the widget that starts the loop, see [what a feedback widget is](/resources/what-is-a-feedback-widget). For how replies should sound, read [how to reply to customer feedback](/resources/reply-to-customer-feedback), and the [notification documentation](/docs/notifications) explains how Slack or Discord alerts feed the first step.

## Frequently asked questions

### What is a product feedback loop in plain terms?

It is the repeating cycle of collecting customer feedback, deciding what to do, building the change and telling the customers who asked. Each step feeds the next, and the loop only works when all four happen regularly.

### Where do most feedback loops break?

Most break between collecting and deciding, when messages sit unread, or between building and telling, when customers never hear that their request shipped. Both gaps make customers stop sending feedback.

### Does a feedback loop need special software?

No, but it needs a shared record of each item, its status and who asked for it. An inbox with statuses and internal notes covers the basics. Tools that send automatic emails are optional, since many teams reply personally.

### How do you know when a feedback loop is closed?

A loop is closed for an item when the people who asked have been told the outcome. Record that in the internal notes, so you can see at a glance which items are done and which customers still need a reply.

---

# What is voice of the customer (VoC)?

> Voice of the customer (VoC) is the practice of collecting what customers say about a product, from messages, reviews, support tickets and calls, and sharing those insights with the people who make decisions. It is a program, not a single tool, and small teams can run a lightweight version.

Source: https://escutaproduto.com/resources/what-is-voice-of-the-customer
Last updated: 2026-10-09

## What does voice of the customer cover?

Voice of the customer, usually shortened to VoC, is the practice of listening to what customers say about a product and making that input visible to the people who decide what to build. The term covers the listening, the analysis and the sharing. A VoC program is complete when product, support and leadership all work from the same customer evidence.

The phrase describes a goal more than a method. It does not prescribe a survey, a tool or a report. It asks one question: do the people making decisions hear customers in their own words, or only through a summary someone wrote?

## Which sources count as voice of the customer?

Customers speak in many places, and each source shows a different part of their experience:

- **In-product feedback.** Messages sent from inside the app, with the page and context attached. Strong on specific bugs and moments of friction.
- **Support tickets.** Problems customers escalate to help. Strong on blockers and confusion about how things work.
- **Reviews and app store comments.** Public opinions, often from people who never write to you. Strong on first impressions and comparisons.
- **Sales and onboarding calls.** Questions prospects ask before they buy and questions new customers ask in their first weeks. Strong on expectations.
- **Cancellation notes.** The reasons customers give when they leave. Strong on the gap between what they wanted and what they got.

A small team does not need all five. Start with the source that already exists, usually in-product feedback and support tickets, and add others when you notice a gap.

## How is VoC different from feedback collection?

Feedback collection is the act of gathering messages. Voice of the customer is what happens after: the messages are read, grouped and shared, so the evidence changes decisions. You can collect feedback without a VoC program, and many teams do. A program needs a routine for reading the evidence and a channel for passing it on.

The difference shows up in meetings. A team that collects feedback can say how many messages arrived. A team with a VoC habit can say what customers are struggling with this month, quote two of them and explain what the team is doing about it.

## What does a lightweight VoC program need?

A small team needs three things:

1. **One inbox for the main channel.** Put in-product messages in one place, with a status on every item. The inbox is where the evidence lives.
2. **A regular reading slot.** Thirty minutes a week is enough for a team of a few people. Read the new items, group the repeats and note the strongest quotes.
3. **A short summary for the team.** Five bullet points each week, with one quote per theme, shared in the place the team already talks.

That is the whole program. It works because the reading slot is fixed and the summary is short enough that people read it.

## How do you share customer voice with the team?

Sharing is where most VoC programs fail. A thirty-page report is read once and forgotten. A short message that names the theme, the count and one customer quote is read by everyone.

Keep the quotes exact and anonymized unless the customer has agreed to be named. Put the count next to the quote, so the team can see whether one customer or many said it. For quotes you want to use publicly, ask permission first.

## What are the common VoC mistakes?

Watch for four mistakes:

- **Confusing loud with common.** One furious customer can dominate a meeting. Counting distinct customers keeps the picture honest.
- **Collecting without acting.** If the team never changes anything, customers stop speaking. Even a small decision recorded in the summary shows the loop works.
- **Buying a dashboard before having a habit.** A chart without a reading routine does not change decisions.
- **Treating one channel as the whole truth.** In-product messages come from people who already use the product. Prospects and churned customers speak elsewhere.

## Building a VoC habit with Escuta Produto

Escuta Produto handles the in-product part of a VoC program. Each product has an inbox with status filters, text search, a 30-day chart, counts by type and the average rating. Use those counts to pick the week's themes, then read the messages behind them.

Escuta Produto does not do AI clustering, sentiment analysis or tags, so the grouping is your work. Use internal notes to record each theme, and export the product's feedback as UTF-8 CSV when you want to count themes in a spreadsheet. For the routine that turns those notes into decisions, see [how to triage customer feedback](/resources/how-to-triage-customer-feedback). For the wider view across several products, read [one feedback inbox for many products](/resources/one-inbox-many-products), and for how the widget captures the messages in the first place, see the [widget documentation](/docs/widget).

## Frequently asked questions

### What is voice of the customer in one sentence?

Voice of the customer is the practice of gathering what customers say about a product from several sources and sharing that evidence with the people who make decisions, so choices rest on what customers actually said.

### Do small teams need a formal VoC program?

Not a formal one. A small team can run a lightweight version with one inbox, a weekly thirty-minute reading slot and a short summary that quotes customers and gives counts for each theme.

### Which sources should a VoC program use first?

Start with the sources you already have, usually in-product messages and support tickets. They are close to the product and cheap to read. Add reviews, sales calls and cancellation notes when you notice a gap.

### How do you share customer feedback without a long report?

Send a short weekly summary with a few themes, the count for each and one exact quote. Keep it short enough that people read it, and ask permission before naming a customer or using their words publicly.

---

# What is a feedback inbox?

> A feedback inbox is a single list where all customer messages arrive, each with a status, its context and private internal notes. It replaces scattered emails, chat threads and spreadsheets, so the team reads and decides from one record.

Source: https://escutaproduto.com/resources/what-is-a-feedback-inbox
Last updated: 2026-10-09

## What does a feedback inbox hold?

A feedback inbox holds every customer message that is meant to inform the product. A message is one item: a bug report, an idea, a piece of praise or a message labeled other. Each item keeps the text the customer sent, the type they chose, the time it arrived and whatever context came with it.

The inbox is a record, not a conversation. Email is built for back-and-forth between two people. A feedback inbox is built for a team to read many items, decide what each one means and track what happened to it.

## Why does one place beat scattered channels?

Feedback arrives in more places than most teams admit. A message comes through the in-app form, another as a reply to a newsletter, a third in a support thread and a fourth in a direct message to a founder. Each channel has its own owner, and no single person sees the whole picture.

One place fixes three problems:

1. **Nothing gets lost.** Every message enters the same list, so an item cannot sit in a personal folder forever.
2. **Repeats become visible.** Five customers describing the same problem are easy to spot when they sit next to each other.
3. **Decisions are shared.** The status and internal notes live with the item, so the next person knows what was decided and why.

The inbox does not need to replace every other channel. It needs to hold the messages you intend to act on.

## What does a feedback inbox need?

A useful inbox needs three things. Without them it becomes a second email account.

- **Statuses.** Every item shows where it stands, such as new, planned, in progress, done or closed. Statuses turn the inbox from a pile into a queue.
- **Search.** The team needs to find every message that mentions a word, a feature or a page. Text search over the message is the minimum.
- **Context.** The page URL, browser and user details should travel with the message. An item without context costs a reply just to ask where the customer was.

Internal notes are the fourth thing that matters most. They are private to the team, so you can record a decision, a reason or a note about who to tell.

## How do filters and product scope work?

When one company runs several products, the inbox must separate them. Escuta Produto gives each product its own inbox, with filters by status and type. A team that runs three products sees each one as a separate list, and each list shows counts by type, text search, a 30-day chart and the average rating.

Keep the statuses the same across products. A shared vocabulary means that the same routine works for every inbox, and a status chart means the same thing wherever you look.

## What should an inbox avoid?

An inbox works best when it is simple. Avoid the features that turn a feedback record into a project tracker:

- **Too many categories.** Escuta Produto uses type and status rather than free tags, because a long tag list is hard to maintain. Use internal notes for anything more specific.
- **Automatic replies that pretend to be personal.** Customers can tell. Reply from your own email instead.
- **Duplicate handling by hand.** Use search and notes to find repeats, since Escuta Produto has no merge feature.

Keep the inbox focused on what customers said and what you decided. Let the rest of your tools handle the build work.

## How do you export an inbox?

Export the product's feedback as a CSV file to count themes, share a quarterly summary or work in a spreadsheet. Escuta Produto exports UTF-8, which opens correctly in Excel and Google Sheets. The export is the escape route: if you ever move away from the inbox, your record goes with you.

## How do you set up a feedback inbox with Escuta Produto?

Create a product in Escuta Produto and add the widget or the hosted feedback page. Every message then appears in that product's inbox. Open the inbox at the start of each week, filter by new, and work through the items with the routine described in [how to triage customer feedback](/resources/how-to-triage-customer-feedback).

For the definition of the decision step, read [what feedback triage is](/resources/what-is-feedback-triage). For the context each item carries, read [what feedback metadata is and why it matters](/resources/what-is-feedback-metadata). To send messages from your own backend or mobile app into the same inbox, use the [REST API documentation](/docs/api).

## Frequently asked questions

### What is a feedback inbox in one sentence?

A feedback inbox is a single list where all customer messages arrive, each with a status, its context and private internal notes, so the team reads and decides from one shared record.

### How is a feedback inbox different from a support inbox?

A support inbox handles conversations that need a reply to one customer. A feedback inbox holds messages for the team to read and decide on, with statuses and notes that track what happened to each item and why.

### What should a feedback inbox show for each message?

Show the message, its type, its status and the time it arrived. Keep the page URL, browser and user details attached, plus private internal notes, so a reader can act on the item without asking the customer basic questions.

### Can I export my feedback inbox?

Yes. Escuta Produto exports a product's feedback as UTF-8 CSV, which opens correctly in Excel and Google Sheets. Use the export for spreadsheet analysis or to keep a copy of your record.

---

# What is feedback metadata and why does it matter?

> Feedback metadata is the context attached to a customer message, such as the page URL, the browser, the country and details about the user like their id or plan. It matters because a message with context can be reproduced and sized, while a bare sentence usually needs a follow-up question.

Source: https://escutaproduto.com/resources/what-is-feedback-metadata
Last updated: 2026-10-09

## What counts as feedback metadata?

Feedback metadata is every piece of information that describes a message but is not the message itself. The text a customer writes is the content. The page they were on, the browser they used, the time they wrote and the account they belong to are metadata.

Metadata answers the questions a reader would otherwise have to ask. Where was the customer? What were they using? Are they a paying customer or a free user? A reader who knows these things can often decide without writing back.

## Which fields does the widget capture on its own?

The Escuta Produto widget collects some metadata automatically, so you do not need to write code for it:

- **Page URL.** The address of the page where the customer opened the form. This is often the most useful field, because it points straight at the screen.
- **Browser.** The user agent string, which names the browser and operating system. Use it to reproduce layout problems that only appear in one browser.
- **Country.** Added by the server when the message arrives, so you do not need to ask the customer for it.

These fields arrive with every message from the widget. They do not depend on whether the customer is logged in.

## Which details do you send yourself?

Some metadata only your application knows. Pass it in through the JS API:

- **Identity.** `identify({ email, name, id })` sets who the customer is. Email and name fill the form fields, and any other key, such as an account id, is stored as metadata on each message.
- **Plan.** A plan name in the `identify` call, such as `plan: "team"`, lets you tell later whether a message came from a paying customer. The plan name is your own label, so use the same names your billing system uses.
- **Feature or state.** `setMetadata({...})` attaches more JSON, up to 4 KB, such as the feature flag a customer was testing or the step of a wizard they were on.

The widget does not capture a device class separately. If you need one, such as mobile or desktop, send it yourself with `setMetadata`, rather than trying to read it from the user agent string each time.

## Why does context make feedback actionable?

Context turns a claim into a case. "The export is broken" can mean a dozen things. "The export is broken" with a page URL, a browser and a plan name tells you which screen, which environment and which kind of customer to start from.

Context also helps you size the problem. If five messages come from one plan and one country, the pattern is a clue. If they come from every plan and every browser, the problem is probably in the product rather than in one setup.

Context does not replace reading the message. It gives the reading a starting point.

## What should you leave out of metadata?

Metadata is easy to over-collect, and that creates a privacy and maintenance problem. Keep to a short list of fields that you will actually use.

- **Do not send secrets or sensitive personal data.** Passwords, card numbers and health details have no place in feedback metadata.
- **Keep the key names stable.** Use `plan`, not `planName` in one place and `subscription` in another. Stable names make it possible to read the same field across messages.
- **Keep values small.** The 4 KB limit on `setMetadata` is generous for a few fields and tight for a full copy of the user's record.

Decide which fields you need before you write the code. A list of five useful keys is worth more than twenty that nobody reads.

## How do you read metadata in the inbox?

Metadata is stored on each message, so it stays with the feedback item. Use the inbox filters and text search to group similar items, and use the internal notes to record what the context revealed. The page URL and browser saved on a bug are the first things to check when you try to reproduce it.

For analysis across many messages, export the product's feedback as UTF-8 CSV and sort or filter in a spreadsheet. Check the export columns before you build a report on a metadata field, so you know which fields it contains.

## How do you add metadata with Escuta Produto?

Queue an `identify` call with the user's details as soon as the script tag is on the page. The queue works before or after the widget script finishes loading, so your code does not have to wait for it:

```js
window.EscutaProduto = window.EscutaProduto || { q: [] };
(window.EscutaProduto.q ||= []).push(["identify", [{ id: user.id, plan: user.plan }]]);
```

Call `EscutaProduto.setMetadata({...})` from your app once the widget has loaded, each time the context changes, such as when a customer opens a new feature. Keep the calls small, because the limit is 4 KB of JSON.

The full list of JS API calls is in the [widget documentation](/docs/widget). For how context fits into the first step of the loop, read [what in-app feedback is](/resources/what-is-in-app-feedback). For the decision that follows, see [what feedback triage is](/resources/what-is-feedback-triage).

## Frequently asked questions

### What is feedback metadata in simple terms?

Feedback metadata is the context attached to a message, such as the page the customer was on, their browser, their country and details about their account. It describes the message without being the message.

### Does the feedback widget capture the device automatically?

The widget saves the page URL and the browser string automatically, and the server adds the country. It does not store a separate device field, so send a device class yourself with the metadata call if you need one.

### What should I avoid putting in feedback metadata?

Avoid passwords, payment details, health information and any sensitive personal data. Keep the fields you actually use, give them stable names and keep values small, since metadata is limited to 4 KB per call.

### How does metadata help a team act on feedback?

It shows where the problem happened and who was affected. A message with a page URL, browser and plan can be reproduced and sized quickly, while a bare sentence usually needs a follow-up question before anyone can act.

---

# How to collect feedback in a SaaS product

> Load the Escuta Produto widget once in your app shell, call identify with the user's email, id and plan right after login, and send account context as metadata. Put entry points in the screens where people get stuck, not on every page.

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

## Why logged-in users give the best feedback

Feedback from a signed-in customer is worth more than a message from an anonymous visitor. The person pays, uses the product every week and knows the workflow. They also tend to describe problems in the terms your team uses, which saves time when you triage.

The widget can collect that feedback from inside the app. The trick is to make sure each message arrives with enough context to act on: who sent it, which plan they pay for, and where in the product they were when it happened. The widget already saves the page URL and browser for you. This article covers the rest.

## Load the widget once in the app shell

Put the script in the layout that wraps every logged-in page, not in each screen. In most single-page apps this is the root layout or the component that renders the navigation. Use the public key from your product settings:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

A route change inside a single-page app does not reload the script, so the widget stays available as customers move between screens. Add your production domain and any staging domain to the allowed origins in product settings. Without that, the widget will not submit from those sites.

If your interface already has a help menu, you do not need the floating button. Set `data-trigger="none"` and open the form from your own menu item. The next sections show how.

## Identify users after login

The widget can fill the form for you when it knows who is using the app. Call `identify` once you know the user, using the queue form below. The queue works even if your code runs before the widget script has finished loading:

```js
window.EscutaProduto = window.EscutaProduto || { q: [] };
(window.EscutaProduto.q ||= []).push([
  "identify",
  [{ email: user.email, name: user.name, id: user.id, plan: user.plan }],
]);
```

Email and name fill the form fields. When an email is known, the form hides the email field, so the customer is not asked to type it again. Any other key, such as `plan` or `id`, is stored as metadata on the feedback item.

Call `identify` again whenever the plan changes, for example after an upgrade. Otherwise the next feedback item will show the old plan, and you will read the wrong context.

## Choose metadata that explains the feedback

Metadata answers the questions you would otherwise ask in a follow-up email. A short set of keys works well for most SaaS products:

| Key | Example | Why it helps |
| --- | --- | --- |
| `plan` | `"team"` | Shows whether the report comes from a paying segment |
| `accountId` | `"acc_8841"` | Links the item to your admin tools |
| `role` | `"admin"` | Separates people who manage the account from members |
| `area` | `"exports"` | Records the part of the app the person was using |

For example, if a customer on the team plan reports that exports time out, the item already shows the plan and account id. You can check how large their data set is before you answer.

Keep the list short and stable. Do not send passwords, tokens, card data or full records of your customers' customers. Metadata is for context, not for storing private data. Escuta Produto has no tags, so use metadata for the context you want to filter by later, and internal notes on each item for your own labels.

## Place entry points where the work happens

People send better feedback when the form is close to the problem. Three places tend to work well in a SaaS dashboard:

1. **Settings and billing screens**, with a "Send an idea" link. Ideas about pricing and account features show up here.
2. **Reports and exports**, with a "Something is wrong with this report?" link that opens the form with the bug type preselected.
3. **Error screens**, where a short "Tell us what happened" button sits next to the error message.

The HTML for a preselected entry point is a plain button:

```html
<button type="button" data-escuta-open="bug">Report a problem</button>
```

The value after `data-escuta-open` chooses the type: `bug`, `idea`, `praise` or `other`. You can also open the form from JavaScript with `EscutaProduto.open({ kind: "idea" })`.

Avoid a permanent floating button over the primary action of a screen, and avoid opening the form automatically. A form that appears after every login trains customers to close it. Let people choose when to write.

## Keep feedback from interrupting the work

Timing matters more than placement. Ask for an opinion after the customer finishes something, such as a successful export or a completed setup, not while they are still in the middle of a task. The article on [asking for feedback after a first success](/resources/feedback-after-first-success) covers how to pick that moment.

Keep the number of entry points small. Two or three well-placed links usually collect more useful messages than a button on every screen.

## Set up Escuta Produto for your SaaS app

Start with one product for the app. Copy its key from the dashboard, add your domains to the allowed origins, and paste the script into the app shell. Add the identify call after login and test it with your own account: send a bug and an idea, then check that the item shows your email, plan and page URL.

Then connect a Slack or Discord webhook for the product, so new items reach the team that answers them. Each alert shows the type, the rating, the sender and a dashboard link. The [notifications guide](/docs/notifications) explains the setup, and the [widget reference](/docs/widget) lists every option.

If you run a mobile companion or an API alongside the web app, the same product can accept those messages too. The [mobile app guide](/resources/collect-feedback-mobile-app) shows how to send feedback from native code, and the [REST API reference](/docs/api) has the full request format.

## Frequently asked questions

### Should the feedback widget load on every page of a SaaS app?

Load the script in the layout that wraps your logged-in pages, so it is available everywhere a customer works. Leave it off signed-out marketing pages if you only want feedback from customers, and set the trigger to none on screens where a floating button would cover controls.

### How do I attach a logged-in user's email to their feedback?

Call EscutaProduto.identify with the user's email, name and id right after login. The form then hides the email field and each item shows who sent it. Call identify again when the plan or account changes.

### What metadata should a SaaS app send with feedback?

Send context that explains the report, such as plan, account id, role and the area of the app the person came from. Keep secrets and sensitive records out of it. Metadata is stored as a JSON object of up to 4 KB.

---

# How to collect feedback in a mobile app

> Mobile apps send feedback through the Escuta Produto REST API with the public product key, a single POST request and no SDK. Add the app version and platform as metadata, handle failed sends in the app, and link the hosted form from your store listing.

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

## Mobile apps use the REST API

Escuta Produto does not ship native SDKs for iOS, Android or React Native. The widget is a script for web pages. A native app sends feedback with one HTTPS request to the REST API, and the request is simple enough to write in a few lines in any language.

The endpoint is `https://escutaproduto.com/api/v1/feedback`. You send a JSON body that includes your product key and a message. The product key is public, so it can sit in the app. The full field list is in the [REST API reference](/docs/api). This article focuses on the parts that matter in an app: metadata, error handling and where the link lives.

## Send the request from your app

A useful request includes the message, the type, an optional rating and the email when the user is signed in. Here is the body you would build in a Swift app:

```swift
struct FeedbackPayload: Encodable {
    let key: String
    let kind: String
    let message: String
    let email: String?
    let metadata: [String: String]
}

func sendFeedback(_ payload: FeedbackPayload) async throws {
    var request = URLRequest(url: URL(string: "https://escutaproduto.com/api/v1/feedback")!)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try JSONEncoder().encode(payload)

    let (_, response) = try await URLSession.shared.data(for: request)
    guard let http = response as? HTTPURLResponse, http.statusCode == 201 else {
        throw URLError(.badServerResponse)
    }
}
```

The success response is `201` with a body that contains `ok` and the new `id`. Checking for 201 is enough to know the message was saved.

On Android, the same request works with the standard library. Remember that the app needs the internet permission, and that network calls must run off the main thread:

```kotlin
val connection = URL("https://escutaproduto.com/api/v1/feedback").openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json")
connection.doOutput = true
connection.outputStream.use { it.write(json.toByteArray()) }
val saved = connection.responseCode == 201
```

In a React Native app, `fetch` does the same job:

```js
const response = await fetch("https://escutaproduto.com/api/v1/feedback", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    key: "pk_your_product_key",
    kind: "bug",
    message,
    metadata: { platform: Platform.OS, appVersion: "2.3.1" },
  }),
});
```

Native HTTP clients usually send no `Origin` header. The API accepts requests without an origin, so the allowed origins list in your product settings does not block the app.

## Add app version and device context

The app version is the most useful piece of metadata you can add. A bug report that says "the sync stopped" is hard to act on. The same report with `appVersion: "2.3.1"` and `platform: "ios"` tells you whether the problem existed in the last release or the one before.

Useful keys for a mobile app are `appVersion`, `build`, `platform`, `osVersion` and `deviceModel`. Keep the set small and do not send device identifiers, precise location or anything that identifies a person beyond what the user typed. Metadata is a JSON object of up to 4 KB, which is plenty for these fields.

Escuta Produto has no tags, so use metadata for version and platform, then search the message text or export the product to CSV when you want to group reports by release.

## Handle failed sends in the app

Mobile networks fail. A user may send feedback in an elevator or on a train. The API will not queue the message for you, so the app should handle the failure itself:

- **Keep the draft.** If the request fails, leave the text on screen and show a retry button. Users lose patience quickly when a long message disappears.
- **Disable the send button while a request is in flight.** Double taps create duplicate items.
- **Retry network errors, back off on 429.** A 429 means more than 10 submissions per minute from the same IP to the same product. Wait a minute before trying again.
- **Do not retry 400 responses.** A 400 means a field failed validation, such as a message that is too short. Show the user what to fix instead.
- **Treat 413 as a size problem.** The request body is limited to 16 KB, so trim very long messages before sending.

These rules keep the inbox clean. Retrying a validation error in a loop produces the same error over and over, and the rate limit eventually blocks real feedback.

## Use the hosted link in store listings and help

Not every feedback path needs code. The [hosted feedback page](/docs/hosted-page) lives at `https://escutaproduto.com/f/your-product-slug`, and it works in any browser. Add `?lang=pt` or `?lang=en` to force a language.

Put the link where people look for help: the support or website field of your store listing, the help screen inside the app, and the replies you send from your support inbox. Use the in-app form for version-specific bug reports, because the hosted page cannot carry app metadata. Use the hosted page for general ideas and for people who do not have the app installed.

## Set up Escuta Produto for your mobile app

Create a product for the app in the dashboard, copy its public key and add a feedback screen with four types: bug, idea, praise and other. Send the request with the app version and platform as metadata, and test it against a staging build before you release.

Connect a Slack or Discord webhook so each new item reaches your team. The [notifications guide](/docs/notifications) explains the setup. When a request ships, close the loop with the people who asked. The [article on releases and feedback](/resources/release-notes-feedback) covers how to link a changelog entry to the requests it answers. If you also run a web app, [collecting feedback in a SaaS product](/resources/collect-feedback-saas) shows how to keep both in one inbox.

## Frequently asked questions

### Is there a native iOS or Android SDK for Escuta Produto?

No. Mobile apps use the REST API directly with a single POST request. The widget is built for web pages, so native code sends the request itself, with a form you design in your app.

### Do I need a secret key in my mobile app?

No. The product key is public and can only create feedback, never read it. It is safe to ship inside an app binary. If the key is abused, rotate it in the product settings.

### What happens if the device is offline when a user sends feedback?

The API saves a request only when it arrives, so the app decides what to do with a failed send. Keep the draft on screen, show a retry option and do not retry automatically on a 400 response.

---

# How to collect feedback for a Chrome extension

> A Chrome extension cannot load the widget script into its own pages, so it sends feedback with fetch to the Escuta Produto REST API. Use the popup or options page for reports, set the hosted form as the uninstall URL for one short exit question, and add the extension version to every item.

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

## Why an extension cannot use the widget script

A Chrome extension runs its pages from its own package, and Manifest V3 forbids loading code from a remote server into those pages. The widget is a script tag that loads `widget.js` from the Escuta Produto domain, so it cannot run in the popup, the options page or the service worker.

The extension can still collect feedback. It sends a request to the REST API from its own pages. The request has the same shape as the one a website makes, and the [REST API reference](/docs/api) lists every field. Two other tools fit the extension model well: the uninstall URL, which asks one question when someone removes the extension, and the hosted form, which works in any browser tab.

## Give the popup a feedback form

Cross-origin requests from extension pages need permission in the manifest. Add the Escuta Produto domain to `host_permissions`:

```json
{
  "host_permissions": ["https://escutaproduto.com/*"]
}
```

Next, send the message from the popup with `fetch`. The function below builds the request, includes the extension version and optionally reads the URL of the tab the user is on:

```js
async function sendFeedback(message, kind = "other") {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const response = await fetch("https://escutaproduto.com/api/v1/feedback", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      key: "pk_your_product_key",
      kind,
      message,
      pageUrl: tab?.url,
      metadata: { extensionVersion: chrome.runtime.getManifest().version },
    }),
  });
  if (response.status !== 201) {
    throw new Error("Feedback failed with status " + response.status);
  }
  return response.json();
}
```

The public key is safe in the extension package, because anyone can read the code and the key can only create feedback. The API returns `201` with the new item `id` when the message is saved.

Reading the tab URL needs the right permissions. If your manifest does not grant access to the active tab, leave `pageUrl` out. The message is still saved, and you can ask the user for the page in the text when it matters.

## Tag every report with the extension version

The extension version is the single most useful piece of metadata. Chrome users update at different speeds, so a bug report can come from three releases at once. `chrome.runtime.getManifest().version` returns the version from your manifest, and the function above sends it with every report.

If you also support a browser build or a store channel, add it as another key, such as `channel: "beta"`. Keep the metadata to a few short values. It is stored as JSON and must stay under 4 KB.

Version numbers answer the first question a maintainer asks: did this start after the last update? Without them, you spend a day asking the reporter which version they run. With them, a cluster of reports that all name version 3.2.0 points straight at the release that broke something. The inbox does not filter by metadata, so note the version in the first triage pass and record the release in an internal note when you confirm the bug.

Keep the popup form short. Four controls are enough: the four types as buttons, a message box, an optional email field and a line that says the current page address is included. A longer form in a small popup gets closed before anyone finishes it, and that feedback never reaches you.

## Use the uninstall URL for one question

Chrome lets an extension open a page when the user uninstalls it. Set that page in the background service worker when the extension installs:

```js
chrome.runtime.setUninstallURL("https://escutaproduto.com/f/your-product-slug?lang=en");
```

The hosted form is a good fit because it needs no code and no sign-in. Keep the question simple, for example "What made you remove the extension?", and make it clear that the answer is optional. The form does not know who uninstalled, so do not promise personal follow-up unless the person writes an email address in the form.

Use the uninstall page sparingly. Uninstall is a moment of frustration, so a respectful, short message helps more than a long survey. The [article on cancellation feedback](/resources/cancellation-feedback) describes the same tradeoff for paid products.

## Link the options page to the hosted form

The options page is where people look for help. Add a plain link to the hosted form, opened in a new tab:

```html
<a href="https://escutaproduto.com/f/your-product-slug?lang=en" target="_blank" rel="noopener">Send feedback</a>
```

Use the popup for in-context reports, where the page URL is useful, and the options page for general ideas and praise. A bug button in the popup footer works well for "something broke on this page".

## Check the allowed origins

Allowed origins protect the widget from being used on sites you do not control. They also apply to browser requests to the API. An extension request sends an `Origin` header that starts with `chrome-extension://` followed by the extension ID.

If you have set allowed origins for the product, the extension's origin must be on that list, or the API returns 403. Check how the settings field expects the value, then send a test message from the popup and confirm that it saves. If you do not use the widget on any website, you can keep the product's origin list empty, but read the [REST API reference](/docs/api) first to confirm how the check behaves.

## Set up Escuta Produto for your extension

Create a product for the extension, copy the public key and add the Escuta Produto domain to `host_permissions`. Build a small feedback form in the popup with four types: bug, idea, praise and other. Send each item with the extension version in metadata, then open the inbox and check that the version appears on a test report.

Add a webhook so new reports reach your team. The [notifications guide](/docs/notifications) explains the setup. Once a fix ships in a new version, reply to the people who reported the problem. The [article on collecting feedback before launch](/resources/collect-feedback-before-launch) covers the same hosted-form approach for early testers, and [collecting feedback from release notes](/resources/release-notes-feedback) shows how to tie a version to the requests it answers.

## Frequently asked questions

### Can I load the feedback widget script inside a Chrome extension popup?

No. Manifest V3 does not allow remotely hosted code to run in extension pages, so the widget script cannot load there. Send feedback with a fetch call to the REST API from the popup or options page instead.

### Can the uninstall page know which user is leaving?

No. The uninstall URL is a plain address that Chrome opens after removal, so it carries no user details. Keep the question short and let people answer it without signing in.

### Why might my extension get a 403 response?

If the product has allowed origins set, a browser request must come from one of them. Extension requests carry an extension origin, so add it to the list and test again. Check the origin rules in the REST API reference.

---

# How to collect feedback for a WordPress plugin

> For a WordPress plugin, add a feedback link to your settings page that opens a hosted form, and send submitted feedback from PHP with wp_remote_post to the Escuta Produto REST API. Avoid loading remote scripts in wp-admin, and send only what the site owner chose to submit.

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

## Why a plugin should not load a remote script in wp-admin

A WordPress plugin runs on sites you do not control, under an administrator who trusts your code. Loading a script from another domain into the admin screen adds a dependency that the site owner did not ask for. It also draws attention from reviewers and from security scanners, which are quick to flag external code in the dashboard.

For most plugins the better path is simpler: a link on your settings page that opens a hosted feedback form, and a PHP function that posts the message when the site owner chooses to send it. Both work without any remote code in the admin.

The [hosted feedback page](/docs/hosted-page) gives you a form at `https://escutaproduto.com/f/your-product-slug`. It works in any browser, follows the site owner's language and needs no install.

## Start with a link on the settings page

Register a settings page for the plugin and link to the form from it. Use the WordPress functions for the menu and escape the URL before printing it:

```php
add_action( 'admin_menu', function () {
    add_options_page(
        'Feedback',
        'Feedback',
        'manage_options',
        'your-plugin-feedback',
        'your_plugin_feedback_page'
    );
} );

function your_plugin_feedback_page() {
    $url = 'https://escutaproduto.com/f/your-product-slug?lang=en';
    echo '<p><a class="button button-primary" href="' . esc_url( $url ) . '" target="_blank" rel="noopener">Send feedback</a></p>';
}
```

You can prefill the email field with the site administrator's address, using `rawurlencode( get_option( 'admin_email' ) )` in the `email` query parameter. Only do this if the plugin's privacy notes say so. An admin email is personal data, and some site owners share their dashboard with colleagues.

## Think carefully about the deactivation moment

Many plugin teams want feedback when someone turns the plugin off. WordPress makes this harder than it sounds. A deactivation hook runs once, in the request that deactivates the plugin, and your plugin's code does not load on later page views while it is inactive. A notice scheduled from the hook will never appear.

Intercepting the Deactivate link with a script on the plugins screen is possible, but it is intrusive. It delays the click, and many administrators will find it annoying. Most plugins do better with the settings page link, a line in the readme and a visible link in the support documentation. Choose the lighter option unless you have a clear reason to believe that a more direct question is worth the friction.

## Send feedback from PHP with wp_remote_post

When the site owner submits a message from a form in your plugin, send it from the server. Server requests have no `Origin` header, so the allowed origins list does not block them. The function below builds the request with the site and plugin details:

```php
function your_plugin_send_feedback( string $message, string $kind = 'other' ): bool {
    $response = wp_remote_post( 'https://escutaproduto.com/api/v1/feedback', array(
        'headers' => array( 'Content-Type' => 'application/json' ),
        'body'    => wp_json_encode( array(
            'key'      => 'pk_your_product_key',
            'kind'     => $kind,
            'message'  => $message,
            'pageUrl'  => home_url( '/' ),
            'metadata' => array(
                'wordpress' => get_bloginfo( 'version' ),
                'plugin'    => YOUR_PLUGIN_VERSION,
                'php'       => PHP_VERSION,
            ),
        ) ),
        'timeout' => 10,
    ) );

    if ( is_wp_error( $response ) ) {
        return false;
    }

    return 201 === wp_remote_retrieve_response_code( $response );
}
```

The function returns `true` only when the API replies with `201`. Show the owner a clear message in either case. If the call fails, tell them so, and keep the text in the form so they can try again.

Call the function only from a handler that checks a nonce and the user's capabilities, so a page visit or a stray request cannot send messages on the owner's behalf. The WordPress functions `check_admin_referer` and `current_user_can` cover both checks.

## Keep metadata to what the site owner submitted

WordPress sites differ in more ways than plugin teams expect. Sending the WordPress version, the plugin version and the PHP version helps you reproduce a bug. Sending the list of every other plugin on the site, the database name or any user records does not. The rule is simple: send only the details that help with the message, and state them in your privacy notes.

Metadata is a JSON object of up to 4 KB. Keep it to a few short values. The request body is capped at 16 KB, so trim very long messages before you send them.

## Respect the rate limit

The API allows 10 submissions per minute from the same IP address to the same product. A single site rarely gets close. Shared hosting is different: many sites can share one outbound IP, and a busy day across them can produce 429 responses. When you see one, show a short message such as "Too many messages from this server, try again in a minute" and keep the draft.

Escuta Produto does not queue requests for you, so the plugin decides what to do. Retrying straight away on a 429 only adds to the problem.

## Set up Escuta Produto for your plugin

Create a product for the plugin in the dashboard. Set its website, copy the public key into the plugin, and add the settings link with the hosted form URL. Submit a test message from a local WordPress install and confirm it arrives with the WordPress and plugin versions in the metadata.

Add a Slack or Discord webhook so new items reach the people who answer support. The [notifications guide](/docs/notifications) covers the setup. If your plugin also runs on a marketing site, the [article on collecting feedback in a Shopify app](/resources/collect-feedback-shopify-app) describes the same server-side pattern for another platform, and [adding a feedback widget to a WordPress site](/resources/feedback-widget-wordpress) covers the case where you want the widget on the public site itself. The [REST API reference](/docs/api) lists every field and response code.

## Frequently asked questions

### Can a WordPress plugin load the Escuta Produto widget in the admin dashboard?

Avoid it. Remote scripts in wp-admin are a sensitive area, and plugin reviewers look closely at external code. Use a link to the hosted feedback form from your settings page, and send feedback from PHP when the site owner submits a form.

### Does a WordPress site hit the feedback rate limit?

Each site usually has its own IP address, so the limit of 10 submissions per minute per IP and product rarely matters for one site. Shared hosting can put many sites behind one IP, so handle a 429 response by asking the owner to try again shortly.

### Is it safe to put the product key in plugin code?

Yes. The public key can only create feedback and never read it, so it can appear in the plugin source. Do not put any secret in the plugin, because the Escuta Produto API does not use one.

---

# 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.

---

# How to collect feedback on internal tools

> For an internal tool, create one product per tool, identify each employee by work email with the widget inside the authenticated app, and point each product's Slack or Discord webhook at the team that owns it. The hosted link is public, so it is not an access control.

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

## Employees are customers too

Internal tools have customers, even though they are your coworkers. The people who file expenses, deploy services or update a shared spreadsheet know exactly what slows them down. Their feedback is cheap to collect and often more specific than a customer's, because they can walk over and ask a follow-up question.

The problem is that internal feedback tends to disappear. It arrives in a direct message, gets forgotten, and the tool stays the same for another year. A feedback inbox with a clear owner per tool fixes most of that.

## One product per internal tool

Create one product for each tool in the dashboard. Name it after the tool, such as "Expenses" or "Deploy dashboard", so the inbox list reads like a map of your internal systems.

Separate products give you three things at no extra cost. Each product has its own public key, its own allowed origins and its own notification webhook. The filters by status and type are per product as well, so the expenses team reads only expense feedback. Splitting products is the closest thing to routing that the tool offers, and it matches how internal teams already own their systems.

Avoid one shared product for everything. A single inbox for twenty tools means every team reads every message, and nobody feels responsible for the item that belongs to them.

## Identify people by work email

Load the widget inside the tool's authenticated layout, after your company login has finished. Then identify the employee with their work email, name, id and team. Team and role become metadata, which helps the owning team see who is asking:

```js
window.EscutaProduto = window.EscutaProduto || { q: [] };
(window.EscutaProduto.q ||= []).push([
  "identify",
  [{ email: user.workEmail, name: user.name, id: user.id, team: user.team, role: user.role }],
]);
```

When the email is known, the form hides the email field, so people do not type it again. Call `identify` again if a person changes team, so later reports carry the right value.

Use the same `identify` call in every tool that belongs to your company, adjusting only the product key. People then keep one habit across tools, and the owning team still sees the right context.

## Keep the widget behind your login

The widget appears wherever you load it, so put the script inside pages that require a company login. Do not add it to a public page of an internal tool, such as a login screen or a status page that anyone can open. Set the allowed origins to the internal domain as well, so the widget cannot be copied to another site.

The hosted feedback page is a different matter. It is public, so anyone with the link can submit a message. Use it for a link inside the company wiki or a help article, and do not treat it as a restricted channel. If a tool must never accept outside input, do not share its hosted link at all.

## Route feedback to the owning team

Each product has its own notification webhook. Create one Slack or Discord channel per team, and point the product at that channel. A new item posts the type, the product name, the rating, the sender and a 500-character excerpt, along with a dashboard link. The owning team sees the report in its own channel and opens the inbox from there.

Keep the channels quiet. An internal tool with a few hundred users might receive a handful of messages a week, which is a good level for a channel. If a tool gets far more, read the messages in the weekly review instead of turning every item into an alert. The [article on Slack channels for product feedback](/resources/slack-channels-for-feedback) covers the trade-offs in more detail.

## Triage with statuses and internal notes

The owning team decides what happens to each item. Use the five statuses to show progress: new, planned, in progress, done and closed. Use the private internal notes on each item for decisions and context, such as "duplicate of the March request" or "waiting on the finance system change".

Tools change hands. When a team hands a tool to another group, change the product's notification webhook to the new owner's channel the same day, and add an internal note on the product's first item explaining the handover. Otherwise alerts keep posting to a channel nobody reads, and the new owners never learn that feedback exists.

Escuta Produto has no tags, so keep labels out of the inbox. If your team wants to group reports by area, use the internal notes or a short metadata key, such as `area`, and export the product to a spreadsheet when you need a summary for a quarterly review.

## Set up Escuta Produto for your internal tools

Create a product for each tool and copy its public key into that tool's authenticated layout. Add the company domain to the allowed origins. Identify an employee, send a test bug and an idea, and check that the item shows the work email and team. Then connect the team's Slack or Discord channel as the product's webhook.

The [widget reference](/docs/widget) lists the options, and the [notifications guide](/docs/notifications) covers the webhook setup. For the customer side of the same idea, [collecting feedback in a SaaS product](/resources/collect-feedback-saas) shows how to identify logged-in users in an app that customers pay for. If your internal tool calls a server, the [REST API reference](/docs/api) explains how to send feedback from a backend job as well.

## Frequently asked questions

### Can I restrict feedback on an internal tool to employees only?

Not with the hosted page. Anyone with its link can send feedback, so it is not access control. Load the widget inside your authenticated internal app, where only signed-in employees see it, and keep the allowed origins set to your internal domain.

### Should all internal tools share one feedback product?

Usually not. One product per tool gives each team its own inbox, key, allowed origins and Slack or Discord channel. Routing then works without rules, because each product already points at the team that owns the tool.

### How do I route feedback to the team that owns a tool?

Give each tool its own product and set that product's notification webhook to the owning team's channel. Escuta Produto has no routing rules, so the product structure is the routing.

---

# How to collect feedback for an open-source project

> For an open-source project, keep bug reports in GitHub issues and collect private feedback with a widget on the docs site, a hosted form linked from the README, and the REST API for command-line tools. Private feedback reaches the users who never open a public issue.

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

## Issues and private feedback do different jobs

A GitHub issue is a public, technical conversation. It works for bugs that someone can reproduce and for proposals that contributors can discuss. It fails for everything else. Many users who love a project never open an issue, because they lack a GitHub account, they feel their question is too small or they do not want to argue in public.

Private feedback fills that gap. A short message such as "The setup guide skipped the environment variable" or "I wish the CLI printed the version" is worth reading even when it does not deserve a public thread. The maintainers can decide which items become issues, and they can reply without starting a debate in front of everyone.

Set the rule early and put it in the contributing guide. Bugs with steps to reproduce go to issues. Everything else can go to the feedback inbox, and a maintainer moves it to an issue if it needs code.

## Put a widget on the docs site

Your documentation site is the best place for the widget, because readers hit problems there. Add the script to the site template, then set the allowed origins in product settings to the docs domain. Without that, the widget will not submit from the site.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Most documentation generators let you add a line to a shared layout or a head include. The point is to load the script on every page, so a reader can report a confusing paragraph where they found it.

You can also add an explicit button to a page you want to highlight, such as the installation guide:

```html
<button type="button" data-escuta-open="bug">Report a problem with this page</button>
```

The widget saves the page URL automatically, so the maintainer knows exactly which paragraph caused trouble. If the docs team wants a different look, use the `data-color` option to match the accent color of the site, and `data-position` to move the button away from the search bar.

## Link the hosted form from the README and releases

The README cannot carry the widget, but it can link to the [hosted feedback page](/docs/hosted-page). Add a line near the top or in the community section, such as "Questions or ideas that do not fit an issue? Send them here". The address looks like `https://escutaproduto.com/f/your-product-slug`.

Release notes are another good place for the link. Readers who just installed a new version are often the ones with the most useful opinions. A closing line such as "Tell us what you think of this release" works without any code, and the hosted page follows the reader's browser language.

Because the hosted page is not indexed by search engines, it stays out of search results, which suits a feedback form.

## Hear from users who never open an issue

Private feedback shows you a different group of users. These are the people who run the project inside a company, who use it in a script they wrote last year, or who are new and do not yet know the contribution rules. Their messages are often vague, and that is fine. A vague message with a version number and a page URL is enough for a maintainer to ask one specific question.

Ask for an email address only when you plan to reply. Escuta Produto does not send email to people who submit feedback, so you reply from your own address, and you should say so in your privacy notice.

Review the inbox on a fixed schedule, such as once a week. Maintainers of popular projects can receive a lot of messages, and a weekly pass with clear statuses keeps the work sustainable. The [article on the weekly feedback review](/resources/weekly-feedback-review-template) gives you a short agenda you can copy.

## Protect a public project from spam

A public form attracts spam, and open-source projects are targets. Escuta Produto protects the form in several layers. Allowed origins make sure the widget only submits from your sites. The form includes a hidden honeypot field that bots fill in and people never see. Rate limits cap submissions at 10 per minute per IP and product, and size limits reject oversized bodies. The form does not use a CAPTCHA, so real contributors never have to solve a puzzle to say thanks.

If spam gets through anyway, use the status "closed" for junk, add a short internal note and move on. Do not let spam shape your roadmap, and do not delete real messages to make the inbox look tidy.

## Close the loop in public and in private

When a private message leads to a fix, say so where it helps the most. A changelog entry that mentions the report, or a comment on the linked issue, shows other users that the feedback led to a change. If the person left an email address, reply from your own mailbox with a short note.

Do not promise dates you cannot keep. A project with volunteer maintainers should say "we are looking at this" rather than "this ships next week". The [article on telling customers their request shipped](/resources/tell-customers-request-shipped) covers the wording for that kind of reply.

## Set up Escuta Produto for your open-source project

Create a product for the project, copy the public key and add the docs domain to the allowed origins. Add the script to the docs layout, then send a test message from a page to confirm the page URL appears on the item. Link the hosted form from the README and the release notes.

Connect a Slack or Discord webhook for the maintainers, so a new message reaches the people who triage. The [notifications guide](/docs/notifications) explains the setup, and the [widget reference](/docs/widget) lists every option. If your project ships a command-line tool, the [developer tools article](/resources/collect-feedback-developer-tools) describes how to send feedback from a CLI, and [feedback form spam protection](/resources/feedback-form-spam-protection) explains the protections in more depth.

## Frequently asked questions

### Should open-source feedback go to GitHub issues or a feedback inbox?

Both, for different jobs. Keep reproducible bugs and code proposals in GitHub issues, where contributors can discuss them publicly. Use a private inbox for general experience, ideas and praise from people who would never open an issue.

### Can I put the feedback widget in my project's README?

No. A README is rendered on GitHub, which does not run third-party scripts. Link to the hosted form from the README instead, and place the widget on your documentation site, where you control the page.

### Does an open-source project need a CAPTCHA on its feedback form?

Not necessarily. The form uses a hidden honeypot field, rate limits of 10 submissions per minute per IP and product, and size limits, which handle most spam without making real users solve puzzles.

---

# How to collect feedback for API and developer tools

> For an API or developer tool, collect feedback from docs pages, error screens and CLI commands, and send a request id and tool version as metadata through the REST API. Relay servers share one IP address, so plan for the limit of 10 submissions per minute per IP and product.

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

## Developer feedback is mostly about errors and docs

Developers rarely write long feedback about a product. They hit an error, copy the message, and move on. When they do write, the message is often about the documentation: a missing parameter, an example that does not run or a sentence that describes the old behavior. Feedback for an API or a developer tool therefore comes from three places: the docs, the error output and the commands people run.

Collect each stream where it happens. A small form on the docs site catches the confusing paragraph. A link near an error message catches the failure. A command in your CLI catches the problem a developer could not describe in a browser.

## Put the widget on the docs pages

The docs site is the main entry point, so load the widget there. Set the allowed origins in product settings to the docs domain, add the script to the page template and let readers report a problem where they found it. The page URL saved with each item tells you which section needs work.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Add an explicit button next to a reference section that often confuses people, such as authentication or pagination:

```html
<button type="button" data-escuta-open="bug">This section is unclear</button>
```

Use the "idea" type for requests and the "bug" type for errors in the docs. A third choice, "other", works for general notes. The [widget reference](/docs/widget) lists the options for placement and color.

## Send a request id with the report

When an error reaches a user in your product, include the request id that your logs use. Developers often copy that id from an error message, and it lets you find the exact request in your own system. Send it as metadata with the report.

```ts
await fetch("https://escutaproduto.com/api/v1/feedback", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    key: "pk_your_product_key",
    kind: "bug",
    message: "Checkout failed with card_declined",
    pageUrl: "https://docs.example.dev/payments",
    metadata: {
      requestId: "req_7c21f0a9",
      sdkVersion: "1.4.0",
      endpoint: "/v2/payments",
    },
  }),
});
```

Keep the metadata short and specific. A request id, the client version and the endpoint name answer the first three questions a maintainer asks. Metadata is a JSON object of up to 4 KB, so there is room, but more is not better. Never include API keys, bearer tokens, card numbers or the full body of a request that contains customer data.

Escuta Produto has no tags, so keep your categories in metadata or in internal notes, and use the text search on the inbox to find related reports.

## Send feedback from a CLI or SDK

A command-line tool is a good place for feedback because the developer is already in the flow. Add a `feedback` command that asks for a message and sends it. The request runs from the developer's machine, so the rate limit counts that person's IP address, which is what you want.

```js
export async function sendCliFeedback(message, { toolVersion, command }) {
  const response = await fetch("https://escutaproduto.com/api/v1/feedback", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      key: "pk_your_product_key",
      kind: "other",
      message,
      metadata: { toolVersion, command },
    }),
  });
  return response.status === 201;
}
```

Ask for consent before you attach anything. The developer should know what the command sends, and the output should say so in one line. If a user runs the command in a CI job, the request comes from the CI runner's IP address, so keep the command manual.

If you publish an SDK, you do not need a native package. Wrap the same request in a small function, and document the one call a developer makes. Escuta Produto does not ship SDKs, so a thin wrapper that you maintain is the usual approach.

## Plan for the rate limit behind a relay

The rate limit is 10 submissions per minute from the same IP address to the same product. For a CLI, that is generous, because each developer's request comes from their own machine. For a backend that forwards reports from every user, it is tight, because all of them leave from one server address.

If you run a relay, you have three reasonable choices. You can send from the client instead. You can keep a queue on your server and send at a steady pace, which means accepting that a report may arrive a minute late. Or you can send the most important reports immediately and hold the rest. When the API returns 429, do not retry at once. Wait a minute and try again. The [REST API reference](/docs/api) lists the other status codes.

## Keep sensitive data out of feedback

Developer tools handle keys, tokens and private data, so take care with what goes into a report. Strip authorization headers from any log you attach. Do not send environment variables, full stack traces that include file contents or request bodies with customer records. A request id and a version are almost always enough for a first reply.

Tell developers what the form stores. A line such as "Your message, the tool version and the command name are sent to the maintainers" builds trust, and it helps the privacy notice match what the code does.

## Set up Escuta Produto for your developer tool

Create a product for the API or tool, copy the public key and add your docs domain to the allowed origins. Add the widget to the docs template and a feedback command to the CLI. Send a test report from each path and check that the metadata and page URL appear on the item.

Connect a Slack or Discord webhook so the team sees reports while they are fresh. The [notifications guide](/docs/notifications) explains the setup. For open-source tools, the [article on collecting feedback for an open-source project](/resources/collect-feedback-open-source) covers the public issue tracker side, and [feedback form spam protection](/resources/feedback-form-spam-protection) explains how the form protects itself from bots.

## Frequently asked questions

### How should a developer tool send feedback from a CLI?

Call the REST API with fetch or an HTTP library from the command. Each user's machine has its own IP address, so the rate limit of 10 submissions per minute per IP and product applies per person, which is what you want.

### What happens if my backend relays feedback from many users?

All of those messages leave from one server IP address, so you can hit the limit quickly. The API returns 429 and does not queue for you. Send from the client when you can, or keep your own queue and retry with a delay.

### Should I include API keys or request bodies in feedback metadata?

No. Do not send secret keys, tokens, full request bodies or personal data. Send an id you can look up in your own logs, such as a request id, along with the tool version and the endpoint name.

---

# How to collect feedback in a two-sided marketplace

> In a two-sided marketplace, keep buyer and seller feedback apart. Use one Escuta Produto product per side for the clearest inbox, or one product with a side value in metadata when volume is low. Ask buyers after delivery and sellers after their first payout, never during checkout.

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

## Buyers and sellers want different things

A marketplace has two customers who depend on each other. A buyer wants a fast, reliable way to find and pay for what they need. A seller wants visibility, fair fees and payments that arrive on time. Their feedback sounds different, and it asks for different fixes. A buyer who reports that search returns the wrong results needs a relevance fix. A seller who reports a delayed payout needs a finance fix.

Mixing those reports into one stream makes both harder to read. The team that owns payouts sees buyer complaints about delivery. The team that owns search reads seller questions about fees. Separating the two sides from the start keeps each group of reports with the people who can act on them.

## Option one: one product per side

The cleanest setup is two products: one for buyers and one for sellers. Each product gets its own public key, its own allowed origins and its own notification webhook. The inbox filters by status and type work per product, so the buyer team reads only buyer messages.

Give each product a clear name, such as "Marketplace buyers" and "Marketplace sellers". Point the buyer product's allowed origins at the buyer site or app, and point the seller product at the seller dashboard. If the two sides share a web app, you can still load two widgets with two keys on different routes, as long as each route uses one key.

Two products cost nothing extra, and they stop the most common problem: a seller reading a buyer's complaint about a missing package and wondering why it reached them.

## Option two: one product with a side in metadata

If your marketplace is small, one product with a `side` value in metadata may be enough. Set the side right before the form opens, so the metadata matches the screen the person is on:

```js
window.EscutaProduto = window.EscutaProduto || { q: [] };
(window.EscutaProduto.q ||= []).push([
  "identify",
  [{ email: user.email, name: user.name, id: user.id, side: "seller" }],
]);
```

The metadata is stored with each item. The limitation is the inbox itself. Escuta Produto filters by status, type and text search, so you cannot click "sellers only" in the dashboard. Choose this option while the volume is low, and move to two products once the inbox grows. Splitting by side later is harder than starting with two products, so decide early if you can.

Escuta Produto has no tags. Use the side value as your grouping key, and use internal notes for decisions on each item.

## Ask each side at the right moment

Timing matters more than the form. Ask buyers after a delivery or a completed booking, when they know whether the product matched the listing. Ask sellers after their first payout or after their listing is approved, when they have experienced the whole flow once.

Avoid the checkout. A buyer who is paying should not see a feedback form, and a seller with a listing still in review is waiting on your team, not answering a survey. Place a quiet link on the order history page for buyers and on the payouts page for sellers. The article on [asking for feedback after a first success](/resources/feedback-after-first-success) describes how to pick the right moment in more depth.

Use the matching type for each entry point. A buyer who reports a problem with an order can use the bug type, while a seller's suggestion about fees fits the idea type. Preselect the type with `data-escuta-open` so people do not have to choose.

## Keep private details out of metadata

Marketplaces hold sensitive data: payment details, delivery addresses, tax records and bank information. None of that belongs in feedback metadata. Use ids rather than names where you can, such as an order id, a listing id or a seller id that your team can look up. Keep the metadata small, as it is capped at 4 KB.

Remember that the inbox is for your team. A buyer's address in a message is still personal data, so write the privacy notice to match what you collect, and remove details from a message before you share it outside the team.

## Route each side's alerts

Each product posts to its own Slack or Discord channel. Create one channel for buyer feedback and one for seller feedback, and point each product's webhook at its channel. A new item shows its type, rating, sender and a 500-character excerpt, so the team that owns the problem sees it first.

If both sides go to one channel, you are back to a shared queue. Keep them apart. The [article on organizing Slack channels for product feedback](/resources/slack-channels-for-feedback) covers the trade-offs of one channel per product and one shared channel.

## Set up Escuta Produto for your marketplace

Create two products, copy each public key into the right part of your app, and set the allowed origins for each site. Send a test report from a buyer account and a seller account, then check that each item lands in the right inbox and that the webhook posts to the right channel.

The [notifications guide](/docs/notifications) explains the webhook setup, and the [widget reference](/docs/widget) lists the options for placement and language. For the first-run experience on the seller side, [collecting feedback in a SaaS product](/resources/collect-feedback-saas) shows how to identify logged-in users with plan and account metadata. You can also route marketplace feedback from a backend job with the [REST API reference](/docs/api) when a report starts in a system other than your web app.

## Frequently asked questions

### Should buyers and sellers use one feedback product or two?

Two is usually easier to read. Each product has its own inbox, key, allowed origins and webhook, so buyer and seller reports never share a queue. Use one product with a side value in metadata only when the volume is small.

### Can I filter a single product's inbox by buyer or seller?

The inbox filters by status, type and text search, not by metadata. If you keep both sides in one product, search for a side value or export the product to a spreadsheet to split it.

### When should a marketplace ask for feedback?

Ask buyers after a delivery or a completed booking, and ask sellers after their first payout or listing approval. Do not ask during checkout or while a listing is pending, because the person is trying to finish something else.

---

# How to collect feedback before you launch

> Before launch, collect feedback with a hosted feedback page that you share with invited testers, and add the widget to your landing page once it is on a live domain. Ask testers specific questions about what they tried, and keep the early items tidy with statuses and notes.

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

## Start before there is a product to install into

Most teams wait until the product is ready before they ask anyone for feedback. That is the wrong order. The earliest opinions are the cheapest to act on, because the product is still flexible and people are willing to say what confuses them. A landing page, a prototype link or a small group of invited testers gives you that signal long before you install a script in an app.

Escuta Produto works at this stage in two ways. The [hosted feedback page](/docs/hosted-page) needs no code and no app, so you can share it right away. The widget can sit on a landing page once that page lives on a domain you control. Both send the same kind of structured item, with the page URL and browser saved automatically.

## Use the hosted page for testers you invite

The hosted page lives at `https://escutaproduto.com/f/your-product-slug`. Share it with each tester in the message that invites them. Add `?lang=pt` or `?lang=en` to match the language of the invitation, and add `?email=` with their address to prefill the form. The prefilled email saves them a step, and it tells you who is writing without asking twice.

Prefilling the email is personal data, so only add it for testers who agreed to take part in the test. Escuta Produto does not send emails, so the invitation and any follow-up come from your own mailbox. Keep the sender name and reply address consistent so testers know who will read their message.

The hosted page is not indexed by search engines, which suits an invitation link. Anyone who has the link can still submit, so do not treat it as private. Share it only with the group you want to hear from.

## Put the widget on the landing page

A landing page is public, so the widget belongs there once the domain is live. Add the script to the page template and set the allowed origins to the landing domain and any staging domain you use. The widget will not submit from a site that is not on the list, so check this before you rely on it.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Use the `data-trigger` option to hide the floating button if the landing page already has a clear call to action. An explicit button with a preselected type works well for a waitlist page:

```html
<button type="button" data-escuta-open="idea">Tell us what you need</button>
```

Visitors who are not yet testers can still tell you what would make the product useful to them. Those messages show you the words people use, which helps with the page copy and the feature list.

## Ask testers for specific feedback

Early feedback is only useful when it is specific. A tester who writes "it's nice" has given you a feeling, not a finding. Ask questions that make people describe what they did:

- What were you trying to do when you opened the product?
- Which step took longest, or made you stop?
- What did you expect to happen, and what happened instead?
- Which part would you miss if it disappeared tomorrow?

Yes or no questions tell you what people think of an idea. Open questions tell you what they do. Put the questions in your invitation email and at the top of the hosted page, so testers see them before they type.

Use the four types to sort what comes back. A bug is something that broke. An idea is a request. Praise tells you what to protect when you build the next version. Use other for anything else, such as a question about pricing or access.

## Use prototypes and staging links carefully

A prototype link is a good way to hear about the design before the code is done. Add the staging domain to the allowed origins, so the widget works there, and share the prototype with a small group. Tell testers which version they are looking at, because a message about an early build is only useful if you know the build.

Remember that a staging environment is often reachable by anyone who finds the link. Keep the widget off pages that show private data, and do not let a test environment hold anything you would not want a stranger to read.

## Keep early feedback tidy

Early feedback piles up quickly, and it is easy to lose the thread when twenty people write in the same week. Use the five statuses to show what happened to each item: new while you read it, planned when you decide to act, in progress while someone works on it, done when it ships and closed when you decide not to act.

Use the private internal notes for context you will need later. A note such as "beta group 1, works on a phone" or "duplicate of the onboarding request" saves time when you read the item again. Escuta Produto has no tags, so use the notes for grouping, and use the text search to find related messages.

Close the loop with the testers who took the time to write. A short reply from your own email, saying what changed because of their message, is the best thank you you can send. The [article on thanking customers for feedback](/resources/thank-customers-for-feedback) covers that message in more detail.

## Move to the in-app widget at launch

When the product is live, keep the hosted page for invitations and help links, and add the widget inside the app for ongoing feedback. Add the production domains to the allowed origins, and check that the page URL on each item points to the right place.

Keep the pre-launch items in the same inbox. You can read the early messages next to the launch messages, which shows you whether the problems that testers described are still present once real customers arrive.

## Set up Escuta Produto before launch

Create a product for the launch, copy its public key and add the landing domain and staging domain to the allowed origins. Share the hosted page with your first testers and send one test item from each route, then check that the page URL, the browser and any prefilled email appear on the item.

Connect a Slack or Discord webhook so new messages reach the founder or the team that reads them. The [notifications guide](/docs/notifications) covers the setup, and the [widget reference](/docs/widget) lists the placement options. For the testing stage itself, [collecting feedback in a beta program](/resources/beta-program-feedback) covers the weekly rhythm, and [collecting feedback in a SaaS product](/resources/collect-feedback-saas) explains how to identify logged-in users once the app is live.

## Frequently asked questions

### Can I collect feedback before the product exists?

Yes. The hosted feedback page works with no app and no code, so you can share it with testers, in a prototype or on a landing page. Create the product first, then send the link.

### How do I add the widget to a landing page before launch?

Add the script to the landing page on a domain you control, and put that domain in the allowed origins. The widget submits only from sites on that list, so add the staging and production domains before you test.

### What should I ask early testers?

Ask what they tried to do, what got in their way and what they expected to happen. Specific questions produce useful answers, while yes or no questions only tell you what people think of the idea.

---

# How to ask for feedback during onboarding

> Ask during onboarding only where a new user has just finished a step or is stuck on one. Put a small entry point next to that step, keep it out of the way of the next button, and attach the step name as metadata so every answer says where it came from.

Source: https://escutaproduto.com/resources/onboarding-feedback
Last updated: 2026-10-09

## Which onboarding steps are worth asking about

Ask during onboarding only where a new user has just done something or is stuck. Those two moments carry the most information. A person who has just connected their first data source can tell you whether the connection flow made sense. A person who opened the same settings page three times without saving can tell you what confused them. A person who has not done anything yet mostly has an opinion about your homepage, and that is not what onboarding feedback is for.

Start by listing the steps of your onboarding in order. Mark each one with three labels: the user has just finished it, the user can get stuck on it, and the user drops off there. Your analytics tool can show drop-off if you use one. If you do not, your support inbox usually shows which steps people ask about most.

Pick two or three steps from that list. Adding a prompt to every screen turns onboarding into a form, and people skip forms.

## Put the entry point next to the step

The best place for an onboarding prompt is inside the step, next to the control the user is working with. A small link labeled "Something unclear here?" beside a confusing field gets a specific answer. The same question on a separate screen, after the user has left the flow, gets a vague one.

Use the widget without its floating button so the prompt appears only where you put it. Set `data-trigger` to `none` on the script tag, then add an element with `data-escuta-open` next to the step. The value preselects the type, so one link can open the form as a bug and another as an idea.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-trigger="none" defer></script>

<button type="button" data-escuta-open="bug">Something is not working here</button>
<button type="button" data-escuta-open="idea">Something is missing here</button>
```

Keep the button text short and in the user's language. The widget form is available in English and Portuguese, and it follows the browser language unless you set `data-locale`.

## Stay out of the way of activation

Activation is the point where a new user gets their first real result. Anything that interrupts the path to that result costs more than the feedback is worth. Three rules keep onboarding prompts honest:

1. Never open a form automatically during a step. Wait for the user to click.
2. Never block the next button. The prompt is a side door, not a gate.
3. Show the same onboarding prompt once per step. Repeating it on every visit makes people feel watched.

If you want a broader question, send it later by email from your own address, after the user has reached their first result. Escuta Produto does not send automatic emails to customers, so that message is yours to write and send.

## Tag each answer with the step

A message such as "this is confusing" is hard to act on without knowing where it happened. The widget saves the page URL automatically, but onboarding steps often share one URL. The step name has to travel with the feedback. Call `EscutaProduto.setMetadata` before the form opens.

```js
document.querySelector("#connect-data-source").addEventListener("click", () => {
  EscutaProduto.setMetadata({ onboardingStep: "connect-data-source" });
  EscutaProduto.open({ kind: "bug" });
});
```

Use a stable step name, not the visible label. Labels change when someone rewrites the copy, and you want the history of answers to survive that change. Metadata can hold up to 4 KB, so a step name and a user role fit easily. Escuta Produto has no tags, so metadata, internal notes and the CSV export are how you group onboarding answers. The [feedback metadata guide](/resources/what-is-feedback-metadata) explains which context is worth capturing.

## Read onboarding feedback as a sequence

Onboarding answers are most useful in order. Filter the inbox to one product, then read the items for one week in the order they arrived. You will often see a pattern. The same step produces confusion on Monday, a workaround shared on Tuesday and a fix request by Thursday. That sequence tells you whether a change helped, which a single message never can. Write the date of each copy or design change in an internal note on the step's items, so the next time you read them you can see which messages came before the fix and which came after.

## A worked example

For example, imagine that 12 new users mention the same step, the one where they connect a data source, in their first week. Their messages use different words: "where is the key", "does this need admin access", "I could not find the button". Those are three ways of describing one missing explanation. The fix is copy near the field, not a new feature, and it should ship first.

When a step produces repeated confusion, decide whether the fix is copy, order or design. Copy fixes are cheap and should ship quickly. Order and design fixes need a planned item and a short internal note in the inbox, so the next person reading the item knows why the decision was made. The [triage routine](/resources/how-to-triage-customer-feedback) covers how to move items through those statuses.

## Setting up onboarding prompts in Escuta Produto

Start with one step. Add the script tag with `data-trigger` set to `none`, add one button with `data-escuta-open`, and set the step metadata on click. Give the product a notification webhook so each new item reaches Slack or Discord with its type and an excerpt. Then review the step's answers once a week.

Read the [widget docs](/docs/widget) before you ship, especially the options for locale and position. If your app is built with Next.js, the [Next.js guide](/docs/nextjs) shows where the script belongs in the root layout. The [notifications docs](/docs/notifications) describe what each Slack or Discord message contains, so you can test the alert before real users depend on it.

## Frequently asked questions

### When is the best time to ask for feedback during onboarding?

Ask when a new user has just finished a step or is stuck on one. Attach the prompt to that step rather than to a screen they reach later. Never open a form automatically, and never block the next button while the person is still setting up.

### Should onboarding feedback use a popup?

Usually not. A small link or button placed next to the step that causes trouble works better than a popup that covers the screen. A popup can work after the user finishes a step, but it should ask one question and be easy to dismiss.

### How do you know which onboarding step a feedback message came from?

Set a step name in metadata with EscutaProduto.setMetadata before the form opens. The page URL is saved automatically, but onboarding steps often share one URL, so the step name is what makes each message specific.

---

# Asking for feedback after a user's first success

> Ask for feedback right after a user's first real result, such as their first report or first sent invoice, and only once they have finished the action. Use one light prompt with the praise type preselected, then read the praise and the friction together.

Source: https://escutaproduto.com/resources/feedback-after-first-success
Last updated: 2026-10-09

## Define the first success before you ask

A first success is the first moment a user gets the result your product promises. For a reporting tool it might be the first report they export. For an invoicing tool it might be the first invoice that reaches a client. For a booking app it might be the first confirmed appointment. The exact event depends on your product, but it should be something a user did on purpose, not something that happened to them.

Write the event down as a sentence before you touch any code: "A user has succeeded when they have ___." If you cannot finish the sentence in one line, the first success is not clear yet, and asking for feedback too early will produce vague answers. Check the sentence against your own data. The users who keep coming back usually reached this moment in their first few sessions.

Once you have the sentence, you have a trigger. The prompt fires after this event, not after a fixed number of days, because a fixed timer ignores what the person actually did.

## Wait one session before asking

Asking the moment the success happens is tempting, but people are often still reading the result. A better rule is to wait until the next session. The user has come back, which means the product mattered enough to return to, and they now have a view of the whole result rather than a fragment of it.

Implementation is simple. When the success event fires, store a flag in your own database or in the user's profile. On the next page load where the flag is set, show the prompt once and clear the flag when the user answers or dismisses it. Do not rely on browser storage for this, because a user who switches devices would see the prompt twice or never.

## Ask with one light prompt

The prompt should be one line and one optional text box. A question such as "How did the first report go?" gives people a reason to answer. A question such as "Please rate your experience on a scale of 1 to 10 and tell us about any improvements" gives them a reason to close the tab.

Use a link or a small card, not a modal. A modal says the product wants something from the user right now. A small card says the user can answer when they have a moment. In the widget, the easiest pattern is a button with `data-escuta-open` set to `praise`, so the form opens with the praise type already selected.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>

<p>Your first report is ready. How did it go?</p>
<button type="button" data-escuta-open="praise">Tell us what you think</button>
```

The form still offers the other types, so a user who wants to report a problem can change the type before sending. Preselecting praise changes the starting point, not the outcome.

## Collect praise and friction in the same prompt

People who reached a first success usually have two things to say. They liked a part of the result, and they hit a snag somewhere on the way. A praise-only prompt misses the snag. A bug-only prompt misses what worked. The open message box handles both, because people write what stands out to them.

When you read the answers, sort each one into what to protect and what to fix. Praise tells you which part of the flow must not break in the next release. Friction tells you where the first success almost did not happen. Both are useful, and the same message often contains both.

Keep the optional star rating in mind too. The product shows an average rating, and the words explain the number. Use the rating to find unusually low or high answers, then read those messages first.

## Keep the prompt from becoming a survey

Escuta Produto does not run surveys, NPS campaigns or multi-question forms. That is deliberate for this moment. A survey after a first success asks the user to switch from doing the task to evaluating it, and the answers become generic. One open message with a type gives people a small, specific thing to say.

If you need a structured score for reporting, collect it separately and on a different schedule. Mixing it into the first success prompt makes the praise look like a grade.

## Turning answers into a praise library

Praise from the first success moment is some of the most honest copy you will get. People describe the result in their own words, and those words are often better than your landing page. Before you use any of it publicly, ask for permission and keep the wording as close to the original as you can. The [guide to turning praise into testimonials](/resources/turn-praise-into-testimonials) covers the permission step and light editing.

For internal use, keep a short list of the phrases customers repeat. When you write release notes or onboarding copy, check that the words match how customers describe the benefit rather than how your team names the feature.

## How to run first success prompts in Escuta Produto

Set up the trigger in your own code, then send the prompt through the widget. Use the praise button described above, and set the first success event as metadata before the form opens, so every answer records which milestone produced it.

```js
EscutaProduto.setMetadata({ milestone: "first-report-exported" });
EscutaProduto.open({ kind: "praise" });
```

Review the inbox weekly and filter by type. Praise items tell you what to protect in the next release, and bug items from this group are often the first sign of a problem in the flow. The [widget docs](/docs/widget) list every option for the button, locale and position, and the [API docs](/docs/api) describe the JSON body if you want to send the praise from your server instead.

If you want the full picture of a feedback program, read [what is in-app feedback](/resources/what-is-in-app-feedback) for the broader case for asking inside the product.

## Frequently asked questions

### What counts as a first success for a product?

It is the first time a user gets the result the product promises, reached through an action they chose to take. Write it as one sentence, such as a user has succeeded when they have exported their first report, then check that your data shows returning users reached it early.

### Should the feedback prompt appear right after the success event?

Usually not. Wait until the user comes back in a later session. That gives them time to see the whole result, and it avoids interrupting the moment they are still reading what the product produced.

### Why use a praise type for a first success prompt?

Praise preselects the form with a positive starting point, and the message box still lets people report problems. Most people at this moment have something to say about what worked and something that slowed them down, so the same prompt collects both.

---

# How to collect feedback on a new feature launch

> Put the feedback entry point inside the new feature, next to the control people use, and preselect the type you need most. Tag each item with the feature name in metadata so launch feedback stays separate from the rest of the inbox during the first weeks.

Source: https://escutaproduto.com/resources/feature-launch-feedback
Last updated: 2026-10-09

## Put the entry point where people use the feature

Feedback about a new feature is most useful when it comes from someone who has just used it. The best place for the entry point is therefore inside the feature itself: next to the button that starts the new flow, or in the empty result panel that appears after the first run. A banner on the dashboard that says "We launched something new, tell us what you think" gets vague praise and generic complaints, because the person has not done anything with the feature yet.

Look at how the feature is opened. If it lives behind a menu item, put the entry point next to the menu item's result, not next to the menu. If it changes an existing screen, put the entry point on the changed area. The rule is that the entry point should sit next to the thing the user is judging.

Escuta Produto does not show a floating button for each feature, so you decide where the entry points go. Set `data-trigger` to `none` on the widget script tag, then place `data-escuta-open` elements where the feature lives. The floating button can stay off for the whole product or for the launch period only.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-trigger="none" defer></script>

<button type="button" data-escuta-open="idea">Tell us about bulk export</button>
```

## Preselect the type you need most

Every launch needs a different kind of feedback at different times. In the first days you usually need bugs, because edge cases appear as soon as real people use the feature. In the second and third weeks you usually need ideas, because people now know what is missing. Praise is useful for knowing what not to change, but you will have plenty of it if the feature works.

Choose the entry point's type to match the question you are asking. A "Report a problem" link can open with `bug` selected, and an "Ask for a change" link with `idea` selected. Both still show the other types, so people can switch if they picked the wrong one. The preselection is a hint, not a filter.

## Mark every item with the feature name

Launch feedback becomes hard to read when it mixes with the rest of the inbox. Six weeks later, nobody remembers whether the complaint about the export was about the new bulk option or the old CSV button. The fix is a feature name in metadata, set before the form opens.

```js
EscutaProduto.setMetadata({ feature: "bulk-export", featureVersion: "2" });
EscutaProduto.open({ kind: "bug" });
```

Use stable names that match your code, not the marketing name of the release. A version key helps when the feature changes in the second week and you want to separate answers for the first design from the second. Metadata holds up to 4 KB, which is far more than a feature name needs, so keep it small and consistent.

Escuta Produto has no tags. Metadata, internal notes and the CSV export are the tools for grouping launch items. Keep the feature name identical across the codebase, the metadata and the release notes so a search in the inbox finds every related message.

## Watch the first week closely

The first week after a launch is when the most important feedback arrives. Check the inbox daily for the feature's entries, and look at the 30-day chart and the counts by type on the product. A spike in new items or in bugs is a signal, and so is a drop in the average rating. A spike means the feature has an edge case you did not test, and it should move to the top of the queue.

Read each bug with the page URL and browser attached. Feature flags, screen sizes and browsers often explain why one person hit a problem that others did not. If a bug cannot be reproduced, close it with a note that describes what you tried, so the next reader does not repeat the same search.

## Separate launch noise from real problems

Some feedback is noise. A user who clicked the wrong button and wrote "this is broken" needs a short reply, not a fix. Other feedback is an early warning. The difference is usually visible in the detail: the message names a step, a data type or a result that is wrong.

Create a simple rule for the first two weeks. Any item that names a wrong result goes to planned work within 48 hours. Any item that asks how to do something goes to the help documentation or to a short reply that links the docs. Any item that asks for a new capability goes to the idea queue, where it waits for the weekly review. The [triage routine](/resources/how-to-triage-customer-feedback) describes how to run that review, and the [bug versus feature request guide](/resources/bug-report-vs-feature-request) helps when a message could be either.

## Closing the loop on a launch

When the first round of fixes ships, reply to the people who reported the problems. A short reply that says the fix is live, and thanks them for the detail, does more for future feedback than any form. The [guide to telling customers their request shipped](/resources/tell-customers-request-shipped) covers how to find who asked and what to write.

Keep the feature's metadata in the note when you close the launch. In three months, you will want to know which fixes came from the launch, and the note saves you from searching the whole inbox.

## How to run a feature launch feedback loop in Escuta Produto

Place one or two entry points where the feature is used, set the feature metadata on each click, and turn on the Slack or Discord webhook for the product during the launch period. Each new item then posts its type, rating and excerpt, which helps the team react quickly.

Use the [widget docs](/docs/widget) for the attributes and the JavaScript methods, and the [notifications docs](/docs/notifications) for what the alert contains. After the launch, leave the metadata in place. Items with the feature name stay searchable, so the history is still useful when you plan the next version.

## Frequently asked questions

### Where should the feedback entry point for a new feature go?

Put it inside the feature, next to the control people use or the result panel they see after using it. A banner on the dashboard gets vague answers because people have not used the feature yet.

### Which feedback type should a feature launch ask for first?

Bugs in the first days, because edge cases appear as soon as real people use the feature. Ideas after two or three weeks, once people know what is missing. Preselect the type on each entry point, but leave the other types available.

### How do you keep launch feedback separate from the rest of the inbox?

Set a feature name in metadata with EscutaProduto.setMetadata before the form opens. Keep the name identical to your code, then search or read the items with that name during the launch period.

---

# How to collect cancellation and churn feedback

> Add one optional question to the cancellation flow, then send the answer to your feedback inbox from your backend using the REST API with the account and reason in metadata. Keep the question skippable, never block the exit, and read the answers by reason every month.

Source: https://escutaproduto.com/resources/cancellation-feedback
Last updated: 2026-10-09

## Why cancellation is the most honest feedback you will get

A customer who cancels has already decided to leave, so they have no reason to be polite or to protect your feelings. That makes their reason more direct than almost any other message you will read. The problem is that most products treat cancellation as a button that disappears the account and nothing else. The reasons are lost, and the same problems come back in the next cohort.

Cancellation feedback is different from a general feedback prompt. The question is narrow, the person is leaving, and the answer should inform the product and the team that handles retention. Treat it as its own moment, with its own copy and its own routing.

## Add one optional question to the cancellation flow

The flow should ask one question: why are you leaving? Provide a short text box, and make it clear the field is optional. Do not add a list of reasons with checkboxes, a rating or a second screen. A long exit flow makes people feel trapped, and trapped people give false answers just to finish.

Put the question on the page where the cancellation is confirmed, before the final button. Let the user cancel even if they leave the box empty. A question that blocks the exit produces anger, which is the worst input for a churn analysis.

Escuta Produto does not include a cancellation form or a survey builder, so the question is part of your own page. The widget can open from that page as well. A good pattern is a small text area in your own markup that submits to your backend when the user confirms.

## Send the reason from your backend

Send the cancellation reason to your feedback inbox from your server, not from the browser. Your server already knows the account, the plan and the cancellation time, and it can attach that context safely. The REST API takes a JSON body with a public key, a message, a kind, an optional email and a metadata object.

```js
// Runs on your server after the cancellation is confirmed
async function sendCancellationReason({ account, reason }) {
  if (!reason || reason.trim().length < 2) return;

  await fetch("https://escutaproduto.com/api/v1/feedback", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      key: process.env.ESCUTA_PUBLIC_KEY,
      kind: "other",
      message: reason,
      email: account.ownerEmail,
      name: account.ownerName,
      metadata: {
        event: "cancellation",
        accountId: account.id,
        plan: account.planName,
        tenureDays: account.tenureDays,
      },
    }),
  });
}
```

The message must be at least two characters, so the guard in the example skips empty or one-letter answers. The response returns `201` with an `id` when the item is stored. A `400` means the body was invalid, and a `429` means too many requests came from the same address in one minute, which is unlikely for a single cancellation but worth retrying later if you send in bursts. Read the full field list and error codes in the [API docs](/docs/api). The OpenAPI 3.1 file at the `openapi.json` route describes the same body if you generate a client.

The key is public, which is safe here because it can only create feedback. It cannot read the inbox. Still, keep it in configuration, not in a hard-coded string, so you can rotate it if you ever need to.

## Choose the kind carefully

Use `other` for a general reason such as "too expensive for what I use" or "moved to a different team". Use `idea` when the reason names a missing capability, such as "I need to export to a spreadsheet and you do not support it". Use `bug` when the reason is an error the customer could not get past. The kind helps you route the item later, and a missing feature is a much more useful signal than a generic complaint.

Do not argue with the reason in the moment. Record it, thank the person in one line, and let the team decide what to do.

## Read churn feedback by reason

Once a month, read the cancellation items together. Filter the inbox to the product, then read the messages in batches of twenty. Group them by reason in a spreadsheet. A simple sheet with three columns works: the reason in the customer's words, the category you assign, and the account tenure from metadata. Use the CSV export for this, because it opens cleanly in Excel and Google Sheets.

Count distinct accounts for each category, not messages. One account can send several notes, and you want to know how many customers left for each reason. For example, if 12 accounts left because a feature was missing and 3 left because of a bug that is now fixed, the feature gap deserves the next planning slot.

Look at tenure too. Reasons from accounts that stayed for a year often point to strategy. Reasons from accounts that left in the first week often point to onboarding. The [onboarding guide](/resources/onboarding-feedback) covers that early stage in more detail.

## What to do with churn reasons

Some reasons are fixable: a missing export, a confusing setting or a bug. Put those in the planned queue with a note that links to the cancellation items. Some reasons are not fixable: the customer's business closed or their budget moved. Close those with a short internal note so you stop re-reading them.

Do not reply to a cancellation by email unless the customer asked you to. If they did ask, reply once, honestly and briefly. Escuta Produto does not send automatic emails to customers, so any follow up is written and sent from your own address. The [guide to saying no](/resources/say-no-to-feature-requests) helps when the reason is a feature you will not build.

## How to set up cancellation feedback in Escuta Produto

Create a product for your app if you do not have one yet, copy its public key, and add the server call above to the code path that confirms the cancellation. Add the product's notification webhook if you want the team to see cancellation reasons as they arrive, or leave it off and read them in the weekly review.

Read the [API docs](/docs/api) before you ship, especially the section on error codes, so your code handles a `429` without losing the reason. Set the allowed origins in the product settings only if you also use the widget on the same product, because the server call does not depend on them.

## Frequently asked questions

### Should the cancellation flow require a reason before the customer can leave?

No. Make the reason field optional and let the customer cancel with an empty box. A required question creates frustration, and frustrated customers give answers that do not help you understand the real reason they left.

### How do you send a cancellation reason to a feedback inbox?

Call the REST API from your server after the cancellation is confirmed. Send the public product key, the reason as the message, a kind such as other, and an account id and tenure in metadata. The key can only create feedback, not read it.

### How often should a team review churn feedback?

Read it once a month in one batch, grouped by reason and counted by distinct account. Monthly is enough to see patterns, and it keeps the review from becoming a reaction to a single loud cancellation.

---

# What to ask customers after a failed payment

> After a failed payment, send the customer a link to update their card before anything else. Ask for feedback only when the card is not the problem, using one open question and the hosted feedback page, so billing trouble and real reasons to leave stay separate.

Source: https://escutaproduto.com/resources/failed-payment-feedback
Last updated: 2026-10-09

## Send the update-payment link first

A failed payment is usually a billing problem before it is a product problem. The card expired, the bank declined a charge or the account ran out of funds. The customer wants the service to keep working, and the fastest way to help them is a link that updates the payment method. Anything else in that message is a distraction.

Your billing system owns this email. Most billing tools send dunning messages automatically, and the wording and timing belong to your team. Put the update-payment link first, in the first lines and as the main button. If you add a feedback link, place it lower, after the payment instructions, and label it clearly as optional.

## Ask one question only when the card is not the problem

Some failed payments are card problems. Others happen because the customer no longer wants to pay. Those two groups need different messages. A card problem needs a link and a short line of reassurance. A customer who is leaving may need a question, because their reason is information you can use.

Your billing system usually knows the decline code, but you may not want to read it inside the feedback form. A simple rule works well. Send the feedback link only after your normal reminder period has passed without a successful payment. A customer who has just fixed their card should not receive a question about why they were leaving, so check the payment status before the message goes out.

## Use the hosted feedback page in the email

The hosted feedback page lives at `/f/<product-slug>`, and it works without a login or a script on your site. Add `?lang=` for the language and `?email=` to prefill the address. The page uses the product's accent color, so it looks like part of the product and not an outside form.

The link in a billing email might look like this, with `your-product` replaced by your product slug and the address filled in by your email template:

```text
https://escutaproduto.com/f/your-product?lang=en&email=customer@example.com
```

The prefilled address appears in the link, so use it only in messages sent to that customer. Do not paste it into a public page or a shared document. The hosted page is not indexed by search engines, which keeps the link out of public results.

Escuta Produto does not send customer emails. The billing system or your own email tool sends the message, and you control the copy. The [hosted page docs](/docs/hosted-page) describe every parameter.

## Write the question as a single line

Keep the feedback question short and open. A line such as "If you are leaving for a reason we could fix, tell us what it was" invites an honest answer without making the customer feel accused. Avoid asking them to rate the product or choose from a list. A customer dealing with a declined card does not want a survey.

Do not ask the customer to explain the payment failure in the feedback box. Card problems belong to the billing link and your support team. Feedback is for the product. If the message says "my bank declined it", reply with the update-payment link and do not file it as product feedback.

## Tell card problems from intent to leave

When the feedback arrives, read it with two questions in mind. First, is this a billing problem the customer can fix? Second, is this a product reason they might leave? A message such as "the card keeps failing on your site" is a billing support item. A message such as "I am not using the reports anymore" is a churn signal.

Use the kind to help. Set the type to `other` for churn reasons and `bug` only when the message describes a product error, such as a checkout page that fails for every card. The distinction matters because billing bugs and product bugs go to different owners.

## Protect the customer from a sales tone

A failed payment is stressful. A message that sounds like a sales pitch, offering a discount or a plan change, feels pushy at that moment. Keep the tone neutral. Say what happened, show the update link, and ask the one question if you need it. Do not offer incentives in the same email. If you have a retention offer, send it separately and only to customers who ask about their options.

## Read failed payment feedback as a churn signal

Read the answers once a month, grouped by reason. Customers who wrote about a missing feature or a changed workflow are telling you why the product stopped fitting their work. Count distinct accounts, not messages. The [guide to cancellation feedback](/resources/cancellation-feedback) uses the same method, and the two lists can be read together.

If many customers mention the same failure in the checkout flow, treat that as a bug. A cluster of messages about a broken payment page is a product issue, even when each customer blames their bank.

## How to route failed payment feedback in Escuta Produto

Create a product for the app, then add the hosted link to the billing email template after the update-payment button. Use the product's notification webhook if you want the team to see churn-related messages quickly, or read the inbox in the weekly review. The [triage routine](/resources/how-to-triage-customer-feedback) explains how to separate billing support from product feedback during that review.

Set the product's allowed origins only if you also use the widget on the same product. The hosted page does not depend on them, and the billing email does not run on your site. The [notifications docs](/docs/notifications) describe the Slack and Discord messages that new items send.

## Frequently asked questions

### Should a failed payment email include a feedback link?

Only as a secondary option placed after the update-payment button. The first job of the email is to get the card fixed. A feedback link can follow, clearly labeled as optional, for customers who want to explain why they might leave.

### How do you separate a card problem from a customer who wants to leave?

Send the feedback link only after your normal reminder period has passed without a successful payment, and check the payment status first. A message about the bank declining a charge is a billing item. A message about no longer using the product is a churn signal worth reading.

### Can you prefill the customer's email on a feedback page after a failed payment?

Yes. The hosted feedback page accepts an email parameter, so the address appears already filled in. Use the link only in messages sent to that customer, because the address is visible in the link itself.

---

# Collecting feedback after a support ticket closes

> Ask for product feedback after a support ticket closes, but keep it separate from the support satisfaction score. Send the ticket id as metadata so each answer links back to its conversation, and ask one optional question instead of a rating form.

Source: https://escutaproduto.com/resources/feedback-after-support-ticket
Last updated: 2026-10-09

## Separate product feedback from support satisfaction

A support ticket closes with a question that is about the agent: was the answer helpful? The product feedback question is different: what should we change in the product? Mixing them produces answers that are hard to use. A customer who liked the agent but hated the feature will rate the ticket highly and say nothing about the feature. A customer who was happy with the feature but waited three days for a reply will rate the ticket low and mention the wait.

Decide which question you are asking before you write the message. If you want to measure the support team, use your help desk's own satisfaction rating or a short internal review. If you want to learn about the product, send a product feedback prompt that is clearly separate. This guide covers the second case. Keeping both channels apart means each one tells you something you can act on.

Escuta Produto is not a help desk. It has no ticket inbox, no agent view and no integration with support tools, so it will not replace your support software. Its job is to collect product feedback and keep the context attached to each item.

## Decide when the ask goes out

The best moment to ask is shortly after the ticket closes, while the customer still remembers the problem. A day or two later, the answer is usually already in the customer's mind, and the reason they wrote in is fading. Do not ask on the same minute, because the customer may still be reading the final reply.

Only ask customers who had a product question. A ticket about a billing address, a password reset or an account transfer rarely has product feedback in it. A ticket about a confusing screen, a missing option or a bug is a much better candidate. Tag the ticket category in your help desk and filter on it before you send anything.

Do not send the ask more than once for the same ticket, and do not send it to customers who asked you to stop contacting them.

## Carry the ticket id as metadata

The reason to attach the ticket id is simple. When a product message says "the export is confusing", the ticket shows the conversation, the product version and the steps the customer described. The id connects the feedback to that history without copying the whole ticket into the inbox.

The widget can carry the id because you set it before the form opens. The hosted feedback page cannot, because it does not accept metadata parameters. So use a page in your app that the closing email links to. That page reads the ticket id from its address, sets the metadata and opens the form.

```js
// On a page such as /help/feedback?ticket=TK-1042
const ticketId = new URLSearchParams(window.location.search).get("ticket");
if (ticketId) {
  EscutaProduto.setMetadata({ source: "support", ticketId });
  EscutaProduto.open({ kind: "other" });
}
```

The ticket id is not personal information on its own, but treat the metadata the same way you treat the rest of the customer record. Do not put the customer's support message into metadata, because the excerpt in the feedback item already shows what they wrote.

If you also want the id to travel from your server, the REST API accepts the same metadata object, so a backend job can create the feedback item directly. Use that only when the customer has already written the product feedback, because the API creates an item without asking the customer anything.

## Keep the question short and optional

One optional question is enough. "Is there anything in the product that made this harder than it should be?" is specific, and it points to the product rather than the agent. A rating grid or a multi-question survey turns a resolved ticket into homework.

Do not frame the question as a request for a review. The customer asked for help, received it, and now has the option to tell you something about the product. Make the choice easy to decline, with no pressure or repeated reminders.

## Read the answers next to the ticket

When an item arrives with a ticket id, open the ticket in your help desk before you decide anything. The ticket shows whether the product problem was already known, whether a workaround was given and whether the customer had to contact you twice. Those details decide whether the fix belongs in planned work, in the help documentation or in a short reply.

Review the items weekly. A cluster of messages that point to one screen is a product signal, even when each ticket was resolved quickly. The [triage routine](/resources/how-to-triage-customer-feedback) describes how to move those items through statuses, and the [guide to reply templates](/resources/reply-to-customer-feedback) helps when you answer the customer.

## Connect the ticket link to the widget

Add a link in the closing message of the ticket. The link points to the feedback page in your app, with the ticket id in its address. Keep the wording plain: a single sentence that says you would like to hear what would make the product easier to use, followed by the link.

Test the whole path yourself before you turn it on. Close a test ticket, open the link, send a message and check that the item in Escuta Produto shows the ticket id in its metadata. Also check that the page works when the customer is not logged in, because many people open support links from their email client.

## How to set up support follow-up in Escuta Produto

Create a product for the app, enable the widget on the feedback page, and set the allowed origins to your app's domain so only your site can submit through the widget. Turn on the product's notification webhook if the support team wants to see product messages right away.

Read the [widget docs](/docs/widget) for the metadata method and the open call. The [API docs](/docs/api) describe the server route if you prefer to create items from your help desk webhook. Use the reply templates and the status workflow you already have, and keep the support rating in the help desk where it belongs.

## Frequently asked questions

### Should product feedback and support satisfaction use the same question?

No. A support rating measures the agent and the response time. Product feedback measures the product. Mixing them makes both answers hard to read, so ask them separately and keep each one with its own owner.

### How do you link a feedback message to a support ticket?

Put the ticket id in metadata before the feedback form opens. Use a page in your app that reads the id from its address, sets the metadata and opens the widget. That way each feedback item points back to the ticket it came from.

### When should you ask for product feedback after a ticket closes?

Shortly after the ticket closes, once and only for tickets that involved the product. Skip billing, account and password requests, and never send the same ask twice for one ticket.

---

# How to collect feedback in a beta program

> Keep beta feedback separate by giving it its own product or a beta flag in metadata. Set expectations with testers before the first message, give them a link that needs no login, and review their reports once a week in a fixed batch.

Source: https://escutaproduto.com/resources/beta-program-feedback
Last updated: 2026-10-09

## Decide whether beta gets its own product

Beta testers report differently from regular customers. They expect rough edges, they are curious about what is coming, and they often write longer messages about things they have not finished testing. If their feedback lands in the same inbox as everyday customer messages, it can drown out the people who depend on the product today. Keeping the two groups apart is the first decision.

You have two options. The first is a separate product in Escuta Produto for the beta. Its inbox, statistics and notification webhook stay independent from the main product, so the beta team can read it on its own schedule. The second is a metadata flag on each item, such as `channel: "beta"`, filtered by hand in the main inbox. The separate product is simpler when the beta has a distinct audience and its own team. The metadata flag works better when a few testers use the same product and you want their reports close to the rest.

Choose one and stick with it for the whole program. Switching in the middle makes the history hard to read.

## Flag beta testers in metadata

If you use one product, set the beta flag when the tester is identified. The `identify` call stores any key you pass beyond email and name as metadata. That means a beta flag set once at login follows every feedback item from that tester.

```js
EscutaProduto.identify({
  email: user.email,
  name: user.name,
  id: user.id,
  beta: true,
  betaCohort: "2026-autumn",
});
```

Use a cohort name when you run more than one beta over the year. It lets you compare the first cohort with the second without guessing from dates. Keep the names stable and short, and document them in your team notes.

Metadata is stored with each item, and it holds up to 4 KB, which is plenty for a beta flag and a cohort name. Escuta Produto has no tags, so this is the way to mark testers. Use internal notes on each item for anything the team needs to remember about the tester.

## Set expectations before the first message

Testers behave well when they know what you want from them. Before the first session, tell them three things: what the beta is for, what kind of feedback helps most and how quickly they can expect a reply. A short welcome email or an in-app note works well. Avoid promising features or dates you cannot keep.

A helpful expectation sounds like this in plain words: "We want to know where the new reports confuse you and what you expected to see. Bugs with the steps to reproduce them help the most. We read every message, but we cannot reply to all of them within a day." Each sentence sets a boundary the tester can rely on, which is the point of setting expectations at the start.

Ask testers to say what device and browser they used, if they are willing. The widget saves the browser automatically, so you may not need to ask, but a description of the workflow they were trying helps more than a list of browsers.

## Give testers a link that needs no login

Not every tester will open the app every day. Some will only read the email that invited them, and others will use a second device. A hosted feedback page at `/f/<product-slug>` works without a login or an installed script, so it is the simplest way for a tester to send a note from wherever they are.

Add `?lang=` for the language and `?email=` to prefill the address. The page uses the product's accent color, so testers recognize it as part of the program. The [hosted page docs](/docs/hosted-page) list every parameter.

Keep the hosted link for testers who need it and the widget for testers who already use the app. Both write to the same inbox, so you do not need to combine them later.

## Run a weekly beta review

Beta feedback needs its own rhythm. Read every new item once a week, in one sitting of about an hour. Sort bugs first and reproduce each one with the page URL and browser saved on the item. Then read the feature messages and the praise, and write a short note on each item that says whether it is planned, closed or needs more detail.

Ask a follow-up question when a report is unclear. A message that says "it broke" needs the steps, the screen and the time. Ask for those details once, politely, and move on if the tester does not reply. Escuta Produto does not send automatic emails to customers, so write the follow-up from your own address.

Share a short summary with the team after each review. Three lines are enough: the bugs fixed this week, the bugs still open and the one idea the team should discuss. A regular summary makes the beta feel like a conversation rather than a pile of messages.

## What to do when the beta ends

When the beta closes, decide what happens to the items. Bugs that were fixed get a reply to the tester who reported them. Ideas that were not built get a short note saying why. The [guide to telling customers their request shipped](/resources/tell-customers-request-shipped) describes how to find the people who asked for a feature.

Testers who helped a lot deserve a thank you. The [guide to thanking customers for feedback](/resources/thank-customers-for-feedback) covers specific thanks that avoid promises you cannot keep. If you plan to ask a tester for a public quote, do it after the beta ends and only with their permission.

## How to run a beta inbox in Escuta Produto

Choose the separate product or the metadata flag, then set up the widget or the hosted page for the beta testers. Use the product's Slack or Discord webhook if the beta team wants to see reports as they arrive, and read the rest in the weekly review. The [widget docs](/docs/widget) describe the identify call and the open method.

Filter the inbox by status while the beta runs, so each item moves from new to planned, in progress and done as the team works through it. The [status workflow guide](/resources/feedback-status-workflow) explains how to define who moves each item and when.

## Frequently asked questions

### Should beta feedback go to a separate product or be tagged in the main inbox?

Use a separate product when the beta has its own audience and team. Use a metadata flag when a few testers use the main product and you want their reports close to the rest. Pick one approach for the whole program so the history stays readable.

### How often should a team review beta feedback?

Once a week in one batch of about an hour. Sort bugs first, reproduce them with the saved page URL and browser, then read ideas and praise. Share a short summary with the team after each review.

### How do you tell beta testers what kind of feedback to send?

Explain what the beta is for, what feedback helps most, such as bugs with steps to reproduce, and how quickly you can reply. Set those expectations before the first message, and avoid promising features or dates you cannot keep.

---

# How to collect feedback on your pricing page

> Add a quiet entry point to your pricing page that asks what visitors are unsure about, and let them choose the question type. Read the objections as a list of reasons people hesitate, grouped by theme, rather than as votes for a new price.

Source: https://escutaproduto.com/resources/pricing-page-feedback
Last updated: 2026-10-09

## Why a pricing page needs its own question

The pricing page is where a visitor decides whether your product is worth the next step. They have already read your landing page, so their questions are specific. Does the plan include the feature they need? Is there a limit on the number of projects? Can they move between plans later? Those questions are product feedback in the purest form, because each one describes something the visitor cannot see from the page.

Most teams answer these questions with a sales email or a chat window, but a visitor who is not ready to talk to anyone still has them. A feedback entry point on the page catches those questions before the visitor leaves. It also shows you which parts of the page are unclear, which is often easier to fix than a missing feature.

## Use an entry point that does not interrupt

The page is for choosing, so the entry point should be quiet. A small text link at the bottom of the comparison table works well. A pop-up that appears when the visitor moves toward the exit makes people feel pushed, and pushed visitors leave faster. Do not show the feedback button on every scroll position or animate it to draw attention.

Turn off the floating button on the widget and add your own link. Set `data-trigger` to `none` on the script tag, then add an element with `data-escuta-open` where the visitor would look for an answer, usually near the question about plans.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-trigger="none" defer></script>

<a href="#" data-escuta-open="other">Still unsure which plan fits? Ask us here</a>
```

Use a plain link in the markup rather than a button that looks like a call to action. A visitor who is comparing options does not want another decision to make. The link should read as an invitation to ask, not a request to buy.

## Give visitors a type that matches the question

Visitors ask two kinds of questions on a pricing page. Some are objections: "this costs more than I expected for a small team". Others are product questions: "does this export to a spreadsheet?" The form offers bug, idea, praise and other. Use `idea` for questions about a capability the visitor wants, and `other` for general questions about the plan.

Give the visitor the choice when the page has more than one kind of question. A second link with `data-escuta-open="idea"` next to a comparison row about a specific feature gets a more precise answer than a single general link. Keep the number of links small, because a page with five feedback links looks like a form.

## Read objections without treating them as votes

The temptation with pricing feedback is to count objections and change the page to match the loudest one. That is a mistake. A visitor who writes "too expensive" might be a fit for a smaller plan, or might not be the customer you want. A visitor who asks for a feature that is on a higher tier may be telling you the tier is poorly described.

Read the messages in batches and group them by the decision they block. A useful set of groups is: the feature is missing, the limit is unclear, the difference between two options is unclear and the price feels wrong for the team size. Each group has a different fix. The first is product work. The second and third are copy changes. The fourth needs a conversation, not a page edit.

Count distinct visitors, not messages, when you compare groups. One visitor who sends three notes in one session should count once. The [guide to triaging customer feedback](/resources/how-to-triage-customer-feedback) explains how to weigh requests by who asked and how often.

## Keep visitor data out of the form

A visitor on the pricing page is usually not logged in, so you do not have an account to identify. Do not try to attach personal details from the browser to make the message more useful. The widget saves the page URL and browser automatically, and that is enough to know the message came from the pricing page.

The URL tells you which page the visitor was on. If you use different paths for each plan, the URL shows which option they were looking at, which is valuable context. Avoid putting prices or account details into metadata, because the feedback inbox should hold the visitor's words, not your billing data.

The [guide to GDPR and LGPD and feedback widgets](/resources/feedback-widget-gdpr-lgpd) covers what the widget collects and how to describe it in your privacy policy. It is general guidance, not legal advice, so check the wording with someone qualified for your market.

## Protect the page from spam

A public page attracts spam. Visitors are not the only people who visit a pricing page, and automated traffic often targets it. The widget has a hidden honeypot field, rate limits of 10 requests per minute per IP for each product, and size limits on the message. Set allowed origins in the product settings so only your site can submit through the widget.

Avoid adding a CAPTCHA to the pricing page. It slows down the visitors you most want to hear from, and the built-in protections handle most spam. The [spam protection guide](/resources/feedback-form-spam-protection) explains why this approach works and when to add more.

## Turn the answers into page changes

Once a week, read the new visitor messages and choose one change for the page. A clearer label on the comparison table, a note under a limit that explains what it means in practice, or a short paragraph that says which option suits a small team are all small, specific fixes. Make one change, then watch whether the same question comes back.

When a question is about a missing feature, add it to the planned queue. When the answer is a page change, make the change and note the date in the item so the team knows when the copy was updated. The [feedback status workflow](/resources/feedback-status-workflow) shows how to define those states.

## How to add a pricing page entry point in Escuta Produto

Create a product for the marketing site or the app, copy its public key and add the script tag with `data-trigger` set to `none`. Place one or two links on the pricing page, then set the allowed origins to the domain of that page. Turn on the notification webhook if the team wants visitor questions to reach Slack or Discord quickly.

Read the [widget docs](/docs/widget) for the attributes and the open method. Check the inbox weekly for questions that name a missing feature or a confusing limit, because those two groups are the most likely to change a decision.

## Frequently asked questions

### Should a pricing page show a pop-up feedback form?

No. A pop-up interrupts a visitor who is comparing options, and that often makes them leave. A quiet text link near the plan comparison invites questions without pushing the visitor toward a different decision.

### How do you read objections about price without changing the page too quickly?

Group the messages by the decision they block, such as a missing feature, an unclear limit or an unclear difference between options. Count distinct visitors, then change one thing at a time and watch whether the same question returns.

### Should you identify visitors on the pricing page?

Usually not. Most visitors are not logged in, and the widget already saves the page URL and browser. Keep billing details and personal data out of the form, so the inbox holds the visitor's own words.

---

# Using empty states to collect product feedback

> An empty state is a moment when a user expects something and finds nothing, which makes it a good place to ask what they hoped to see. Write the empty state copy as a question, open the widget from a button there, and tag the screen name in metadata so the answers stay specific.

Source: https://escutaproduto.com/resources/empty-state-feedback
Last updated: 2026-10-09

## Why an empty state is a moment of intent

An empty state is what a user sees when a list, report or dashboard has no data yet. The screen says "no invoices", "no reports" or "nothing here". Most teams treat that screen as a gap to fill with a generic call to action. It is also a rare moment when the user's expectation is visible. They came to the screen for something, and the screen did not give it to them. That mismatch is valuable.

A user who sees an empty invoice list may expect to see invoices from an import they did not finish, invoices from a different account or a filter they forgot to clear. Each of those expectations describes something about the product that you can learn from. A question at that moment captures the expectation while it is still fresh.

Ask only when the empty state is a surprise, not when it is the normal first screen for a new account. A user who has never imported anything is not confused by an empty list. A user who imported last week and sees nothing now is.

## Write the empty state copy as a question

Most empty states explain what would fill them. Add a second line that asks what the user expected. The copy is short and specific: "No reports yet. Were you expecting to see something here?" The question invites an honest answer, and it does not sound like a survey.

Keep the main call to action where it is. The feedback prompt sits beside it, as a secondary option. A user who wants to create a report should still see the create button first, because that action helps them more than writing feedback.

Avoid the words "survey", "rate" or "help us improve". They make the moment feel like homework. The best prompt uses the user's own situation: "Were you expecting reports from last month's import?" That question is specific enough to get a useful answer.

## Connect the button to the widget

Use the widget without its floating button, so the prompt appears only on the screens you choose. Set `data-trigger` to `none` on the script tag, then add a button with `data-escuta-open` on the empty state. The value preselects the type. For this moment, `idea` fits most cases because the user is describing what they expected, which is usually a missing or unclear capability.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-trigger="none" defer></script>

<section>
  <h2>No reports yet</h2>
  <p>Were you expecting to see something here?</p>
  <button type="button" data-escuta-open="idea">Tell us what you expected</button>
</section>
```

If the empty state is built with a framework component, keep the attribute on the element that the user clicks. The widget listens for clicks on that attribute, so the button can live inside any component.

## Tag the screen in metadata

The widget saves the page URL automatically, but an empty state often lives at a URL shared with other states, such as a list page that shows a different message after a search. A screen name in metadata makes each answer specific. Set it before the form opens, using a short name that matches your code.

```js
document.querySelector("#empty-reports").addEventListener("click", () => {
  EscutaProduto.setMetadata({ screen: "reports-empty", importStatus: "none" });
  EscutaProduto.open({ kind: "idea" });
});
```

Use only details that help you understand the expectation. A screen name and a simple status are useful. A full list of the user's data is not needed, and metadata has a 4 KB limit anyway. Keep the data minimal, because everything in metadata is stored with the feedback item.

## Read the answers as missing features and unclear copy

Empty state answers fall into two groups. The first group describes a feature the user expected and did not find. Those answers become ideas, and you can count distinct users who asked for the same thing. The second group describes confusion: the user thought data should be there, but the import or filter that would have produced it was unclear. Those answers point to copy or onboarding changes, and they are often cheaper to fix.

Read the screen name alongside the message. If many answers come from one screen, the screen is the problem. If answers come from many screens about the same missing thing, the problem is probably a missing feature. The [guide to prioritizing bugs and feature requests](/resources/problem-behind-feature-request) explains how to look for the underlying problem in a request that sounds like a feature.

## Keep the prompt from nagging

An empty state is seen many times by the same user, so the prompt must not repeat on every visit. Show the feedback button on the empty state, but do not open the form automatically, and do not add a second prompt to the same screen. If a user has already answered, the button can stay visible for the next time they need it. Repeated requests make people feel that the product is asking the same question over and over.

Respect the moment as well. A user who has just signed in for the first time and sees an empty dashboard is still deciding whether the product is for them. Give them the create action first, and keep the feedback prompt quiet. The [onboarding feedback guide](/resources/onboarding-feedback) covers how to ask during setup without interrupting it.

## Adding an empty state entry point in Escuta Produto

Find the empty states that users actually see after the first week. Add one button to each, with `data-escuta-open` set to `idea`, and set the screen metadata on click. Check the inbox after a week, and read the answers for that screen together. Escuta Produto has no tags, so the screen name in metadata is the grouping key.

Use the [widget docs](/docs/widget) for the attributes and the open method. If your screens are rendered by a framework, the [Next.js guide](/docs/nextjs) shows where the script goes in the layout so every page can use the same entry point. For the wider case for asking inside the product, see [what is in-app feedback](/resources/what-is-in-app-feedback).

## Frequently asked questions

### Why ask for feedback on an empty state?

An empty state is a moment when the user expected something and did not find it. That mismatch shows what they hoped the product would do, which makes it a good time to ask what they expected to see.

### Should an empty state open the feedback form automatically?

No. Show a button or link on the empty state and let the user choose to open the form. Automatic forms interrupt the user, and repeated prompts on the same screen make people feel the product is nagging them.

### How do you know which empty state a feedback message came from?

Set a screen name in metadata with EscutaProduto.setMetadata before the form opens. The page URL is saved too, but a list page can show several empty messages, so the screen name tells you which one prompted the message.

---

# How to collect feedback from release notes

> Give each release note entry its own feedback button, and set the release version in metadata when the button is clicked. Choose the type that matches the change, then read the answers against that release so you can close the loop with customers on the next update.

Source: https://escutaproduto.com/resources/release-notes-feedback
Last updated: 2026-10-09

## Why release notes are a good place to ask

Release notes are read by people who care about the product. Someone who opens the changelog is already interested in what changed, and they often have a specific opinion about one entry. They read the new export option and think "that is what I needed" or "that broke my workflow". Asking right there captures the opinion while the change is fresh.

The catch is that a single "tell us what you think" link at the bottom of a long changelog gets a single vague answer. Feedback is much more useful when each entry has its own entry point. The reader can point at the change they mean, and the answer carries the release it refers to.

Release notes also give you a natural rhythm. Each release has a version and a date, so feedback can be grouped by release, and the closing loop can happen in the next set of notes. That makes this moment easier to run than a general feedback prompt.

## Give each entry its own feedback button

Add a small button or link to each changelog entry. The label should point at the change, not at the product in general. "Tell us about the new export" is specific. "Give feedback" is generic. Keep the label short so it does not compete with the entry text.

Use the widget without its floating button, so the entry points are the only way to open the form on the changelog page. Set `data-trigger` to `none` on the script tag, then add a button for each entry. The release version goes into metadata when the button is clicked, so each button needs a data attribute with the version.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-trigger="none" defer></script>

<article>
  <h2>Bulk export is here</h2>
  <p>Export many items to a spreadsheet at once.</p>
  <button type="button" class="release-feedback" data-release="2026.10.1">Tell us about bulk export</button>
</article>
```

Changelog pages are often static, so the same markup works in a Markdown or CMS template. The only requirement is that each button carries the release version as an attribute.

## Mark the release in metadata

The release version is the key that links feedback to the change. Set it before the form opens, using the version number your team already uses.

```js
document.querySelectorAll(".release-feedback").forEach((button) => {
  button.addEventListener("click", () => {
    EscutaProduto.setMetadata({ release: button.dataset.release });
    EscutaProduto.open({ kind: "idea" });
  });
});
```

Use the same version string in your release notes, your code and your metadata. A mismatch between "2026.10.1" and "v2026.10.1" makes the search harder later. Metadata holds up to 4 KB, which is more than a version number needs, so keep the data limited to the release and the entry name.

Escuta Produto's hosted feedback page does not accept metadata parameters. That is why the release tag has to come from the widget on the changelog page. A hosted link works for a general note at the end of the page, but it cannot say which entry the reader meant.

## Pick the type for each entry

Each entry should preselect the type that matches the change. A new feature is best read with `idea`, because readers tell you what they want next. A fixed bug needs `bug` when the reader says the fix did not work. A change that broke a workflow usually needs `bug` as well, because it describes something that now fails.

A plain `data-escuta-open` attribute with a type is enough when you do not need the release tag, because the widget opens the form with that type selected. The click handler above is needed only when you want to tag the release, so choose the approach that fits each entry. Both open the same form, and the reader can still change the type.

Do not offer every type on every entry. A changelog is for reading, and a crowded set of choices makes people skip the link.

## Read feedback against the release that prompted it

Review release feedback once the release has been out for a week. Filter the inbox by product, then group the items by the release value in metadata. For each release, read the bugs first, then the ideas, then the praise. A bug that appears only after a release is a regression, and it should move to the top of the queue.

Count distinct customers for each entry. A single enthusiastic reader can write three messages about one change, and that is still one person. The [triage routine](/resources/how-to-triage-customer-feedback) describes how to weigh requests by how many people asked and who they are.

Praise on a release entry is useful for copy. Ask permission before you quote it, and keep the wording close to the original. The [guide to turning praise into testimonials](/resources/turn-praise-into-testimonials) covers the permission step.

## Close the loop in the next release notes

Feedback from a release is only useful if you respond to it. In the next set of notes, mention the fixes and the ideas that shipped, and say which earlier feedback prompted them. A line such as "Thanks to everyone who reported the export timeout last week, it now completes for large files" tells readers that the feedback mattered.

Reply to the customers who asked for each change, too. The [guide to telling customers their request shipped](/resources/tell-customers-request-shipped) explains how to find those people with notes and search. Writing the changelog itself in customer language is covered in [how to write a changelog customers read](/resources/write-a-changelog).

Do not promise a change in the release notes if it is not planned. A note that says "we are looking at this" is honest, and a note that promises a date you cannot keep is worse than silence.

## How to link release notes to feedback in Escuta Produto

Create a product for the app, copy its public key and add the widget script to the changelog page with `data-trigger` set to `none`. Add one button per entry with the release version in `data-release`, then add the click handler above. Turn on the product's Slack or Discord webhook if the team wants to see feedback about a new release as it arrives.

The [widget docs](/docs/widget) explain the open method and the metadata call. The [notifications docs](/docs/notifications) describe what each alert contains, so you can check that the release value shows up as expected. Use the status workflow to move items from new to planned and done as the next release ships.

## Frequently asked questions

### Should each release note entry have its own feedback link?

Yes, when the entry describes a change people can use. A separate button lets the reader point at the change they mean, and the answer can carry the release version. A single link at the bottom of a long changelog gets vague answers.

### How do you link feedback to a specific release?

Put the release version in a data attribute on each button, then set it as metadata with EscutaProduto.setMetadata when the button is clicked. Use the same version string in your changelog, your code and your metadata so the items are easy to group.

### How do you close the loop on feedback about a release?

Mention the fixes and the ideas that shipped in the next release notes, and say which earlier feedback prompted them. Reply to the customers who asked for each change, and avoid promising work that is not planned.

---

# Add a feedback widget to a React app

> Put the widget script in index.html of your Vite React app with defer. Then render a small component inside your auth provider that calls identify when a user signs in. Any element with data-escuta-open opens the form. The script loads once, so the widget keeps working across client-side routes.

Source: https://escutaproduto.com/resources/feedback-widget-react
Last updated: 2026-10-09

## Where the script tag goes in a Vite project

Put the script in `index.html`, the file Vite uses as the entry point for the whole app. Add it to the `head` with `defer`, so the 5 KB (compressed) widget never blocks the first render. A single-page app loads this file once, which means the widget is ready on every route without any extra work.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My app</title>
    <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
```

In a Create React App project the file is `public/index.html`. CRA is no longer maintained, so new projects should start with Vite. The `data-key` is the public key from your product settings. It is safe to ship in the bundle because it can only create feedback, never read it.

## Pass the signed-in user from your auth context

Identify attaches a name and email to the feedback the person sends next, and it hides the email field in the form. Render a component inside your auth provider so it can read the user without prop drilling:

```tsx
// src/components/feedback-identity.tsx
import { useEffect } from "react";
import { useAuth } from "../auth";

export function FeedbackIdentity() {
  const { user } = useAuth();
  const id = user?.id;
  const email = user?.email;
  const name = user?.name;

  useEffect(() => {
    if (!id || !email) return;
    const w = window as typeof window & { EscutaProduto?: { q?: unknown[][] } };
    w.EscutaProduto = w.EscutaProduto || { q: [] };
    (w.EscutaProduto.q ||= []).push(["identify", [{ id, email, name }]]);
  }, [id, email, name]);

  return null;
}
```

Then render it beside your app, inside the provider:

```tsx
// src/main.tsx
createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <AuthProvider>
      <App />
      <FeedbackIdentity />
    </AuthProvider>
  </StrictMode>,
);
```

Keep identify to the fields you would show the person themselves. The call runs in the browser, so anything you pass can be read with developer tools. Send an id, an email, a name and, if you want it, a plan label. Never send session tokens, API keys or internal role data. Passing the values as an object to `push` means nothing gets concatenated into code, so a name with quotes or angle brackets is stored as plain data.

## Why the effect depends on user fields

The dependency array holds the id, email and name, not the user object. An auth provider can create a new user object on every render, and depending on the object would push identify on every render. With primitive dependencies, the push happens only when a field actually changes.

React 18 and later run effects twice in development under StrictMode, so you will see two identical identify pushes in dev. Production runs the effect once. Both are harmless, because the same fields are sent again.

The widget has no call that clears identity. If your app shares a browser between accounts, reload the page after sign-out so the next person starts with a clean page.

## Open the form from your own buttons

The floating Feedback button appears by default. For a settings menu, a help page or an empty state, use the `data-escuta-open` attribute on any element. The value preselects the type: `bug`, `idea`, `praise` or `other`.

```tsx
<button type="button" data-escuta-open="bug">Report a problem</button>
```

Add `data-trigger="none"` to the script tag if you want only your own buttons. Once the script has loaded, the JavaScript API is also available as `window.EscutaProduto.open({ kind })`. Call it only after load, or check that the object exists first, because the script may still be downloading when a user clicks early.

## Routes, navigation and what the widget sees

React Router, TanStack Router and similar libraries change routes with the browser History API. The document does not reload, so `index.html` and the widget stay in place. Nothing needs to re-run on each route.

Each feedback item saves the page URL, so a message sent from `/projects/42/billing` shows that path in the inbox, not just the home page. Combined with the browser name the widget saves, that is usually enough to reproduce a layout bug without asking the customer for a screenshot.

## Content Security Policy for a React build

A production Vite build loads your code from external files and has no inline scripts. The policy only needs the widget host added to two directives:

```text
script-src 'self' https://escutaproduto.com;
connect-src 'self' https://escutaproduto.com;
```

The widget loads its script from the first host and posts feedback to the second. If you already allow other origins, add this one next to them. For more on how these directives behave with third-party widgets, read [Content Security Policy for third-party widgets](/resources/content-security-policy-widgets).

The Vite dev server is different. It injects a small inline script for React Fast Refresh, so a strict policy can break `npm run dev` even when production works. Allow that inline script only in development, or set a nonce in your dev server headers.

## Send feedback from your own React form

If you want a custom form, skip the widget and call the [REST API](/docs/api) from your component:

```ts
await fetch("https://escutaproduto.com/api/v1/feedback", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    key: "pk_your_product_key",
    kind: "idea",
    message,
    email,
    pageUrl: window.location.href,
  }),
});
```

A JSON content type triggers a CORS preflight, so your site's origin must appear in the allowed origins for the product. Otherwise the API returns 403. Localhost is a different origin, so add your dev URL with its port too. The API returns 201 with an id on success and 429 when one IP sends more than 10 requests a minute for the product. Show a short retry message on 429 instead of silently failing.

For most React apps the widget is the simpler choice. It already handles layout, the Escape key and focus return, so you only write code for your own entry points.

## Check your first item in the Escuta Produto inbox

1. In the product settings, add `http://localhost:5173` (the default Vite dev URL) to the allowed origins.
2. Start the app, sign in, open the Feedback button and send a test idea.
3. Open the product inbox. The new item shows the page URL, the browser, and the name and email from identify.
4. Optionally, set a Slack or Discord webhook so each new item posts to a channel. See [Slack and Discord notifications](/docs/notifications).

If the item shows no name, the identify effect did not run. Check that `FeedbackIdentity` sits inside the provider that has the user, and that the user object is not null when the page renders.

For the basic widget options, read the [widget reference](/docs/widget). If your team also builds with Vue, the [Vue install guide](/resources/feedback-widget-vue) follows the same pattern with a composable.

## Frequently asked questions

### Where should the feedback widget script go in a Vite React app?

In index.html, inside the head, with the defer attribute. Vite serves that file for the whole single-page app, so the script loads once and stays loaded while people move between routes.

### How do I pass the logged-in user to the feedback widget in React?

Render a component inside your auth provider that reads the current user and pushes an identify call onto the widget queue inside a useEffect. The effect runs again only when the user id, email or name changes.

### Does the feedback button keep working after React Router navigation?

Yes. Client-side routes change the URL without reloading the page, so the widget stays loaded. Feedback sent from any route records the page URL where the person was when they sent it.

### Will a strict Content Security Policy block the widget in a React app?

Not in production builds, which load your code from files. The policy only needs the widget host in script-src and connect-src. The Vite dev server injects a small inline script for Fast Refresh, so allow that in development only.

---

# Add a feedback widget to a Vue app

> Load the widget script from index.html in a Vite Vue 3 app. Call identify from a composable that watches the signed-in user, so each feedback item carries their name and email. Vue Router moves between routes without a page reload, so the widget keeps working. Any button with data-escuta-open opens the form.

Source: https://escutaproduto.com/resources/feedback-widget-vue
Last updated: 2026-10-09

## Add the script to index.html in a Vite Vue project

A Vite Vue 3 project has one HTML entry file at the root, next to `package.json`. Put the widget script in its `head` with `defer`. The 5 KB (compressed) script then loads without blocking the first render, and it stays loaded for the life of the page.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Vue app</title>
    <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
```

The `data-key` is your public product key. It can only create feedback, so shipping it in the page is expected. Do not add the script inside a component. A component that mounts twice would load the widget twice, and the script belongs to the whole document anyway.

## Identify the user from a composable after login

Identify attaches a name and an email to the next feedback item and hides the email field in the form. A composable keeps that logic in one place. It accepts any reactive source, so it works with a Pinia store, a ref in a plain module or a computed value:

```ts
// src/composables/useFeedbackIdentity.ts
import { watch, type WatchSource } from "vue";

type FeedbackUser = { id: string; email: string; name?: string };

declare global {
  interface Window {
    EscutaProduto?: { q?: unknown[][] };
  }
}

export function useFeedbackIdentity(source: WatchSource<FeedbackUser | null>) {
  watch(
    source,
    (user) => {
      if (!user) return;
      window.EscutaProduto = window.EscutaProduto || { q: [] };
      (window.EscutaProduto.q ||= []).push([
        "identify",
        [{ id: user.id, email: user.email, name: user.name }],
      ]);
    },
    { immediate: true },
  );
}
```

Call it once in your root component, where the session lives:

```vue
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useAuthStore } from "./stores/auth";
import { useFeedbackIdentity } from "./composables/useFeedbackIdentity";

const { currentUser } = storeToRefs(useAuthStore());
useFeedbackIdentity(currentUser);
</script>

<template>
  <RouterView />
</template>
```

Using `immediate: true` matters. A user who is already signed in when the app starts gets identified on the first run, not only after their next login. The watch fires again only when the reference changes, which happens when your store sets a new user object at login or logout.

Keep the payload to what you would show the person. The identify call runs in the browser, so anything in it can be read with developer tools. Send an id, an email, a name and maybe a plan label. Never send tokens or internal role data.

## Vue Router navigation and the widget

Vue Router changes the address with the History API and swaps route components without a full reload. The `index.html` script stays loaded, so the Feedback button is present on every route. You do not need to re-run the script in `router.afterEach`, and doing so would only load it again.

The page URL saved with each feedback item comes from the page the person was on when they sent it. A message sent from `/teams/acme/settings` shows that path in the inbox, which helps when a bug only appears on one screen.

## Open the form from a Vue template

Vue passes `data-*` attributes straight through to the element, so a button can open the form with the preselected type:

```vue
<template>
  <button type="button" data-escuta-open="idea">Suggest a feature</button>
  <button type="button" data-escuta-open="bug">Report a problem</button>
</template>
```

Valid values are `bug`, `idea`, `praise` and `other`. For a floating button you do not want, add `data-trigger="none"` to the script tag and keep only your own buttons. The widget handles focus and the Escape key, so you do not need a custom modal for the form.

## Set a Content Security Policy for a Vue build

A Vite production build compiles single-file component templates ahead of time and loads your code from external files. The policy therefore only needs the widget host:

```text
script-src 'self' https://escutaproduto.com;
connect-src 'self' https://escutaproduto.com;
```

The unsafe-eval keyword is needed only if your app compiles templates in the browser, which happens when you import the full build of Vue with a runtime template string. Most Vite projects do not. If your CSP blocks something in development, check whether the Vite dev server's inline preamble is the cause. The [CSP guide for widgets](/resources/content-security-policy-widgets) explains how to debug blocked requests.

## Link to the hosted page from a Vue menu

Some teams want a feedback link in a help menu rather than a floating button. The [hosted feedback page](/docs/hosted-page) works for that, and it takes the language and a prefilled email as query parameters:

```ts
const url = new URL("https://escutaproduto.com/f/your-product-slug");
url.searchParams.set("lang", "en");
if (user.value?.email) url.searchParams.set("email", user.value.email);
window.open(url.toString(), "_blank", "noopener");
```

Be careful with the email. A query string can end up in server logs, browser history and shared links. For signed-in users, the widget with identify is the better choice, because it sends the email in the request body. Use the hosted link for people who are not signed in, or for a public page where you want a clean URL.

## Check the Vue install in your Escuta Produto inbox

1. Add `http://localhost:5173` to the allowed origins of your product, so the widget works in `npm run dev`.
2. Sign in, open the Feedback button and send a test idea.
3. In the product inbox, check that the item carries the name and email from identify, plus the page URL.
4. Repeat the test after navigating to another route. The page URL should change to match the route.

If the name is missing, confirm that the composable is called in a component that is mounted for the whole session. A component inside a route view unmounts when the user leaves that route, which stops the watch.

The [widget reference](/docs/widget) lists every option, and the [Nuxt guide](/resources/feedback-widget-nuxt) covers the same pattern for server-rendered Vue apps.

## Frequently asked questions

### Where do I put the feedback widget script in a Vue 3 project?

In the head of index.html, the entry file Vite serves for the app, with the defer attribute. The script then loads once and stays available on every Vue Router route.

### How do I identify a logged-in user in Vue without repeating code?

Write a composable that takes a reactive user source and watches it. When a user appears, it pushes identify onto the widget queue. Call it once in your root component and every page inherits the identity.

### Does Vue Router navigation affect the feedback widget?

No. Router navigation changes the URL with the History API and swaps components without reloading the document, so the widget stays loaded. Do not add the script again in navigation guards.

### Can I use the Escuta Produto widget with a strict Content Security Policy in Vue?

Yes. Single-file component templates compile at build time, so the app does not need unsafe-eval. Allow the widget host in script-src and connect-src, and the script and feedback requests will pass.

---

# Add a feedback widget to a Nuxt app

> Add the widget script under app.head in nuxt.config.ts with defer and your data-key. Call identify from a .client.ts plugin that watches the current user, because window does not exist on the server. Server-side code can send feedback through the REST API from a server route.

Source: https://escutaproduto.com/resources/feedback-widget-nuxt
Last updated: 2026-10-09

## Add the widget script under app.head in nuxt.config.ts

Nuxt lets you set head tags once for the whole app. Add the widget to `app.head.script` in `nuxt.config.ts`. Nuxt then includes the tag in every server-rendered page, and the browser loads the script after the HTML arrives.

```ts
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: "https://escutaproduto.com/widget.js",
          defer: true,
          "data-key": "pk_your_product_key",
        },
      ],
    },
  },
});
```

The `defer` attribute keeps the widget from blocking hydration. The `data-key` attribute is passed through unchanged, so the widget reads its options as usual. If you prefer the script at the bottom of the body, add `tagPosition: "bodyClose"` to the same entry. Either position works, because the widget attaches its button after it loads.

Put this in `nuxt.config.ts` rather than a page, so every route inherits it. A script added inside a page component would appear only on that page, and it would be added again each time the page mounts.

## Identify the user from a client-only plugin

Identify must run in the browser, where the widget lives. A plugin whose file name ends in `.client.ts` runs only on the client. The plugin below watches the current user from Nuxt's shared state, so it identifies the person at login and again on any later change:

```ts
// plugins/feedback-identity.client.ts
type FeedbackUser = { id: string; email: string; name?: string };

declare global {
  interface Window {
    EscutaProduto?: { q?: unknown[][] };
  }
}

export default defineNuxtPlugin(() => {
  const user = useState<FeedbackUser | null>("currentUser");

  watch(
    user,
    (next) => {
      if (!next) return;
      window.EscutaProduto = window.EscutaProduto || { q: [] };
      (window.EscutaProduto.q ||= []).push([
        "identify",
        [{ id: next.id, email: next.email, name: next.name }],
      ]);
    },
    { immediate: true },
  );
});
```

The key passed to `useState` must match the key that your session code writes to. If a server middleware or a composable fetches `/api/me` and sets `currentUser`, both sides use the same string. A mismatch is the most common reason for an item with no name.

## Why identify must not run on the server

During server rendering `window` is undefined, so a plain `<script>` block that touches the widget would throw on the server. Keeping the call in a `.client` plugin avoids that. It also avoids hydration mismatches, because the server never renders user-specific feedback code.

Identify on the server would not reach the feedback item anyway. The widget runs in the visitor's browser, so the identity has to be set there. Server code can still send feedback itself, as described below.

## Open the form from a Nuxt page

Nuxt renders the page on the server, and the attribute on the button is plain HTML, so it appears in the first response:

```vue
<template>
  <button type="button" data-escuta-open="praise">Tell us what you like</button>
</template>
```

Valid types are `bug`, `idea`, `praise` and `other`. The widget attaches its behavior once the script runs in the browser. A click before the script has loaded has nothing to open yet. For early visitors, keep the floating button as the fallback, and use the attribute buttons for places where people already have a reason to give feedback.

## Content Security Policy in a Nuxt app

Nuxt inlines a small bootstrap script in server-rendered HTML. A strict policy must allow that script too, usually with a nonce or hash generated per request. Nuxt's security module (nuxt-security) manages those headers for you, and it has options for nonces and the directives you list.

The widget needs its own two entries, whatever approach you use:

```text
script-src 'self' https://escutaproduto.com;
connect-src 'self' https://escutaproduto.com;
```

If you set headers yourself in a Nitro route rule or a server middleware, merge these values with the ones you already have rather than replacing the whole policy. The [CSP guide for widgets](/resources/content-security-policy-widgets) shows how to find the blocked request in the browser console when something fails.

## Send feedback from a Nuxt server route

Sometimes the feedback starts on the server, for example from a form that posts to your own API route. A Nitro server route can forward the message to the [REST API](/docs/api):

```ts
// server/api/feedback.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody<{ message?: string; email?: string }>(event);
  const message = body.message?.trim() ?? "";
  if (message.length < 2 || message.length > 4000) {
    throw createError({ statusCode: 400, statusMessage: "Message length is invalid" });
  }

  return await $fetch<{ ok: boolean; id: string }>("https://escutaproduto.com/api/v1/feedback", {
    method: "POST",
    body: { key: "pk_your_product_key", kind: "other", message, email: body.email },
  });
});
```

The check runs before the request leaves your server, so bad input never reaches the API. The API still enforces its own limits: it returns 400 for invalid data, 413 for a body over 16 KB and 429 above 10 requests per minute per IP and product. Let your form show a retry message on 429.

## Check the Nuxt install in your Escuta Produto inbox

1. Add `http://localhost:3000` (the default Nuxt dev URL) to the allowed origins of your product.
2. Sign in, open the Feedback button and send a test praise item.
3. Open the product inbox and check that the item carries your name and email, along with the page URL.
4. Navigate to another page, send a second item and confirm its page URL changed.

If nothing appears in the inbox, open the browser console on your page and look for blocked requests. A missing allowed origin shows up as a 403 from the API, and a CSP block appears as a console error about the widget host.

The [Vue guide](/resources/feedback-widget-vue) covers the same identify pattern for a client-only Vue app, and the [performance notes](/resources/feedback-widget-performance) explain how defer keeps the script from slowing your pages.

## Frequently asked questions

### Where should I add the feedback widget script in a Nuxt app?

In nuxt.config.ts under app.head.script, with src, defer and your data-key. Nuxt renders the tag in every server response, so the widget is available on all pages from the first load.

### Why does identify need a client-only plugin in Nuxt?

The identify push writes to window, which does not exist while Nuxt renders on the server. A plugin named with the client suffix runs only in the browser, so the call is safe there and never runs on the server.

### Can a Nuxt app send feedback from the server?

Yes. A server route can forward a feedback message to the REST API with your public key. Server requests send no Origin header, so the allowed origins list does not apply to them. Validate the message before forwarding it.

### Does the Nuxt widget keep working after client-side navigation?

Yes. Nuxt navigates between pages on the client without reloading the document, so the script stays loaded. The identify plugin also keeps working, because it watches shared state rather than running once per page.

---

# Add a feedback widget to a SvelteKit app

> Place the widget script in src/app.html with defer. Pass the signed-in user from a +layout.server.ts load function, then call identify in an $effect in the root layout. Set the Content Security Policy with kit.csp in svelte.config.js, so SvelteKit manages nonces and hashes for its own scripts.

Source: https://escutaproduto.com/resources/feedback-widget-sveltekit
Last updated: 2026-10-09

## Place the widget script in src/app.html

`src/app.html` is the template SvelteKit wraps around every page. Add the widget to its `head`, with `defer`, and keep the `%sveltekit.head%` placeholder where it is:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%sveltekit.assets%/favicon.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    %sveltekit.head%
    <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
  </head>
  <body data-sveltekit-preload-data="hover">
    <div style="display: contents">%sveltekit.body%</div>
  </body>
</html>
```

Because `app.html` is shared by every route, the script loads once per full page load. SvelteKit's client router then takes over, and the widget is not requested again. Avoid placing the script in a `+page.svelte` file. That would add it per route and could load it twice on the same page.

The `data-key` is your public product key, which can only create feedback. It is safe in the HTML.

## Pass the user from a load function to the layout

Identify needs the signed-in user, and the usual place for it is the session your `hooks.server.ts` reads. The root layout's server load function returns only the fields the widget needs:

```ts
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from "./$types";

export const load: LayoutServerLoad = async ({ locals }) => {
  const user = locals.user
    ? { id: locals.user.id, email: locals.user.email, name: locals.user.name }
    : null;
  return { user };
};
```

Returning a small object instead of `locals.user` keeps password hashes, tokens and internal roles out of the page data. SvelteKit sends load data to the browser as serialized JSON, so names with quotes or angle brackets stay as data.

Set `locals.user` in `src/hooks.server.ts`, where your session lookup runs. Declare its type in `src/app.d.ts` so the layout compiles. The `getUserFromSession` function below stands in for your own session code:

```ts
// src/hooks.server.ts
import type { Handle } from "@sveltejs/kit";

export const handle: Handle = async ({ event, resolve }) => {
  const session = event.cookies.get("session");
  event.locals.user = session ? await getUserFromSession(session) : null;
  return resolve(event);
};
```

## Identify the user in the root layout

Read the data in the root layout and call identify from an effect. The effect runs in the browser only, which is where the widget lives:

```svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import type { Snippet } from "svelte";
  import type { LayoutData } from "./$types";

  let { data, children }: { data: LayoutData; children: Snippet } = $props();

  $effect(() => {
    const user = data.user;
    if (!user) return;
    window.EscutaProduto = window.EscutaProduto || { q: [] };
    (window.EscutaProduto.q ||= []).push([
      "identify",
      [{ id: user.id, email: user.email, name: user.name }],
    ]);
  });
</script>

{@render children()}
```

The effect reads `data.user`, so it runs again whenever SvelteKit invalidates the layout data. That happens after login, after logout and after any action that calls `invalidateAll`. The ordinary case, a page navigation with the same user, does not re-run it.

Two details trip people up. First, the effect reads the root layout's data, so a user returned only from a page-level load is invisible to it. Either return the user from the layout, which every route inherits, or move the effect into that page. Second, `$effect` runs only in the browser, which is the only place the widget exists, so the identify call never runs during server rendering.

The global type for `window.EscutaProduto` goes in `src/app.d.ts`, inside the existing `declare global` block:

```ts
// src/app.d.ts
declare global {
  interface Window {
    EscutaProduto?: { q?: unknown[][] };
  }
}
export {};
```

## Open the form from a SvelteKit route

Svelte keeps attributes as written, so a button can preselect a type:

```svelte
<button type="button" data-escuta-open="bug">Report a bug</button>
```

For a floating button you do not want, add `data-trigger="none"` to the script tag in `app.html`. Valid types are `bug`, `idea`, `praise` and `other`. Because SvelteKit renders on the server first, the attribute is present in the initial HTML, and the widget wires it up after it loads.

## Client navigation in SvelteKit

SvelteKit intercepts clicks on internal links, fetches the next route's data and swaps components without a full document load. `window`, the widget and its queue all survive those swaps. Feedback sent from any route records the URL where the person was when they sent it.

If a link must reload the page, for example to leave the app for a non-SvelteKit route, add `data-sveltekit-reload` to that anchor. Use it sparingly. A full reload resets the page, and the widget loads again from the beginning.

## Set the Content Security Policy in svelte.config.js

SvelteKit can write your Content Security Policy for you, and it knows about its own inline scripts. Put the policy in `kit.csp` in `svelte.config.js`:

```js
// svelte.config.js
const config = {
  kit: {
    csp: {
      directives: {
        "script-src": ["self", "https://escutaproduto.com"],
        "connect-src": ["self", "https://escutaproduto.com"],
      },
    },
  },
};

export default config;
```

SvelteKit adds the quotes around keywords such as self, so write them bare in the array. In the default auto mode, prerendered pages get hashes for their inline scripts and server-rendered pages get nonces. You do not manage those values by hand.

Some guides put the policy in a `handle` hook instead. That works, but then your hook sets the header on every response, and you must keep SvelteKit's own inline scripts allowed yourself. Use the hook only if you already control every header. The [CSP guide for widgets](/resources/content-security-policy-widgets) explains what each directive covers.

## Verify the SvelteKit setup in the Escuta Produto inbox

1. Add `http://localhost:5173` (the default Vite dev URL that SvelteKit uses) to the allowed origins of your product.
2. Sign in, open the Feedback button and send a test bug.
3. In the inbox, check that the item shows your name and email, plus the page URL.
4. Navigate to another route with a normal link, send a second item and confirm the page URL changed without a reload.

If the name is missing, check that `data.user` is not null on the page where you tested. A layout load function that returns only `locals` fields may be returning null for a logged-out session.

The [widget reference](/docs/widget) lists every option. For a server-rendered app with a different framework, the [Astro guide](/resources/feedback-widget-astro) shows how to keep the widget across view transitions.

## Frequently asked questions

### Where do I add the feedback widget script in SvelteKit?

In src/app.html, inside the head element, with defer. SvelteKit renders that template for every page, so the script is present on the first response and stays loaded while people navigate client-side.

### How do I pass the signed-in user to identify in SvelteKit?

Return the user from a load function in +layout.server.ts, then read data.user in the root layout and call identify inside a $effect. SvelteKit serializes the data safely, so you do not escape it yourself.

### Should I set the Content Security Policy in hooks or in svelte.config.js?

Use kit.csp in svelte.config.js for the policy. SvelteKit then adds nonces or hashes for its own scripts. Set headers in a handle hook only when you already control every response header yourself.

### Does SvelteKit client navigation reload the feedback widget?

No. SvelteKit intercepts link clicks and swaps page components without reloading the document, so the widget stays loaded. Add data-sveltekit-reload to a link only when you need a full page load.

---

# Add a feedback widget to an Astro site

> Add the widget script to your base layout with is:inline and defer. Astro keeps the script as written, and ClientRouter keeps the widget working across view-transition navigations. Logged-in pages can identify the user from Astro.locals, while static pages work anonymously through data-escuta-open buttons.

Source: https://escutaproduto.com/resources/feedback-widget-astro
Last updated: 2026-10-09

## Add the widget to your base layout

Most Astro sites share one layout file that wraps every page. Add the widget script to its `head` in that file, and mark it with `is:inline`:

```astro
---
// src/layouts/Base.astro
const { title } = Astro.props;
---
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>{title}</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script is:inline src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
  </head>
  <body>
    <slot />
  </body>
</html>
```

Astro processes the `script` tags it finds in components, bundling local modules and rewriting some attributes. The `is:inline` directive tells Astro to leave the tag exactly as written. Without it, the remote script and its `data-key` could be changed or moved. The `defer` attribute keeps the widget from blocking the page while the HTML renders.

Every page that uses the layout gets the widget, so you do not add it per page. Keep it in one place so a future redesign cannot drop it from some pages by accident.

Astro builds static pages at build time and server-rendered pages at request time, and the widget script behaves the same in both. It runs in the browser after the HTML arrives, so the only part that differs is identify, which needs a user that exists on the server. After you add the layout, view the page source and confirm the script tag sits in the head. If it appears anywhere else, look first for a missing `is:inline` directive.

## Keep the widget across view transitions

Astro's view transitions swap the page body on navigation, which is faster than a full reload. Enable the router by importing `ClientRouter` from `astro:transitions` and placing it in the head:

```astro
---
import { ClientRouter } from "astro:transitions";
---
<head>
  <ClientRouter />
</head>
```

`ClientRouter` is the current name for the component. Older tutorials show `ViewTransitions`, the earlier name, and those examples still describe the same behavior. With the router enabled, the script in the layout head stays loaded between pages, so the floating button and the `data-escuta-open` buttons keep working.

If you add an inline script that must run again on every navigation, give it the `data-astro-rerun` attribute. Astro runs such scripts again after each page swap. The identify call in the next section uses it for that reason.

## Identify logged-in users on server-rendered pages

An Astro site with server rendering can read the session in middleware and pass the user to the layout. Set `Astro.locals.user` in `src/middleware.ts`, and declare its type in `src/env.d.ts`. Then render the identify call only when a user exists:

```astro
---
// src/layouts/Base.astro (inside the head, after the widget script)
const user = Astro.locals.user;
---
{user && (
  <script is:inline define:vars={{ user }} data-astro-rerun>
    window.EscutaProduto = window.EscutaProduto || { q: [] };
    (window.EscutaProduto.q ||= []).push([
      "identify",
      [{ id: user.id, email: user.email, name: user.name }],
    ]);
  </script>
)}
```

The `define:vars` attribute passes the object into the script. Astro serializes the values for you, so a name with quotes or a closing script tag stays inside a string. Do not build the script text by concatenating values yourself. Send only the fields you would show the person, because the script runs in the browser and anyone can read it.

## Static sites without a login

A fully static site has no user at build time, and no identify is needed. Skip the conditional block above and let the widget run anonymously. Visitors can still send feedback with the floating button, and each item records the page URL and browser.

Use the attribute buttons where a page has a clear reason for feedback, such as a docs page or a pricing comparison:

```astro
<button type="button" data-escuta-open="idea">Suggest a feature</button>
```

Static pages also suit the [hosted feedback page](/docs/hosted-page). Link to it from a footer when you want a page that is not tied to the widget at all.

## Content Security Policy for an Astro site

Pages built as static HTML use inline scripts only where you write them. A policy that allows the widget host works for the external script. The `define:vars` block above is an inline script whose content changes per user, so a static hash cannot cover it. For a strict policy, move the values into a data attribute and read them from an external file:

```astro
<div id="escuta-user" data-user={JSON.stringify(user)} hidden></div>
<script is:inline src="/escuta-identify.js" data-astro-rerun></script>
```

```js
// public/escuta-identify.js
const el = document.getElementById("escuta-user");
const user = el && el.dataset.user ? JSON.parse(el.dataset.user) : null;
if (user) {
  window.EscutaProduto = window.EscutaProduto || { q: [] };
  (window.EscutaProduto.q ||= []).push(["identify", [user]]);
}
```

Astro escapes attribute values, so the JSON stays intact. Set the policy in your host's headers. On Cloudflare Pages, for example, a `_headers` file can set it for every path:

```text
/*
  Content-Security-Policy: script-src 'self' https://escutaproduto.com; connect-src 'self' https://escutaproduto.com
```

For the directives and how to debug a blocked request, see the [CSP guide for widgets](/resources/content-security-policy-widgets).

## Check the Astro install in your Escuta Produto inbox

1. Add `http://localhost:4321` (the default Astro dev URL) to the allowed origins of your product.
2. Open a page, click the Feedback button and send a test idea.
3. In the inbox, confirm the item shows the page URL and browser. If you added identify, it should also show the name and email.
4. Navigate to a second page with a normal link. Open the widget there too, and confirm the page URL changed.

If the button disappears after navigation, check that `ClientRouter` sits in the head and that the widget script uses `is:inline`. A script Astro bundled can behave differently after a swap.

For the full option list, read the [widget reference](/docs/widget). The [Nuxt guide](/resources/feedback-widget-nuxt) covers the server-rendered Vue equivalent, and the [widget performance notes](/resources/feedback-widget-performance) explain why defer matters on content sites.

## Frequently asked questions

### Why does the Astro widget script need is:inline?

Astro bundles the script tags it finds in your components. The is:inline directive tells Astro to output the tag exactly as written, so the widget loads from escutaproduto.com with its data-key intact.

### Does the feedback widget work with Astro view transitions?

Yes. Add ClientRouter from astro:transitions to the head of your layout. Navigations swap the page body and keep the script loaded, so the Feedback button keeps working on each page.

### Can I use the feedback widget on a static Astro site without login?

Yes. Static pages have no signed-in user, so skip identify. The floating button and any data-escuta-open buttons work for anonymous visitors. Each feedback item still records the page URL and browser.

### How do I pass a logged-in user to identify in Astro?

Set Astro.locals.user in middleware for server-rendered pages, then render an identify call in the layout. For a strict CSP, put the user in a data attribute and read it from an external script.

---

# Add a feedback widget to a React Router or Remix app

> Put the widget script in the head of the root Layout in app/root.tsx. Return the signed-in user from the root loader, then pass that user to a component that calls identify in an effect. Client-side navigation does not reload the document, so the Feedback button works on every route.

Source: https://escutaproduto.com/resources/feedback-widget-react-router
Last updated: 2026-10-09

## Place the script in the root Layout

In React Router framework mode, `app/root.tsx` exports a `Layout` function that renders the HTML document for every route. Add the widget to its `head`. The script then arrives with the first response on every page:

```tsx
// app/root.tsx
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
import type { Route } from "./+types/root";

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
        <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}
```

The `Layout` export is the document shell, not a route component. Anything you place in it runs on every page, including error pages. That is the right place for a script that should always be present. The `data-key` is your public product key, which can only create feedback.

## Return the signed-in user from the root loader

The root loader runs on every request, so it is the single place to read the session. Return only the fields the widget needs:

```tsx
// app/root.tsx (continued)
import type { Route } from "./+types/root";
import { getCurrentUser } from "./session.server";

export async function loader({ request }: Route.LoaderArgs) {
  const user = await getCurrentUser(request);
  return {
    user: user ? { id: user.id, email: user.email, name: user.name } : null,
  };
}
```

`getCurrentUser` stands for your own session lookup. React Router serializes loader data into the page and escapes it, so the object reaches the browser as data. Keep the returned object small, because it travels in every page response.

## Identify the user in a component

Pass the user to a component that calls identify in an effect. Render it from the default export so it sits inside the app once:

```tsx
// app/root.tsx (continued)
import { useEffect } from "react";
import { Outlet } from "react-router";
import { FeedbackIdentity } from "./components/feedback-identity";

export default function App({ loaderData }: Route.ComponentProps) {
  return (
    <>
      <FeedbackIdentity user={loaderData.user} />
      <Outlet />
    </>
  );
}
```

```tsx
// app/components/feedback-identity.tsx
import { useEffect } from "react";

type FeedbackUser = { id: string; email: string; name?: string | null };

export function FeedbackIdentity({ user }: { user: FeedbackUser | null }) {
  const id = user?.id;
  const email = user?.email;
  const name = user?.name ?? undefined;

  useEffect(() => {
    if (!id || !email) return;
    const w = window as typeof window & { EscutaProduto?: { q?: unknown[][] } };
    w.EscutaProduto = w.EscutaProduto || { q: [] };
    (w.EscutaProduto.q ||= []).push(["identify", [{ id, email, name }]]);
  }, [id, email, name]);

  return null;
}
```

The effect depends on primitive values rather than the user object, so it pushes identify only when a field changes. A new loader result with the same user does not repeat the call. React runs effects twice in development under StrictMode, which sends the same identify twice there. That is harmless.

Keep identity in the loader data, not in a client-side store that you fill yourself. The loader is the one source that always matches the session cookie the server checked.

## Client-side navigation in React Router

Links and form submissions in React Router go through the router. It fetches the next route's loader data, renders the new route and updates the URL with the History API. The document stays the same, so the widget script keeps running and the Feedback button stays on screen.

When the loader data for the root route refreshes, for example after sign-in with a redirect, the effect sees the new user and identifies them. A feedback item then records the page URL for the page the person was on, which is what you want for triage.

## Remix apps and the same root file

Remix v2 used the same root `Layout` pattern before it merged into React Router. The imports come from `@remix-run/react` instead of `react-router`, and the typed route module comes from the Remix type generator. The widget markup, identify component and loader code are the same. Check your Remix version's guide for the exact import paths.

## Content Security Policy for a React Router app

Framework-mode apps render inline scripts for hydration. Those scripts need a nonce under a strict policy, and React Router's docs describe how to pass one through the `Scripts` component and your server entry. The widget only needs its own host:

```text
script-src 'self' https://escutaproduto.com;
connect-src 'self' https://escutaproduto.com;
```

Put these directives in the same header that carries your nonce. Do not replace the nonce source with `'unsafe-inline'`, because that weakens protection for the whole page. The [CSP guide for widgets](/resources/content-security-policy-widgets) explains how to read a blocked request in the browser console.

## Open the form from a route module

Any route module can open the form with the attribute on a button. The value preselects the type, and the valid values are `bug`, `idea`, `praise` and `other`:

```tsx
// app/routes/pricing.tsx
export default function Pricing() {
  return (
    <button type="button" data-escuta-open="praise">
      Tell us what you like
    </button>
  );
}
```

React renders `data-*` attributes as written, so no extra prop is needed. Because the script is loaded in the layout, the attribute works on every route. Use the floating button for general feedback and these buttons where a route has a clear question to ask.

## Confirm the React Router install in your inbox

1. Add `http://localhost:5173` (the default dev URL for React Router's Vite plugin) to the allowed origins of your product.
2. Sign in, open the Feedback button and send a test idea from a route that needs a user.
3. In the inbox, check that the item shows the name and email from the loader, along with the page URL.
4. Move to another route with a link, send a second item and confirm its page URL matches the new route.

If identity is missing, check that the root loader returns a user for the session you are testing. A logged-out visit returns null, which is correct and sends no identify call.

For the full options, read the [widget reference](/docs/widget). The [React guide](/resources/feedback-widget-react) covers the Vite version of this setup, and the [SvelteKit guide](/resources/feedback-widget-sveltekit) shows the same idea with page data.

## Frequently asked questions

### Where does the feedback widget script go in a React Router app?

In the head of the Layout export in app/root.tsx. That function renders the document shell for every route, so the script is in the first response and stays loaded during client-side navigation.

### How do I identify a user with React Router loader data?

Return the user from the root loader, read it in the default export with loaderData, and pass the fields to a component that calls identify inside an effect. The effect depends on the id, email and name only.

### Does client-side navigation in React Router reload the feedback widget?

No. React Router fetches route data and renders the next route without reloading the document. The script stays loaded, so the widget keeps working on each page without extra setup.

### Does the same setup work in Remix?

Yes. Remix apps use the same root Layout pattern, with the Links, Meta, Outlet and Scripts components imported from the Remix package. The widget markup and identify code are identical.

---

# Add a feedback widget to an Angular app

> Add the widget script to src/index.html with defer. Create a small service that pushes identify onto the widget queue, then call it from an effect in the root component whenever the signed-in user changes. Angular Router moves between pages without reloading the document, so the widget keeps working.

Source: https://escutaproduto.com/resources/feedback-widget-angular
Last updated: 2026-10-09

## Add the script to src/index.html

An Angular CLI project has one HTML file that the browser loads first, at `src/index.html`. Add the widget to its `head` with `defer`:

```html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My Angular app</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
</head>
<body>
  <app-root></app-root>
</body>
</html>
```

The script loads once with the page, so you do not add it to any component. A component that adds the tag would insert it again each time the component mounts, which is the wrong pattern for a page-wide widget. The `data-key` is your public product key, which can only create feedback.

The `base href` line is required by the Angular Router to build URLs correctly, and it is already in a default project. Leave it in place, because the feedback item records the page URL that the router produced.

## Create a service that identifies the user

Put the widget calls in one injectable service. Use the `DOCUMENT` token from `@angular/common` rather than the global `window`, because it works the same way during server rendering:

```ts
// src/app/feedback-identity.service.ts
import { Injectable, inject } from "@angular/core";
import { DOCUMENT } from "@angular/common";

export type FeedbackUser = { id: string; email: string; name?: string };

type EscutaWindow = Window & { EscutaProduto?: { q?: unknown[][] } };

@Injectable({ providedIn: "root" })
export class FeedbackIdentityService {
  private readonly view = inject(DOCUMENT).defaultView as EscutaWindow | null;

  identify(user: FeedbackUser): void {
    if (!this.view) return;
    this.view.EscutaProduto = this.view.EscutaProduto || { q: [] };
    (this.view.EscutaProduto.q ||= []).push([
      "identify",
      [{ id: user.id, email: user.email, name: user.name }],
    ]);
  }
}
```

The service pushes onto the queue the widget reads when it loads. If the widget has not loaded yet, the call still works, because the queue holds the data until it does. The object you pass is copied field by field, so a name with quotes or angle brackets stays as plain data.

## Call identify from the root component

Your auth service already knows the current user. Read it in an `effect` in the root component, so identify runs when the session loads and again whenever the user changes:

```ts
// src/app/app.component.ts
import { Component, effect, inject } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { AuthService } from "./auth.service";
import { FeedbackIdentityService } from "./feedback-identity.service";

@Component({
  selector: "app-root",
  imports: [RouterOutlet],
  template: `<router-outlet />`,
})
export class AppComponent {
  constructor() {
    const auth = inject(AuthService);
    const feedback = inject(FeedbackIdentityService);

    effect(() => {
      const user = auth.currentUser();
      if (user) feedback.identify(user);
    });
  }
}
```

This handles two cases in one place. A user who is already signed in when the app starts gets identified as soon as the session restores. A user who signs in later gets identified when the signal updates. Keep the payload to the fields you would show the person, because the identify call runs in the browser.

Zone-based change detection does not need extra handling for the queue push itself, because pushing an array triggers nothing. If you notice change detection running on every widget event in the Angular DevTools profiler, wrap the call in `NgZone.runOutsideAngular`. Most apps never need it.

## Angular Router navigation and the widget

The Angular Router uses the History API. It swaps the routed component and updates the address without reloading the document, so the script in `index.html` stays loaded. The Feedback button is present on every route, and nothing needs to re-run on navigation.

Each feedback item stores the URL the router produced, so a message from `/orders/1042/refund` shows the path in the inbox. That is useful when a bug depends on one screen. Pair it with the browser name the widget saves, and most layout issues can be reproduced from the item alone.

## Open the form from an Angular template

A template can open the form with a `data-escuta-open` attribute on any element. Angular passes static attributes through unchanged:

```html
<button type="button" data-escuta-open="bug">Report a bug</button>
<button type="button" data-escuta-open="idea">Suggest a feature</button>
```

Valid types are `bug`, `idea`, `praise` and `other`. Add `data-trigger="none"` to the script tag if you want only your own buttons. Use the attribute rather than a `(click)` handler, because the widget then owns focus handling and the Escape key, and you do not need a custom dialog.

## Angular server-side rendering and the window guard

With Angular SSR, the component tree renders on the server first. The `DOCUMENT` token exists there, but its `defaultView` is null, and that is what the guard in the service checks. The identify call therefore does nothing during server rendering and runs only in the browser.

The `effect` in the root component can also run on the server in some setups. The guard makes that harmless. Do not read `window` directly in components, because that throws during server rendering.

## Content Security Policy for an Angular build

Angular CLI builds load their code from external bundles, and the default setup needs no inline scripts for the app. The widget needs its host in two directives:

```text
script-src 'self' https://escutaproduto.com;
connect-src 'self' https://escutaproduto.com;
```

Angular may inject inline styles for component styling. If a strict `style-src` blocks them, Angular reads a nonce from the `CSP_NONCE` injection token, which you provide on the server. Check the Angular security guide for your version, because the setup changed across releases. The [CSP guide for widgets](/resources/content-security-policy-widgets) covers reading a blocked request in the browser console.

## Check the Angular install in your Escuta Produto inbox

1. Add `http://localhost:4200` (the default Angular dev server URL) to the allowed origins of your product.
2. Sign in, open the Feedback button and send a test bug from a page that needs a user.
3. In the inbox, check that the item shows the name and email from the signal, along with the page URL.
4. Navigate with a routerLink to a second page, send another item and confirm the page URL changed.

If identity is missing, log the value of the auth signal in the root effect. A null value means the session has not loaded yet, which the effect handles on the next change.

For the complete option list, read the [widget reference](/docs/widget). The [React Router guide](/resources/feedback-widget-react-router) shows the same root-level pattern, and the [Vue guide](/resources/feedback-widget-vue) covers a composable version.

## Frequently asked questions

### Where do I put the feedback widget script in an Angular app?

In the head of src/index.html, with the defer attribute. Angular CLI builds serve that file as the entry point, so the script loads once and stays loaded while the Angular Router changes pages.

### How do I identify the logged-in user in Angular?

Create a service that pushes identify onto the widget queue, then call it from an effect in the root component that reads the current user signal. The effect runs whenever the user changes, including on app start when a session already exists.

### Does the Angular Router reload the feedback widget?

No. The router uses the History API and renders the next component without reloading the document. The widget stays loaded, so the Feedback button is available on every route.

### Does the widget work with Angular server-side rendering?

Yes, with a guard. The identify service reads the window through the DOCUMENT token and returns early when there is no default view, which is the case during server rendering. The script itself only runs in the browser.

---

# Add a feedback widget to a Ruby on Rails app

> Add the widget script to the head of your application layout with defer. Render the signed-in user into a meta tag with to_json, which ERB escapes, then call identify from a nonced inline script. Allow the widget host in the Content Security Policy initializer, and Turbo visits will keep the widget working.

Source: https://escutaproduto.com/resources/feedback-widget-rails
Last updated: 2026-10-09

## Add the widget to the application layout

Rails renders every page through `app/views/layouts/application.html.erb`. Put the widget script in its `head`, with `defer`. The script then loads once per full page load, and the widget is present on every page that uses the layout:

```erb
<!DOCTYPE html>
<html lang="en">
  <head>
    <title><%= content_for(:title) || "My app" %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
  </head>
  <body>
    <meta name="escuta-user" content="<%= escuta_user_json %>">
    <%= yield %>
  </body>
</html>
```

The `data-key` is your public product key, which can only create feedback. Rails 8 apps may use Propshaft and the `stylesheet_link_tag :app` helper in the same head, so keep your existing asset tags in place. The widget script does not depend on them.

## Build the user data safely in a helper

Put the user data in a helper, in `app/helpers/application_helper.rb`, not in the view. Only the fields the widget needs leave the server, and the helper returns `"null"` for signed-out visitors so the page still renders:

```ruby
module ApplicationHelper
  def escuta_user_json
    return "null" unless current_user

    {
      id: current_user.id.to_s,
      email: current_user.email,
      name: current_user.name
    }.to_json
  end
end
```

Rendering the result with `<%= %>` is the important part. ERB escapes the JSON for an HTML attribute, so a quote or angle bracket in a name becomes an entity, and the browser decodes it back to the same text. The value can never close the attribute or start a tag. Do not wrap it in `raw` or `html_safe`, because that would remove the protection.

Use `current_user` from your authentication, whether that is Devise, the Rails 8 authentication generator or your own helper. The helper only needs the method to exist in the view context.

## Identify the user from a nonced inline script

A small inline script reads the meta tag and calls identify. Rails adds a nonce to inline scripts when you ask for it with `javascript_tag nonce: true`, which lets the policy allow this one script without allowing all inline code:

```erb
<%= javascript_tag nonce: true do %>
  (function () {
    var el = document.querySelector('meta[name="escuta-user"]');
    var user = el ? JSON.parse(el.content) : null;
    if (!user) return;
    window.EscutaProduto = window.EscutaProduto || { q: [] };
    (window.EscutaProduto.q ||= []).push(["identify", [user]]);
  })();
<% end %>
```

Place this block in the body, after the meta tag. The `JSON.parse` call works because ERB already escaped the attribute, so the content is valid JSON again. Send only the fields you would show the person, because the browser can read them with developer tools.

## Turbo visits and identity

Turbo Drive replaces the body on each link click instead of loading a new document. The `head` stays, so the widget script keeps running. Turbo does re-evaluate script elements inside the new body, and it copies the nonce attribute, so the identify block runs again after each visit.

That repeat is harmless. It pushes the same user onto the queue again. When someone signs in or out, the next full page load or the next Turbo visit reads the new meta tag and identifies the current person. If your sign-out button uses a form submission that redirects, the browser does a full load and the state resets cleanly.

## Content Security Policy initializer for Rails

New Rails apps ship a commented-out policy initializer. Uncomment it in `config/initializers/content_security_policy.rb` and add the widget host to `script-src` and `connect-src`, then enable nonces for scripts:

```ruby
Rails.application.configure do
  config.content_security_policy do |policy|
    policy.default_src :self
    policy.script_src :self, "https://escutaproduto.com"
    policy.connect_src :self, "https://escutaproduto.com"
  end

  config.content_security_policy_nonce_generator = ->(_request) { SecureRandom.base64(16) }
  config.content_security_policy_nonce_directives = %w[script-src]
end
```

The nonce generator gives each request a fresh value, and the `javascript_tag nonce: true` block receives it automatically. The widget script is allowed by its host, and the identify block is allowed by its nonce. Anything else that tries to run inline is blocked.

If a blocked request shows up after a deploy, check the browser console for the directive name. A missing `connect-src` entry blocks feedback submissions from the widget, while a missing `script-src` entry blocks the script itself. The [CSP guide for widgets](/resources/content-security-policy-widgets) explains each case in more detail.

## Send feedback from a Rails controller

Sometimes feedback starts on the server, for example from a support form or a background job. Wrap the call in a small service object in `app/services/escuta_feedback.rb`, which sends it to the [REST API](/docs/api) with the standard library and checks the response:

```ruby
require "net/http"
require "json"

class EscutaFeedback
  ENDPOINT = URI("https://escutaproduto.com/api/v1/feedback")

  def self.send!(message:, kind: "other", email: nil)
    request = Net::HTTP::Post.new(ENDPOINT, "Content-Type" => "application/json")
    payload = { key: "pk_your_product_key", kind: kind, message: message }
    payload[:email] = email if email
    request.body = payload.to_json

    http = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)
    http.use_ssl = true
    http.open_timeout = 3
    http.read_timeout = 5

    response = http.request(request)
    raise "Escuta feedback failed: #{response.code}" unless response.code == "201"

    JSON.parse(response.body)
  end
end
```

Server requests carry no Origin header, so your allowed origins list does not apply to them. The API still rejects a body over 16 KB with 413 and limits each IP to 10 requests a minute per product with 429. Validate the message length in your controller before calling the service, and retry 429 later rather than failing the user's request.

## Open the form from an ERB view

A button with `data-escuta-open` opens the form, with the type preselected:

```erb
<button type="button" data-escuta-open="bug">Report a problem</button>
```

Valid types are `bug`, `idea`, `praise` and `other`. For a floating button you do not want, add `data-trigger="none"` to the script tag in the layout. The widget handles focus and the Escape key, so the attribute is all the view needs.

## Check the Rails install in your Escuta Produto inbox

1. Add `http://localhost:3000` (the default Rails dev URL) to the allowed origins of your product.
2. Sign in, open the Feedback button and send a test bug from a page that renders the layout.
3. In the inbox, confirm the item shows your name and email from the meta tag, plus the page URL.
4. Sign out, reload, and send another item. The item should show no name, which proves the signed-out state.

If the name never appears, view the page source and check the meta tag's content. An empty value or `null` means the helper ran without a current user.

For every option, read the [widget reference](/docs/widget). The [Django guide](/resources/feedback-widget-django) uses a similar template pattern, and the [Laravel guide](/resources/feedback-widget-laravel) covers the same job in PHP.

## Frequently asked questions

### Where should the feedback widget script go in a Rails app?

In the head of app/views/layouts/application.html.erb, with the defer attribute. Every page rendered with that layout gets the script, and Turbo Drive does not reload it when people move between pages.

### How do I pass current_user to the feedback widget safely in Rails?

Build a small hash of the id, email and name, convert it with to_json, and put it in a meta tag with ERB output. ERB escapes the attribute, and the script parses it with JSON.parse, so no value can break out of the markup.

### Does the feedback widget work with Turbo in Rails?

Yes. Turbo Drive swaps the page body and keeps the head, so the widget script stays loaded. Inline scripts in the body run again on each visit, which only pushes the same user again.

### What does a Rails Content Security Policy need for the widget?

Add the widget host to script-src and connect-src in config/initializers/content_security_policy.rb. If you use nonces for your inline identify script, set a nonce generator and list script-src as a nonce directive.

---

# Add a feedback widget to a Django app

> Add the widget script to base.html with defer. Build the signed-in user as a dictionary in a context processor, render it with json_script, and read it in a small static JavaScript file. Allow the widget host in script-src and connect-src with django-csp, so no inline script is needed.

Source: https://escutaproduto.com/resources/feedback-widget-django
Last updated: 2026-10-09

## Add the widget to base.html

Most Django projects keep one base template that every page extends. Put the widget script in its `head` with `defer`, so it loads once per page and never blocks the render:

```django
{% load static %}
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block title %}My app{% endblock %}</title>
    <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
    <script src="{% static 'js/escuta-identify.js' %}" defer></script>
  </head>
  <body>
    {% if escuta_user %}
      {{ escuta_user|json_script:"escuta-user" }}
    {% endif %}
    {% block content %}{% endblock %}
  </body>
</html>
```

The `data-key` is your public product key, which can only create feedback. The second script is your own identify file, served from your origin. Both are `defer`, so they run in order after the HTML is parsed: the widget first, then your identify file.

The `json_script` output sits in the body and is not executed, because its type is `application/json`. It only carries data for the script to read.

## Build the user dictionary in a context processor

A context processor adds `escuta_user` to every template, so you do not repeat the code in each view. Put this function in `myproject/context_processors.py` and return a plain dictionary with only the fields the widget needs:

```python
def escuta_user(request):
    user = getattr(request, "user", None)
    if user is None or not user.is_authenticated:
        return {"escuta_user": None}

    return {
        "escuta_user": {
            "id": str(user.pk),
            "email": user.email,
            "name": user.get_full_name() or user.username,
        }
    }
```

Register the function in `TEMPLATES` in `myproject/settings.py`, under the `context_processors` option of your template backend:

```python
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                # ...the defaults your project already has...
                "myproject.context_processors.escuta_user",
            ],
        },
    },
]
```

Returning `str(user.pk)` keeps the id a string, which is how the widget expects it. A user object itself cannot be converted to JSON, so the dictionary is what the template receives. Keep the values small, because they travel in every page response.

## Read the data in a static JavaScript file

The identify code lives in `static/js/escuta-identify.js`, not in the template. The file reads the JSON from the `json_script` element and pushes the call onto the widget queue:

```javascript
(function () {
  var el = document.getElementById("escuta-user");
  if (!el) return;

  var user = JSON.parse(el.textContent);
  window.EscutaProduto = window.EscutaProduto || { q: [] };
  (window.EscutaProduto.q ||= []).push(["identify", [user]]);
})();
```

Reading `textContent` returns the JSON exactly as the server built it, because `json_script` escapes the characters that matter for HTML, such as angle brackets and ampersands. The file has no inline code, so a strict policy only needs to allow your own origin for it.

Avoid writing `{{ request.user.email }}` inside a `<script>` block. Django's autoescaping produces HTML entities, which are not valid inside JavaScript, and an unusual value can still break the page. The filter and the static file avoid both problems.

## Set the Content Security Policy with django-csp

The widget needs its host in two directives. The [django-csp](https://django-csp.readthedocs.io/) package sends the header from settings in `myproject/settings.py`. Version 4.0 and later read a single dictionary, while older releases used separate `CSP_*` settings, so check the version you have installed:

```python
MIDDLEWARE = [
    # ...the middleware you already have...
    "csp.middleware.CSPMiddleware",
]

CONTENT_SECURITY_POLICY = {
    "DIRECTIVES": {
        "default-src": ["'self'"],
        "script-src": ["'self'", "https://escutaproduto.com"],
        "connect-src": ["'self'", "https://escutaproduto.com"],
    }
}
```

Because the identify code is in a static file, this policy has no nonce and no `'unsafe-inline'`. If you already have inline scripts elsewhere in the project, move them to static files or add a nonce for them, rather than loosening the policy for the whole site. The [CSP guide for widgets](/resources/content-security-policy-widgets) explains how to read a blocked request in the browser console.

## Send feedback from a Django view

When feedback starts on the server, for example from a contact form that a view handles, post it to the [REST API](/docs/api). Put this helper in `myproject/feedback.py`. The standard library is enough for one request:

```python
import json
import urllib.request

ENDPOINT = "https://escutaproduto.com/api/v1/feedback"

def send_feedback(message, kind="other", email=None):
    payload = {"key": "pk_your_product_key", "kind": kind, "message": message}
    if email:
        payload["email"] = email

    request = urllib.request.Request(
        ENDPOINT,
        data=json.dumps(payload).encode("utf-8"),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(request, timeout=5) as response:
        return json.load(response)
```

The API returns 201 with an id on success. It returns 400 for invalid data, 413 for a body over 16 KB and 429 above 10 requests per minute per IP and product. `urlopen` raises an error for those responses, so catch `urllib.error.HTTPError` in the view and show a retry message on 429. Server requests carry no Origin header, so your allowed origins list does not apply.

## Keep the widget working with htmx partials

If your project uses htmx, partial swaps replace fragments of the page and leave the `head` alone. The widget script and the identify file were loaded with the page, so they keep running after a swap. Put the `json_script` element in the base template, as shown above, rather than inside a partial, so a swap that removes the element cannot change the identity the widget already holds.

## Open the form from a Django template

A button with `data-escuta-open` opens the form, with the type preselected:

```django
<button type="button" data-escuta-open="idea">Suggest a feature</button>
```

Valid types are `bug`, `idea`, `praise` and `other`. To hide the floating button and use only your own, add `data-trigger="none"` to the widget script tag in `base.html`. Templates that extend the base get the attribute behavior without any extra script.

## Check the Django install in your Escuta Produto inbox

1. Add `http://localhost:8000` (the default Django dev server URL) to the allowed origins of your product.
2. Sign in, open the Feedback button and send a test idea from a page that uses the base template.
3. In the inbox, confirm the item shows your name and email, plus the page URL.
4. Sign out, reload the page and send another item. That item should have no name, which confirms the signed-out path.

If the name is missing, check the page source for the `escuta-user` element. If it is absent, the context processor returned None, which usually means the setting was not registered.

For the complete option list, read the [widget reference](/docs/widget). The [Rails guide](/resources/feedback-widget-rails) covers the same layout pattern in Ruby, and the [Laravel guide](/resources/feedback-widget-laravel) covers it in PHP.

## Frequently asked questions

### Where do I add the feedback widget script in a Django project?

In the head of your base template, usually templates/base.html, with the defer attribute. Every template that extends the base gets the script, so the Feedback button appears on each page.

### How do I pass the logged-in user to the widget in Django?

Return a dictionary with the id, email and name from a context processor, then render it with the json_script filter. The filter escapes the JSON for a script tag, and a small static file reads it and calls identify.

### Why not put request.user directly into a script block in Django?

A user object cannot be converted to JSON, and writing values straight into a script block risks breaking out of it. Build a plain dictionary with only the fields you need, and let json_script handle the escaping.

### Which django-csp settings does the feedback widget need?

Add the widget host to script-src and connect-src in your CONTENT_SECURITY_POLICY setting, using django-csp 4.0 or later. Because the identify code lives in a static file, you do not need a nonce for it.

---

# Add a feedback widget to a Laravel app

> Add the widget script to the head of your Blade layout with defer. Render the signed-in user into a meta tag with json_encode, which Blade escapes, then call identify from a Vite module. Set the Content Security Policy in middleware, allowing only the widget host next to your own origin.

Source: https://escutaproduto.com/resources/feedback-widget-laravel
Last updated: 2026-10-09

## Add the widget to the Blade layout

Laravel views usually extend one layout, such as `resources/views/layouts/app.blade.php`. Put the widget script in its `head` with `defer`. Every view that extends the layout then gets the widget, and the browser loads it once per full page load:

```blade
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ config('app.name') }}</title>
    <script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
  </head>
  <body>
    @auth
      <meta name="escuta-user" content="{{ json_encode([
        'id' => (string) auth()->id(),
        'email' => auth()->user()->email,
        'name' => auth()->user()->name,
      ]) }}">
    @endauth
    @yield('content')
    @stack('scripts')
  </body>
</html>
```

The `data-key` is your public product key, which can only create feedback. The `@vite` directive loads your own bundle, and that bundle is where the identify code lives. Keep the widget script ahead of it in the head, so the queue exists before your module runs.

The `@auth` block renders the meta tag only for a signed-in user. For visitors who are signed out, the tag is absent and the identify code does nothing.

## Escape the user data inside the meta tag

Blade's `{{ }}` syntax escapes its output with `htmlspecialchars`, so `json_encode` produces JSON whose quotes become `&quot;` inside the attribute. The browser decodes the entities when it reads the attribute, which gives back the original JSON text. A name with quotes, angle brackets or ampersands can never end the attribute or start a tag.

Build the array with only the fields the widget needs. Cast the id to a string, because the widget expects text. Do not use `{!! !!}` for this output, because that skips escaping and would let a crafted name break the markup.

If your user model keeps the display name in another column, change the key accordingly. The identify call should send the same fields you would show the person, since the browser can read them with developer tools.

## Identify the user from a Vite module

The identify code lives in a module that Vite bundles with your app. It reads the meta tag and pushes the identify call onto the widget queue:

```js
// resources/js/escuta-identify.js
const el = document.querySelector('meta[name="escuta-user"]');

if (el) {
  const user = JSON.parse(el.content);
  window.EscutaProduto = window.EscutaProduto || { q: [] };
  (window.EscutaProduto.q ||= []).push(["identify", [user]]);
}
```

Import it from your main entry file, so it runs once on each full page load:

```js
// resources/js/app.js
import "./escuta-identify.js";
```

A module file avoids inline scripts entirely. The browser runs it from your own origin, which keeps the Content Security Policy simple. Modules are deferred by default, so the widget script in the head has already queued its code by the time this runs.

If you prefer an inline script, Blade's `@json` directive works inside a `<script>` block when you pass the HEX flags explicitly. Those flags encode angle brackets, ampersands and quotes, so the value cannot close the script tag:

```blade
<script>
  window.EscutaProduto = window.EscutaProduto || { q: [] };
  (window.EscutaProduto.q ||= []).push(["identify", [@json($escutaUser, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT)]]);
</script>
```

That inline version needs a nonce once you enforce a strict policy, so the module approach is simpler when you have one.

## Set the Content Security Policy in middleware

Laravel does not set a Content Security Policy by default. Add one with a small middleware that writes the header on each response:

```php
// app/Http/Middleware/ContentSecurityPolicy.php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class ContentSecurityPolicy
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->headers->set(
            'Content-Security-Policy',
            "default-src 'self'; script-src 'self' https://escutaproduto.com; connect-src 'self' https://escutaproduto.com"
        );

        return $response;
    }
}
```

Register it on the web group in `bootstrap/app.php`, which is where Laravel 11 and later configure middleware:

```php
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [
        \App\Http\Middleware\ContentSecurityPolicy::class,
    ]);
})
```

Vite's dev server serves code from a different origin and injects its own inline helpers, so a strict header can break `npm run dev`. Apply the middleware only in production, or add the Vite dev origin and its sources in development. The [CSP guide for widgets](/resources/content-security-policy-widgets) explains how to find a blocked request in the browser console.

## Livewire and Inertia navigation

Livewire's `wire:navigate` and Inertia both swap the page content without a full reload, which is similar to Turbo. The widget script in the head stays loaded, so the Feedback button keeps working. For Inertia, read the user from the shared page props in a component and call identify in an effect, using the same pattern as the React guide. For Livewire, the meta tag and the module run once per full load, so a sign-in that happens through navigation needs a full page load to refresh the identity.

## Send feedback from a Laravel controller

When feedback starts on the server, for example from a support form, send it with the HTTP client. Validate the message first, then post it to the [REST API](/docs/api):

```php
// app/Http/Controllers/FeedbackController.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

public function store(Request $request)
{
    $data = $request->validate([
        'message' => 'required|string|min:2|max:4000',
    ]);

    $response = Http::timeout(5)
        ->acceptJson()
        ->post('https://escutaproduto.com/api/v1/feedback', [
            'key' => 'pk_your_product_key',
            'kind' => 'other',
            'message' => $data['message'],
            'email' => $request->user()?->email,
        ]);

    if ($response->status() === 429) {
        return back()->with('status', 'Too many messages right now. Please try again in a minute.');
    }

    return back()->with('status', 'Thanks, we received your message.');
}
```

The API returns 201 with an id on success, 400 for invalid data and 413 for a body over 16 KB. It also limits each IP to 10 requests a minute per product with 429, so the controller shows a retry message in that case. Server requests carry no Origin header, so the allowed origins list does not apply to them.

## Open the form from a Blade view

A button with `data-escuta-open` opens the form, with the type preselected:

```blade
<button type="button" data-escuta-open="praise">Tell us what you like</button>
```

Valid types are `bug`, `idea`, `praise` and `other`. To hide the floating button, add `data-trigger="none"` to the script tag in the layout. The attribute works in any Blade view that uses the layout.

## Check the Laravel install in your Escuta Produto inbox

1. Add `http://localhost:8000` (the default `php artisan serve` URL) to the allowed origins of your product.
2. Sign in, open the Feedback button and send a test bug from a page that uses the layout.
3. In the inbox, check that the item shows your name and email, plus the page URL.
4. Sign out, reload and send another item. That item should have no name, which confirms the signed-out case.

If the name is missing, view the page source and look for the `escuta-user` meta tag. If it is absent, the `@auth` block did not render, so check that the layout is the one the page actually uses.

For every option, read the [widget reference](/docs/widget). The [Rails guide](/resources/feedback-widget-rails) covers the same layout pattern in Ruby, and the [Django guide](/resources/feedback-widget-django) covers it in Python.

## Frequently asked questions

### Where do I add the feedback widget script in a Laravel app?

In the head of your main Blade layout, such as resources/views/layouts/app.blade.php, with the defer attribute. Every view that extends the layout gets the script, and the Feedback button shows on each page.

### How do I pass the logged-in user to the widget in Laravel safely?

Render a meta tag whose content is json_encode of the id, email and name. Blade escapes the output for the attribute, and a Vite module parses it with JSON.parse before calling identify, so no value can break out of the markup.

### Can I use the Laravel @json directive inside a script block instead?

Yes, if you pass the HEX flags explicitly, such as JSON_HEX_TAG and JSON_HEX_AMP. Those flags encode angle brackets and ampersands, so the value cannot close the script tag. The meta tag approach avoids inline code altogether.

### How do I set a Content Security Policy for the widget in Laravel?

Add a middleware that sets the Content-Security-Policy header, and include the widget host in script-src and connect-src. Apply it to the web group in bootstrap/app.php. Skip it in local development, where Vite needs its own sources.

---

# Add a feedback widget to a WordPress site

> Paste the widget script tag into your WordPress footer, using a header and footer code plugin or a child theme. Then add your domain to allowed origins, clear page caches and send a test message from the live site.

Source: https://escutaproduto.com/resources/feedback-widget-wordpress
Last updated: 2026-10-09

## Which WordPress method should you pick?

To add the Escuta Produto feedback widget to WordPress, paste its single script tag into the site footer. You can do that with a header and footer code plugin, which needs no code files, or with a child theme, which keeps the script in code you can review. Both load the widget on every page.

| Method | Good for | Watch out for |
| --- | --- | --- |
| Header and footer code plugin | Owners who want no code files | One more plugin to keep updated |
| Custom HTML block in a block theme footer | A single site on a block theme | Lost if you switch themes |
| Child theme with a small PHP snippet | Developers who keep code in version control | The child theme must stay active, or the snippet stops running |

Whatever you choose, do not edit the parent theme's footer file. A theme update overwrites that edit, and you will only notice when the button disappears.

## Add the snippet with a header and footer plugin

This is the fastest route. Install a header and footer code plugin. WPCode and Insert Headers and Footers are two widely used options, and most plugins in this group work the same way.

1. Open the plugin's footer scripts field. The label varies, but it sits beside the header field.
2. Paste the script tag from your product's install page:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

3. Save it. Keep the snippet in the footer, not the header. The script uses defer, so the page does not wait for it before rendering.

Make sure the snippet applies to the whole site. Some plugins offer per-page rules, and a rule that excludes your contact page means no feedback button there.

## Add the snippet in a child theme with wp_enqueue_script

Developers usually prefer code they can review and commit. Create a child theme, then put the following in its functions.php file. It registers the widget as a script on front-end pages and replaces the output tag, so the data-key and defer attributes survive.

```php
<?php
add_action( 'wp_enqueue_scripts', function () {
    wp_enqueue_script( 'escuta-produto-widget', 'https://escutaproduto.com/widget.js', array(), null, true );
} );

add_filter( 'script_loader_tag', function ( $tag, $handle, $src ) {
    if ( 'escuta-produto-widget' !== $handle ) {
        return $tag;
    }
    return '<script src="' . esc_url( $src ) . '" data-key="pk_your_product_key" defer></script>' . PHP_EOL;
}, 10, 3 );
```

Passing null as the version tells WordPress not to append a version query string, so the URL stays the same from one release to the next.

## Pass logged-in users to the widget

When a visitor is logged in, you can fill in their email and name for them. The identify call hides the email field, so each message is tied to the account that sent it. Add this to the same functions.php file. It runs only for logged-in users.

```php
add_action( 'wp_enqueue_scripts', function () {
    if ( ! is_user_logged_in() ) {
        return;
    }
    $user = wp_get_current_user();
    $data = wp_json_encode(
        array(
            'email' => $user->user_email,
            'name'  => $user->display_name,
            'id'    => (string) $user->ID,
        ),
        JSON_HEX_TAG | JSON_HEX_AMP
    );
    wp_add_inline_script(
        'escuta-produto-widget',
        'window.EscutaProduto = window.EscutaProduto || { q: [] };' . PHP_EOL .
        '(window.EscutaProduto.q ||= []).push(["identify", [' . $data . ']]);',
        'before'
    );
}, 20 );
```

The JSON_HEX_TAG flag escapes angle brackets, so a display name that contains a closing script tag cannot break out of the inline script. The email and name fill the form. The id is stored as metadata on each message, and you can add other keys the same way if your team needs them.

Caching needs a check here. A page cache that stores one copy of a page can serve the logged-in version to a logged-out visitor, or the reverse. Configure your caching plugin so logged-in visitors bypass the cache.

## Allow your domain in allowed origins

The product key appears in your HTML, so anyone can copy it. To stop other sites from sending feedback with your key, list your domains under Allowed origins in the product settings. Add each full address with https, such as https://example.com, and the www version too if visitors reach the site both ways.

Requests from any other site are rejected with a 403 response. A missing origin is the most common reason a live install delivers nothing, so check this before you debug anything else.

## Check the live site after caches clear

Plugins that combine, minify or defer JavaScript can change how the widget loads. If the button goes missing after you enable an optimization feature, exclude the widget file from that feature. Then clear every cache, including the page cache and any CDN, and open a published page in a private window.

Work through this list on the published site:

1. Confirm the floating Feedback button appears in the corner.
2. Open the browser developer tools and check the console for errors and the network tab for the widget file.
3. Send a test message while logged out and confirm it arrives.
4. Log in as a test user and confirm the email field is hidden.
5. Open the item in your inbox and check that the page URL and browser were saved.

The block editor preview is not a reliable test, because footer scripts may not run there. Test the published page.

## How WordPress feedback appears in Escuta Produto

Each message lands in the inbox of the product you created for this site. The saved page URL shows which post or page it came from, so a bug on a pricing page is easy to locate. Use the type filter to separate bugs, ideas, praise and other messages, and the status filter to track what you have decided.

Add a Slack or Discord incoming webhook in the product settings, and each new item posts its type, rating, sender, an excerpt and a dashboard link. Internal notes stay private to your team, so you can record decisions without replying to the customer.

## Related

- Setup details for every option are in the [widget docs](/docs/widget).
- Notification setup is in the [notifications docs](/docs/notifications).
- Other builders follow the same pattern. See [Webflow](/resources/feedback-widget-webflow) and [Shopify](/resources/feedback-widget-shopify-store).
- For the background, read [What is a feedback widget?](/resources/what-is-a-feedback-widget).

## Frequently asked questions

### Will the widget slow down my WordPress site?

The script is about 5 KB compressed, has no dependencies and loads with defer, so it does not hold up page rendering. Caching and optimization plugins can still change how scripts load, so check the published page after each change.

### Do I need a child theme to add the widget?

No. A header and footer code plugin works without one. A child theme suits developers who want the script in version control. Either way, avoid editing the parent theme's files, because a theme update overwrites those edits.

### Why does my live WordPress site show no feedback button?

The usual causes are a caching plugin serving an old copy of the page, script optimization that rewrites the tag, or a footer snippet limited to some pages. Clear every cache, then test a published page in a private window with the developer tools open.

### Why is feedback from my WordPress site rejected?

A 403 response means the site address is not in allowed origins. Add your domain with https, including the www version if visitors use it. Only the sites on that list can send feedback through the widget.

---

# Add a feedback widget to a Webflow site

> Paste the widget script into the footer custom code in your Webflow site settings, then publish. Test on the published domain, not the Designer canvas, and add the live domain to allowed origins.

Source: https://escutaproduto.com/resources/feedback-widget-webflow
Last updated: 2026-10-09

## Where does Webflow put custom code?

Webflow has two places for custom code. Site-wide code lives in the site settings, in the custom code section, and runs on every published page. Page code lives in the page settings and runs only on that page. Both areas are available on plans that allow custom code, so confirm your site's plan before you start.

For the widget, the site-wide area is the right default. Put the script in the footer code field, which sits before the closing body tag, and every page gets a floating Feedback button.

## Add the snippet to every page through site settings

Follow these steps once:

1. Open the site settings and go to the custom code section.
2. Find the footer code field and paste the snippet from your product's install page:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

3. Save the custom code.
4. Publish the site. Custom code only reaches visitors after a publish, so a saved change that you have not published is invisible to customers.

The script uses defer and is about 5 KB compressed with no dependencies, so it adds little weight to the page. It renders inside a Shadow DOM, which keeps its styles from mixing with your Webflow design.

## Add the widget to one page only

Sometimes you want a feedback prompt on a pricing page or a help article, and nowhere else. The trick is to hide the floating button site-wide and place your own button on the page you choose.

First, change the footer snippet so the floating button is off:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-trigger="none" defer></script>
```

Then add an Embed element to the target page, and paste a button into it:

```html
<button type="button" data-escuta-open="idea">Suggest a feature</button>
```

The value after data-escuta-open preselects the type, so a button labeled as a bug report should use bug. Any element with that attribute opens the form, so you can style the button to match your design. Remember that data-trigger none removes the floating button from every page, so only use it when a button on each page is what you want.

## Publish before you test

The Designer canvas does not run your site's custom code, so the widget will not appear while you edit. Publish, then open the live domain in a private window. Scroll to the bottom of the page, check that the floating button appears, and send a short test message.

Webflow also gives each site a webflow.io address for staging. If your team tests there, add that address to allowed origins as well, or feedback sent from staging will be rejected.

## Keep allowed origins in step with your domains

The product key is public, so restrict where it works. In the product settings, add each domain that serves the site, using the full address with https, such as https://www.example.com. A site that answers at both the bare domain and the www address needs both on the list.

Requests from any other origin get a 403 response. If a test works in the Designer but the published site sends nothing, compare the address in your browser with the list in allowed origins.

## What Webflow feedback looks like in Escuta Produto

Messages from a Webflow site land in the inbox of its product. Each item saves the page URL, which tells you whether a complaint came from the homepage, a blog post or a product page. Filter by type to separate bugs from ideas and praise, then set statuses as you decide what to do.

Use the internal notes to record why you closed an idea or who asked for a feature, so the next person does not start from zero. The 30-day chart in the inbox helps you notice a jump in bug reports right after a site update.

## When the button does not appear on the live site

Work through the likely causes in order, because the first one is the most common.

1. Confirm the footer code is saved and the site was published after you saved it. A saved change that has not been published does not reach visitors.
2. Open the published page and look at the browser console. A blocked script usually means a browser extension, an ad blocker or a Content Security Policy is stopping the widget file.
3. Check the snippet for data-trigger="none". If you added it while testing a single-page button and forgot to remove it, no page will show the floating button.
4. Compare the address in your browser with allowed origins. A test from the webflow.io address fails if only the custom domain is listed.
5. Clear the browser cache or open the page in a private window, then test again. An old copy of the page can keep an earlier version of the code.

Keep a short list of these checks in your team's notes. Most install problems come down to one of these five. The whole list takes about five minutes on a small site. Run it after every install, and again whenever you change the footer code or add a domain, because either change can break the path without any visible error on the Designer canvas.

## Related

- Every option, including identify and the JavaScript API, is in the [widget docs](/docs/widget).
- If you cannot add custom code, a [hosted feedback page](/docs/hosted-page) needs only a link.
- Compare this setup with [Add a feedback widget to a WordPress site](/resources/feedback-widget-wordpress).
- For the difference between a widget and a plain email link, read [Feedback widget vs a mailto link](/resources/feedback-widget-vs-mailto-link).

## Frequently asked questions

### Does the Webflow widget have to go in the footer?

The footer is a good default because the script is deferred and adds a floating button. Head placement also works. The rule that matters is to add the script once per page, because two copies can create two buttons.

### Why don't I see the widget while I edit my Webflow site?

Site custom code runs on the published site, not in the Designer canvas. Publish, then open the live domain or your webflow.io staging address in a private window and look for the floating Feedback button.

### Can I show the feedback button on some Webflow pages but not others?

Yes. Set the trigger to none in the site-wide snippet, then add a button with the data-escuta-open attribute using an Embed element on each page you choose. The script still loads site-wide, but only your chosen pages show a button.

### Why is feedback from my published Webflow site rejected?

A 403 response means the address is not in allowed origins. Add the full https address of your custom domain, including the www version if you use it, and the webflow.io staging address if you test there.

---

# Add a feedback widget to a Framer site

> Paste the widget script into the end-of-body custom code field in your Framer site settings, then publish. Test on the live domain rather than the editor, and link to the hosted feedback page where custom code is unavailable.

Source: https://escutaproduto.com/resources/feedback-widget-framer
Last updated: 2026-10-09

## Which Framer setting loads the widget on every page?

Framer sites take one script tag in the site-wide custom code area. In the site settings, find the custom code section and the field for code at the end of the body tag. Anything you paste there loads on every published page, which is what the widget needs. The feature is available on plans that allow custom code, so check your plan first.

If custom code is not available for your site, you still have a working option. Link visitors to the hosted feedback page, which needs no script at all. Both routes end in the same inbox.

## Paste the snippet at the end of the body

Copy the snippet from your product's install page, paste it into the end-of-body field, and save:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

The script is about 5 KB compressed and has no dependencies. Because it is deferred, your design loads first and the floating Feedback button appears when the page is ready. Keep the field limited to this snippet, so you can find it again when you rotate the product key or move the site.

## Why you must publish before the widget appears

Framer's editor shows your design, but the public site is what visitors see. Changes to custom code and to the design both reach the public address only after you publish. If you test in the editor and see nothing, that tells you nothing about the live install.

Publish, then open the live domain in a private window. Use your custom domain or the default Framer address, and add whichever you test on to the allowed origins list in the product settings. Without that entry, a test message from the live site is rejected with a 403 response.

## Add a feedback button with an Embed component

A floating button in the corner is not always the right prompt. A pricing page or a changelog may need a clear call to action instead. Framer's Embed component lets you paste HTML into a page, so you can place your own button where it fits.

First, set the floating button off in the site-wide snippet by adding data-trigger="none" to the script tag. Then add an Embed component to the page and paste:

```html
<button type="button" data-escuta-open="bug" style="padding: 8px 14px; cursor: pointer;">Report a problem</button>
```

The data-escuta-open attribute opens the feedback form and preselects the type, here bug. Give the button inline styles, because the embed is isolated from your Framer styles and you will want it to match your design by hand. Publish and click it on the live page to confirm the form opens.

## Open the widget from a menu link

Some sites put feedback in the main navigation. You can do that without any extra code by linking to the hosted feedback page for your product:

```text
https://escutaproduto.com/f/your-product-slug
```

Add ?lang=pt or ?lang=en to set the language. The hosted page uses your product's accent color and is not indexed by search engines, so it works as a clean, separate destination. Use the link wherever you cannot place code at all.

## Pass a visitor's email to the form

Most Framer sites are public marketing pages, so visitors are usually anonymous. If you already know someone's email, for example from an invite email, you can prefill the hosted page with the email parameter, which fills the field so the visitor does not retype it.

Inside the widget, the identify call fills the email and name fields and hides the email field. That fits sites where people log in through another tool and you can read their details in code. For a plain Framer marketing site, the anonymous form with an optional email is the honest default.

## Where Framer feedback lands in Escuta Produto

Every message appears in the inbox of the product connected to your site, with the page URL, the browser and the country added by the server. Filter by type to separate bugs from ideas and praise, then move each item through the statuses: new, planned, in progress, done or closed.

Use internal notes for context that never goes to customers. If you connect a Slack or Discord webhook, each new message posts a summary with a link back to the dashboard, so you can answer quickly.

## What to check when feedback does not arrive

Start with the simplest question: did the page you are testing come from the latest publish? Framer keeps the editor and the public site separate, so an old published version can still be live after you changed the code. Publish again, then reload the live page in a private window.

Next, check the snippet itself. It should be the exact script tag from your product's install page, with your product key in the data-key attribute. A copied key with a missing character will load the script without ever matching your product, and the form will show an error when someone sends a message.

Then check allowed origins. If you added a custom domain after the first test, the new address is not on the list until you add it. Requests from an unlisted site are rejected with a 403 response, so a working test on one address and silence on another points straight at this setting.

Finally, open the browser console on the live page. A blocked script or a policy error is visible there, even when the page looks normal. If you find nothing, send a test message from a second device on a different network, which rules out a cached copy of the page on your own machine.

## Related

- The full widget reference, including data-trigger and data-escuta-open, is in the [widget docs](/docs/widget).
- The hosted page options are in the [hosted page docs](/docs/hosted-page).
- For a different builder with the same script approach, see [Add a feedback widget to a Webflow site](/resources/feedback-widget-webflow).
- To decide how to prompt visitors, read [What is a feedback widget?](/resources/what-is-a-feedback-widget).

## Frequently asked questions

### Does Framer need code to add the feedback widget?

Yes, for the floating button, which needs one script tag in the end-of-body custom code field. Without custom code, add a button or menu link that points to your hosted feedback page. That route needs no script at all.

### Why don't I see the feedback button in the Framer editor?

The editor is not the public site. Custom code reaches visitors after you publish. Open the live domain in a private window, check for the floating Feedback button and send a short test message.

### Can Framer visitors send feedback without custom code?

Yes. Link to your hosted feedback page at escutaproduto.com with your product slug in the path. Use a button or a text link, and add the lang parameter when you want Portuguese or English.

### Will the widget slow down a Framer site?

It should not noticeably slow the page. The script is about 5 KB compressed, has no dependencies and loads with defer. It draws inside a Shadow DOM, so its styles do not leak into your design. Compare page speed on the live site before and after install.

---

# Add a feedback widget to a Shopify store

> Paste the widget script into layout/theme.liquid just before the closing body tag, after duplicating your theme. Identify signed-in customers with the Liquid customer object, and add your store's domains to allowed origins.

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

## Where does the snippet go in a Shopify theme?

Put the widget script in your theme's main layout file, layout/theme.liquid, just before the closing body tag. Every storefront page renders through that file, so the floating Feedback button then appears on all of them. Open your theme's code editor from the Online Store area of the Shopify admin to find the file.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
</body>
```

Only the closing body tag is shown above so you can see where the script belongs. Keep the existing closing tags in the file and paste the script on the line before them.

Escuta Produto does not ship a Shopify app, so the snippet lives in your theme code. That works in any theme where you can edit code. It also means a theme update from the theme store can overwrite your change, which is why the next step matters.

## Duplicate your theme before you edit code

Before you touch any file, duplicate the live theme from the Themes area of the admin. Edit the copy, preview it, and publish it when you are satisfied. If an update or an edit goes wrong, the previous version is one click away.

Also keep a note of the snippet's location. When you update the theme later, check that layout/theme.liquid still contains the script, because a theme update replaces files you have not customized and can drop a change you made by hand.

## Identify signed-in customers with Liquid

A guest can send feedback with an email field. A signed-in customer should not have to type their email again, and their feedback is more useful tied to their account. Shopify exposes the logged-in customer in Liquid, so you can pass their details to the widget.

Add this above the widget script in theme.liquid:

```liquid
{% if customer %}
<script>
  window.EscutaProduto = window.EscutaProduto || { q: [] };
  (window.EscutaProduto.q ||= []).push(["identify", [{
    email: {{ customer.email | json }},
    name: {{ customer.name | json }},
    id: {{ customer.id | json }}
  }]]);
</script>
{% endif %}
```

The json filter outputs a properly quoted and escaped JavaScript value, so a name with an apostrophe will not break the script. The identify call sets the email and name for every message from that visit, and it hides the email field. The id is stored as metadata, so you can match feedback to a customer record in Shopify if you need to.

Because the queue pattern works whether the widget has loaded or not, the order of these two scripts does not matter.

## Allow both your domain and the myshopify address

Shoppers reach your store at your custom domain, and Shopify also serves it at a myshopify.com address. Both can show up in a visitor's browser, so add both to allowed origins in the product settings. Use the full https address for each, including the www version of your domain if it resolves.

Requests from any other origin are rejected with a 403 response. If feedback works on your domain but not on the myshopify address, the missing origin is almost certainly the cause.

## Test as a guest and as a signed-in customer

Open the live store in a private window and test as a guest first. Check that the floating button appears on a product page and a collection page, and send a message. Then check the inbox to confirm the page URL shows the product.

Next, sign in with a test customer account in a normal window. The email field should be hidden and the name filled in. If the field still asks for an email, check the page source for the identify block and confirm the customer object is present for signed-in visitors.

Test on mobile too. A floating button in the corner can sit on top of a sticky add-to-cart bar, so move it with the position option if it covers a control.

## Why the checkout is not a place for the widget

Shopify renders checkout separately from your theme files, so theme code does not add scripts there. That is a reasonable boundary. Keep feedback forms on product, collection, blog and account pages, where shoppers are browsing and not paying.

If a customer wants to report a checkout problem, give them a link to a page you control, such as your contact or help page, where the widget is available.

## When the button is missing on one template

The snippet in layout/theme.liquid loads on every page that uses that layout, which covers the storefront templates. Some pages use a different layout file, though. The password page is one example, so a store that is locked behind a password can show no button to testers who have not entered it.

If the button is missing on one page, look at the rest of the storefront first. Check a product page, a collection page and the homepage in a private window. If only one type of page is missing the button, look at that template's layout setting, and check whether the page uses a separate layout file before you add the snippet there.

Keep the snippet itself in one place. Pasting it into several layout files creates two copies of the script, and two copies can produce two buttons.

## Reading store feedback in Escuta Produto

Each store feedback item lands in the inbox of its product, with the page URL and browser saved. A bug report with a product URL points you to the exact page and variant the shopper was on. Filter by type to separate bugs, ideas and praise, and by status to track what you have decided.

Internal notes keep the reasoning private, and a rating average gives you a rough sense of satisfaction over time. For Slack or Discord alerts, add a webhook in the product settings, and each new message posts its type, rating and a link to the dashboard.

## Related

- Full options and the identify call are in the [widget docs](/docs/widget).
- If you prefer a link to a feedback page, see the [hosted page docs](/docs/hosted-page).
- For a WordPress store or site, read [Add a feedback widget to a WordPress site](/resources/feedback-widget-wordpress).
- To decide what to do with what arrives, read [What is feedback triage?](/resources/what-is-feedback-triage).

## Frequently asked questions

### Where do I paste the Shopify feedback widget code?

Paste the script into the theme file named layout/theme.liquid, on the line just before the closing body tag. Duplicate your theme first, so you keep a copy of the working version before you edit.

### Will the widget fill in the email of a signed-in customer?

Yes, if you add the identify snippet with the Liquid customer object. Signed-in customers see their email and name filled in, and the email field is hidden. Guests still see the email field and can leave it empty.

### Does Escuta Produto have a Shopify app?

No. The widget is one script tag, so it works in any theme where you can edit code. Escuta Produto sends alerts to Slack and Discord through webhooks, and it does not sync with Shopify or other platforms.

### Why does the feedback button not appear at checkout?

Shopify renders checkout separately from your theme, so theme code does not add scripts there. Keep feedback forms on product, collection, blog and account pages, and point customers with checkout problems to a help page.

---

# Add a feedback widget to a Wix site

> Add the widget script in your Wix site's custom code area, set it to the end of the body on all pages, then publish. Add your Wix domains to allowed origins, and use the hosted feedback page where custom code is not available.

Source: https://escutaproduto.com/resources/feedback-widget-wix
Last updated: 2026-10-09

## Where is custom code in a Wix site?

Wix keeps site-wide scripts in a custom code area in the site dashboard settings. You add a code snippet, choose where it runs on the page and which pages it applies to, then save and publish. This feature is available on plans that allow custom code. If your site's plan does not include it, skip to the hosted page option below, which needs no code.

Adding the widget takes one snippet. Copy the script tag from your product's install page:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

## Set the placement and page scope

When you add the snippet, Wix asks two questions. Answer them like this:

1. **Placement**: choose the end of the body, so the script loads after the page content. The widget is deferred anyway, so this keeps the page's own content first.
2. **Pages**: choose all pages, so the floating button appears site-wide. A snippet limited to specific pages gives you a button only where you chose.

Give the snippet a clear name, such as Escuta Produto widget, so you can find and remove it later. Save, then publish the site. Wix shows your changes to visitors only after a publish.

Wix's own editor preview may not reflect every custom code change, so verify on the published site instead.

## Add a button where visitors expect it

The floating Feedback button sits in a corner, and a Wix design can have its own fixed elements there, such as a chat launcher or a cookie notice. If they overlap, you have two choices. You can move the widget with the position option set to left, or you can turn the floating button off and place your own.

To turn it off, add data-trigger="none" to the script tag. Then add a button to the page in the Wix editor, and link it to a custom action that opens the widget. The simplest version uses the data-escuta-open attribute, which opens the form when an element carrying it is clicked. For most Wix sites, the hosted feedback page link is the simpler choice.

## Use the hosted feedback page as the fallback

The hosted page works on any Wix site, whatever its plan. Create a link pointing to your product's feedback address and add it to the menu, footer or a contact section:

```text
https://escutaproduto.com/f/your-product-slug
```

Add ?lang=pt or ?lang=en for a language. The page uses your product's accent color and is not indexed by search engines, so it works as a clean destination for feedback that does not need the widget. Visitors fill out the same form, and the messages reach the same inbox.

## Identify members when Wix allows it

Plain custom code cannot see who is logged in on a Wix site. The snippet runs on every page, but it has no access to the member's details. Filling in the email and name for a logged-in member needs code that runs in Wix's developer platform, Velo, which can read the current member and then call the widget's identify function.

If you do not use Velo, leave the email field on the form. Anonymous feedback with an optional email is still useful, and the page URL and browser are saved either way.

## Check the published site

Publish first, then test in a private window. Work through these checks:

1. The floating button appears on the homepage and on an inner page.
2. A test message from a logged-out visitor arrives in the inbox.
3. The item shows the page URL you tested on, which confirms the widget is on the right page.
4. The button does not cover a Wix control on a phone-sized screen.

If a message is rejected, check allowed origins in the product settings. Add the custom domain you publish to, with https, and the default Wix address if visitors can reach the site there. Then test again on the live address.

## Troubleshooting the live site

If the button is missing or messages fail, check the published site before you change anything. Make sure you clicked publish after saving the custom code, and that the snippet applies to all pages. A snippet limited to one page explains why only that page shows the button.

Then check the browser console. A blocked script often comes from an ad blocker or a privacy extension, so test in a private window with extensions turned off. If the console shows a policy error, the site's security settings are blocking the widget file.

Finally, compare the address you tested with allowed origins. A default Wix address and a custom domain are different origins, and each one needs its own entry when visitors can reach the site at both.

## Reading Wix feedback in Escuta Produto

Messages from your Wix site appear in the inbox of its product, each with the page URL and browser. Filter by type to separate bugs from ideas and praise, and by status to show what you have planned or closed. Keep notes on each item that explain your decision, because you will forget the context within a month.

For fast reactions, connect a Slack or Discord webhook. New items post their type, rating, sender, a 500-character excerpt and a link back to the dashboard, which is useful when a bug report comes in on a checkout or booking page.

## Related

- The widget reference, including placement options and the JavaScript API, is in the [widget docs](/docs/widget).
- The link-based option is covered in the [hosted page docs](/docs/hosted-page).
- For another builder with a similar footer script, read [Add a feedback widget to a Squarespace site](/resources/feedback-widget-squarespace).
- For the difference between a message and a request, read [Bug report vs feature request](/resources/bug-report-vs-feature-request).

## Frequently asked questions

### Can I add the feedback widget to a Wix site without coding?

Pasting the snippet is the only code involved, and no programming is needed. You do need the custom code area, which is available on plans that allow custom code. Without it, link visitors to the hosted feedback page instead.

### Why does the feedback button cover a Wix element?

The floating button sits in a corner, and other fixed elements can overlap it. Move it with the position option set to left, or turn the floating button off and place your own button where it fits your layout.

### Are Wix members identified automatically?

No. A plain custom code snippet cannot read who is logged in. Wix's developer platform, Velo, can read the current member and call the identify function. Without it, the form stays anonymous with an optional email field.

### Why is feedback from my published Wix site rejected?

A 403 response means the address is not in allowed origins. Add your custom domain with https, and the default Wix address if visitors reach the site there, then send a test message from the live site.

---

# Add a feedback widget to a Squarespace site

> Paste the widget script into the site-wide footer code injection area, then publish. To limit it to some pages, use page-level header injection or a code block button. Check the live site and add your domain to allowed origins.

Source: https://escutaproduto.com/resources/feedback-widget-squarespace
Last updated: 2026-10-09

## Is code injection the right place for the widget?

Squarespace has a code injection area, which lets you add scripts to the site's header or footer. For the widget, the footer is the right place. A script there loads on every page, and the floating Feedback button appears in the corner. The code injection setting is available on plans that allow custom code, so confirm yours before you start.

If you cannot use code injection, the hosted feedback page is a complete alternative. You link to it from a button or menu item, and visitors send feedback without any script on your site.

## Add the snippet to the site footer

Open the site's settings, find the advanced section, and open code injection. Paste the footer snippet into the footer field:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Save the change, then open the live address in a private window rather than trusting the editor preview. The script uses defer, so it does not block the page, and it adds no dependencies.

Keep the footer field limited to scripts you know. Several tools inject code there, and when you remove one later, you want to know exactly which snippet belongs to which tool.

## Limit the widget to some pages

Sometimes a feedback prompt belongs on a help article or a pricing page only. You have two good routes.

The first is page-level header injection. Each page in Squarespace has its own advanced settings, and a page-level header code injection area there runs only on that page. Paste the same script tag into the pages you choose, and leave the footer empty.

The second route is to keep the floating button off site-wide and place your own button where it belongs. Add data-trigger="none" to the snippet, then add a code block to the page with a button:

```html
<button type="button" data-escuta-open="idea">Suggest a feature</button>
```

Any element with the data-escuta-open attribute opens the form. The value preselects the type, so use bug for a problem report and praise for thanks. The code block is a good fit for a call to action that sits inside your layout, not in a floating corner.

## Confirm your domain is allowed

Squarespace sites are reachable at a custom domain and at a squarespace.com address, and sometimes both redirect. In the product settings, list each address that serves the site under Allowed origins, using the full https form such as https://example.com. Include the www version if visitors use it.

Requests from any other origin get a 403 response. A test that works on the squarespace.com address but fails on your custom domain usually means the custom domain is missing from the list.

## Test the live site on a phone

Open the published site in a private window on a desktop, then on a phone. Check that the floating button appears, does not cover a call to action, and opens the form. Send one message from each device, then confirm both arrive in the inbox with the page URL saved.

If the button covers something important on a phone, move it with the position option set to left, or hide the floating button and use a code block button instead.

## How Squarespace feedback reaches Escuta Produto

Every message is sent to the inbox of the product you set up for the site. The page URL tells you which page a message came from, which matters on a site with many pages, such as a portfolio or a course catalog. Filter by type and status, and add internal notes to record what you decided.

Use the 30-day chart to see when feedback rises. A jump right after a site update is a useful signal to check that the change did not break something.

## When the widget does not appear on a page

If the floating button is missing from one page or from the whole site, check these in order.

First, confirm the snippet is in the field you expect. Footer code injection applies to the whole site, while page-level header injection applies only to the page where you pasted it. A snippet in the wrong field will load on some pages and not others, which looks like a random bug.

Second, check for data-trigger="none". If you turned off the floating button to place a code block button, the floating button will stay hidden on every page until you remove the attribute. That is correct behavior, but it surprises people who forget the change.

Third, look at the browser console on the published page. A blocked script often comes from a browser extension, an ad blocker or a policy that rejects the widget file. Test in a private window with extensions off to rule them out.

Fourth, compare the address with allowed origins. A custom domain that is missing from the list still loads the button, but the feedback request fails with a 403 response. The button looks fine, so people often miss this one.

Finally, clear your browser cache and retest. A copy of the page saved before you edited the code can hide the change until the cache is refreshed.

## Related

- Options such as the trigger, position and language are in the [widget docs](/docs/widget).
- For the link-only option, see the [hosted page docs](/docs/hosted-page).
- For another builder with a footer script, read [Add a feedback widget to a Wix site](/resources/feedback-widget-wix).
- For a short definition of the tool category, read [What is a feedback widget?](/resources/what-is-a-feedback-widget).

## Frequently asked questions

### Where do I add the Squarespace feedback widget?

Paste the script tag into the footer field of code injection, which loads it on every page. Save and publish, then open the live site to confirm the floating Feedback button appears in the corner.

### Can I show the widget on only some Squarespace pages?

Yes. Use page-level header code injection on each page you choose, or hide the floating button and add a button in a code block on those pages. Either way, check each page on the live site.

### Why does feedback from my Squarespace site get rejected?

The most common cause is that the site address is not in allowed origins. Add your custom domain with https, including the www version if visitors use it. Requests from any site not on that list are rejected.

### What if my Squarespace plan does not allow code injection?

Link visitors to your hosted feedback page instead. Add a button or menu item that points to the product's feedback address. That needs no code on your site, and the messages arrive in the same inbox.

---

# Add a feedback widget to a Ghost blog

> Paste the widget script into the site footer in Ghost code injection, then publish and check a live post. Member details need a theme edit, because code injection cannot read the signed-in reader. Add your domain to allowed origins.

Source: https://escutaproduto.com/resources/feedback-widget-ghost
Last updated: 2026-10-09

## Code injection or a theme edit?

Ghost has a code injection area in the site settings, with separate fields for the site header and the site footer. The footer field is where the widget goes. Anything you paste there is added to every page, so the floating Feedback button appears on posts, the homepage and the archive pages.

Code injection is static HTML. It does not run Handlebars, the template language Ghost themes use, so it cannot read the signed-in member or insert their details. If you want the widget to know who is reading, you need to edit the theme, which requires a self-hosted Ghost site or a theme you control. The widget works without that edit, so start with code injection.

## Add the widget to the site footer

Open the Ghost admin settings, find code injection, and paste the footer snippet into the site footer field:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Save, then open a published post in a private window. The Feedback button should appear in the corner. Ghost's preview is useful for layout, but test the published address to confirm the script runs for real readers.

## Why code injection can't see the signed-in member

Ghost sends member data to templates, not to the footer field. A snippet that says "identify this member" would have to know the member's email at render time, and static code injection cannot do that. The result would be a fixed value, or nothing at all.

The fix is a theme template. Ghost exposes the current member to Handlebars as the member object, and you can check for a signed-in member with a conditional. Add a block like this to a template your theme renders on every page, such as the default template:

```hbs
{{#if @member}}
<script>
  window.EscutaProduto = window.EscutaProduto || { q: [] };
  (window.EscutaProduto.q ||= []).push(["identify", [{
    email: "{{@member.email}}"
  }]]);
</script>
{{/if}}
```

This sample passes only the email. Ghost escapes its output, so a name with quotation marks could appear with escaped characters in the form. The email is the field that matters most, because it hides the email box for the reader. Keep the identify block above the widget script, or rely on the queue pattern, which works in either order.

If you cannot edit the theme, skip identify. Readers can still send feedback with an email field, which is enough for most blogs.

## Add a prompt to a single post

A floating button is good for general feedback. A post that teaches a technique might need a more specific prompt, such as a question about a code sample. The floating button stays on, and you add a button inside the post.

In the post editor, add an HTML card where you want the prompt and paste:

```html
<button type="button" data-escuta-open="bug">Report a problem with this post</button>
```

The attribute opens the form with the bug type already selected. The page URL is saved with the message, so you can see exactly which post the reader meant. Use the same approach for ideas, with data-escuta-open="idea", on posts that announce or describe an upcoming feature.

If you want the floating button off everywhere and only the in-post prompts visible, add data-trigger="none" to the footer script. Test a post afterward, because that choice removes the button from every page.

## Check a live post as a reader and as a member

Open a published post in a private window while logged out. Send a message and confirm it arrives. Then sign in as a test member and open the same post. If your theme includes the identify block, the email field should be hidden. If the field still shows, check that the identify block is present in the rendered page source for members.

Look at the item in your inbox afterward. The page URL should match the post, which is the proof that the widget is on the right page.

## Reader feedback in Escuta Produto

Reader messages land in the inbox of the product you created for the blog. Filter by type to separate bugs in a tutorial from ideas for new posts and praise from readers. Use statuses to show what you have planned, so you can reply to readers once the thing they asked for ships.

Internal notes are where you record the decision, such as a post you updated because of a bug report. The 30-day chart shows whether a spike of messages follows a new post, which helps you judge what readers care about.

## Troubleshooting a silent install

When you finish the install and nothing arrives, go through these checks in order.

Start with the footer field. If the script sits in the site header field instead, it still loads, but a mistake in the other field can hide the button on posts. Confirm the snippet is in the site footer, save, and reload a post in a private window.

Next, check the post you are testing. A post with its own code injection that changes the page structure can hide a button placed inside the post body. Test on a plain published post first, then add the in-post prompt once the floating button works.

Then check the member identify block. If members see the email field after signing in, the template conditional is probably not rendering for them. View the page source while logged in as a member and look for the identify call. If it is missing, the edit is in the wrong template file, or the template does not render on that page.

Last, check allowed origins. A feedback request from a custom domain that is not listed will fail with a 403 response, even though the button is visible. Add the live address with https, then test again.

## Related

- Full widget options are in the [widget docs](/docs/widget).
- The link-only option, which needs no theme access, is in the [hosted page docs](/docs/hosted-page).
- For a similar footer-injection install, read [Add a feedback widget to a Squarespace site](/resources/feedback-widget-squarespace).
- For the difference between a message and a request, read [Bug report vs feature request](/resources/bug-report-vs-feature-request).

## Frequently asked questions

### Where do I paste the Ghost feedback widget script?

Paste it into the site footer field of code injection in the Ghost admin settings. Save, then open a published post in a private window to confirm the floating Feedback button appears in the corner.

### Can Ghost identify logged-in members automatically?

Not from code injection, because that area is static and cannot read the reader. You need a theme template with a conditional for members. Without a theme edit, readers can still send feedback with the email field.

### Can I add a feedback prompt to one post only?

Yes. Add an HTML card to the post with a button that carries the data-escuta-open attribute. The floating button can stay on site-wide, and the page URL saved with each message shows which post the reader meant.

### Does feedback from a Ghost post show which post it came from?

Yes. The widget saves the page URL and browser automatically, so each message in the inbox shows the post address. That makes it easy to find the tutorial a reader meant when they report a bug in a code sample.

---

# Add a feedback widget to a Carrd landing page

> Add an embed element to your Carrd page, paste the widget script into it, then publish. Carrd sites often collect pre-launch feedback, so the hosted feedback page link works well as a fallback for plans without embed code.

Source: https://escutaproduto.com/resources/feedback-widget-carrd
Last updated: 2026-10-09

## Why a Carrd page suits a feedback widget

A Carrd site is usually one landing page, sometimes before a product exists. The early visitors on that page are the people most able to tell you what is confusing. A feedback widget lets them say so from the page itself, without an email client or a separate survey.

Carrd does not let you change every part of the page's code, but it has an embed element for custom HTML. That element is where the widget lives. It is available on plans that allow custom code, so check your plan first. If embed code is not available, the hosted feedback page gives you the same result with a plain link.

## Add the widget with an embed element

In the Carrd editor, add an embed element to your page, choose the code option, and paste the script tag from your product's install page:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

The script draws its floating button on the page, so the embed box itself can stay empty. Place the element near the bottom of the page, where an empty block is least noticeable. Publish, then open the live page in a private window and look for the Feedback button in the corner.

Carrd shows your edits in the editor, but visitors see only what you publish. Make a habit of testing the live URL after every change.

## Collect pre-launch feedback

Before launch, use the feedback types to learn different things. Bugs show where a form breaks or a link goes nowhere. Ideas show what visitors expect the product to do. Praise tells you which line of copy is already working, so you can keep it when you revise the page.

Set the product's statuses to match your stage. Planned means you intend to act, closed means you read it and decided against it, and done means it is in the product. A one-page launch project can run on new and closed alone, but the statuses help you keep the list manageable.

Pre-launch feedback also tends to come from people who are interested enough to visit. Reply to the ones who left an email, and say what you changed because of their message.

## Use the hosted page as a fallback

Some Carrd plans will not run the embed code, and some landing pages are better served by a button than a floating widget. The hosted feedback page works on any Carrd page, because it is just a link:

```text
https://escutaproduto.com/f/your-product-slug
```

Add a button element in Carrd that points to that address. Add ?lang=pt or ?lang=en when you want the form in Portuguese or English. If you already know a visitor's email, for example from a waitlist, add it with the email parameter so the field is filled in.

The hosted page is not indexed by search engines, so it is safe for feedback you do not want in public results. It uses your product's accent color, so it looks like part of the same project.

## Check the published page on a phone

Carrd pages are often built mobile-first, and a floating button in the corner can cover a call to action on a small screen. Open the live page on a phone and check the bottom corner. If the button is in the way, move it to the other side with the position option set to left.

If you want no floating button at all, add data-trigger="none" to the snippet, then place a button in the Carrd layout that carries the data-escuta-open attribute. The value selects the type, such as bug or idea.

## One product key for every Carrd page

Each Carrd site you run for one product can share the same product key. Every page then sends feedback to one product inbox, which keeps your launch, docs and pricing feedback together. The saved page URL shows which page a message came from, so you still know whether a bug is on the homepage or a sign-up page.

If you run unrelated landing pages, give each its own product in Escuta Produto. Then their inboxes, statuses and notifications stay separate.

## How Carrd feedback shows up in Escuta Produto

Messages arrive in the product's inbox with the page URL, browser and country added automatically. Filter by type, search for a word that many visitors used, and set statuses as you decide. Internal notes keep your reasoning private.

For fast replies, connect a Slack or Discord webhook. Each new item posts its type, rating, sender, an excerpt and a link to the dashboard. Replies go from your own email, because Escuta Produto does not send messages to customers for you.

## If the button does not show

Start by confirming the page is published, since Carrd visitors only see the saved public version. Open the live URL in a private window and look at the corner of the screen.

If it is still missing, check the embed element. Make sure the code is pasted in the code option and that the script tag is complete, with the data-key attribute and the closing tag. A pasted fragment often comes from copying only part of the snippet.

Then check the browser console for a blocked script or a policy error. Ad blockers sometimes block widgets by name, so test with extensions turned off. Finally, check allowed origins. Requests from a domain that is not on the list fail with a 403 response, which means the button shows but messages do not arrive.

## Related

- Widget options such as position and trigger are in the [widget docs](/docs/widget).
- The link-based option is in the [hosted page docs](/docs/hosted-page).
- For a different builder with an embed approach, read [Add a feedback widget to a Framer site](/resources/feedback-widget-framer).
- To understand the collect step, read [What is a feedback inbox?](/resources/what-is-a-feedback-inbox).

## Frequently asked questions

### Can I add a feedback widget to a Carrd page?

Yes, with an embed element that holds the script tag, if your plan allows custom code. Without that, add a button that links to your hosted feedback page. That route needs no code and works on any Carrd page.

### Is feedback before launch worth collecting?

Yes. Early visitors often point out confusing copy or a broken link that you no longer notice. Use bugs and praise to learn what works on the page, and use ideas to see what people expect before you build.

### Will the widget cover my call to action on a phone?

It can, because the floating button sits in a corner. Move it with the position option set to left, or turn the floating button off and add your own button with the data-escuta-open attribute next to your call to action.

### Can one product collect feedback from several Carrd pages?

Yes. Use the same product key on each page so all feedback lands in one inbox. The page URL saved with each message shows which page it came from, so you can still tell a homepage bug from a sign-up page problem.

---

# Add a feedback widget to a Bubble app

> Add the widget script to the script area in your Bubble SEO and metatags settings, then identify the Current User from a page-load workflow. Deploy to the live version and test there, and add the live domain to allowed origins.

Source: https://escutaproduto.com/resources/feedback-widget-bubble
Last updated: 2026-10-09

## Where does Bubble let you add a script?

Bubble apps have a script area in the SEO and metatags settings, where you can add script and meta tags to the page header. Anything you put there is included on every page of the app. That makes it the right place for the widget's single script tag, so the floating Feedback button appears throughout your app.

The script goes in the header area, not inside a page element. Use the defer attribute as shown in the install snippet so the page does not wait for the widget.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

## Identify the Current User with a workflow

The header area cannot use Bubble's dynamic data, so the widget cannot know who is logged in from there. The workaround is a workflow. Create a workflow that runs when the page loads, add a condition so it runs only when the Current User exists, and then add a Run JavaScript action. That action comes from the free Toolbox plugin, so install Toolbox from the plugin marketplace first if your app doesn't have it.

In that action, pass the Current User's email and name as properties. Inside the JavaScript, call the widget's identify function with those values. The queue pattern is the safe way to do it, because it works whether the script has loaded yet or not:

```js
window.EscutaProduto = window.EscutaProduto || { q: [] };
(window.EscutaProduto.q ||= []).push(["identify", [{
  email: properties.email,
  name: properties.name
}]]);
```

Check how your Bubble version names the properties inside the action, because the name you give each property in the action has to match the name the code reads. Test with a logged-in user. The email field should be hidden in the form.

A condition is important. If you run identify for a logged-out visitor, the email and name fields are filled with empty values, which is confusing. Run the action only when there is a user.

## Open the widget from your own button

Many Bubble apps have a help or feedback button in the navigation, and that is a better prompt than a floating corner button in some layouts. To use your own button, set the floating button off in the script tag by adding data-trigger="none" to the snippet.

Then add a button element with a workflow on click. The workflow runs a Run JavaScript action that calls the widget's open function with the type you want:

```js
window.EscutaProduto.open({ kind: "bug" });
```

Use kind "idea" or "praise" for other prompts. Only call open after the page has loaded, since the script has to be present. The button should say what happens, such as Report a problem, so visitors know where the message goes.

## Test the live version, not the development version

Bubble keeps a development version of your app, which you edit, and a live version that visitors use. Changes you make in the editor reach the live app only after you deploy. A widget test in the development environment does not prove that the live app works.

Deploy the change to live, then open the live domain in a private window. Sign in with a test user to check identify, and sign out to check the anonymous form. If a test fails, the live version is the one to debug, because that is what customers see.

## Keep allowed origins in step with Bubble's domains

Your app answers at your custom domain, and Bubble also gives it a default address on its own domain. If you test on a Bubble address, add that address to allowed origins too, using the full https form. Requests from any site not on the list are rejected with a 403 response.

Add the live domain first, since that is what customers use. Only add the other addresses if your team tests there, and remove any you no longer use.

## Bubble feedback in Escuta Produto

Each message from your app lands in the inbox of its product, with the page URL and browser saved. In a Bubble app, the page URL often shows a route with parameters, so you can see which screen the visitor was on. Filter by type to separate bugs, ideas and praise, then set statuses as you decide.

Internal notes are the right place for context the customer never sees, such as a link to the Bubble workflow that needs a fix. Slack and Discord webhooks in the product settings send each new message to your team channel, so a bug report reaches the person who can fix it quickly.

## Common Bubble mistakes to avoid

A few problems come up again and again on Bubble apps, and each one is quick to avoid.

The first is running identify without a condition. A visitor who is not logged in has no Current User, so the email and name fields are empty. Add the condition, so the workflow runs only when a user exists.

The second is testing only in the editor. The development version is useful for building, but customers use the live version. Check the live domain after every deploy, because a change that looks right while you edit can behave differently once it is live.

The third is putting dynamic data in the header. The header area is static, so Bubble values written there do not change per user. Use a workflow for anything that depends on the visitor.

The fourth is leaving test domains on the allowed origins list. Remove any Bubble address you no longer test on. The live domain should always be on the list, and nothing else should need to be.

The fifth is forgetting that the floating button appears on every page of the app, including screens where a feedback prompt would confuse people, such as a checkout or a sign-up step. Either accept it, or turn the floating button off and place your own button on the screens where feedback fits.

## Related

- The full widget reference is in the [widget docs](/docs/widget), including the identify call and the open function.
- If you would rather send people to a page than add a widget, see the [hosted page docs](/docs/hosted-page).
- For a different no-code builder with a header script, read [Add a feedback widget to a Framer site](/resources/feedback-widget-framer).
- For how to sort what arrives, read [What is feedback triage?](/resources/what-is-feedback-triage).

## Frequently asked questions

### Where do I put the widget script in a Bubble app?

Put it in the script area of the SEO and metatags settings, which adds it to the header of every page. Use the defer attribute from the install snippet. The floating Feedback button then appears on each page of the app.

### Can Bubble pass the logged-in user to the feedback widget?

Yes, but from a workflow, not from the header script, because dynamic data is not available there. Run identify on page load with the Current User's email and name, and only when a user is logged in.

### Why don't my Bubble changes show on the live app?

Bubble keeps a development version and a live version. Changes reach the live app only after you deploy them. Test on the live domain in a private window, because a check in the development environment does not confirm the live install.

### Can I open the feedback form from my own Bubble button?

Yes. Set the floating button to none with the trigger option, then add a button whose click workflow runs the open function with a kind such as bug or idea. Call it only after the page has loaded.

---

# Install a feedback widget with Google Tag Manager

> Create a Custom HTML tag that holds the widget script, fire it on All Pages, and test in GTM preview mode before you publish the container. Keep the data attributes exact, and allow the widget script and feedback API in your Content Security Policy.

Source: https://escutaproduto.com/resources/feedback-widget-google-tag-manager
Last updated: 2026-10-09

## When is Google Tag Manager the right install path?

Google Tag Manager is useful when someone other than a developer manages a site's scripts, such as a marketing lead who already uses tags for analytics. If your team already has a container on every page, adding the widget as one more tag is quick and keeps the site's code untouched.

If the site has no container yet, a direct script tag in the site code is simpler. Add GTM only when you already use it or want other tags managed in one place.

## Create a Custom HTML tag with the snippet

In your GTM container, create a new tag. Choose the Custom HTML tag type, and paste the script tag from your product's install page into the HTML field:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Give the tag a clear name, such as Escuta Produto widget, so the team can find it. Leave the option that supports document.write turned off unless a vendor specifically requires it. The widget does not need that option, and document.write can delay the page, so keeping it off is the safer default.

Keep the attributes exactly as written. The data-key attribute identifies your product, and the other options such as data-color, data-position, data-locale and data-trigger are read from the same tag. A typo or a removed attribute changes the widget's behavior without an obvious error.

## Fire the tag on every page

Attach a trigger to the tag. The built-in All Pages trigger fires on every page view, so the floating Feedback button appears site-wide. Choose it, save the tag, and make sure the tag is set to fire on the trigger you chose.

If you only want the widget on some pages, use a trigger with a condition on the page path instead. Keep the condition simple, and test each page you expect to include. A page that the trigger misses will have no button and no obvious error for the visitor.

## Test in preview mode before you publish

Use GTM's preview mode before you publish the container. Preview mode runs your tags on the live site in a debug panel, and it shows which tags fired on each page. Open your site in preview mode and check that the widget tag fires on the homepage and on an inner page.

Then confirm the result on the published site, after you publish the container version. Open a page in a private window, check that the floating Feedback button appears in the corner, and send a short test message. Look for the item in your inbox with the page URL saved.

Publish only after the preview checks pass, because publishing sends the new container version to every visitor.

## Identify users through a separate tag

GTM can also send the signed-in user's email to the widget, but do this as a second tag rather than changing the widget tag. The identify call uses the queue pattern, so it works in either order. Read the email from a data layer variable that your site already pushes for logged-in users, then fire the tag only on pages where that variable exists.

Only do this when your site already puts the user's email in the data layer. If it does not, the form stays anonymous with an optional email field, which is a perfectly good default.

## Check the Content Security Policy first

A Content Security Policy is a header that tells the browser which sources the site may load scripts from and send requests to. If your site sends one, the policy must allow the widget. The script loads from escutaproduto.com, so script-src has to include that host. Feedback is sent to the same host through its API, so connect-src has to include it too.

If the widget loads but nothing sends, a policy blocking connect-src is a likely cause. Check the browser's console for a message about a blocked request. The fix belongs in the site's policy, not in GTM.

## Consent and tag firing

If your site uses a consent tool that holds tags until a visitor agrees, the widget tag will wait too. That is the behavior you chose when you set up consent, and it is worth knowing before you launch. A visitor who has not agreed will not see the floating button on that page view.

Check the trigger and consent settings together. A tag that never fires looks the same as a broken widget, so use preview mode to see whether the tag fired at all.

## Feedback from tag-managed sites in Escuta Produto

Messages arrive in the inbox of the product you set up for the site, with the page URL and browser saved. Because GTM often runs on marketing pages as well as the app, you can see which marketing page a complaint came from. Filter by type and status to keep the list tidy.

Internal notes record decisions, such as a campaign landing page you changed because of a bug report. Slack and Discord webhooks in the product settings send each new message to your team, with a link to the dashboard.

## Related

- Every widget option, including data-trigger and the JavaScript API, is in the [widget docs](/docs/widget).
- A link-based alternative that needs no tags is in the [hosted page docs](/docs/hosted-page).
- For another install approach, read [Add a feedback widget to a WordPress site](/resources/feedback-widget-wordpress).
- For the collection step as a whole, read [How to collect customer feedback](/resources/how-to-collect-customer-feedback).

## Frequently asked questions

### Is Google Tag Manager a good way to add the feedback widget?

It is a good fit when someone who manages tags already uses a container on every page. Keep the data attributes intact, fire the tag on All Pages, and test in preview mode before you publish the container.

### Why should the document.write option stay off?

That option makes GTM write the tag with document.write, which can delay the page. The widget does not need it because it loads with defer. Leave the option unchecked unless a vendor requires it.

### How do I know the widget tag fired?

Use GTM preview mode to see which tags fired on each page, then confirm the Feedback button on the published site. The browser developer tools also show whether the widget script loaded without errors.

### Can something stop the widget from loading through GTM?

Yes. A trigger that does not fire, a consent setting that holds tags, or a Content Security Policy that blocks the script or the feedback API will each stop the widget. Check preview mode, the console and the policy header.

---

# How to use RICE to prioritize customer feedback

> RICE scores a request as reach times impact times confidence, divided by effort. With customer feedback, reach is the number of distinct customers who asked in a fixed period. Use the score to rank features, not to replace judgment.

Source: https://escutaproduto.com/resources/rice-prioritization-feedback
Last updated: 2026-10-09

## What RICE means for feedback

RICE is a scoring model with four inputs: **Reach**, **Impact**, **Confidence** and **Effort**. You multiply the first three, then divide by effort. The result is one number you can sort by, so a long list of requests becomes a ranked list you can defend in a planning meeting.

Customer feedback fits RICE well because part of the input already sits in your inbox. Reach is how many people asked, and confidence is how much evidence you have behind the impact estimate. You still estimate impact and effort yourself.

The formula, written out: RICE score = (Reach x Impact x Confidence) / Effort

## Count reach from distinct customers

Reach is the number of distinct customers who would hit the problem in a set period. Pick the period first, for example the last 90 days, and use it for every request so the scores compare.

Count people, not messages. One customer who sends the same request three times counts once. If your items carry an email or an ID, sort by that field in a spreadsheet and count the unique values. If feedback is anonymous, count each message as one person and say so in your notes. The result is then a ceiling, not an exact count.

Reach can also cover people who never wrote. Two customers asking for a change may stand for twenty people who hit the same wall and left quietly. Check your analytics or support tickets when you have them, and adjust the reach number with a note that explains the adjustment.

## Score impact and confidence honestly

Impact is how much the change moves your goal for each person it reaches. Use a short scale and write it down before you score anything:

- **3** for massive: the customer cannot finish their work without it.
- **2** for high: a clear, repeated gain in daily use.
- **1** for medium: a noticeable improvement.
- **0.5** for low: a pleasant touch.
- **0.25** for minimal: a cosmetic change.

Confidence measures how much evidence supports your reach and impact numbers. Use 1.0 when you watched several customers struggle with the problem, 0.8 when you have consistent written feedback, and 0.5 when you are mostly guessing. Low confidence is honest, and it lowers the score, which is the point.

Effort is the team time needed to ship the change. Ask the engineer who would build it, and round to the nearest half week. Write the estimate next to the request so the next review starts from the same assumption.

## Work through an example

The numbers below are invented for illustration. Your reach counts will come from your own inbox.

| Request | Reach (last 90 days) | Impact | Confidence | Effort (weeks) | Score |
| --- | --- | --- | --- | --- | --- |
| Export to Google Sheets directly | 6 | 2 | 0.5 | 1 | 6.0 |
| Fix slow loading on the invoice page | 3 | 3 | 1.0 | 3 | 3.0 |
| Dark mode for the dashboard | 14 | 0.5 | 0.8 | 2 | 2.8 |

The export row is 6 x 2 x 0.5 = 6, divided by 1, so 6.0. The invoice row is 3 x 3 x 1.0 = 9, divided by 3, so 3.0. The dark mode row is 14 x 0.5 x 0.8 = 5.6, divided by 2, so 2.8.

The dark mode request has the most people behind it, yet it ranks last. That is the model working as intended: many people asked, but the gain per person is small and the team is unsure of it. The invoice page ranks second because each affected customer loses real work. Your ranking will look different, and that difference is the reason to run the model.

## Where RICE misleads

RICE rewards whatever is easy to count. A customer who writes often can inflate reach if the rest of your base does not care. Compare the distinct count with the rest of the inbox before you trust a high number.

Bugs do not fit the model well. A crash that blocks checkout has a small reach in the inbox and a huge impact. Score bugs by frequency and severity, as described in [how to prioritize bugs by frequency and severity](/resources/prioritize-bugs-frequency-severity), and keep RICE for features and changes.

Effort estimates drift. Re-score the top ten requests every quarter, because a request that took two weeks last year may take one now, or the reverse.

## Run RICE on your Escuta Produto inbox

Escuta Produto does not calculate RICE, and it has no tags for grouping requests. You do the counting with the tools it does have:

1. Filter the product inbox to type **idea**, then search for the words customers use for one request.
2. Read each match and note the distinct customers in an internal note on the item, with the date you counted.
3. Export the product's feedback as CSV and copy the reach counts into a sheet with one row per request.
4. Add impact, confidence and effort columns, then sort by the score.

Reach counts are easier to trust when every item carries an email. The identify call that fills that field is described in the [widget docs](/docs/widget).

Keep the reach numbers in the same sheet each week so the trend stays visible. For the spreadsheet steps, read [how to analyze customer feedback in a spreadsheet](/resources/analyze-feedback-spreadsheet). For what happens after the ranking, read [how to turn customer feedback into a roadmap](/resources/turn-feedback-into-roadmap).

## Frequently asked questions

### What does RICE stand for?

RICE stands for Reach, Impact, Confidence and Effort. You multiply reach, impact and confidence, then divide by effort. A higher score means the request ranks higher in the planning discussion.

### How do you measure reach from customer feedback?

Count the distinct customers who asked for the same thing within a fixed period, such as the last 90 days. Count people rather than messages, and use the same period for every request so the scores can be compared.

### Is RICE better than sorting requests by how many there are?

Usually, yes. Counting requests ignores how much each person gains and how sure you are about it. RICE adds impact, confidence and effort, so a small request from many people does not automatically outrank a high-impact change.

---

# How to apply the Kano model to feature requests

> The Kano model sorts features into basic needs, performance features, delighters and indifferent or reverse features. Classify each request by how customers would feel with and without it. Use the labels to decide what must work first.

Source: https://escutaproduto.com/resources/kano-model-feature-requests
Last updated: 2026-10-09

## What the Kano model measures

The Kano model sorts product features by how they change customer satisfaction. Some features are expected, so their absence hurts and their presence goes unnoticed. Others add satisfaction the more you provide. A few delight people who never asked for them. Knowing which kind a request is changes how much effort it deserves.

Noriaki Kano and his colleagues proposed the model in the 1980s. Teams still use it because it forces a question that a raw count of requests skips: what does the customer feel when this exists, and when it does not?

## The five categories

| Category | What it means | Typical feedback wording |
| --- | --- | --- |
| Basic | Customers expect it. Missing it causes frustration. Having it earns little praise. | "I expected this to be there" |
| Performance | More is better. Satisfaction rises with each improvement. | "Could it be faster?" or "I need more columns" |
| Delighter | Nobody asked for it, but people are pleased when it appears. | Rare in requests, more often found in praise |
| Indifferent | Customers do not care either way. | "It would be nice, I guess" |
| Reverse | Some customers want the opposite of what you built. | "Stop forcing me through the tour" |

Basic needs are the easiest to miss in planning, because they never show up as a request. Customers only mention them when they are absent.

## Ask the functional and dysfunctional question

The classic Kano survey asks two questions about each feature: how would you feel if it existed, and how would you feel if it did not? The answers place the feature in a category.

Escuta Produto has no surveys, so you rarely get clean answers in a form. You infer the category from what customers wrote. Read each request and ask two questions of your own:

1. If we ship this, does the complaint disappear, or does the customer just feel less annoyed?
2. If we do not ship it, does the customer leave, or do they find a workaround?

A complaint that disappears when the feature exists points to a basic need. A customer who leaves without it points to a basic need as well, and the stronger case is the one where the absence drives churn.

## Classify requests from your feedback

Work through the idea items in the inbox. For each request, write the category in an internal note, for example "Kano: basic" followed by one line of reasoning. Notes are private, so your team sees the call without customers seeing it.

Start with the requests that repeat, because repeats show what most customers expect. Then read the praise items for delighter hints. A sentence like "I did not expect this to be included" points to one.

A customer who writes "the export is missing the due date column" describes a basic expectation. A customer who asks "could the reports load in under a second" wants a performance improvement. Both are useful, but they deserve different responses.

## Use the categories to order the roadmap

Ship basics first, because a missing basic can cost you customers. Then order performance work by how much each improvement gives people who asked. Delighters are a bonus, so schedule them after the basics and the performance items that matter most. Skip indifferent requests unless they are cheap to build.

Reverse requests usually point to a setting, not a removal. If some customers dislike a tour or a prompt, offer a way to turn it off rather than deleting it for everyone.

## Where the Kano model breaks down

The model describes how satisfaction changes. It does not tell you how many people want something. A basic need requested by one customer can still be a basic need, and a delighter can be expensive to build. Combine Kano with reach and effort, as in [the RICE approach to feedback](/resources/rice-prioritization-feedback).

Categories also move. A performance feature becomes a basic need once customers come to expect it, and a delighter becomes a basic need once it is common. Re-label the top requests once a year.

Free-text feedback is a weak substitute for the paired questions the model was built on. Treat each label as a working hypothesis and confirm it with a few direct conversations before a large build.

## Add a Kano label to your Escuta Produto triage

Escuta Produto gives you statuses (new, planned, in progress, done and closed), a type for each item (bug, idea, praise or other) and private internal notes. It has no tags, so the Kano label lives in the note.

1. Filter the product inbox to type idea and status new.
2. Read each request, then write the category and one reason in the internal note.
3. Move the requests you will build to planned, and close the ones you decided against with a note that explains why.
4. Add an entry point that opens the idea form directly. The widget accepts `data-escuta-open="idea"` on any element, so the requests you collect arrive typed as ideas. Setup details are in the [widget docs](/docs/widget).

For the next step after labeling, read [how to find the problem behind a feature request](/resources/problem-behind-feature-request). For the whole path from requests to a plan, see [how to turn customer feedback into a roadmap](/resources/turn-feedback-into-roadmap).

## Frequently asked questions

### What is the Kano model in product management?

The Kano model classifies features as basic, performance, delighter, indifferent or reverse. The category shows how customer satisfaction responds to the feature, which helps you decide what to build first and what can wait.

### Can you apply the Kano model to free-text feedback?

Yes, with limits. Free text rarely contains the paired questions the model needs, so you infer the category from wording and repeats. Treat the label as a working hypothesis and confirm it with a few conversations before a large build.

### Which Kano category should you ship first?

Usually the basic needs, because a missing basic causes frustration that drives customers away. Performance improvements usually come next, and delighters come last unless they are cheap to build.

---

# Prioritize bugs by frequency and severity

> Rank bugs on two separate scales: how often customers hit them and how much damage they cause. Plot them on a 2x2 grid, fix frequent high-severity bugs first, and let rare cosmetic issues wait in the backlog.

Source: https://escutaproduto.com/resources/prioritize-bugs-frequency-severity
Last updated: 2026-10-09

## Score frequency and severity separately

A bug can be common and trivial, or rare and critical. A typo in a help text is seen by many people, but it blocks nobody. A failed payment is rare, yet every case costs money and trust. Score the two dimensions separately and combine them afterward. A single gut-feel number hides exactly the bugs you need to see.

## Build the 2x2 grid

Draw a grid with severity on one axis and frequency on the other. Place each open bug in one cell.

| | Rare | Frequent |
| --- | --- | --- |
| **High severity** | Fix soon, and find the root cause | Fix now |
| **Low severity** | Backlog, and document the workaround | Schedule with related work |

The top right cell goes first. The top left cell still gets a date, because a rare failure that loses data can damage trust in one afternoon. The bottom row can wait, but batch those fixes with other work in the same area so you do not pay the context-switching cost many times.

Escuta Produto does not draw a grid view, so keep this in a shared spreadsheet or document, with one row per bug and a link back to the inbox item.

## Estimate frequency from feedback

Frequency is the number of distinct customers who hit the bug in a set period. Feedback gives you a partial count, so be clear about what you are counting.

Start by grouping bug items that describe the same symptom. The widget saves the page URL and the browser automatically, so use those fields together with the words in each message. Then count distinct customers. If items carry an email or an ID, count the unique values. If they are anonymous, count the items and call the total a minimum.

For example, nine bug items mention the invoice page not saving in one browser, and they come from six distinct customers. That is a frequent bug, even though the team heard about it nine times. The gap between messages and people is exactly what this count is meant to expose.

Quiet customers are missing from the count. Someone who leaves without writing still hit the bug. When you have error logs or support tickets, use them to check whether the feedback count understates the problem.

## Estimate severity from what the customer lost

Severity is the damage done to someone who hits the bug. Judge it by what the customer lost, not by how upset they sound. Four levels work for most teams:

- **Critical**: data loss, a login that cannot succeed, a payment that fails, or a security problem.
- **High**: a core task cannot be finished and there is no workaround.
- **Medium**: the task takes longer, but a workaround exists.
- **Low**: visual or wording issues that do not stop work.

Critical and high belong in the top row of the grid. Medium depends on reach: put it in the top row when many customers hit it on a main path.

Two reports show the difference. One says the export button shows an error and no file downloads, which costs the customer a month-end report. The other says a tooltip overlaps the menu on a narrow window. The first is high severity even if it arrives once a week, because it blocks a core task with no workaround. The second is low severity even if many people see it, because nothing is lost.

## What goes first

Apply three rules in order:

1. Any critical bug goes to in progress right away, however rare it is.
2. High-severity bugs that many customers hit come next. These are the fix now cell.
3. Batch the low-severity frequent bugs into one release. Close low-severity rare bugs that have a clear workaround, with an internal note that describes the workaround.

A bug that moves from rare to frequent after a release should move up right away, because the rising count is itself the evidence.

Reply to every customer whose bug you schedule. Read [how to follow up on a bug report](/resources/follow-up-on-bug-reports) for wording that asks for missing detail without sounding like a support script.

## Set up the grid with Escuta Produto

Escuta Produto gives you most of the inputs: a type filter (bug), statuses (new, planned, in progress, done and closed), the page URL and browser saved with each item, the 30-day chart and private internal notes. A weekly routine looks like this:

1. Filter the product inbox to type bug and status new.
2. For each item, read the page URL and browser, then search the inbox for the words customers used to find other reports of the same symptom.
3. Write the severity level and the distinct customer count in an internal note, for example "high, 4 customers, invoice page".
4. Move the bug to planned or in progress, and place it in the grid.
5. Watch the 30-day chart after each release. A rise in bug items means the grid needs a fresh look.

Every new item also posts to Slack or Discord when you have a webhook set up, so critical reports reach the team quickly. Setup steps are in the [notifications docs](/docs/notifications). For the wider triage routine, read [what is feedback triage](/resources/what-is-feedback-triage).

## Frequently asked questions

### How do you decide which bug to fix first?

Rank by severity first, then by frequency. Critical bugs such as data loss or blocked payments go first even when they are rare. High-severity bugs that many customers hit come next.

### How do you estimate bug frequency from customer feedback?

Group reports that describe the same symptom and count the distinct customers who sent them over a fixed period. Use the page URL and browser saved with each item to confirm the reports match, and treat the count as a minimum.

### Should cosmetic bugs ever be fixed?

Yes, when the fix is cheap or when related work already touches the same screen. Low-severity bugs that customers hit often can be scheduled together, while rare cosmetic issues can wait in the backlog.

---

# How to build a tagging system for customer feedback

> Escuta Produto has no tags, so build a short taxonomy from the built-in type, an area label sent as metadata and internal notes. Keep the area list stable, write a definition for each label, and review the list every quarter.

Source: https://escutaproduto.com/resources/feedback-tagging-taxonomy
Last updated: 2026-10-09

## Why a tagging system needs few tags

Tags help you group feedback by what it is about. Too many tags make the list impossible to maintain. One person adds "checkout-error", another adds "payment bug", a third adds "stripe", and a single problem now sits in three buckets. A small list that everyone uses beats a large list that nobody trusts.

Escuta Produto has no tags. Every item has a type (bug, idea, praise or other) and a status (new, planned, in progress, done or closed). For anything else you have three tools: private internal notes, metadata that your app sends with each submission, and a CSV export that you analyze in a spreadsheet.

## Separate area, type and sentiment

Keep three dimensions apart, because each answers a different question:

| Dimension | Question it answers | Example values | Who sets it |
| --- | --- | --- | --- |
| Type | What kind of message is this? | bug, idea, praise, other | The customer, in the form |
| Area | Which part of the product is it about? | checkout, reports, onboarding, settings | Your app, or you |
| Sentiment | How does the customer feel? | Usually skip it | You, if at all |

Type is already built in, so do not copy it into a tag. Area is the dimension you need. Sentiment is tempting but usually adds noise, because the same words can read as angry in one message and neutral in another. Add sentiment only when a specific question depends on it.

## Store the area where it is known

Set the area in your app at the moment the page knows it. The widget accepts metadata through its JavaScript API, so the screen that renders checkout can declare that its feedback belongs to the checkout area:

```js
EscutaProduto.setMetadata({ area: "checkout" });
```

Call it after the widget script has loaded, from the component that renders that part of the product. Metadata is limited to 4 KB of JSON. Before you rely on it for counts, export a sample and confirm that the metadata field appears in the file.

## Write a short tag guide

A tag guide is a one-page document. For each area, write:

- The name, in lowercase, with one word where possible.
- What it includes, in one sentence.
- What it excludes, and which area takes those cases instead.

For example: "checkout: payment step and cart totals. Excludes order history, which belongs to orders." A new person should be able to pick the right area in under a minute, so keep the wording plain.

Share the guide with everyone who reads the inbox. When two people disagree about an item, update the guide rather than deciding the case on your own.

## Keep the taxonomy maintainable

Review the list once a quarter. Merge areas that hold few items, and split any area that holds most of your bugs, because a broad area is too vague to act on. Rename areas that no longer match the product's screens. Retire an area only after you have moved its old items.

Keep the list short. If you pass about ten areas, you probably have sub-areas that can fold into their parents. Use the same list across products so you can compare them. The reasons for a shared list are covered in [one feedback inbox for many products](/resources/one-inbox-many-products).

## Handle items that fit two areas

Some feedback spans two areas. A customer who cannot save a report template could mean reports or settings. Pick the area where a fix would most likely happen, and mention the other area in the internal note. Do not give one item two area labels, because the counts then reward a single customer's message twice.

If the same confusion keeps appearing across two areas, the boundary between them is unclear. Redraw the line in the tag guide, then relabel the items from the last quarter that fall on the moved side. This is also the moment to check that area names still match the product's menus and screens. Customers use the product's own words, so names that match them are easier for everyone on the team to apply.

## Apply the taxonomy in your Escuta Produto inbox

1. Add the area to your app's metadata, as shown above, so new items arrive labeled.
2. For older items, write the area as a short internal note, such as "area: reports", while you read them during triage.
3. Export the product's CSV each week and count areas by type in a spreadsheet. The steps are in [how to analyze customer feedback in a spreadsheet](/resources/analyze-feedback-spreadsheet).
4. When you want a closer look at one area, use the inbox search for the words customers used about it.

Widget options such as the entry points and the locale are in the [widget docs](/docs/widget). For the decision that follows a tag, such as moving an area's bugs to fix now, see [prioritize bugs by frequency and severity](/resources/prioritize-bugs-frequency-severity).

## Frequently asked questions

### How many tags should a feedback system have?

Escuta Produto has no tags, so the question is how many areas to track in metadata and notes. Keep it to a handful that match your screens, and let the built-in type cover bug, idea and praise.

### Should you tag feedback sentiment?

Usually not. Sentiment labels are hard to apply consistently by hand, and the same words can read differently in different messages. Track type and area first, and add sentiment only when a specific question depends on it.

### Where do feedback labels live in Escuta Produto?

Send an area as metadata from your app so it is stored with each item, and use private internal notes for labels you add by hand. Use the CSV export to count them in a spreadsheet.

---

# Find the problem behind a feature request

> The problem behind a feature request is the outcome the customer is trying to reach. Ask what they were doing, why it matters and what they do today. Write the problem in an internal note, then group requests that share it.

Source: https://escutaproduto.com/resources/problem-behind-feature-request
Last updated: 2026-10-09

## Customers describe solutions, not problems

Customers know their pain better than anyone. They are less reliable about the fix. A request like "add a button to export to Excel" describes a solution the customer imagined. The real problem might be that month-end totals take an hour to copy by hand. A button is one way to remove that hour, and it may not be the best one.

Treat every feature request as the start of a conversation. The request tells you where to look, and the customer's description of their day tells you what to build.

## Ask why until the job is clear

Ask why two or three times. Stop when you can name the task, the person and the moment when the problem happens. These questions work well in a reply:

1. What were you trying to finish when you needed this?
2. What do you do today instead?
3. What happens if this takes another month?

The third question separates real problems from nice-to-haves. If nothing bad happens without the feature, the request is probably a delight rather than a need. That is still useful information, but it is a different decision.

## Use the jobs-to-be-done frame

Jobs-to-be-done is a way to describe the task behind a request. Write it as one sentence: "When I [situation], I want to [motivation], so I can [outcome]." Filling in the three parts keeps you from building for the solution the customer named.

For example: "When I close the month, I want to see totals by client, so I can send invoices on time." That job can be served by an export, a filter, a summary report or a reminder. Choosing among them is a design decision, and the job tells you what success looks like.

## Compare requests with their problems

The table below pairs common requests with the problems they usually point to. The pairs are examples, not a lookup table, so check each one against what the customer actually wrote.

| Request | Likely problem |
| --- | --- |
| Add a button to export to Excel | Finance copies rows by hand at month end |
| Let me filter by date range | Support cannot find last week's complaints quickly |
| Send me a message for every refund | The team misses refund requests over the weekend |

Two different requests can share one problem. Two customers asking for different features may both be fighting the same slow search. Group by problem, not by request wording, or you will count one pain as several. When you build your own table, keep the same columns each time: the request as the customer phrased it, the problem in one sentence, and the number of distinct customers who described that problem. A table in that shape turns a pile of wording into a short list you can take into a planning meeting.

## Write the problem in an internal note

After a conversation, write the problem in one sentence in the internal note of each feedback item. Start with the job, then list the solutions people asked for: "Job: close the month with totals by client. Solutions asked: export button, date filter."

This note becomes the grouping key. When you count requests for a problem, you count notes, not the wording of each message. That reduces noise and makes the trend easier to see across weeks.

Revisit the notes once a quarter. A problem that many notes described last quarter and none describe now may have been solved by a release, which is worth recording before you reopen the request.

## Reply and confirm the problem

Confirm the problem before you commit to a solution. Reply from your own email, because Escuta Produto does not send messages to customers on your behalf. Restate the job in your reply and ask whether you understood it correctly. "It sounds like month-end totals take too long to build by hand. Is that right?" Replies like this often surface the real need, and customers feel heard.

Use the reply templates in [how to reply to customer feedback](/resources/reply-to-customer-feedback) so each reply stays short and specific. If your app calls identify for signed-in users, the item already holds the sender's email, and the follow-up takes a minute. The identify fields are listed in the [widget docs](/docs/widget).

## Find the problem in your Escuta Produto inbox

1. Filter the product inbox to type idea and read the new items.
2. When the item includes an email address, follow up by email with the job question.
3. Write the job in the internal note, and keep the solutions requested in the same note.
4. Search the inbox for the nouns and verbs of the job, such as "month" or "invoice", to find other customers describing the same problem.
5. Once the problem has an owner and a score, move the item to planned.

For scoring what you find, see [how to use RICE to prioritize customer feedback](/resources/rice-prioritization-feedback). When a problem has no good fix, read [how to say no to a feature request](/resources/say-no-to-feature-requests) before you reply.

## Frequently asked questions

### What is the problem behind a feature request?

It is the outcome the customer is trying to reach, such as finishing month-end totals faster. The requested feature is one possible solution, and several different features can solve the same problem.

### What questions reveal the real problem?

Ask what the customer was trying to finish, what they do today instead and what happens if the change takes longer. Ask why once or twice more until you can name the task, the person and the moment.

### Should you build exactly what customers request?

Not always. Build the simplest thing that solves the underlying problem. Sometimes that is the requested feature, and sometimes a filter, a template or a reminder does the job with much less work.

---

# How to weigh feedback from paying vs free users

> Weigh feedback by fit, not only by payment. Send a segment field with your identify call, then compare requests by segment. Paying customers usually shape direction, while free users can reveal the blockers that stop people from becoming paying customers.

Source: https://escutaproduto.com/resources/paying-vs-free-user-feedback
Last updated: 2026-10-09

## Payment is one signal, not the whole answer

Paying customers have committed money, so their requests deserve weight. They are also the people your roadmap should serve most directly. But payment alone does not tell you whether a request matches your target customer. A large account can ask for things your product will never do, and a free user can describe the exact blocker that keeps a whole group from ever paying.

Weigh each request on three questions: is the sender in the group you are trying to serve, how many distinct people from that group asked, and how well the request fits the direction you have chosen?

## Send a segment field with identify

Escuta Produto stores extra keys you pass to identify as metadata, so you can attach a segment to each sender. Use a name that fits your business:

```js
EscutaProduto.identify({
  email: user.email,
  name: user.name,
  id: user.id,
  segment: user.segment,
});
```

Pick a short list of values, such as "paying" and "free", and use the same values in every product. Write down what each value means. A free account might be one that never paid, or one that stopped paying, and those two groups behave differently. Decide which meaning your field uses before you read any counts. Set the field from your own records after login, and send only what you are comfortable storing with the feedback. The full options are in the [widget docs](/docs/widget).

If you submit feedback from your server through the REST API, put the same field in the metadata object of the request body. The hosted feedback page cannot receive identify data, so requests from it arrive without a segment unless you add one another way.

## Compare requests across segments

For each request, count the distinct senders in each segment and place the numbers side by side. Read them against the size of each segment. A request asked by two of ten paying accounts means something different from a request asked by two of a thousand free users.

Here is an invented example to show the shape of the comparison:

| Request (invented example) | Paying senders | Free senders | What it suggests |
| --- | --- | --- | --- |
| Single sign-on for teams | 9 | 1 | Matters to larger accounts already paying |
| Template library | 2 | 6 | Blocks new users from reaching their first result |

The single sign-on request belongs on the roadmap for the paying segment. The template library deserves a closer look, because free users cannot reach value, and that affects who ever becomes a paying customer. Neither row settles the decision alone, but together they show where to look next.

## When free users matter more

Free users matter most in three cases:

- They describe the first-run experience. If they stall during setup, the paying segment never grows.
- They cannot reach the feature that makes the product worth paying for.
- They are the next group you want to win, so their requests show what that group needs.

A concrete case helps. Consider a free user who stops during setup because the import step rejects their file format. Few people may hit that problem, but each one is a lost chance to grow the paying group. A paying account asking for advanced reporting is a larger request, yet it serves a group you already keep. Both can be right, and fixing the import usually costs less and opens a path to growth.

When free users reject a change, do not assume paying customers want the opposite. Ask a few paying customers directly before you reverse a decision.

## Avoid the loudest-segment trap

The biggest risk is letting one segment set the roadmap through volume alone. Write the rule before you start, for example: "paying customers decide scope, and free-user feedback decides onboarding fixes." Review the rule once a quarter.

A weighted score can help, but only if you can explain every weight. A simple rule that the team can defend is worth more than a precise formula nobody understands.

## When segments disagree

Sometimes paying customers ask for one thing while free users ask for the opposite. Do not settle that by counting who is louder. Write both positions in one internal note, with the distinct counts for each segment and the problem each group describes. Then ask which group the next quarter's work is meant to serve. That answer usually settles the trade-off.

If the direction is still open, run a few short conversations with people from each segment before you commit to either side. Record what you learned in the same note, so the decision and its reason stay together when the team changes.

## Segment the inbox with Escuta Produto

1. Send the segment field with identify for logged-in users, and in the REST API metadata for server-side submissions.
2. Filter the product inbox to type idea and praise, and read items from both segments.
3. Write the segment counts for each request in an internal note, or in your weekly sheet.
4. Export the CSV each week and compare segments with a pivot table, using [how to analyze customer feedback in a spreadsheet](/resources/analyze-feedback-spreadsheet).
5. If you run several products, use [how to choose which product to work on](/resources/choose-which-product-to-work-on) to compare the segments across them.

For the request-level scoring that follows, see [the problem behind a feature request](/resources/problem-behind-feature-request).

## Frequently asked questions

### Should feedback from paying customers count more?

Often it should count more for direction, because those customers have committed to the product. Still, weigh each request by whether the sender matches your target customer and how many distinct people from that group asked.

### How do you record a customer's segment in feedback?

Pass a segment field to identify in your app for logged-in users, or include it in the metadata when you use the REST API. Extra keys passed to identify are stored as metadata with the feedback.

### When should you prioritize feedback from free users?

When they describe a blocker during setup or a missing first-value step, because those problems stop people from becoming paying customers. Their requests can also show what the next group you want to reach needs.

---

# How to deduplicate feature requests

> Deduplicate feature requests by searching for the words customers use, reading each match, then naming one canonical request in an internal note. Count distinct customers, not messages. Escuta Produto has no merge button, so the note does the linking.

Source: https://escutaproduto.com/resources/deduplicate-feature-requests
Last updated: 2026-10-09

## Why duplicates distort your count

Ten messages asking for the same thing look like a strong signal. If three of them come from one customer who kept sending reminders, the real count is much smaller. Duplicates also split the discussion. One request sits in the inbox as "CSV export", another as "spreadsheet download", and nobody sees them together until the backlog is a mess.

Deduplicating means grouping the messages that ask for the same outcome, giving the group one name, and counting the people behind it.

## Search for the words, then read the matches

Start with the nouns and verbs in the request. Search the product inbox for them, then read every match. Search finds the words customers used, not the same idea expressed in different words, so try synonyms as well: "export", "download", "spreadsheet" and "CSV" can all describe one request.

Read the matches instead of trusting the search. Some results describe a different request that happens to share a word. "Export" might mean a PDF in one message and a CSV in another, and grouping them would hide a real difference.

## Name one canonical request

Write one short name for each cluster and use it everywhere. A good canonical name describes the outcome and includes the object:

- Weak: "Better export"
- Strong: "Export invoices as CSV with custom columns"

When two requests look similar but ask for different outcomes, keep them apart. Two customers who both write "make search better" may want faster results in one case and better filters in the other. A single name that merges them would hide a split, and the roadmap would miss one of the two fixes.

Keep the canonical names in a spreadsheet or shared document, with one row per request. Each row holds the name, the underlying problem from [the problem behind the request](/resources/problem-behind-feature-request), and the distinct customer count.

## Count distinct customers

For each cluster, count people rather than messages. Use the email or ID on each item where it exists. The widget fills in name and email when your app calls identify, and the form asks anonymous senders for an email, so many items will have one. The identify call is described in the [widget docs](/docs/widget).

Two rules keep the count honest. First, one person counts once per request, however many times they wrote. Second, anonymous items cannot be matched to each other, so count them separately and label the total as a ceiling, because some may come from the same person.

Write the count and the date in the internal note of the most complete item in the cluster. The next review then starts from the same number instead of recounting from scratch.

## Close duplicates without losing the sender

When a cluster has a canonical item, set the duplicate items to closed. Write a short internal note that names the canonical request. Closed means you decided not to track the item separately, which is exactly what a duplicate is.

Then reply to the sender if you have an email address. Tell them the request is tracked and what it is called. A reply makes the customer feel heard and makes it less likely they will send the same request again.

## A cleanup routine for old repeats

Old backlogs hide the biggest clusters, and they take longer to clear than a normal week. Expect the first pass to take a full afternoon for a large backlog. Run a one-time cleanup, then keep a weekly habit:

1. Sort the idea items by date and work through the oldest fifty.
2. For each new item, search for its key words before you read it in full.
3. Attach the item to an existing canonical name, or create a new one.
4. Once a month, review the canonical list. Merge names that describe the same outcome, and split any name that covers two outcomes.

Once a canonical request ships, the cluster is the list of people to tell. The steps for that are in [how to tell customers their request shipped](/resources/tell-customers-request-shipped).

## Deduplicate inside Escuta Produto

Escuta Produto has no merge feature, no tags and no AI grouping. The routine above works with what it does provide:

1. Filter the product inbox to type idea.
2. Use text search with the keywords for each canonical request.
3. Record the canonical name and the distinct customer count in an internal note.
4. Set duplicates to closed, with a note that names the canonical request.
5. Export the CSV each week, so the counts sit in a sheet next to the roadmap.

Keep a single area or theme label for each cluster if you use one, so related requests stay near each other. The labeling approach is in [how to build a tagging system for customer feedback](/resources/feedback-tagging-taxonomy).

## Frequently asked questions

### How do you find duplicate feature requests?

Search the inbox for the key words of each request, including synonyms, then read every match. Search finds words rather than ideas, so the reading step matters. Group matches that ask for the same outcome under one canonical name.

### Should you count duplicate messages as votes?

No. Count distinct customers. One person who sends the same request three times is one vote. Record anonymous messages as a ceiling, because you cannot tell whether they came from the same person.

### What happens to customers whose requests become duplicates?

If you have their email, reply from your own address and say the request is tracked under its canonical name. Then close the duplicate item with an internal note, so the decision is recorded and the sender knows you heard them.

---

# How to analyze customer feedback in a spreadsheet

> Export a product's feedback as CSV, open it in Excel or Google Sheets, and count items by type, status and month with a pivot table. Add one column for your own theme label, and keep formulas simple enough for anyone on the team to check.

Source: https://escutaproduto.com/resources/analyze-feedback-spreadsheet
Last updated: 2026-10-09

## Export the file from the product

Open the product in the dashboard and export its feedback as CSV. The file is UTF-8, so accents and non-Latin characters survive, and it opens correctly in both Excel and Google Sheets.

Export one product at a time. Each product has its own feedback, so combining several products in one sheet mixes unrelated work. If you need every product together, add a product column yourself after each export.

The inbox keeps changing while you work, so note the export time and check the file the same day. Comparing the row count with the inbox count is a quick test that the export is complete.

Keep the original file untouched. Work in a copy, so you can always rebuild the analysis from the source.

## Check the columns before you build anything

The first row holds the column names. Read it, and write down what each column contains. Then scan the first few rows to see how the values look: how type and status are spelled, how dates appear, and whether messages contain line breaks.

Messages can also contain commas and quotation marks. Use the CSV import option in your spreadsheet rather than pasting the text, so each field lands in the right cell. If you rename a column, note the change, because formulas point at column letters.

Write a short data dictionary in a second sheet: each column name, what it holds, and the exact spelling of every value you rely on. A new teammate can answer most of their questions from that sheet without a meeting.

## Count by type, status and month

A pivot table answers most weekly questions. Set it up like this:

1. Select the whole table and insert a pivot table on a new sheet.
2. Put the type column in the rows.
3. Put the status column in the columns.
4. Put any column that is never empty, such as the message column, in the values area, and set it to count.

For a monthly trend, group the date column by month. If the spreadsheet reads the dates as text, convert them to real dates first, or the grouping will not work. Name the pivot sheet with the export date, so the file explains its own numbers when someone else opens it.

## Add your own theme column

The export does not include a theme, and you should add one by hand. Put a short label in a new column beside the message, such as "checkout" or "reports". Escuta Produto has no tags, so this column is your tag. Keep the label list fixed for the whole sheet. If you change the list, relabel the rows you already have. Use the same names as your [tagging guide](/resources/feedback-tagging-taxonomy), so the spreadsheet and the internal notes match.

With the theme in the rows and the type in the columns, the pivot shows where bugs and ideas cluster. That view is often the first useful picture of your feedback.

## Simple formulas that answer real questions

Counts with conditions are enough for most weeks. This formula counts bug items that arrived on or after 1 September 2026, assuming column B holds the type and column C holds the date:

```text
=COUNTIFS(B:B,"bug",C:C,">="&DATE(2026,9,1))
```

Counting distinct customers takes one helper column. If column D holds the email, this formula marks the first appearance of each email with a 1, and leaves anonymous rows at zero:

```text
=IF(D2="",0,IF(COUNTIF($D$2:D2,D2)=1,1,0))
```

Add the helper column and sum it to get the distinct count. Keep a check cell that compares the pivot total with a count of the message column, so a broken import shows up as a mismatch before anyone reads the numbers. Anonymous rows have no email to match, so the total is a minimum, not an exact number.

## Keep the sheet honest

A spreadsheet makes numbers look precise. Three habits keep them trustworthy:

- Compare the row count with the inbox count on the same day, so you know the export is complete.
- Write the export date at the top of the sheet, because the numbers change every day.
- Keep last month's sheet, so trends compare like with like.
- Write the filters you applied in the first row of the sheet, such as type idea and status new, so a colleague can reproduce the numbers.

The export contains customer emails and messages. Store it where only your team can open it, and delete copies once the analysis is done.

## Put the sheet in your weekly routine

Use the sheet in the weekly review. Export on the same day each week, refresh the pivot, and read the top three themes aloud. Then update statuses in Escuta Produto. The spreadsheet is a view, not the system of record, so changing a value in the sheet does not change an item's status.

For the meeting itself, follow the [weekly feedback review template](/resources/weekly-feedback-review-template). To get new items to the team between exports, set up [Slack and Discord notifications](/docs/notifications).

## Frequently asked questions

### How do you analyze customer feedback in Excel?

Export the product's feedback as CSV, then build a pivot table with type in the rows and status in the columns. Add a theme column by hand, then count items by theme and month to see where feedback clusters.

### Can Google Sheets open a feedback CSV export?

Yes. The export is UTF-8 CSV and opens correctly in Google Sheets and Excel. Use the import option so commas and line breaks inside messages land in the right cells.

### How do you count unique customers in a feedback spreadsheet?

Add a helper column that marks the first time each email appears, then add up the marks. Give anonymous rows a zero, because they have no email to match, and treat the total as a minimum.

---

# How to group customer feedback with an LLM

> Export feedback as CSV, remove personal details, then ask a language model to propose themes and assign each message to one. Check a sample by hand, count the results in a spreadsheet, and treat the output as a draft. Escuta Produto does no AI clustering itself.

Source: https://escutaproduto.com/resources/group-feedback-with-llm
Last updated: 2026-10-09

## What a language model can and cannot do

A language model can read hundreds of short messages and suggest themes faster than one person. It can notice that "the report is slow" and "the dashboard takes forever" describe one issue. It can also invent a theme no customer mentioned, merge unrelated messages or miscount.

Escuta Produto does not cluster feedback with AI, so this step runs outside the product, on an export you control. Treat the model as a first draft. Your reading of the messages decides the themes.

## Export and clean the file first

Start with the CSV export described in [how to analyze customer feedback in a spreadsheet](/resources/analyze-feedback-spreadsheet). Before you export, confirm which fields your app sends with each item, using the [widget docs](/docs/widget). Then, before you send anything to a model, remove what it does not need:

- Names and email addresses.
- Phone numbers, account numbers or other identifiers that customers typed into a message.
- Any metadata field that identifies a person.

Keep the message text, the type, the date and the page URL if you need to locate a problem later. Give each row a row number, and use that number to check the model's answers against the source.

## Write a clustering prompt

Give the model the rules, the output format and the input in one prompt. Be specific about the number of themes, the length of each name, and what to do with unclear messages. A workable starting point:

```text
You are reviewing customer feedback for one software product.
Each row has a row number, a type and a message.
Propose at most 8 themes. Name each theme in 2 to 4 words, for example "slow report loading".
Assign every row to exactly one theme, or to "unclear" if the message does not fit any theme.
Return CSV with two columns: row, theme.
Then list each theme with two exact quotes from the rows that support it.
Do not invent a theme that no message supports.
```

The quotes are the most useful part. They let you check each theme in a minute.

## Check the output against the source

Never trust a theme you have not checked. Run three checks:

1. Read a sample. Pick 30 rows at random, and compare each assigned theme with the message.
2. Verify the quotes. Search the export for each quoted sentence. A quote that does not appear in the file is a sign the model made it up.
3. Look at the unclear rows. If many land there, the theme list is too narrow.

Count the themes in your spreadsheet with a pivot table, not with the model's arithmetic. Counting belongs in the tool built for it. If the themes change a lot between runs on the same file, tighten the prompt or the theme limit before you act on any of them. Ask for a second run on a different sample of rows. Themes that appear in both runs with similar wording are more likely to be real, while a theme that appears once and then vanishes is usually noise. Drop it rather than act on it.

## Decide what the themes are worth

A theme is a description, not a decision. Before you act, connect each theme to the rest of your work. How many distinct customers mention it? Are the messages bugs or ideas? Does the theme match a problem you already understand? The method in [the problem behind a feature request](/resources/problem-behind-feature-request) is a good test: what is the customer trying to finish?

A small theme with a severe consequence still matters. One message about lost data can outweigh fifty mild complaints about wording, so read the severity of each theme as well as its size.

Keep the theme names in one shared list, and use them as labels in your internal notes so the team speaks the same language.

## Privacy and customer data

Customer messages can be personal data, especially when they include names, emails or details about a person's business. Before you send them to any outside service:

- Read the provider's data terms, including whether submitted data is used to train models.
- Use the tool your company has approved for customer data, if you have one.
- Remove identifiers, and leave out anything sensitive such as health details, payment information or account credentials.
- Mention in your privacy policy if you process customer feedback this way.

Rules differ by country. Read [feedback widgets, GDPR and LGPD](/resources/feedback-widget-gdpr-lgpd) for the basics, and ask a lawyer about your own situation. This is general guidance, not legal advice.

## Bring the themes back into Escuta Produto

Escuta Produto has no tags and no AI analysis, so the themes live in internal notes:

1. Filter the product inbox to type idea or bug, and open the items that belong to a theme you care about.
2. Write the theme name in the internal note of those items, for example "theme: slow reports".
3. Use text search for the theme's key words to catch items the model missed.
4. Review the theme list in your weekly meeting, and revisit it when the product changes. The [weekly feedback review template](/resources/weekly-feedback-review-template) has a slot for it.

For the labeling rules that keep themes stable over time, see [how to build a tagging system for customer feedback](/resources/feedback-tagging-taxonomy).

## Frequently asked questions

### Can you group customer feedback with a language model?

Yes, with checks. Export the feedback, remove personal details, ask the model for a short list of themes with one theme per message, then verify a sample by hand. Count the results in a spreadsheet rather than trusting the model's arithmetic.

### How do you verify AI-generated feedback themes?

Read a random sample of rows against their assigned theme, check that each quoted sentence exists in the export, and review the unclear rows. If the themes change a lot between runs, the theme list is too loose.

### Is it safe to send customer feedback to an AI service?

It depends on the service and on what the messages contain. Check the provider's data terms, remove names and emails, avoid sensitive details, and follow the privacy rules that apply to your customers. Get legal advice for your own situation.

---

# A weekly feedback review template

> A weekly feedback review is a 30-minute meeting with a fixed agenda: check the trend, read new bugs and ideas, set statuses and agree who replies. Copy the agenda, roles and checklist below and run the review on the same day each week.

Source: https://escutaproduto.com/resources/weekly-feedback-review-template
Last updated: 2026-10-09

## The 30-minute agenda

Run the same agenda every week. A predictable structure keeps the meeting short, and it makes this week's numbers easy to compare with last week's.

| Minutes | Step | Output |
| --- | --- | --- |
| 0 to 5 | Look at the 30-day chart and the average rating | One sentence on the trend |
| 5 to 15 | Read new bugs and decide each status | Bugs marked planned, in progress or closed |
| 15 to 22 | Read new ideas and search for repeats | Canonical request list updated |
| 22 to 26 | Read praise and note what to protect | Praise saved for copy and regression checks |
| 26 to 30 | Assign replies and close what needs no action | A named owner for every reply |

If you run short of time, protect the bug and reply steps. Those affect customers soonest, and ideas can wait a week.

## Roles for a small team

You can run this alone. A team should split the roles:

- **Reader**: opens the inbox, reads each item and summarizes it.
- **Decider**: sets the status and says yes, no or later.
- **Engineering contact**: estimates effort for bugs and planned items.
- **Replier**: owns the replies and sends them from their own email.

A solo founder covers all four roles. Write each decision in the internal note while you read, so the next review starts from a clean record. For a larger team, rotate the reader role so everyone reads the inbox and hears what customers are saying.

## Prepare before the meeting

Preparation takes about ten minutes and saves the meeting:

1. Open the inbox filtered to status new, or export the product's feedback as CSV.
2. Check the 30-day chart and the average rating for the week.
3. Note any release that shipped since the last review, because bugs often follow a release.
4. Bring last week's canonical request list, so duplicates are caught early.

Keep this preparation list in the same place each week, such as a pinned document or the first page of your sheet. A checklist that always sits in the same spot is far more likely to get done in a busy week.

## What the meeting must produce

Every meeting ends with four outputs. If one is missing, the meeting did not finish:

- A status set on every item you read.
- A one-sentence trend note for the week.
- An owner and a date for each reply that matters.
- A short list of requests you decided against, each with a reason written down. The wording for saying no is in [how to say no to a feature request](/resources/say-no-to-feature-requests).

## Copyable checklist

Paste this into a document and tick the boxes during the meeting:

- [ ] Open the inbox filtered to status new
- [ ] Read every bug and set its severity and status
- [ ] Search the inbox for repeats of each new idea
- [ ] Update the canonical request list and the distinct customer counts
- [ ] Save praise you may use, after asking permission
- [ ] Close items that need no action, with an internal note
- [ ] Check the 30-day chart and the average rating
- [ ] Assign an owner and a date to every reply

## Adjust the review for several products

Run one review per product, or one review that rotates through products. With several products, keep the agenda the same and rotate the order each week, so the product at the bottom of the list still gets attention. Keep the time box: two products in 30 minutes means about 15 minutes each, and bugs still come first in each block. Note how long each block really takes for two weeks. If bugs regularly run over, shorten the ideas block to one pass and move deeper discussion to a separate session.

If you run a larger portfolio, the routine is described in [a feedback routine for running five products](/resources/feedback-routine-multiple-products).

## Run the review in Escuta Produto

1. Filter the product inbox to status new, and read the items in the list.
2. Use the type filter to separate bugs from ideas, and work the bugs first.
3. Set each item's status as you decide. The statuses are new, planned, in progress, done and closed.
4. Write the decision in the internal note, so the next review starts from it.
5. Keep Slack or Discord alerts on for awareness, using the [notifications docs](/docs/notifications). The meeting is where decisions happen, not the alert.

When a planned item ships, the next step is to tell the people who asked. The steps are in [how to tell customers their request shipped](/resources/tell-customers-request-shipped), and the status meanings are in [how to design a feedback status workflow](/resources/feedback-status-workflow).

## Frequently asked questions

### How long should a weekly feedback review take?

About 30 minutes with a fixed agenda. Start with the trend, then bugs, then ideas, praise and replies. If time runs short, protect the bug and reply steps, since those affect customers soonest.

### Who should attend a weekly feedback review?

Anyone who can change priorities or reply to customers. A solo founder can run it alone. A team usually needs a reader, a decider, an engineering contact and someone who owns the replies.

### What should a feedback review produce?

A status for every item read, a one-sentence trend note, an owner and a date for each reply that matters, and recorded decisions about requests you will not build. Without those outputs, the same items come back next week.

---

# How to reply to customer feedback (with templates)

> Reply to every feedback item that has an email address, ideally within one working day. Thank the sender, say what you did with the message, ask at most one question and sign with your name. Send from your own email, then log the reply as an internal note.

Source: https://escutaproduto.com/resources/reply-to-customer-feedback
Last updated: 2026-10-09

## Reply within a working day, even when the answer is no

Customers judge a product by how quickly someone reads what they wrote. A short reply that says "got it, we are looking at this" beats a perfect answer three weeks later. Reply to every item that has a sender email. If an item has no email, there is nobody to reply to, so record your decision in the internal note and move on.

A reply does three jobs. It shows the message was read, it says what you did with it, and it asks for the one detail you need. Anything beyond that is optional.

## Templates for a bug report

Lead with what you did, then ask for what you need. Do not promise a release date unless you control it.

> Hi Ana, thanks for the report about the export button doing nothing on Safari. I can reproduce it on the current version, so it is now with the team. I will write again when the fix is live. If you remember the last thing you clicked before it failed, that helps us confirm the fix.

If you cannot reproduce it yet, say so and keep the door open. Section "When you can't reproduce it" in the follow-up guide covers the details: [how to follow up on a bug report](/resources/follow-up-on-bug-reports).

## Templates for a feature idea

An idea is not a request for a reply on the spot. Acknowledge the idea, show you understood the problem behind it, and tell the customer where it stands.

> Hi Ana, thanks for suggesting a monthly CSV export. We have added your idea to our list. Before we decide, I want to understand what you would do with the file. Is it for a monthly report, or for something else? Your answer helps us choose the right shape for it.

If you have already decided, use the same structure with a clear answer. For a no, see [how to say no to a feature request](/resources/say-no-to-feature-requests).

## Templates for praise

Praise is easy to answer badly. Skip the corporate thank you and name the thing they liked.

> Thank you, Ana. Your note about the onboarding checklist made our week. We will keep building on that part. If you are comfortable with it, may we quote one line on our site? We will use your first name only if you say so.

Asking to quote someone is a separate step. Keep it out of the first reply if you are not sure they want it. The full process is in [how to turn customer praise into testimonials](/resources/turn-praise-into-testimonials).

## Templates for an unclear message

Some messages say "it's broken" and nothing else. Do not guess and do not ask five questions. Ask one question that narrows the problem.

> Hi Ana, thanks for writing in. I want to be sure I understand. When you say the page feels slow, is it the first load, or the wait after each click? Tell me which page you were on and I will take it from there.

One question works better than a list. People answer one question. A list of four gets one answer, if any.

## Keep the tone plain and specific

Most reply problems come from tone, not content. A few habits fix most of them:

- **Use the customer's words.** If they wrote "the list jumps around", write "the list jumps around" back, not "we are aware of a layout issue".
- **Say what you did.** "I checked the page URL you sent and reproduced it" is more useful than "thanks for your patience".
- **Skip stock phrases.** Phrases like "we value your input" tell the customer nothing new.
- **Sign with your first name.** The reply comes from a person, so write it like one.
- **Keep it short.** Four to six sentences cover most replies.

## Timing and follow-up

Send the first reply quickly, even if it only says you are looking. Then send a second message when something changes: a fix, a decision, or a question answered. If a customer does not answer your one question, do not chase them more than once. Note the item as waiting and move on.

## Where the reply goes

Escuta Produto does not send emails to customers. Every reply goes from your own email address to the address saved on the feedback item. That address comes from the email field on the form, or from the email passed through the identify call of the widget. The [widget documentation](/docs/widget) explains how identified users skip the email field. Use your normal mail client so replies land in the thread you already use with the customer.

## When a customer replies with more anger

A reply sometimes makes things worse. If the customer answers with more anger, do not match it. Acknowledge the frustration in one sentence, answer the question they actually asked, and offer one concrete next step. Avoid repeating the apology in every message, because repeated apologies sound scripted. If the thread turns into a long argument, move it to a short call, then write the outcome back in the same thread so the record stays complete.

## Replying from Escuta Produto

The inbox gives you the context you need before you write. Open an item and read the message, the page URL and the browser saved with it, and any metadata such as plan or app version. Copy the sender email, write the reply in your mail client, then set the status. Add an internal note with what you sent and when. The note is the record that stops two people from answering the same message twice, and it helps the next person who opens the item.

For the broader weekly routine that decides which items get a reply first, read [how to triage customer feedback](/resources/how-to-triage-customer-feedback).

## Frequently asked questions

### How fast should you reply to customer feedback?

Aim for a first reply within one working day, even if it only says you have read the message and are looking into it. A quick acknowledgement matters more than a complete answer.

### Should you reply to every piece of feedback?

Reply to every item that has a sender email address. For items without an email, record your decision in an internal note so the team knows the item was read and handled.

### Who sends the reply when you use Escuta Produto?

You do. Escuta Produto does not send emails to customers. You write the reply in your own email client, sent to the address saved on the feedback item, then log what you sent as an internal note.

### What should a reply to a feature idea say?

Thank the customer, say the idea is on your list, and ask one question about the problem behind it. Avoid promising a date. If you have already decided, give a clear yes or no with the reason.

---

# How to say no to a feature request

> Say no with the real reason in plain words, and offer a workaround only if you would support it. Record the decision on the feedback item so nobody re-decides it next month, and tell the customer the idea was considered and where it stands.

Source: https://escutaproduto.com/resources/say-no-to-feature-requests
Last updated: 2026-10-09

## Say no with the real reason

A no works when the customer can see why. "We are not building that right now" sounds like a brush-off. "We are not building a bulk delete yet because deleting feedback removes the trail our team uses to spot repeats" sounds like a decision someone made on purpose.

Start with the reason, then the decision. Most people accept a no they understand, even when they wanted the yes. What they do not accept is a vague answer that leaves them guessing whether anyone read the request.

## Why a clear no is kinder than a vague maybe

A vague maybe feels polite in the moment. It costs more later. The customer waits, checks back, asks again and eventually concludes that you ignore them. A clear no closes the loop in one message. The customer knows where they stand and can plan around it.

This does not mean every no has to be blunt. Warm wording helps. Clear wording matters more.

## Offer a workaround only if you would support it

A workaround is useful when it solves most of the problem and you are willing to explain it again next month. Do not offer a hack you would hate to support. If the customer follows a workaround and it breaks, the no becomes a second disappointment.

Ask yourself one question before you offer one: would I be comfortable if this workaround became the official answer for everyone who asks? If the answer is no, skip it and say what you can do instead, even if that is just the reason.

## Write the reply in four parts

Keep a no to four short parts. You can use this as a template:

> Hi Ana, thanks for asking about a bulk delete for old feedback. We are not going to build it now. Deleting items would remove the history our team uses to spot repeats, and that matters more to most customers than a clean list. If you need to hide old items, closing them with a note keeps them out of the way while the record stays. We have noted your request, and if the situation changes for us, I will tell you.

The four parts are: the thanks, the decision, the reason, and what the customer can do now. The last sentence is optional but useful, because it tells the customer their request had weight.

## Record the decision where the team can find it

A decision that lives only in your head comes back. Someone reads the same request next month, has no context and starts the debate again. Record the decision on the item with a note: what you decided, the date and the reason. If the request came from several people, note that too.

Then set the status. Use closed for a decision not to act. Use planned if you said yes. The status and the note together answer the question "what happened to this?" for anyone on the team.

## When no should become not yet

Some requests are not wrong, just early. A no for now can be a not yet, but only if you can say what would change your mind. Write that condition in the note: "revisit when more than one team uses the API for exports." Then, when the condition is met, you have a reason to reopen the item instead of a vague hope.

Review these notes during your weekly triage. A not yet with no condition is really a no, and it should be closed as one.

## Keeping the customer after a no

Customers who hear no often stay, if the no was honest and they felt heard. Two things help. First, acknowledge the cost to them, not only the reason for you. "I know this would save you time" tells them you understood their situation. Second, thank them for the specific idea, not for "the feedback". Specific thanks shows you read the request.

If the customer is angry, do not argue the decision in the same thread. Reply once with the facts and let the answer stand. Repeated defenses turn a closed item into a fight.

## Handle the customer who will not accept no

Some customers push back, and a few push hard. Listen once more, then check whether they gave you new information. A new fact, such as a second team that needs the feature, is a reason to look again. A repeat of the same argument is not. Say that kindly: "I understand this matters for your team. I have not changed my decision, and I do not want to keep you waiting for a yes that will not come." Then stop replying to the same point. A calm, final answer helps more than a longer debate, and it protects the time your team needs for planned work.

## Saying no in Escuta Produto

Open the item in the product inbox. Read the full message and the metadata, so you know the customer's plan and context before you decide. If the item came from several people, search for the same words to find the others. Escuta Produto has no merge feature, so use search and a short note that lists the other items.

Write the reply from your own email, to the address saved on the item. Then add an internal note with the decision, the reason and the date. Set the status to closed or planned. The [widget docs](/docs/widget) explain how the email reaches the item in the first place.

For the rest of the weekly routine, including how to group repeats before deciding, see [how to triage customer feedback](/resources/how-to-triage-customer-feedback). For the wording of the reply itself, [how to reply to customer feedback](/resources/reply-to-customer-feedback) has more templates.

## Frequently asked questions

### How do you say no to a feature request without upsetting the customer?

Give the real reason in plain words, say what you decided, and offer a workaround only if you would support it. Thank the customer for the specific idea. Most people accept an honest no they understand.

### Should you offer a workaround when you decline a request?

Offer one only when you would be comfortable making it the official answer for everyone who asks. A workaround that breaks later turns a no into a second disappointment, so skip hacks you would hate to support.

### How do you record a feature request decision?

Add an internal note on the feedback item with the decision, the date and the reason. Then set the status to closed for a no or planned for a yes, so the next person who reads the item sees the outcome.

### What is the difference between a no and a not yet?

A not yet names the condition that would change your mind, such as more customers needing the feature. Without a condition, a not yet is really a no, and it should be closed as one during triage.

---

# How to write a changelog customers read

> Write each changelog entry for the customer who was affected, in their words, and group entries by what changed for them. Credit the requests behind a change, link to the feedback it answers when you can, and publish on a steady rhythm on your own site or in email.

Source: https://escutaproduto.com/resources/write-a-changelog
Last updated: 2026-10-09

## Write for the person who was affected

A changelog is for customers, not for the team that built the change. Most readers want to know one thing: does this change affect me, and what do I do now? Start each entry with the situation the reader is in, then the change.

Compare two entries. "Refactored the export pipeline to stream rows" is true and useless to a customer. "Exports of more than 10,000 rows now finish instead of timing out" tells a customer whether to try again. Write the second kind.

## Group entries by what changed for users

Group by the user's view of the product, not by the code area. Common groups are "New", "Improved", "Fixed" and "Changed". Some teams add "Removed" when a feature goes away, because that is the group readers scan for when something breaks.

Keep each group short. Three to five entries per release is enough for most small products. If you have twenty, most readers will skip the list, and the one change they needed is lost.

## Credit the requests behind each change

A changelog entry that says "thanks to everyone who asked for a monthly export" tells customers their feedback had an effect. That is one of the few ways to show them the loop works. Credit the request, not the person, unless the person agreed to be named.

Keep credit honest. If one customer asked and the change came from your own analysis, say that or leave credit out. Invented credit gets noticed quickly by the people it names.

## Link each entry to the feedback it answers

Linking a change to the feedback behind it closes the loop in public. You do not need to publish the feedback itself. A line like "This came from requests about slow exports on large accounts" is enough. Use the internal notes in your inbox to find the original items, and search the text when you do not remember the wording.

Keep a short list of the feedback items each release answers. The list is also your checklist for who to tell, covered in [how to tell customers their request shipped](/resources/tell-customers-request-shipped).

## A changelog entry template

Use the same shape every time so readers learn to scan it. This example is for an imaginary export feature in your own product:

> **Exports now finish for large accounts.** If you exported more than 10,000 rows, the file used to time out. Exports now finish on the first try. Thanks to everyone who reported the timeouts.

The bold line says what changed. The next sentence tells the reader what it means for them. The last sentence credits the feedback, and it is optional when no one asked for the change.

## How often to publish

Publish on a rhythm your team can keep. Every two weeks works for many small teams. A monthly post is fine when releases are slow. Publishing once a year is a changelog nobody reads, because by the time it appears, the customer has forgotten the bug.

If nothing shipped, skip the post. An empty changelog entry trains readers to ignore the next one.

## Where the changelog lives

Escuta Produto has no built-in changelog. The changelog lives on your own site, in a docs page, or in a monthly email you send from your own address. What Escuta Produto gives you is the input: every item with its status, its notes and its sender, so you know which entries to write and whom to credit.

Choose one place and link to it from your product. A reader who cannot find the changelog will assume you have not shipped anything.

## Put the change that matters most first

Most readers see only the top of the page. Put the change that affects the most people first, and mark anything that needs action from the reader. For example, if an older endpoint will stop working on a set date, put that notice above the new features and say what to change. A quiet breaking change buried in the middle of a list is the one that generates support requests.

Avoid long introductions. A single sentence that says what this release is about is enough. Readers who want more will scroll.

Date every release. Readers use the date to judge whether a fix is recent, and an undated list looks like it stopped years ago. Write the entries in the same tense each time and keep the groups in the same order. Readers learn the layout after two or three releases, and a layout that changes every time makes the list harder to scan.

## Keeping statuses and the changelog in step

The easiest changelog to write comes from the status workflow. When an item moves to done, it is a candidate for the next entry. Items in progress are not, because nothing has shipped yet. Items closed as a decision not to act stay out.

This makes the status a real signal. Set done only when the change is live for users, not when the code is merged. For the rest of the status workflow, see [how to design a feedback status workflow](/resources/feedback-status-workflow).

If you publish the changelog on a page you control, the [hosted feedback page](/docs/hosted-page) is a good place to point readers who want to suggest the next change. Link them to the place they can ask, and to the changelog they can read.

## Frequently asked questions

### What should a changelog entry say?

Say what changed for the customer, in their words, and what it means for them. Start with the situation they were in, such as a timeout or a missing option, then the change. Skip internal code details.

### How often should you publish a changelog?

Pick a rhythm your team can keep, such as every two weeks or monthly. Skip the post when nothing shipped, because empty entries teach readers to ignore the next one.

### Should you credit customers who requested a change?

Credit the request rather than the person, unless the person agreed to be named. Credit only what really caused the change, because invented credit is easy for the named customers to notice.

### Does Escuta Produto include a changelog?

No. Escuta Produto collects and organizes feedback, and you publish the changelog on your own site, docs or email. The inbox shows which items are done, so you know what to write about.

---

# How to tell customers their request shipped

> Find every customer who asked by searching the inbox and reading the internal notes, then send a short message from your own email when the change is live. Keep it to three lines: what shipped, where to find it and thanks for the request. Record who you told.

Source: https://escutaproduto.com/resources/tell-customers-request-shipped
Last updated: 2026-10-09

## Find everyone who asked for it

The same request rarely arrives once. One customer writes it in the widget, another emails support, a third asks in a call that someone types up later. If you only tell the person who sent the most recent message, you miss most of the people who wanted the change.

Start by listing every item that matches. Work through the inbox filtered to the product, search for the key words, and read each result. A request for "a monthly export" may appear as "month by month", "per month report" or "a file for accounting". Read the message, not just the title you would have used.

## Use search, then the internal notes

Search covers the text of each message. It does not cover tags, because Escuta Produto has no tags. Use the internal notes for the rest. When you triage a request, write a short note with the feature name, such as "monthly export", so a later search finds it without guessing the wording.

If you passed metadata when the feedback came in, use it too. A value such as a feature name in the metadata can narrow the list fast. Metadata lives with the item, so it is also what you read when you decide who to tell.

## Export the list when it grows

When a request has twenty or more items, a spreadsheet is faster than the inbox. Export the product's feedback as CSV. The file is UTF-8 and opens in Excel and Google Sheets. Check the column headers first, then filter the message text for your key words and keep only the rows that have an email address. You need the sender email and the message for each reply.

Keep the exported list as a working file. Mark who you told as you go. Delete the file when the work is done, because it contains customer email addresses.

## Write the shipped message in three lines

The best shipped message is short. Use three parts: what shipped, where to find it, and thanks for the request. Here is a template you can adapt:

> Hi Ana, the monthly CSV export you asked about is live now. You can find it in the export menu of the product, and it covers any month you choose. Thanks for the request, it is the reason this shipped.

Do not oversell. If the feature does a bit less than the request, say so: "It covers monthly totals, not daily rows, so tell me if you need more." Customers forgive a smaller change that works. They do not forgive one that was described as more than it is.

## Send it from your own email

Escuta Produto does not send emails to customers, so the message goes out from your own address. Write it in your mail client and send it to the email saved on the feedback item. Use a plain message, not a newsletter layout. A personal note gets read, and it invites a reply if the feature is not quite right.

If several customers asked, send each message on its own, not as one message with everyone in copy. Copying a list exposes their addresses to each other, and it reads like a broadcast.

## Choose the timing

Send the message after the change is live for users, not when the code is merged. If you have a changelog post for the release, send the message the same day it goes up, so the reader can click through to the details. For a small change, a same-day message works best. For a larger release, wait until the first users have the change, which usually means a day or two.

Do not send a message before the change is available. The first customer who tries it and finds nothing is more annoyed than one who never heard about it.

## Record who you told

The record is the point. Add an internal note on the item: "Told by email on the release date." Set the status to done once the change is live. The note stops a second person from sending the same message, and it tells the next triage what has already been closed.

For a long list, keep the count in the note or in your spreadsheet, not in your memory. A list of forty names, half sent, is easy to lose.

## Handle the customer who asked for something else

Some customers read the shipped message and reply that the change is not quite what they wanted. Treat that as new feedback. Thank them, ask what was missing, and log the reply as a new item or as a note on the existing one. Do not argue that the feature matches the request. If the gap is small, plan a follow-up. If it is large, say honestly that it solves a different problem and explain what you will do next.

## Finding and notifying requesters in Escuta Produto

Start in the product inbox and filter to the status that holds the request, usually planned or in progress. Search the key words, then open each match and read it. Use the notes to find the items you already grouped. Export the rest as CSV if the list is long.

When you send each message, use the email on the item. Record the send in an internal note. Then set the item to done. For the rules behind the statuses, see [how to design a feedback status workflow](/resources/feedback-status-workflow). For the changelog entry that goes with the release, see [how to write a changelog customers read](/resources/write-a-changelog).

If someone asked through a form that did not collect an email, there is nothing to send to. Do not guess an address from the name. Record that the request is done, and mention it in your changelog so the next person who asks sees the answer. New requests reach your team's Slack or Discord channel as they arrive, as described in the [notifications docs](/docs/notifications), so you can spot a request before it goes stale.

## Frequently asked questions

### When should you tell customers their request shipped?

Send the message after the change is live for users, not when the code is merged. Send it the same day as the changelog entry if you have one, so the reader can find the details right away.

### How do you find everyone who asked for a feature?

Search the inbox with several phrasings of the request, read the matches, and use internal notes to group them. For long lists, export the feedback as CSV and filter the message text in a spreadsheet.

### Should you send one email to all the customers who asked?

Send each person an individual message. A single message with everyone in copy exposes their addresses to each other and reads like a broadcast, while a personal note invites a reply.

### What if the feature does less than the customer asked for?

Say so plainly in the message. Describe what the change covers and what it does not, then invite a reply if they need more. Customers accept a smaller change that works far better than an overstated one.

---

# Should you have a public roadmap?

> A public roadmap helps when customers plan around your releases, but every item on it becomes a promise. Many small teams do better with a private inbox, a changelog and direct replies. Escuta Produto has no public roadmap or voting board, so you share progress through your own channels.

Source: https://escutaproduto.com/resources/public-roadmap-pros-and-cons
Last updated: 2026-10-09

## What a public roadmap does well

A public roadmap shows customers where the product is going. For some products that matters a lot. A team selling to other companies may need to plan its own rollout around a release. A developer tool with many integrations may need to know when a platform change is coming. In those cases, a roadmap is information the customer can act on.

It also signals that the team is active. A visible list of planned work can reassure people who worry that a small product will stall.

## What it costs you

Every item on a public roadmap is a promise, whether you intended it or not. Readers hear "planned" as "will ship". When the date slips or the scope changes, the customer remembers the roadmap, not your reasons.

A roadmap also invites debate in public. Items that look wrong to a reader get comments, and a comment thread can take more time than the feature. Someone has to moderate it, answer it and keep the tone calm when the answer is no.

Finally, a roadmap shows what you have not built. For a small product, that list can look long to a new visitor, even when the team is working hard.

## The promises you start making

Once a roadmap is public, the team has to decide what counts as a commitment. Is a "next" item a promise for next quarter, or just a direction? Customers will assume the stronger meaning. If you do not define the words, your reply to a delayed item will sound like a broken promise even when it was never one.

If you do publish a roadmap, write a short note on what each column means. Say that dates are estimates and that scope can change. Most readers accept that. They dislike discovering the rule only after something moves.

## Alternatives that keep the loop closed

Most of the benefit of a public roadmap comes from telling customers what happened to their request. You can do that without a board:

- **A changelog.** Each release says what changed and credits the requests behind it. Readers see progress without a list of plans. See [how to write a changelog customers read](/resources/write-a-changelog).
- **Direct replies.** When a request moves to planned or done, tell the people who asked. This feels personal, and it works well with a small customer base.
- **A status on each item.** Keep your own statuses clear and close items with a reason, so the team knows what is being worked on.
- **Shared updates in your newsletter or product emails.** Use the channel your customers already read.

These options give you the signal without the list. They also let you say "not yet" to one customer without a public comment thread around it. Pick one or two of these and do them well before you add a third channel.

## What happens when a roadmap goes quiet

A public roadmap that stops changing reads as abandoned. Customers check it, see the same items for six months, and assume the product has stalled, even when the team is busy. If you publish one, agree on an update rhythm before launch and keep to it. If you cannot keep that rhythm, remove the page or mark it as archived with the date it was last reviewed. An honest archive is better than a stale list.

Moving an item from now to later in public also draws questions. Prepare one sentence for that case before it happens. Something like: "We are moving this to later so we can finish the export work first. The request is still on the list." A short, plain sentence keeps the conversation from turning into a debate about your priorities.

## Escuta Produto has no public roadmap

Escuta Produto does not have a public roadmap, public voting boards or a public feedback page where customers vote on ideas. The product is a private inbox for the team. Feedback comes in through the widget, the hosted page or the REST API, and the team sees it in one place with five statuses: new, planned, in progress, done and closed.

If you need a public voting board or a roadmap page that customers browse, that is not what Escuta Produto is for. It works well when you want feedback kept private and tied to the product, and when you tell customers what happened through your own channels.

## How to decide for your product

Ask four questions before you publish anything:

1. **Do customers plan around our releases?** If not, a public list adds little.
2. **Can we keep dates honest?** If the team cannot keep the list current, do not publish it.
3. **Who will answer comments?** Name a person who has time for it every week.
4. **What do we do when we say no?** A roadmap needs a clear way to show things that were dropped.

If you answer no to the first question, start with a changelog and direct replies. Revisit the roadmap when customers ask for it repeatedly. Many small products find that asking the question is enough signal to act on.

For the internal side of the decision, the weekly routine in [how to turn customer feedback into a roadmap](/resources/turn-feedback-into-roadmap) keeps your private plan separate from anything you publish. Alerts in Slack or Discord help the team see new requests as they arrive, which is set up in the [notifications docs](/docs/notifications).

## Frequently asked questions

### Is a public roadmap good for a small software product?

Often not. A public roadmap helps when customers plan around your releases. For small teams, it adds promises and comment threads. A changelog and direct replies usually tell customers what they need to know.

### What are the main risks of a public roadmap?

Each item reads as a promise, so delays and scope changes damage trust. It also invites public debate that someone must moderate, and it shows the work you have not done, which can look like a long list to new visitors.

### Does Escuta Produto have a public roadmap?

No. Escuta Produto has no public roadmap, voting board or public feedback page. It is a private inbox for your team, and you share progress through your own changelog, emails and direct replies.

### What can you use instead of a public roadmap?

A changelog that credits requests, direct replies when a request moves to planned or done, and clear statuses that your team keeps current. These let customers see progress without a list of promises.

---

# How to follow up on a bug report

> Follow up on a bug report in four steps: acknowledge it within a working day, ask for the one detail you are missing, tell the customer what you are doing, then report the fix and ask them to confirm. Use the page URL and browser already saved with the report.

Source: https://escutaproduto.com/resources/follow-up-on-bug-reports
Last updated: 2026-10-09

## Acknowledge it within a working day

A bug report is a small emergency for the person who wrote it. They lost time, maybe a sale, and they are not sure anyone noticed. A quick acknowledgement tells them the report arrived and someone is looking at it.

Keep the first reply short. Say you read it, say what you will check next, and say when you will write again. "I am trying to reproduce this today and will write again tomorrow" is enough. Do not promise a fix at this stage. You do not know the cause yet.

## Ask for the one detail you are missing

Most bug reports arrive with less than you need. Before you ask the customer for anything, check what is already there. The widget saves the page URL and the browser's user agent automatically, so you may already know the page and the browser. Read the metadata too, because plan and app version often narrow the problem.

If something is still missing, ask one question. "Which browser are you using?" is answered by the user agent in most cases, so ask for what the report does not show. A typical gap is the step before the failure.

> Hi Ana, thanks for the report about the export failing on the reports page. I can see you were on Safari, which helps. One thing I still need: what did you click just before the error appeared? That will let me reproduce it.

## Use the context you already have

Context turns a vague report into a checkable one. Use these in order:

1. **The page URL.** It tells you which screen and which route.
2. **The browser.** It narrows the problem to one engine, or rules it out.
3. **The metadata.** A plan, an app version or a team name can point to a single release or account.
4. **The message.** Read it once for the symptom, then again for any clue about timing.

With those four, a report usually becomes something you can check. Without them, the follow-up turns into a long string of questions the customer may stop answering.

## Say what you are doing, not what you hope

Tell the customer your next step. "I am checking whether the change in the last release affects the reports page" is specific and honest. "We are fixing this soon" is a promise you may not keep.

If the investigation stalls, write again anyway. A short "still looking, nothing new yet, I will update you Thursday" keeps the thread alive. Silence is what makes customers leave.

## Report the fix and ask for a retest

When the fix is live, write to the customer who reported it. Say what was wrong in plain words, what you changed, and how they can check it. Then ask them to confirm.

> Hi Ana, the export on the reports page is fixed and live now. The problem happened when a report had no date range, and that case now works. Could you try the same export once more and tell me if it works for you? Thanks for the detail about Safari, it pointed us to the right place.

Keep the message to what the customer needs. Do not describe every line of code. A bug report answer that reads like a changelog wastes their time.

## When you cannot reproduce it

Sometimes you cannot make the bug happen. Do not close the report with a shrug. Write back, say what you tried, and ask for one more detail. Name the browser version, the account type or the step before the error, and ask whether the problem still happens. Then decide.

If you still cannot reproduce it, say so and set the item to closed with an internal note listing what you tried. Keep the door open: tell the customer to write again if it returns, with the page URL and the time it happened. A reopened report with a timestamp is much easier to diagnose.

## Report a bug that affects many customers

When one problem hits many people, do not write a long reply to each one. Send a short update to everyone who reported it, from your own email: what is broken, who is affected, what you are doing and when you will write again. Keep the same update for each person so the team answers consistently. When the fix is live, follow up with each reporter, because a shared update does not tell them the fix worked for their case.

## Following up on a bug in Escuta Produto

Open the item in the product inbox and read the message, the page URL, the browser and the metadata before you write anything. Set the status to in progress while you work on it, so the rest of the team knows someone has it.

Send the follow-ups from your own email, to the address saved on the item. Record each message as an internal note. When the fix is live and the customer has confirmed it, set the status to done. If the fix is for a whole group of reports, search the inbox for the same words to find the others, because Escuta Produto has no merge feature, and you will want to follow up with each of them.

For the broader weekly routine that puts bugs first, see [how to triage customer feedback](/resources/how-to-triage-customer-feedback). For the reply wording, [how to reply to customer feedback](/resources/reply-to-customer-feedback) has templates for each kind of message. If the same bug keeps arriving without details, check the [widget docs](/docs/widget) for how the form is set up on your site.

## Frequently asked questions

### How fast should you respond to a bug report?

Acknowledge it within a working day, even if you have not found the cause. Say what you will check next and when you will write again. A prompt acknowledgement matters more to the customer than an early guess.

### What details should you ask for in a bug report follow up?

Check what is already saved first, such as the page URL, browser and metadata. Then ask for one missing detail, usually the step the customer took just before the error. Several questions at once often get no answer.

### How do you tell a customer a bug is fixed?

Say what was wrong in plain words, what you changed and how they can check it. Ask them to retry and confirm. Keep the message short and avoid technical detail they do not need.

### What do you do if you cannot reproduce a reported bug?

Write back, say what you tried and ask for one more detail. If it still cannot be reproduced, close the item with an internal note listing your attempts, and ask the customer to write again with a timestamp if it returns.

---

# How to turn customer praise into testimonials

> Sort praise into three piles: thanks, specific comments and quotable lines. Ask the sender for permission before you publish, and use their exact words with light edits only. Keep a praise log in internal notes so a useful quote is still there when you need it.

Source: https://escutaproduto.com/resources/turn-praise-into-testimonials
Last updated: 2026-10-09

## Sort praise from testimonial material

Not all praise is quotable. "Great product, thanks!" is kind, but it tells a stranger nothing. "The checklist during setup showed me which step I had skipped, and I stopped guessing" is a testimonial waiting to be used. Read each praise item and sort it into one of three piles.

- **Thanks.** Warm, general, short. Reply to it and move on.
- **Specific.** Names a feature, a moment or a result in the customer's own words. Worth a follow-up.
- **Quotable.** Specific and clear enough to stand alone on a page without context.

Most praise lands in the first pile. The second and third piles are where the testimonial work starts, and they are usually smaller than you expect.

## Ask permission before you publish a word

Praise written to you is not a testimonial until the person agrees to share it. Ask first, in a short message from your own email. Name the line you want to use, say where it would appear and offer to show them the final wording.

> Hi Ana, your note about the setup checklist made my week, thank you. Would you be comfortable with us quoting the line about skipping a step? We would use it on our pricing page with your first name and the name of your company, and I can send you the final text before it goes live.

Some people prefer to stay anonymous. Respect that. A quote with a role and a company, such as "product manager at a logistics company", can work just as well without a name.

## Edit lightly and keep the sender's voice

Light edits are fine: fix a typo, cut a filler word, or shorten a long sentence. Do not change what the person meant. Do not turn "it saved me a morning each week" into "it transformed how our whole company works". The first is a real claim in a real voice. The second is marketing copy the customer did not write.

If you cut a quote, mark the cut with an ellipsis, and make sure the shortened version still says what they said. Send the edited version back for approval when the cut changes the meaning.

## Pick the places where praise helps

Testimonials work best near the decision they inform. Common places are:

1. **Near the top of a sign-up or setup page**, where a visitor wonders whether the product is worth the time.
2. **In a product update email**, next to the change the customer asked for.
3. **On a page about one feature**, where the quote describes that feature.
4. **In a sales call**, when a prospect asks how the product works in practice.

Do not put a quote on a page where it does not match the claim around it. A quote about support speed next to a claim about pricing confuses people and hurts trust.

## Keep a praise log in internal notes

Good praise disappears. The message sits in the inbox until someone archives it, and the next time you need a quote, you cannot find it. Keep a small log. Add an internal note to each quotable item with the exact line, the date and whether permission was given.

Use the note to record the status of the request too: "asked on the first, permission pending" or "approved for the onboarding page". That single note stops two people from asking the same customer twice.

The inbox search finds items by their message text, so a short note with the feature name helps you find the praise later. You can also export the feedback as CSV and keep the quotable rows in a spreadsheet.

## Handle praise you cannot use

Some good praise cannot become a testimonial. The customer may be a competitor, may work for a client who forbids quotes, or may simply be unhappy later. Do not force it. Thank them, file the note as thanks, and move on.

Also watch for praise that hides a bug. "Love the product, the export is a bit slow" is praise and a report at once. Handle the report separately, following the steps in [how to follow up on a bug report](/resources/follow-up-on-bug-reports).

## Track the permission, not just the quote

Permission can change. A customer who agreed to a quote in March may not want it on a new page in October, and a company may be acquired or change its name. Keep the date of the permission in the note next to the quote. Before you reuse an old quote in a new place, check whether the terms still hold. If you are unsure, ask again. Asking twice costs one short message, while a quote used without consent can cost you the relationship.

## Pulling praise together in Escuta Produto

Use the inbox filtered by type set to praise. The type buttons in the widget are bug, idea, praise and other, so the praise filter shows the items customers chose as praise. Read each item, sort it into the three piles, and write a note for each quotable line.

Set nothing public from the inbox. The Escuta Produto dashboard is for the team. Publishing the quote is your decision, made on your own site or in your own materials, after the customer agrees.

For the wording of the request itself, the templates in [how to reply to customer feedback](/resources/reply-to-customer-feedback) cover praise and the follow-up. For how praise fits the weekly review, see [how to triage customer feedback](/resources/how-to-triage-customer-feedback). Praise sent from your app through the [REST API](/docs/api) lands in the same inbox as widget feedback, so the same steps apply.

## Frequently asked questions

### Can you use customer praise as a testimonial without asking?

No. Ask the person first, from your own email. Name the line you want to use, say where it would appear and offer to show the final wording. Publish only after they agree.

### What makes praise usable as a testimonial?

Specific detail in the customer's own words. A comment that names a feature, a moment or a result can stand alone on a page. General thanks is kind, but it works as a reply rather than a quote.

### How much can you edit a customer quote?

Edit lightly. Fix typos, trim filler and shorten long sentences without changing the meaning. Send the edited version for approval if a cut changes what they said.

### How do you keep track of testimonials you have collected?

Add an internal note to each quotable feedback item with the exact line, the date and the permission status. Export the feedback as CSV if you want the quotable rows in a spreadsheet for the whole team.

---

# How to design a feedback status workflow

> A feedback status workflow uses a few statuses, each with one clear promise: new means unread, planned means decided, in progress means someone is working on it, done means shipped and told, and closed means decided not to act. Define who moves items and when, then check stale items every week.

Source: https://escutaproduto.com/resources/feedback-status-workflow
Last updated: 2026-10-09

## Five statuses and the promise each one makes

A status is a promise about what the team is doing with an item. Keep the set small so everyone uses it the same way. Escuta Produto uses five: new, planned, in progress, done and closed. Each one makes a different promise.

| Status | The promise it makes | Who should see it as true |
| --- | --- | --- |
| New | Nobody has decided yet. | The team, during the weekly review. |
| Planned | We have decided to act, but no one is working on it yet. | The team, and anyone you have told. |
| In progress | Someone is working on it now. | The team, and the customers who asked. |
| Done | It shipped for users, and the people who asked were told. | Everyone. |
| Closed | We read it and decided not to act, or it was a duplicate or spam. | Everyone, with a note explaining why. |

The most common mistake is using a status for a feeling. "Planned" is not a hope, and "done" is not "merged into the main branch". If a status cannot be true or false, it is not a status.

## Who moves an item, and when

Give each move an owner. In a small team, one person owns triage and moves items from new. Engineers move items to in progress when they start, and the person who ships the change moves it to done. Nobody else needs to touch the status.

Write the owners down. A list with one line per status is enough. When a new person joins, that list answers the question "who should I ask before I change this?"

## Allowed moves and the ones to avoid

Most items follow a short path: new to planned or closed, planned to in progress or closed, and in progress to done. Closed can be the end of a path, and some teams reopen a closed item when a new request arrives. Those are the normal moves.

Avoid a few moves:

- **New straight to done.** If it shipped without anyone deciding, you have lost the record of why.
- **Done without a reply.** The customer who asked should hear about the change. See [how to tell customers their request shipped](/resources/tell-customers-request-shipped).
- **Closed without a note.** A closed item with no reason looks like neglect, and the next person will reopen it to find out.

Escuta Produto does not block a move, so these rules live in your team's habits. Write them down and review them when someone breaks one.

## Write down what done means

Done is the status most teams get wrong. Define it in one sentence the whole team agrees on. A useful definition is "the change is live for users and the people who asked have been told." Under that definition, a merged branch is not done, and a released change that nobody announced is not finished either.

Add the definition of closed too. "We decided not to act, and the note says why" is a good one. Without it, closed becomes a graveyard, and nobody trusts the list.

## A weekly check on stale items

Items drift. A planned item from three months ago may no longer matter, and an in progress item may have stopped weeks ago. Check both each week. Filter to planned and in progress, sort by the date the item came in, and read the oldest ones first.

For each stale item, decide one of three things: move it forward, close it with a reason, or add a note saying what would change your mind. An item that sits untouched is a broken promise, even if nobody mentions it. If you find more than a handful of stale items, the problem is usually the process, not the backlog. Shrink the list before you add new rules.

## Handle the items that never fit

Some items do not fit the five statuses cleanly. A duplicate is closed with a note that points to the main item. A message that mixes a bug and an idea should be split into two items when you can, so each one gets a clear status. Spam is closed with a short note so nobody reads it twice. When you are unsure, pick the status that describes the next action, and write the reason in a note.

## Running the workflow in Escuta Produto

Each product has an inbox with filters by status and by type, plus a text search. Use the status filter in your weekly review: start with new, then planned, then in progress. The 30-day chart and the counts by type show whether the inbox is growing faster than you can clear it.

Set the status on the item, then add an internal note whenever the status changes meaning, such as a decision to close or a plan to revisit. The note is private to your team, and it is where the reason lives. The status tells you what happened, and the note tells you why.

If you export the feedback as CSV, the status column lets you count how many items sit in each state, which is useful for a monthly review of the whole process. For the weekly routine that uses these statuses, see [how to triage customer feedback](/resources/how-to-triage-customer-feedback). For the reply that goes with each move, see [how to reply to customer feedback](/resources/reply-to-customer-feedback). New items also arrive in Slack or Discord through the [notifications setup](/docs/notifications), so the team sees each new item as it arrives.

## Frequently asked questions

### How many statuses should a feedback workflow have?

Five is enough for most teams: new, planned, in progress, done and closed. Each one should make a clear promise. More statuses usually mean people pick different ones for the same situation.

### What does done mean for a feature request?

Done means the change is live for users and the people who asked have been told. A merged branch or an internal demo is not done, so define the rule once and keep the whole team to it.

### Who should change the status of a feedback item?

Name one owner for triage, who moves items from new. Engineers move items to in progress when they start, and whoever ships the change moves it to done. Write the owners down so nobody guesses.

### How often should you review stale feedback items?

Review planned and in progress items every week, oldest first. For each one, move it forward, close it with a reason, or note what would change your mind. An untouched item is a broken promise.

---

# How to thank customers for feedback

> Thank customers for the specific thing they told you, in a short message from your own email. Thank people for bug reports as well as praise, and make small gestures that cost you nothing. Avoid promises about dates or features you do not control.

Source: https://escutaproduto.com/resources/thank-customers-for-feedback
Last updated: 2026-10-09

## Thank them for the specific thing

"Thanks for your feedback" is what every company says. Customers have heard it so often they skip past it. A thank you that names the specific thing works better, because it shows you read the message. Name the screen, the step or the idea they described.

> Thanks, Ana. Your note about the reports page loading slowly on large accounts helped us find the query that was taking most of the time.

The sentence above does two jobs. It shows the message was read, and it tells the customer what their feedback changed. Keep the second part honest. Only say the feedback helped if it did.

## Keep it short and human

A thank you does not need a paragraph. Two or three sentences cover most cases. Write it as you would talk to a person you respect. Avoid stock phrases such as "we truly value your input", which sound like they were written for a hundred people at once.

Sign it with your first name. A thank you from a named person feels personal, and it invites a reply if the customer wants to say more. If the reply came from a team inbox, a first name still helps.

## Thank people for bugs too

Many teams thank praise and ignore bug reports. That is a mistake. A bug report takes effort, and the customer who wrote it is trying to help you. Thank them for the detail, not for the complaint.

> Hi Ana, thanks for including the browser and the step before the error. That made the bug quick to reproduce. We are working on it now and I will write again when it is fixed.

Thanks for a bug only works if you follow it with action. Otherwise it reads as a polite way to end the conversation. The steps for that follow-up are in [how to follow up on a bug report](/resources/follow-up-on-bug-reports).

## Small gestures that cost you nothing

Some of the most effective thanks cost nothing. Here are a few that work well for small teams:

- **Reply to the message the same day it arrives**, even if you only say you are looking into it.
- **Tell the customer when their idea was discussed**, even when the answer is no.
- **Credit the request in a changelog entry** when the change ships, with permission to use the name.
- **Ask a follow-up question** that shows you want to understand the problem, not just close the item.
- **Let them know when they helped a decision**, such as "your message is one of the reasons we moved this to next month".

None of these needs a discount, a gift or a free upgrade. Those can be generous, but they are not what makes a customer feel heard.

## Promises you should not make

A thank you can turn into a promise without you noticing. Avoid these phrases unless you control the outcome:

- "This will be ready next week."
- "We will definitely add this."
- "You will be the first to try it."

Replace them with what you know. "We have added this to our plan and I will tell you when it is in progress" is honest. If the plan changes, you can still keep that promise, because you only promised to tell them.

Watch for the same trap with discounts or offers in a reply to feedback. Mixing a thank you with a sales offer makes the thanks feel like a transaction. Keep those messages separate.

## Thanks in a shared thread

Sometimes feedback arrives in a thread that several people read, such as a shared channel or a public comment. In that case, thank the person in a line and keep the rest of the thread for the substance. Long thanks in public read as performance. A short line reads as respect.

## When a thank you needs a follow-up

A thank you that leads nowhere can damage trust. If you tell a customer their idea was discussed, tell them what happened to it later, even if the answer is no. If you tell them you are looking into a bug, write again when you have news. Keep a short note of every promise you made in a reply, and check those notes during the weekly review. Name a date in the note when you expect news, so the reminder has a deadline. A thank you for a bug fix works best when it comes with the fix itself. The thanks are only as good as the follow-up that comes after.

## Thanking customers from Escuta Produto

Open the item in the product inbox and read the message before you write. The page URL, browser and metadata tell you what the customer was doing, so you can name the thing they described with confidence. Write the thank you from your own email, to the address saved on the item.

Record the thank you as an internal note if it came with a commitment, such as "I will tell her when this ships". The note is the reminder. For the full set of templates for replies, including thanks for praise and for unclear messages, see [how to reply to customer feedback](/resources/reply-to-customer-feedback). For how thanks fits into the work after a change ships, read [how to tell customers their request shipped](/resources/tell-customers-request-shipped). If your app sends feedback itself, the [REST API docs](/docs/api) show how to include the email and page details in each request.

## Frequently asked questions

### How do you thank a customer for feedback in a way they notice?

Name the specific thing they told you, such as the screen, step or idea, and say what it changed if it really changed something. Keep it to two or three sentences and sign it with your first name.

### Should you thank customers who report bugs?

Yes. Thank them for the detail that makes the bug easier to fix, such as the browser or the step before the error. Follow the thanks with action, or it reads as a polite way to end the conversation.

### What should you avoid saying when you thank a customer?

Avoid promises about dates or features you do not control, such as saying something will be ready next week. Say what you know instead, and promise only to keep them informed.

### Do you need to give a gift or discount to thank customers?

No. Replying the same day, crediting the request in a changelog and telling someone when their idea was discussed all work well and cost nothing. Keep any offer separate from the thank you.

---

# How to turn customer feedback into a roadmap

> Start from themes that repeat across feedback, not from single requests. Turn each theme into a bet with a problem, a group of customers and a clear success signal. Sort bets into now, next and later, and keep a link from each roadmap item back to the feedback that justified it.

Source: https://escutaproduto.com/resources/turn-feedback-into-roadmap
Last updated: 2026-10-09

## Start from themes, not from single tickets

A roadmap built from single requests reflects who wrote most recently, not what matters most. Start by grouping the feedback into themes. A theme is a problem customers keep describing in different words, such as "I cannot get my data out in the format my accountant wants." One request for a monthly CSV export is an item. Five requests across three months that all describe the same reporting pain are a theme.

Grouping takes patience. Read the feedback in batches, write a short name for each theme, and record which items belong to it. Do not worry about getting it perfect. A rough set of themes is far more useful than a perfect list of features.

## Turn themes into bets

A bet is a theme with a plan attached. Write it as one sentence with three parts: the customer problem, the group of customers who have it, and the signal that would show you solved it. For example: "Teams who report monthly to finance need a clean monthly export, and we will know it worked when those teams stop asking for workarounds."

Keep the signal honest. You do not need a percentage or a survey to define it. "Requests for the workaround stop" is a fine signal if you will actually watch for it.

## Use now, next and later

Now, next and later is a simple way to sort bets without fake dates. Now means the team is working on it. Next means it is decided and waiting for capacity. Later means it is worth keeping on the list but not yet chosen.

Sorting by time window, not by date, keeps the list honest. When a bet slips, it moves from now to next, which is a normal change. You do not have to rewrite a calendar.

Limit now to what the team can really do. A "now" list of twelve items is a wish list. Two or three is a plan.

## Keep a link from each item to its feedback

The roadmap is only as trustworthy as its links back to customers. For each item, keep a note with the feedback items that justify it. Use the item's internal note to list the count of distinct customers who asked and the themes they described. This makes the roadmap defensible when someone asks why you built one thing before another.

The link also tells you who to tell. When a bet ships, the people linked to it are the ones to thank and inform. That is the step most teams skip, and it is the one customers notice.

## Set how often the roadmap changes

Review the roadmap on a fixed rhythm. Monthly works for most small teams, with a short check each week for anything that moved. Changing the roadmap every day teaches the team to ignore it, and changing it once a year means it is wrong most of the time.

Record each change with a short reason. "Moved the export bet from now to next because the reporting fix took longer" is useful the next time someone asks why it slipped.

## What a roadmap is not

A roadmap is a plan for the team, not a list of promises to customers. It is also not a count of votes. Feedback is input, not a ballot, and a loud group of users can outweigh a quiet group that matters more to your business. Weigh each theme by how many distinct customers describe it, who they are, and how well it fits the direction you chose.

Do not publish the roadmap as a promise. If you want customers to see progress, share what shipped through a changelog and direct replies. For the reasons and the alternatives, see [should you have a public roadmap](/resources/public-roadmap-pros-and-cons).

## Plan for the bets you will not build

Every roadmap has a list of ideas that will never ship. Keep that list on purpose. Write down the bets you considered and dropped, with a short reason for each. When a customer asks about one of them six months later, you can point to the reason instead of reconsidering from scratch. The list also protects the team from the same debate every quarter. A dropped bet with a clear reason is a decision, not a failure.

## Building the roadmap from Escuta Produto

Escuta Produto does not have a roadmap feature. It keeps the feedback, and you keep the roadmap in a tool of your choice, such as a document, a spreadsheet or your project tracker. The inbox gives you the inputs: each item with its type, status, notes and sender.

Use the statuses to show where a theme sits. Items you have decided to act on are planned, items being built are in progress, and shipped items are done. Filter by type and by status to see which kinds of requests are waiting. The 30-day chart shows whether a theme is growing.

Export the feedback as CSV when you want to group themes in a spreadsheet, then bring the bets back into your roadmap document. Check which columns the export includes, and keep enough of each row, such as the message text, to find the source feedback again. For the status rules that make these filters reliable, see [how to design a feedback status workflow](/resources/feedback-status-workflow). For the weekly routine that feeds the roadmap, see [how to triage customer feedback](/resources/how-to-triage-customer-feedback). Alerts for new items are set up in the [notifications docs](/docs/notifications).

## Frequently asked questions

### How do you turn customer feedback into a roadmap?

Group the feedback into themes that describe the same problem, turn each theme into a bet with a clear customer problem and a success signal, then sort the bets into now, next and later. Link each item back to the feedback that justified it.

### What is the now, next and later roadmap format?

It sorts work into three time windows without fixed dates. Now is in progress, next is decided and waiting for capacity, and later is kept on the list but not yet chosen. Items move between columns as priorities change.

### How often should you update a feedback-based roadmap?

Review it monthly with a short weekly check for changes. Record each move with a reason, so the team can see why a bet slipped. Changing it daily teaches people to ignore it.

### Does Escuta Produto have a roadmap feature?

No. Escuta Produto keeps the feedback, with statuses, notes and CSV export. You keep the roadmap in your own document, spreadsheet or project tracker and link it back to the feedback items.

---

# One feedback inbox for many products

> Keep feedback for all your products in one Escuta Produto dashboard. Each product has its own key, allowed origins, hosted page, accent color and webhook, so you read one product at a time while the statuses and routine stay the same.

Source: https://escutaproduto.com/resources/one-inbox-many-products
Last updated: 2026-10-09

## Why one tool per product fragments your attention

When every app gets its own feedback tool, the routine breaks down. You log in to five dashboards, each with its own settings, notification rules and export format. A customer who writes about two of your products gets replies from two systems that know nothing about each other. Most small teams end up checking the tool they open most often, which is rarely the one with the most urgent bugs.

The real cost is not the subscription line. It is the minutes spent deciding where to look, and the items that never get read because they sit in a dashboard you forgot existed.

## How per-product keys keep feedback apart

Escuta Produto treats each product as its own unit. Every product has its own widget key (the data-key value in the script tag), its own hosted page at /f/ followed by the product slug, its own accent color and its own notification webhook. Feedback from the widget or the REST API is filed under the product whose key sent it. A bug report from your scheduling app never lands in the invoicing app's inbox.

Inside the dashboard, each product has an inbox with filters for status and type, plus text search. Those filters apply to the product you are reading. That scoping is the point. You read one product at a time, with its own context, and then move on.

## Set up each product the same way

Use the same steps for every product so nothing gets skipped:

1. Add the product with its name, website and accent color. Use the name your customers use, not an internal codename, because it appears in every alert.
2. Add its allowed origins. Only these sites can submit through the widget, so list every domain where the widget will run.
3. Copy the product's key into the widget snippet on that product's site, and only there.
4. Set the notification webhook to the Slack or Discord channel you want for that product.
5. Send one test message from each site and confirm it appears under the right product.

The last step is the one people skip. A key pasted into the wrong app sends feedback to the wrong inbox, and you may not notice for weeks.

Finish each product's setup in one sitting instead of across several weeks. A half-configured product, with a key on the site but no origins or webhook yet, is the state where feedback goes missing and nobody notices. Treat the five steps as a checklist you complete before you call the product live.

## Keep the statuses and types the same everywhere

Every product uses the same five statuses (New, Planned, In progress, Done and Closed) and the same four types (bug, idea, praise and other). Because those lists are fixed, the discipline lives in what each status means. Agree on that before you start. If one product treats Planned as "maybe someday" and another treats it as a commitment, your counts stop meaning the same thing.

Write the rule down once and keep it near your routine. Two sentences are enough: "Planned means I will build it this quarter. Closed means I read it and decided no."

## What stays separate and what you share

| Setting | Per product | Shared across your work |
| --- | --- | --- |
| Widget key | Yes | No |
| Allowed origins | Yes | No |
| Hosted page and accent color | Yes | No |
| Notification webhook | Yes | No |
| Statuses and types | No, same for all | Yes |
| Your review routine | You choose | Yes |

## What you give up by reading everything in one place

One place has real advantages. You build one habit, you export the same way every time, and you learn the status rules once. You also see the total volume of feedback, which shows where your attention actually goes.

The trade-offs are real too:

- **A loud product can crowd out a quiet one.** If you only open the inbox with the most new items, the quiet product's bug waits. Give every product a short slot each week.
- **The same person can write to two products.** Pass a stable id and a segment to identify. Those values are stored as metadata, so you can match one customer across products in your CSV export.
- **Filters are not tags.** Escuta Produto has no tag system, so a problem that crosses products, such as a shared login bug, lives in your internal notes or your spreadsheet.

## A daily check that scales

Ten minutes a day keeps one inbox honest. Open the dashboard, look at the New count for each product, and read only the new bugs. Leave ideas and praise for the weekly pass. If a product's 30-day chart jumps after a release, open that product first.

Keep the daily check on the one dashboard page you already open each morning, not on a separate bookmark for each product. One page with the counts beats five tabs that you visit once a month and forget.

This works for three to ten products. The longer weekly version lives in [the feedback routine for five products](/resources/feedback-routine-multiple-products).

## How to set up one inbox for your products with Escuta Produto

Start with the [widget docs](/docs/widget). For each product, create it in the dashboard, add its allowed origins, then paste its key into that product's site only. Route alerts with the [notifications guide](/docs/notifications), one webhook per product, so a bug in one app pings the channel for that app. When you are ready to decide where your time goes, read [how to choose which product to work on](/resources/choose-which-product-to-work-on).

## Frequently asked questions

### Can one dashboard hold feedback for several products?

Yes. Each product in Escuta Produto has its own widget key, allowed origins, hosted page, accent color and notification webhook, and you open each product's inbox from the same dashboard. Feedback from one product never appears in another product's inbox.

### Do all products share the same statuses?

Yes. Every product uses the same five statuses (New, Planned, In progress, Done and Closed) and the same four types (bug, idea, praise and other). Agree on what each status means before you start, so the counts match across products.

### What is the main downside of reading many products in one place?

A busy product can crowd out a quiet one if you only read the inbox with the most new items. Give every product a short slot in your weekly routine, and keep cross-product themes in internal notes or a spreadsheet export.

---

# A feedback routine for running five products

> Run feedback for five products with a short daily count check, one fixed weekly slot per product, and one triage order: bugs, repeated ideas, praise, then closing the rest. When time runs short, skip praise and chart-watching first.

Source: https://escutaproduto.com/resources/feedback-routine-multiple-products
Last updated: 2026-10-09

## Give each product a fixed slot every week

A routine for five products has one hard limit, which is your hours. Every product wants them, and the loudest one will take them all unless you assign slots. Start by giving each product a weekday for its deep review. With five products, Monday to Friday covers everything once a week.

Put each slot in your calendar as a recurring event with the product name in the title. The slot is not a promise to fix anything. It is a promise to read that product's new items and decide what happens to each one. Keep the same weekday every week. If a product's customers write mostly over the weekend, review it on Monday so you see the full batch.

## Check the counts every day and nothing else

Each morning, open the dashboard and look at the New count for each product. That takes a few minutes. If one count is higher than usual, or a bug count has moved, make a note and move on. Do not read ideas or praise during the daily check. Those belong in the deep slot, and reading them in pieces makes repeats hard to notice.

The 30-day chart is the other view you might be tempted to watch daily. Glance at it the morning after a release, and otherwise leave it for the weekly review.

## What order should the triage follow?

Every deep slot follows the same order, so the most urgent work always comes first:

1. **Bugs first.** Open every New bug. Use the page URL and browser saved on each item to reproduce it. Then fix it, move it to Planned, or write a short internal note explaining why it is not a bug.
2. **Ideas with repeats.** Search the product's inbox for the two or three words customers use most. Count distinct people, not messages. A single request with a clear reason can go to Planned later. A request with no context can close with a note.
3. **Praise.** Read it, copy any line that describes what the product does well, and move on. Praise tells you what to protect when you change things.
4. **Close the rest.** Every item that stays New after its slot is a decision you postponed. Close it, plan it, or write a note saying what you are waiting for.

Nothing should stay New when its slot ends. If it does, your daily count will keep the product on your mind without ever moving it forward.

## Time-box each product and stop on time

Give each product a fixed box of about 45 minutes. Set a timer. When it rings, stop, even in the middle of an idea. Before you close the tab, write one line in your notes app about where you stopped, such as "Product B: read bugs up to Tuesday, ideas not started". Next week, start from the New filter, not from memory.

Time boxes make the routine sustainable. Without them, one loud product takes the whole afternoon and the quiet ones slip for a month. The box also forces a choice. If an item needs a longer look, it becomes a task for the next slot instead of an open-ended hour.

## What do you do when one product needs extra time?

Launches, outages and migrations change the picture. If a product shipped a large change on Friday, move its slot to Monday and give it a short second pass on Thursday. Take the time from the product that had the least in its slot last week. Do not skip a product for a whole week, even if all you do is read its bugs.

## What to skip when the week gets busy

When time runs short, skip in this order: praise first, then ideas with a single request and no context, then the 30-day chart. Never skip bugs. A bug left in New for two weeks is often the one a customer writes about again, and that second message is harder to answer than the first.

## Keeping the routine honest over several months

Every month, read your closed items from the previous four weeks. If you closed a request that later came back from three people, your close note was too quick. If a product's slot ran out every week, its box is too small or it needs a different day. The routine is a tool you adjust, not a rule you obey.

Watch for one drift in particular. Teams often start the routine with real discipline and then let the daily check quietly replace the weekly slot. The counts feel like progress, but nothing moves from New to a decision. If you notice the weekly slot has been skipped twice in a row, cut the daily check down to bugs only until the slot is back.

## How to run this routine with Escuta Produto

Escuta Produto gives each product its own inbox, so the weekday routine maps directly onto the dashboard. Add the five products, point each product's [notification webhook](/docs/notifications) at the channel you watch, and use the status and type filters as your starting point for each slot. The daily check reads the counts. The deep slot uses the filters and text search. Alerts tell you a bug arrived, but the decisions happen in your slot, not in the chat channel.

If you want the reasons behind one inbox for all products, read [one feedback inbox for many products](/resources/one-inbox-many-products). To order your slots by impact, read [how to choose which product to work on](/resources/choose-which-product-to-work-on).

## Frequently asked questions

### How many products can one person review each week?

There is no fixed limit. Give each product one short weekly slot and check the New counts every morning. Five products fit a Monday to Friday schedule, and with ten products you shorten each slot without skipping bugs.

### What should I do first when several products report bugs?

Start with the bug that blocks paying customers, such as a broken sign-in or checkout, and then work down. Use each product's New count and its 30-day chart to rank them, and move the rest to the next slot with a short note.

### Should I reply to every feedback item?

Reply when you have something useful to say, especially after you fix a bug or plan an idea. Closing an item with an internal note is a decision too. Replies come from your own email, because Escuta Produto does not send automatic emails to customers.

---

# Using feedback to choose which product to work on

> Choose the next product to work on by counting distinct customers who asked, reading the direction of each rating and comparing revenue from your billing tool. Do not follow the product with the most messages by default.

Source: https://escutaproduto.com/resources/choose-which-product-to-work-on
Last updated: 2026-10-09

## Why the loudest product is a trap

The product with the most messages feels like the one that needs you most. It generates the most notifications, it is the one customers mention in support, and it is the one you open first. Following the noise is an easy habit, and it often sends a week of work to whichever app has the most chatty users.

Good product decisions start from a different question: where will a week of my work help the most people who pay me? Feedback can answer that question, but only if you read it carefully, separate messages from people and bring in the one number feedback cannot show you.

## Count people, not messages

Start with unique people. Ten messages from one enthusiastic customer are one opinion repeated ten times. Five messages from five different customers about the same problem are a pattern.

In the product's inbox, use the text search to find the words each request uses, then count distinct senders. Email addresses help here when people give them. If you pass an email or an id with identify, you can also see whether the same person wrote again. Record the count in your notes for each product, with the date.

## Read the direction of the rating

The average rating is useful for direction, not for a verdict. A product at 3.8 that fell from 4.4 last month needs attention. A product at 4.6 with only three ratings tells you almost nothing.

Look at the 30-day chart next to the rating. A drop that starts the day after a release points to a specific change. A slow decline with rising bug counts points to a product that accumulated problems over several months, and that needs a repair plan rather than a single fix.

## Add the revenue fit from your billing tool

Escuta Produto does not know what your customers pay, so take that number from your billing tool or payment dashboard. Compare it with the unique-people count for each product. A product with strong revenue and many people asking for better onboarding is a strong candidate. A product with a lot of praise and little revenue may be a good place to test ideas, but a poor place to spend a whole month.

If you pass a segment value with identify, that metadata appears on each feedback item. You can then see whether the people asking belong to the segment you want to grow. That detail can decide between two requests that look equal in volume.

## A worked example with three products

For example, suppose product A has 12 messages this month from 4 customers, mostly about slow exports. Product B has 3 messages from 3 customers, all asking for the same integration. Product C has 2 messages from 2 customers, both reporting a broken checkout, and those two customers are among your largest accounts.

A loud reading picks product A, because it has the most messages. A careful reading looks at each row. Product A has a real problem, but four people is a small group, and exports are a known pain point you can schedule. Product C has the smallest volume and the highest stakes, because a broken checkout stops paying customers. The right first move is C's bug, then A's export speed, with B's integration written down for a later month.

## Write the decision down and recheck it

Write the choice in one internal note per product: the date, the signals you used and what would change your mind. For example, "Product C first, because checkout bugs block paying customers. Revisit if export requests reach 10 distinct people." Keeping the reason in writing stops you from reopening the same debate every Monday.

Recheck the decision at the end of each month. Use the 30-day chart and the New counts. If the numbers moved, change the plan and note the change. The habit matters more than the exact cadence.

## What feedback cannot tell you

Feedback tells you what customers say, not what they would pay for. A product can have many vocal users who never pay, and a quiet product can have one large customer who writes once a quarter. Use feedback to judge demand and urgency, then check revenue and effort before you decide. When the signals disagree, say so in the decision note and start with the smaller, reversible bet.

## What if two products are equally important?

Sometimes two products matter about the same. Do not split a week between them unless both bugs are small. Split work by stage instead. One product gets a fix this week and the other gets a discovery pass next week, where you read its ideas and write short specs. That keeps both moving without a context switch every afternoon.

## How to choose with Escuta Produto

Use the product list in the dashboard as your starting point. For each product, open the inbox, filter to New bugs and ideas, count unique people with text search and read the rating trend on the 30-day chart. Put the result beside revenue from your billing tool, and write the decision in your notes. For the setup behind per-product signals, the [hosted page docs](/docs/hosted-page) explain how each product collects its own messages. The [feedback routine for five products](/resources/feedback-routine-multiple-products) shows how to read each product on a schedule, and [one feedback inbox for many products](/resources/one-inbox-many-products) explains why the counts stay separate.

## Frequently asked questions

### How do I choose which product to build next?

Compare the number of distinct customers asking in each product, the direction of its rating over the last 30 days and the revenue it brings in from your billing tool. Pick the product where a fix or feature reaches the most paying people, and write down why.

### Is the number of feedback messages a good signal?

Only partly. Many messages from one person are one opinion repeated. Count distinct people, compare bugs with ideas, and read a sample of the text before you trust the total.

### What if the quietest product brings in the most revenue?

Give it attention anyway. Happy customers rarely write in, so a quiet product can be healthy. Watch for sudden silence after a release, and make sure its feedback link is easy to find on its help pages.

---

# How to organize Slack channels for product feedback

> Use one Slack or Discord webhook per product. Give a product its own channel when someone owns it, or send everything to one shared channel where each alert names its product. Mute and limit members to control noise.

Source: https://escutaproduto.com/resources/slack-channels-for-feedback
Last updated: 2026-10-09

## One webhook per product is the building block

Each Escuta Produto product has one notification webhook, which is a Slack or Discord incoming webhook URL. Every new item posts an alert with its type, the product name, rating stars, the sender, a 500-character excerpt and a link to the dashboard. The webhook decides where alerts go, so a channel plan is really a plan for which webhook goes where.

Because the webhook is set per product, you can start with one channel and split later by changing a single product's webhook. The other products keep sending alerts to their channel untouched.

## Option one: a channel per product

A channel per product works best when each product has an owner who watches it. The people in a product channel care about that app only, so the alerts carry clear meaning without any extra reading. Name the channel after the product, for example "feedback-scheduler", so nobody has to ask what it is for.

The downside is channel sprawl. Five products mean five channels to join, mute and archive. Keep the list short. If a product has no owner who would read its channel, do not create one for it. Send its alerts to the shared channel instead.

## Option two: one shared channel with the product in every alert

A shared channel works when one person reads everything. Each alert names its product, so you can scan one feed and know where each item belongs. That product name in every message is what makes the shared option workable. Without it, a shared channel becomes a mess of unlabeled messages.

Use this option for a solo founder or a small studio where a single person triages. It keeps the number of channels low and gives you one place to check from your phone. The trade-off is that a busy product can bury a quiet one in the feed, so pair the shared channel with the weekly review described in [the feedback routine for five products](/resources/feedback-routine-multiple-products).

Discord works the same way. An incoming webhook on a Discord channel accepts the same alerts, so the two options apply to a Discord server as well as to Slack.

## What you cannot split inside one product

Each product has one webhook, so every type goes to the same place. Bugs, ideas, praise and other messages all arrive in the same channel. You cannot send only bugs to a channel that pings you, and you cannot route praise to a separate channel for marketing. If you need that separation, read the channel for bugs during the day and treat everything else as your weekly list.

Be honest about spam too. The form has a hidden honeypot field and rate limits, and those catch most bots. Some spam still gets through, so expect a few low-value alerts and close them with a short note.

## Control the noise without losing alerts

Slack and Discord both let members choose how they are notified for each channel. Use those settings rather than hiding the channel. Mute the shared channel during deep work, and set notifications so that only mentions of you interrupt you. The alerts stay in the channel, so nothing is lost. You can read the feed later in one sitting.

Keep the member list small too. A channel with five people who never read it is worse than a channel with one person who reads it every day. Add only the people who will act on an alert, and remove anyone who has left the team.

Agree on a rule for who acts on which alert. For example, a bug alert goes to the person who owns that product, and an idea alert waits for the weekly review. Writing the rule down prevents two people from fixing the same bug, or both assuming the other one will answer.

## Keep the weekly review out of the channel

The alert channel is for awareness. The decisions happen during your weekly review in the dashboard, where you can change status, write internal notes and filter by type. Do not try to triage inside Slack or Discord, because a chat thread has no status field and no place for your reasoning.

When you change a webhook, update it in the product's settings. The old channel stops getting that product's alerts, so tell the people in it before you switch. A short message in the old channel that points to the new one saves a week of missed alerts.

## Which option fits which team?

Use a channel per product when each product has a different owner, for example a designer who handles one app and a developer who handles another. Use one shared channel when you are the only person reading. A mixed setup is fine too: a shared channel for the small products and a dedicated channel for the one product that gets the most traffic.

Check each channel every few months. When a channel has been silent for a month, first confirm that its product still gets feedback at all. A quiet channel can mean the webhook broke rather than that customers stopped writing, so send one test message from that product's widget and watch for the alert.

## How to set up Slack channels for feedback with Escuta Produto

Create the incoming webhook in Slack or Discord for each channel, then paste it into that product's notification settings. The [notifications guide](/docs/notifications) lists the steps for each platform. Send one test message from the product's widget and check that the alert names the right product. Then decide whether each product gets its own channel or sits in the shared feed.

For the reasoning behind one dashboard above many channels, read [one feedback inbox for many products](/resources/one-inbox-many-products).

## Frequently asked questions

### Should each product have its own Slack channel?

Give a product its own channel when one person or team owns it and reads its alerts. If one person watches everything, a shared channel works well, because each alert names its product.

### Can I send only bugs to one Slack channel?

Not from the same product. Each product has one webhook, and it receives every type of feedback. Read the channel for bugs during the day and treat the rest as your weekly list.

### Does Escuta Produto work with Discord as well as Slack?

Yes. Each product's notification webhook accepts a Slack or Discord incoming webhook URL, so you can use one platform for some products and the other for the rest.

---

# How agencies can collect feedback for client products

> Give each client their own product with a separate key, allowed origins, hosted page and webhook. Share weekly CSV summaries instead of logins, because Escuta Produto has no client accounts, team roles or client portal.

Source: https://escutaproduto.com/resources/agency-client-feedback
Last updated: 2026-10-09

## Use one product per client site

Give each client their own product in Escuta Produto, with its own widget key, allowed origins, hosted page, accent color and notification webhook. A client who runs two sites gets two products, because each site may have different users and different bugs. Keep the product name plain and recognizable, such as the client's brand and the app name.

One product per client keeps the separation clean. A bug from one client's app cannot land in another client's inbox, and you never have to explain which feedback belongs to whom. The hosted feedback page at /f/ followed by the product slug can also be shared with the client directly.

## Set allowed origins so each widget works only on its site

Each product has a list of allowed origins, and only those sites can submit through the widget. For a client's product, list the client's domains and any staging domain you use for testing. If a client launches a new domain, add it before you ship the snippet there. Until you do, submissions from that domain will be refused, and the API answers 403 for origins that are not allowed.

The key itself is safe to place in a client's HTML. A public key can only create feedback and can never read it, so the snippet you hand over does not expose their inbox.

## What clients can and cannot see

Escuta Produto has no client accounts, team roles or client portal. A client cannot log in to see their feedback, and you cannot give them a limited view of the dashboard. Plan around that before you promise anything. Escuta Produto also does not offer white-labeling, so do not promise a version of the tool with a client's name on it.

What you can do is share the hosted feedback page link, which uses the product's accent color, and send exports and summaries yourself. Those two pieces give the client a visible way to send feedback and a clear view of what came in.

## Send a weekly summary built from the export

Once a week, open each client's product and export its feedback as CSV. The file is UTF-8 and opens in Excel and Google Sheets, so you can count bugs, ideas and praise without cleaning the text first.

Then write a short summary in your own email: the number of new bugs, the top two ideas with the number of distinct people who asked, the average rating if it has enough ratings behind it, and the one item you plan to fix next. Keep it to a few lines. Use the same headings every week, so the client learns where to look and notices quickly when something changes. A short summary that arrives every week builds more trust than a long report that arrives once a quarter.

## Keep client data apart

A CSV holds the names and email addresses people typed into the form. Treat it as personal data. Send each export only to the client who owns that product, store it in a folder for that client, and check your contract for how long you keep it and where it can go. Do not paste one client's feedback into another client's summary, even as an anonymous example.

Keep internal notes internal too. The notes on each item are for your own reasoning, so do not copy them into a client summary without rewriting them.

## What do you do when a client leaves?

Export the product's CSV first and hand it over with a final summary, since that is the record the client will most likely want. Then delete the product from its settings and remove the snippet from the client's site, so the widget stops loading on their pages. Keep your own copy only as long as your contract allows.

## Onboard a client without a long handover

Give the client a short onboarding note with three items: where to paste the snippet, which domains are allowed, and who to contact when a message looks wrong. If the client's developer is busy, paste the snippet yourself, but tell them first. Then check the first submission from the live site, not from staging. A missing origin is an easy mistake when the live domain differs from the one you tested on, and the widget will refuse submissions from it until you add the domain.

## Should one agency share a single product across clients?

Avoid it. A shared product mixes feedback from different businesses into one inbox, and you lose the clean separation that makes each summary accurate. Even when two clients use similar apps, give each its own product, its own origins and its own webhook. The extra setup takes a few minutes, and it prevents the kind of mix-up that costs trust.

## How to set up client feedback with Escuta Produto

Create one product per client in the dashboard and fill in its name, website, accent color and allowed origins. Send the client the snippet with that product's key, and the link to the hosted feedback page. Point the notification webhook at your own channel, or at a shared channel with the client if they have one. The [widget docs](/docs/widget) cover the snippet options, and the [hosted page docs](/docs/hosted-page) explain the link. For a broader view of the weekly habit, read [a feedback routine for five products](/resources/feedback-routine-multiple-products). If you run several client products beside your own, [one feedback inbox for many products](/resources/one-inbox-many-products) explains how to keep them apart.

## Frequently asked questions

### Can an agency give each client their own feedback product?

Yes. Create one product per client site, each with its own widget key, allowed origins, hosted page, accent color and notification webhook. Feedback for each client stays in its own product inbox.

### Can clients log in to see their feedback?

No. Escuta Produto has no client accounts, team roles or client portal. Share a CSV export or a short written summary with each client instead.

### Does Escuta Produto offer white-labeling for agencies?

No. It does not offer white-labeling, so do not promise clients a branded version of the tool. The hosted page uses the product's accent color, which you can set to match the client's brand.

---

# Feedback metrics for a portfolio of products

> Track four numbers for each product: unique people who wrote in, open items, average rating with its count, and the 30-day trend. Compare products on those, not on total messages, praise counts or ratings from very few people.

Source: https://escutaproduto.com/resources/portfolio-feedback-metrics
Last updated: 2026-10-09

## Four numbers for every product

Track four numbers for each product, in the same order every month: unique people who wrote in during the last 30 days, open items, average rating with its count, and the direction of the 30-day trend. That set is enough to compare products without building a reporting system of your own.

Open items are the sum of Planned and In progress. They show the work you have promised and not yet delivered. New items are different. They show what you have not read yet, so track them in the daily check instead of the monthly review. Write the export date next to every monthly number, so figures from different months are never compared by mistake.

## Read the 30-day chart for direction

The dashboard shows a 30-day chart of feedback volume for each product. Read it for shape, not for exact values. A steady line with a single spike on the day after a release tells you the release caused reports. A line that climbs for three weeks tells you something is accumulating, even if no single day looks alarming.

Pair the chart with the counts by type. If bugs rise while ideas stay flat, the product is getting harder to use. If ideas rise while bugs stay flat, customers are settling in and asking for more, which is usually a healthier sign.

## Open items show the backlog you carry

Open items measure how much promised work sits in front of you. A product with three open items is easy to finish. A product with thirty is carrying a backlog that will shape every planning decision. Compare open items with the weekly time you actually have for each product. If the backlog is ten times bigger than what you can clear in a month, the feedback is telling you to close or plan items more honestly, not to add more.

## The average rating needs a count next to it

The average rating is useful only with enough ratings behind it. Three ratings can swing the average by a full point, and a product with ten ratings can still be noisy. Always write the rating count next to the average in your monthly sheet. When the count is small, treat the average as a hint and rely on the text of the messages.

## Metrics that mislead across products

Some numbers look useful and make the comparison worse:

- **Total messages across products.** A chatty app with a large audience will always win. Compare each product with its own history instead.
- **Praise count as success.** Praise is a signal to protect something, not a score. Read it, but do not reward a product for having it.
- **Average rating from very few people.** See the section above. A small count makes the average look more certain than it is.
- **Message volume during a launch.** Launch weeks spike for reasons that have nothing to do with product health. Mark launch weeks in your sheet and read them separately.

## A monthly sheet with one row per product

Keep one spreadsheet with one row per product, and update it on the first working day of each month. Use the numbers from the dashboard and the unique-people count from your export. The rows below are illustrative, to show the layout:

| Product | Open items | Average rating (count) | Unique people, 30 days | Trend |
| --- | --- | --- | --- | --- |
| Scheduler | 4 | 4.5 (12) | 9 | Flat |
| Invoicing | 11 | 3.9 (5) | 6 | Bugs rising |
| Notes app | 2 | 4.8 (31) | 14 | Ideas rising |

The Trend column is where you write one sentence about what changed and why. A row without that sentence is a number nobody will act on.

## Which numbers should drive a decision?

Use the unique-people count and the open items together. Unique people tell you how widely a product matters, and open items tell you how much work is already waiting. A product with many unique people and a small backlog is a strong place to spend the next week. A product with few people and a large backlog needs a cleanup before it needs new work.

Review the sheet with the same person each month if you can. A sheet that changes hands every quarter loses its meaning, because each new reader may define the Trend column differently. Put a short note at the top that defines each column, so anyone reading the sheet in six months knows what the numbers measure.

## How to get these numbers in Escuta Produto

Each product has its own inbox and dashboard view. Open the product, read the status counts for open items, then read the average rating and its count. The 30-day chart and the counts by type sit in the same view. For unique people, export the month's CSV and count distinct email addresses or names in a spreadsheet. Where people did not give an email, count rows with clearly different text, and note in your sheet that the count is an estimate. For the setup side, the [hosted page docs](/docs/hosted-page) explain how each product collects its messages. The [way to choose which product to work on](/resources/choose-which-product-to-work-on) shows how to turn these four numbers into a decision. The [weekly routine for five products](/resources/feedback-routine-multiple-products) explains when to read each row.

## Frequently asked questions

### Which feedback metrics should I track for each product?

Track unique people who wrote in during the last 30 days, open items (Planned plus In progress), the average rating with its count, and the direction of the 30-day chart. Those four are enough to compare products.

### Why is total feedback volume a misleading metric?

Volume favors products with more users or a chattier audience. Compare each product with its own history instead, and mark launch weeks so that spikes do not look like a lasting trend.

### How many ratings do I need before the average means something?

There is no exact number, but a handful of ratings can swing the average widely. Always write the count next to the average, and rely on the text of the messages when the count is small.

---

# Customer feedback for indie hackers and solo founders

> A solo founder needs one product, one widget and one notification channel to start. Budget about thirty minutes a week for triage, reply to people yourself, and skip charts and ratings until you have enough data to read them.

Source: https://escutaproduto.com/resources/feedback-for-solo-founders
Last updated: 2026-10-09

## Start with the smallest setup that works

You need three things to start: one product, one widget on your site and one notification webhook to a channel you read. Add the product, set its allowed origins to your domain, paste the widget snippet with its key into your page, and point the webhook at a channel you already open every day. That is the whole setup.

If you already have a contact form, keep it for now. The widget does not have to replace everything on day one, and running both side by side for a month shows you which one people actually use.

The snippet is short:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Skip the rest until you need it. The hosted page link is useful when people without the widget want to write in, so add it to your footer or onboarding email at that point. The REST API matters when you have a mobile app or a backend that submits feedback, and you will know when that is you.

## Budget the time before you add anything

Decide how much time feedback can take each week before you set anything up. Thirty minutes is a reasonable start for a founder who is still building. Put that block in the calendar and use it for one triage pass: read every New item, fix or plan what matters, and close the rest with a short note.

Protect the thirty minutes by booking a fixed day, and treat it as a meeting you do not cancel. Friday afternoon works for many founders because the week's messages are complete by then. During a launch, keep the triage but shorten it to ten minutes rather than dropping it, because launch week is when people tell you what broke.

Check alerts for a few seconds at a time, not all day. A notification that buzzes every hour during deep work will eat the focus you need to build. Mute the channel while you build, then check it once after lunch and once before you stop.

## Get alerts where you already look

Choose the notification channel you already read every day. For many solo founders that is a Slack or Discord server they already use. A new app you have to open is one more place for feedback to hide. Each alert shows the type, the rating stars, the sender, a 500-character excerpt and a dashboard link, so you can judge most messages from the alert and open the dashboard only for the ones that matter.

## Reply yourself, from your own email

Escuta Produto does not send automatic emails to customers, so replying is your job. That sounds like extra work, but it is the best part of this setup. A short reply to someone who reported a bug tells them you read it, and often gets you a clearer description of the problem. Use your own address, write in your own voice and keep replies to three or four sentences.

When you know a customer's email, the hosted page accepts an email parameter, so a link in your onboarding message can prefill the field. The widget also asks for an email when the visitor is not identified, so you get a way back to the person in most cases.

Keep a few reply templates in your notes app for the common cases: a bug you fixed, an idea you will not build, and a question that needs more detail. Edit each one before you send it, so it still sounds like you and not like a form letter.

## Ask for the feedback you need

Do not wait for feedback to arrive by accident. When you are unsure whether to build something, put a small button next to the related part of your product. The button can open the widget with the idea type already chosen, and the text next to it can ask one specific question. A narrow question gets more useful answers than a general request for feedback.

## What to ignore while you build

Skip the 30-day chart during the first months. There is not enough data for a trend to mean much, and checking it every day feels productive while changing nothing. Skip the average rating until you have a meaningful count of ratings. Escuta Produto does not run surveys or NPS campaigns, so do not wait for one to tell you how customers feel. Read the messages you receive and reply to them. That is the signal you need at this stage. Once you have a few dozen messages, the chart starts to mean something, and you can add it to your routine.

## How much feedback is too much for one person?

Watch for the point where you stop replying. If the New count grows for two weeks, your routine is too big for the time you have. Cut the alerts to bugs only, or move the weekly pass to a shorter window and keep it. A small routine you keep beats a complete one you abandon by the third week.

## How to set this up with Escuta Produto

Start with one product, the [widget docs](/docs/widget) and one [notification webhook](/docs/notifications). Add the hosted page link to your site footer when you want people without the widget to write in. For a broader routine that still works when you add a second product, read [one feedback inbox for many products](/resources/one-inbox-many-products). For the weekly habit itself, read [a feedback routine for five products](/resources/feedback-routine-multiple-products).

## Frequently asked questions

### What is the smallest useful feedback setup for a solo founder?

One product, the widget snippet with its key on your site, allowed origins for your domain, and one Slack or Discord webhook to a channel you read. Add the hosted page link later, when people without the widget need a way to write in.

### How much time should feedback take each week?

About thirty minutes of triage is a reasonable start for a founder still building. Use it for one pass: read every New item, fix or plan what matters, and close the rest with a short note.

### Should a solo founder reply to every message?

Reply to messages that show a real problem or a useful idea, and keep replies short. Escuta Produto sends no automatic emails, so replies come from your own address, which is a good way to stay close to users.

---

# Using feedback to decide when to sunset a product

> Decide to sunset a product when its feedback shows falling use, bugs you cannot fix and questions you can no longer answer, not just a quiet inbox. Tell customers early, collect exit feedback with the widget and export the data first.

Source: https://escutaproduto.com/resources/sunset-product-with-feedback
Last updated: 2026-10-09

## Signals that a product is fading

A product that is fading rarely announces it. The signals show up in the feedback first. Watch for falling volume over several months, a growing count of questions about features the product no longer has, bugs nobody can reproduce because the environment changed after a release, and customers who say they are moving to another tool.

Look at the mix as well as the total. A product that gets fewer messages but more bugs is in trouble. A product that gets fewer messages with calm ideas and occasional praise may simply be stable and small. Those two cases need very different decisions.

## A quiet inbox is not proof

Low volume alone does not justify a sunset. Some products are quiet because they work. Happy customers rarely write in, and a small tool used every day by a few people can be worth keeping. Before you decide, read the text. Are people still asking for new things? Are they still reporting bugs they care about? Are they writing from pages you still maintain?

Use the mix of signals to answer one question: will the next six months of work on this product help the people who still use it? If the answer is no, you have a sunset case. If the answer is only maybe, keep the product and give it a small, fixed weekly slot instead of a full-time effort.

## Write the decision down with dates

Write the sunset decision in your notes app with four facts: the signals you used, the date you will announce it, the date the product stops accepting feedback and the reason. Keep the reason honest. "Not enough time to maintain it alongside the other products" is a valid reason. You do not need to justify the decision to customers in detail, but you do need to be clear about the timeline.

## Tell customers early and in writing

Tell the people who use the product early enough that they can move. A notice a few weeks ahead is kinder than a surprise, and it gives them time to export their own data or switch. If people depend on a particular data format or integration, name the export format in the notice so they can plan the move. Reply to each person who wrote about the product, from your own email, with the closing date, the reason in one or two sentences and a recommended alternative if you have one.

A short notice on the product's website or hosted feedback page helps too, but it reaches only the people who visit. Email the people who wrote in, because they are the ones who told you they use the product.

## Collect exit feedback in the product

Before the product closes, ask the people still using it what would make them leave or what they would need to stay. The widget can do this with one button. Add it to the sign-in area or the settings page of the product. The widget script must be on that page for the button to work, and the data-escuta-open value sets the type so the form opens already on the right choice:

```html
<button type="button" data-escuta-open="other">Tell us what would make you stay</button>
```

Exit feedback is most useful when it is specific. Ask one question, such as "What would you need to keep using this?", and read every answer in your weekly slot. Some answers will point to a replacement you can recommend. Others will show a feature the product never delivered, which is useful to know before you write the final notice.

## Export before you delete

Before you delete anything, export the product's feedback as CSV. The file holds every item with its status, type, rating and metadata. That record is what you need for a contract, a dispute or your own archive, and it is easy to lose once the product is gone. Open the file and check that its row count matches the dashboard total before you delete anything, so you know the export is complete.

Then delete the product from its settings and remove the widget snippet from every site that used it. Store the export and your notes together in a folder you can find in a year. Check that the folder is private, because the export contains names and email addresses.

## When should you not sunset?

Do not sunset a product that still has a small group of loyal users who depend on it, unless you can give them a clear replacement. Do not sunset it because the feedback is uncomfortable, either. Negative feedback that describes a fixable problem is a reason to fix it, not to close the product. Sunset when the cost of keeping the product going is clearly higher than what it returns to the people using it.

## How to run a sunset with Escuta Produto

Use the product's inbox to read every item from the last six months with the status and type filters, and export the CSV before you change anything. Put the exit question on the widget button, write the notice, and reply to each person from your own email. The [widget docs](/docs/widget) show the options for the button and the trigger, and the [hosted page docs](/docs/hosted-page) cover the link you can share with people who still need to write in. For the routine that makes these calls possible, read [using feedback to choose which product to work on](/resources/choose-which-product-to-work-on). Your weekly slots, described in [a feedback routine for five products](/resources/feedback-routine-multiple-products), are where the exit feedback gets read.

## Frequently asked questions

### How do I know when to sunset a product?

Look for several signals together: falling volume over months, a growing share of questions about features the product no longer has, bugs that cannot be reproduced after a release, and customers saying they are moving to another tool. One signal alone is rarely enough.

### Is a quiet feedback inbox a reason to shut a product down?

Not by itself. Quiet products can be healthy because happy customers rarely write. Check the text of recent messages, and decide to sunset only when the next six months of work would not help the people who still use it.

### How much notice should customers get before a product closes?

Give people enough time to export their data or move, usually several weeks rather than days. Write to each person who used the product from your own email, with the closing date, the reason and a recommended alternative if you have one.

---

# Spotting feedback patterns across products

> Find cross-product complaints by searching each product for the same words, labeling themes in internal notes and tracing them to shared code. Fix the shared component once, then reply in every affected product's inbox.

Source: https://escutaproduto.com/resources/cross-product-feedback-patterns
Last updated: 2026-10-09

## Why the same complaint shows up in several products

Products built by one person or team often share parts. The login screen, the billing flow, the email that confirms an account and the code that loads a profile frequently come from one codebase or one library. When one of those parts breaks, customers report it in every product that uses it, each in their own words. One person writes "I can't get back in", another writes "my reset link is dead", and a third writes "I keep getting logged out". Each message looks like a separate issue in its own inbox.

That is why a routine that reads each product in isolation misses the big fix. You see five small bugs and fix five small things, when one shared component needed one repair.

## Label cross-product themes in internal notes

Escuta Produto has no tags, so give each cross-product theme a consistent label in the internal notes of the items it touches. A simple convention works: start the note with the word theme, followed by a short name such as login or billing. Spell the label the same way every time, because a spreadsheet filter or a notes search only finds the exact words you used.

Keep the list of themes short. Five labels you use every week are more useful than twenty you used once. A label is private to your dashboard, so it never reaches customers, which makes it a safe place for your own reasoning.

## Search each product for the same words

Once a month, search each product's inbox for the words that matter across your products: login, password, sign in, billing, invoice, email and export. The text search runs inside the product you have open, so repeat the search for each product. Write down the number of distinct people per product for each word.

If the same word shows up in three products within the same two weeks, you have a pattern worth investigating, even when each product has only a handful of messages. A pattern is a question, not a verdict, so check it before you act.

## Find the shared component behind the complaint

When a pattern appears, trace it to the part that the affected products share. Ask what they have in common. Is it the same authentication library, the same payment provider, the same transactional email service or the same onboarding screen? Open the code for one product, look at its imports or configuration, then compare it with the others.

Write the answer in the theme note, for example "theme: login, shared auth package, affects three products". That one line tells you where the fix belongs and saves you from repeating the search next month.

## Count across products in a spreadsheet

Export each product's feedback as CSV. Each file covers one product, so combine them yourself. Add a product column to each file, paste them into one sheet and filter by the theme words. The UTF-8 files open cleanly in Excel and Google Sheets, so you can sort and count without a cleanup step.

Count distinct people, not rows. A customer who wrote twice about login in two products is one person with two issues, and a single fix probably helps them both times. Use the email column where it exists, and mark rows without an email as estimates.

## Decide whether the pattern is worth a shared fix

Not every repeated complaint justifies a shared fix. A pattern across three products is strong when the shared part is the cause, and weak when the words only look similar. For example, "export is slow" in two products may have two different causes. Check the reproduction steps and the page URLs before you decide the code is shared. Also ask whether the problem is a setting rather than shared code. A wrong configuration in one app can look like a shared bug until you compare the two configs side by side.

## Announce the fix in every product

Fixing a shared component once is the efficient part. Telling customers is not. Reply to each person who asked about the problem, in each product's inbox, with the same short message: what was broken, what changed and what they should do now. Set the status to Done on each item once you have replied, so the inbox shows the work is finished.

A shared fix that ships quietly leaves customers wondering whether you noticed. A reply in each product closes that loop and tells the person they were heard, which is the goal of this whole routine.

Keep a short record of which items you answered and when, in the theme note. The next time a similar issue appears, you can see whether the last fix reached everyone who asked.

## How to find cross-product patterns with Escuta Produto

Keep one inbox per product, use the same type and status rules across them, and run a monthly search for your shared words. Store the theme labels in your notes, export each product's CSV when you need a count, and merge the files in a spreadsheet. The [notifications guide](/docs/notifications) explains how each product's alerts reach its channel, so a login complaint in one product does not get lost in another. For the reasoning behind one inbox, read [one feedback inbox for many products](/resources/one-inbox-many-products), and for the weekly habit that makes this search possible, read [a feedback routine for five products](/resources/feedback-routine-multiple-products).

## Frequently asked questions

### Why does the same complaint show up in several products?

Products built by one team often share login, billing, email or onboarding code. When a shared part breaks, customers report it in each product using different words, so each inbox shows a small, separate bug.

### How do you label cross-product themes without tags?

Use internal notes with a consistent label, such as the word theme followed by a short name like login or billing. Spell the label the same way every time, so you can find it later in your notes or a spreadsheet.

### How do I count a pattern across products?

Export each product's CSV, add a product column, combine the files in one spreadsheet and filter by the theme words. Count distinct people rather than rows, since one person may write twice about the same issue.

---

# Keeping the feedback experience consistent across apps

> Keep feedback forms consistent across your apps by sharing placement, trigger style, type names, locale rules and statuses. Let each product differ only in its key, accent color and product name, set through one template.

Source: https://escutaproduto.com/resources/consistent-feedback-experience
Last updated: 2026-10-09

## Decide what should match and what can differ

Start with a list of three columns: what must match across your apps, what can differ by brand and what belongs to one product only. Must match: the form's type names, the five statuses, the reply style and the placement of the feedback button. Can differ: the accent color, the language setting and the product name. Product specific: the product's key, its allowed origins and its webhook.

Write the list once. Every new app should copy it instead of reinventing it. Consistency comes from a shared decision, not from memory.

## Use the same placement and trigger

Put the feedback button in the same corner of every app. The widget default is the right side, which you can set explicitly with data-position="right". Choose one position and keep it. A customer who learns the button in one app will look for it in the same place in the next. If one app needs the button on the left for layout reasons, treat that as a deliberate exception and write it down.

Use the same trigger style too. The default floating button works everywhere. Where a page already has its own help link, set data-trigger="none" to hide the floating button, then open the form from that help link with an element that has data-escuta-open. That keeps the entry point consistent without a second button competing with the help link.

## Match the copy before you match the color

Customers notice wording before they notice color. If one app says "Send feedback" and another says "Report an issue", people will wonder whether they are reporting a bug or sharing an idea. The form uses four types: bug, idea, praise and other. Choose labels that map to those types in every app, and use the same sentence in the text around the button.

Language matters too. The widget supports English and Portuguese, and data-locale sets it explicitly. The default follows the browser language, which is usually right, but set it when your audience and your interface language differ. Use the same locale rule across all apps, so a Portuguese visitor does not get English in one product and Portuguese in another for no reason.

## Give each product its own accent color

The accent color is the one place where each product should look different. Set the color in each product's widget snippet with data-color, and keep the same value as the product's accent color so the hosted feedback page matches the widget. That gives each app its own identity while the structure stays the same. Pick colors with enough contrast against the page background, and test the button on both light and dark pages.

Each app loads its own snippet with its own key and color:

```html
<!-- Scheduler app -->
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_scheduler_key" data-color="#1d4ed8" data-position="right" data-locale="en" defer></script>

<!-- Invoicing app -->
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_invoicing_key" data-color="#0f766e" data-position="right" data-locale="en" defer></script>
```

## Keep the configuration in one template

Write the snippet once as a template, and let the key and color be the only values that change per app. Keep the template in a shared note or a snippet library, and copy it into each project. When you change a shared setting, such as the position, change the template first and then update each app on purpose.

Do not hand-edit the attributes in five places. Small differences pile up. An app with a different position and no comment explaining why will confuse the next person who maintains it.

## Match your replies and statuses

Replies should sound like the same person wrote them. Use the same greeting, the same length and the same sign-off across products. Keep replies short: one line that thanks the customer, one line that says what you did, and one line that says what happens next.

Statuses matter just as much. New, Planned, In progress, Done and Closed mean the same thing in every product, as described in [one feedback inbox for many products](/resources/one-inbox-many-products). When a status changes meaning between apps, your weekly routine starts comparing unlike things. Write the meaning in one place and link to it from your routine.

## Common inconsistencies to avoid

- **Different allowed origins for the same domain.** A staging domain added to one product and forgotten in another makes the widget work in one app and fail in the other.
- **Two entry points.** One app shows a help link and a floating button. Customers see two options and pick either at random.
- **Mismatched types.** One app uses only bug and idea, while another uses all four types. Your counts then mean different things.
- **Forgotten locales.** A Portuguese app with English labels, or the reverse, damages trust quickly.

## Check consistency once a quarter

Every quarter, open each app's feedback button, submit a test message and compare the result. Check the placement, the labels, the language, the status of the test item in the dashboard and the alert that arrives in the channel. Five minutes per app catches most drift before a customer does. Write the date of each check in your notes, so you can see how long any drift lasted.

## How to keep the widget consistent with Escuta Produto

Create one product per app in the dashboard, with its own allowed origins, webhook and accent color. Use the [widget docs](/docs/widget) to check every option, and keep the template with the same position, trigger and locale rules across apps. Use the [hosted page docs](/docs/hosted-page) if some apps need a link instead of a button. For the weekly routine that keeps all of them readable, see [a feedback routine for five products](/resources/feedback-routine-multiple-products).

## Frequently asked questions

### What should stay the same across feedback widgets in different apps?

Keep the placement, the trigger style, the four type names, the locale rules and the five statuses the same. Let the product key, accent color and product name change, and set them from one shared template.

### Should each app use a different accent color for the widget?

Yes, that is the one place where each product should look different. Set a data-color value per app that has enough contrast with its page, and keep the rest of the snippet identical.

### Why set the locale explicitly when the browser language already works?

The default follows the browser, which is usually right. Set the locale when your interface language and your audience differ, and use the same rule in every app so visitors get the same language across products.

---

# What should a feedback button say?

> Use a verb that says what the visitor will do, such as Send feedback or Report a bug, and match the label to the page they are on. Translate the label rather than the word, and replace the default Feedback button when a page needs its own wording.

Source: https://escutaproduto.com/resources/feedback-button-copy
Last updated: 2026-10-09

## Why the label does more work than the design

A visitor sees a feedback button once and decides in a second whether to click it. The label answers two questions: is this for me, and what happens if I click? A vague label makes people hesitate. A specific label sets the expectation before the panel opens.

The floating button in the Escuta Produto widget reads "Feedback" in English and in Portuguese, next to a speech bubble icon. That works as a default. The word is short and familiar, but it names a thing rather than an action, so the visitor still has to guess what the form will ask.

## Verbs or nouns: which reads better?

A verb tells the visitor what they will do. "Send feedback" says they will write something. "Report a bug" says they will describe a problem. A noun such as "Feedback" or "Ideas" names a place to go. That is fine for a help menu, but it is weaker on a button that sits on top of the page.

Use a verb when the button appears near a task that can fail, such as an export screen or a settings page. Use the noun when the button lives in a global corner and the visitor only needs a way to write to you.

Keep labels to two or three words. Long labels get cut off on phones and make a page look crowded. Start with the verb, and drop words such as "our" or "the" that add nothing.

## Let the page choose the feedback type

The widget form asks for a type: Bug, Idea, Praise or Other. You can skip that question by preselecting the type from the element that opened the form. Any element with the attribute `data-escuta-open` opens the panel, and the attribute value sets the type:

```html
<button type="button" data-escuta-open="bug">Report a bug</button>
<button type="button" data-escuta-open="idea">Suggest a feature</button>
```

This turns one generic button into labels that match the page. For example, a help article about exporting data can offer "Report a problem with export" as a bug, while a team page can ask for ideas.

| Page | Button label | Type it opens |
| --- | --- | --- |
| Export screen | Report a problem with export | Bug |
| Feature overview | Suggest an improvement | Idea |
| Finished onboarding | Tell us how it went | Praise |

Two details matter. An element with `data-escuta-open` and no value opens the panel without changing the type. The panel starts on Idea when the page loads, and it keeps the last type chosen until the visitor reloads. If someone picks Bug, closes the panel and then clicks the floating button, the form still shows Bug. Make sure that is what you want, or set the type explicitly on every trigger.

## Localize the label, not only the words

The text on the floating button is fixed. It reads "Feedback" in both languages, and no attribute changes it. The form around it does switch with `data-locale`, so a Portuguese page shows a Portuguese panel behind an English button. If that mix bothers you, hide the default button and use your own translated label.

Write each label for the language a visitor reads, not as a word-for-word translation. "Report a bug" becomes "Reportar um bug" in Portuguese, and "Suggest a feature" becomes "Sugerir uma melhoria". Translated text is often longer, so test both versions beside the other buttons on the page.

## Replace the default button

When you need your own label, hide the floating button and place your own trigger. Set `data-trigger="none"` on the script tag:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-trigger="none" defer></script>
```

Then put any element with `data-escuta-open` where you want it, such as a help menu, a footer link or a settings row. Keyboard users reach it like any other button. When the panel closes with Escape, focus returns to the element that opened it, so the flow stays predictable.

If a button needs a script handler, you can call the widget directly with `EscutaProduto.open({ kind: "praise" })`. The call works after the script has loaded, and the queue form covers calls made before that.

## Pick wording you can keep

Labels change more often than people expect. Choose words that still make sense after a redesign, and avoid product names that might change. Put the label in your translation files next to the other buttons, so one person can update all of them in the same review.

Keep a short list of every trigger label on your site, with its page and its type. When a new page adds a button, the list shows whether the wording already exists somewhere else, and whether the type it opens matches what the page is for. A spreadsheet with three columns is enough for most sites.

Once the labels are set, check the form itself. The [widget docs](/docs/widget) list every attribute, and [how to make a feedback widget accessible](/resources/accessible-feedback-widget) covers the keyboard and screen reader path for a custom trigger. For the wider question of when to ask, see [how to collect customer feedback](/resources/how-to-collect-customer-feedback).

## How to set this up in Escuta Produto

Start with the default. Watch which type visitors pick from the floating button for a week, using the type counts on the product dashboard. If most messages on a page are bugs, add a bug trigger there. If praise dominates a page, a praise trigger may fit better. Then hide the default button with `data-trigger="none"`, add your labelled triggers with `data-escuta-open`, and check the inbox to confirm each one arrives with the type you expected.

## Frequently asked questions

### Should a feedback button say Feedback or Send feedback?

Send feedback is usually clearer because it names the action. Feedback alone is short and familiar, but it describes a thing rather than what the visitor will do.

### Can the feedback button text be translated automatically?

The default Escuta Produto button text is fixed at Feedback in both languages. To use a translated label, hide the default button with the trigger setting and add your own element with the open attribute.

### How do I preselect the feedback type from a button?

Add the data-escuta-open attribute to any element and set its value to bug, idea, praise or other. Clicking that element opens the form with that type already selected.

---

# Designing a feedback widget for mobile screens

> Put the trigger in a bottom corner where thumbs reach, keep the form short, and test the panel with the keyboard open. Move the floating button away from your tab bar, or hide it and open the widget from your own menu.

Source: https://escutaproduto.com/resources/feedback-widget-mobile-design
Last updated: 2026-10-09

## Where the thumb reaches on a phone

On a phone, the bottom corners are the easiest places to tap with a thumb. The Escuta Produto widget puts its floating button there by default, on the right, with `data-position="right"`. If your visitors skew left-handed or your interface already uses the right corner for a control, set `data-position="left"` and compare.

The panel itself is at most 360 pixels wide and never wider than the screen minus 16 pixels on each side. The four type buttons share one row, so their labels stay short. The star ratings are the weak spot: each star is a small button, so a visitor with large thumbs may miss one. If rating matters to you, consider making it optional on mobile and checking the tap target on a real device.

## What the keyboard covers

When a visitor taps the message box, the phone keyboard opens and takes up a large part of the screen. The widget focuses the message box about 30 milliseconds after the panel opens, so the keyboard appears right away. That is helpful for typing, but it means the visitor is looking at the top of the form while the bottom of the panel may be hidden.

The panel is fixed to the bottom of the screen. Its maximum height is the screen height minus 100 pixels, and it scrolls inside itself, so the message box, the rating, the email field and the send button can all be reached by scrolling the panel. The widget sizes itself with `vh` units and does not read the visual viewport, so how the panel fits with the keyboard open depends on the browser. Test on iOS Safari and on Android Chrome with a real keyboard open. An emulator does not show the keyboard behavior accurately.

The form order matters on a phone. The widget shows the type buttons, then the message, then the rating, then the optional email field, then the send button. Visitors who already typed a message will usually reach the email field last, which is what you want. Do not add more required fields above the message.

## Bottom navigation and the floating button

Many mobile sites use a fixed tab bar along the bottom. The floating button sits 20 pixels above the bottom edge and 20 pixels from the chosen side. That is exactly where the last or first item of a tab bar sits. The widget uses very high stacking values, so the open panel and the button draw over your tab bar. The panel will cover the tabs when open, and the button will sit on top of a tab when closed.

There are two practical fixes:

1. Move the floating button with `data-position="left"` if your left-most tab matters less than the right-most one.
2. Hide the floating button with `data-trigger="none"`, then add a "Send feedback" item to your own menu and give it the `data-escuta-open` attribute.

The second option is usually better in an app-like layout. The panel still opens over the page, and the close button in the top corner hides it again. There is no offset option for the floating button, so if neither position works, the menu item is the cleanest way out.

## Test on a real phone before you ship

Run this checklist on at least one iPhone and one Android phone:

1. Open a page at the narrowest width your phone shows, and tap the floating button. The panel should fit with a margin on both sides.
2. Tap the message box and type three or four lines. Confirm you can still reach the send button.
3. Turn the phone sideways and confirm the panel still fits. Its height limit recalculates with the screen size.
4. Open the page with your tab bar visible. Confirm the floating button does not hide a tab you need.
5. Scroll the page behind the open panel. The widget does not lock page scrolling, so the background can move while the panel is open. That is acceptable for a short form, but know it happens.

If anything fails, change the position or hide the trigger before you tune the copy. Layout problems hurt more than wording.

## Mobile layout options in Escuta Produto

The widget covers the main cases with three settings: `data-position` for left or right, `data-trigger` to hide the floating button, and `data-escuta-open` to open the panel from any element on the page. It has no offset or size option, so placement and spacing are the job of your page. The [widget docs](/docs/widget) list every attribute.

Mobile apps do not have a native SDK in Escuta Produto. A native app should send feedback through the [REST API](/docs/api), which lets you build a form that follows the platform's own design. For a web page, the panel above is enough.

For the label and accessibility side of the same panel, read [what a feedback button should say](/resources/feedback-button-copy) and [how to make a feedback widget accessible](/resources/accessible-feedback-widget).

## Set up a mobile-friendly trigger in Escuta Produto

Start with the defaults and test them on a phone. If the floating button clashes with your navigation, change the position first. If it still clashes, add `data-trigger="none"` to the script tag and place a labelled item in your menu with `data-escuta-open="idea"` or `data-escuta-open="bug"`, whichever matches where the item sits. Then send a test message from the phone and confirm it appears in the inbox with the right type and page URL.

## Frequently asked questions

### Where should a feedback button go on a phone?

A bottom corner is easiest to reach with a thumb. The Escuta Produto widget defaults to the right side and can move to the left. Check that it does not cover a tab bar or a control you need.

### Does the feedback form work when the phone keyboard is open?

The panel scrolls inside itself, so visitors can reach every field. Fit depends on the browser because the widget does not read the visual viewport. Test on iOS and Android with the keyboard open.

### How do I stop a feedback button covering my mobile navigation?

Move the button to the other side with the position setting, or hide it with the trigger setting and open the form from an item in your own menu. The second option works best in app-style layouts.

---

# How to make a feedback widget accessible

> Use real buttons for every control, label every field, return focus to the trigger when the panel closes, and check the contrast of the button color. Test with the Tab and Escape keys first, then with a screen reader, because automated checks miss focus order.

Source: https://escutaproduto.com/resources/accessible-feedback-widget
Last updated: 2026-10-09

## Use real buttons for every control

A control that looks like a button should be a button. Keyboard users move between controls with Tab and press them with Enter or Space. A `div` with a click handler gets none of that for free, and a screen reader may not announce it at all.

The Escuta Produto widget builds every control as a real `button` element: the floating button, the close button, each feedback type, each star and the send button. A keyboard user can reach all of them in order. The same check applies to any widget you build or install, so Tab through it before you trust it.

## Dialog semantics and what they promise

The panel has `role="dialog"`, a name taken from its title ("Send us feedback" or "Envie seu feedback"), and `aria-modal="false"`. The false value matters. It tells assistive technology that the rest of the page is still active. The widget does not trap focus, so Tab can move out of the panel into the page behind it. The page is not made inert.

For a short form that is a reasonable choice, because a visitor who wants to leave the panel can. Do not describe the widget as a modal dialog in your own documentation, and do not add a focus trap on top without testing how it interacts with the page's own menus.

## Escape, focus and returning to the trigger

Pressing Escape anywhere on the page closes an open panel. When the panel opens, the widget remembers which element had focus. When the panel closes, focus returns to that element, and if nothing was focused and the floating button is showing, focus goes to the floating button. The message box receives focus about 30 milliseconds after opening, so a keyboard user can start typing at once.

Run this test: Tab to a button that has `data-escuta-open`, press Enter, type a sentence, press Escape, and confirm that focus lands back on the same button. If focus lands on the top of the page, a screen reader user loses their place, so fix it before release.

## Labels, names and visible prompts

Every form field needs a name a screen reader can announce. The email field has a real label tied to its input, so it reads as "Email (optional, so we can reply)". The message box has no visible label. Its accessible name comes from the placeholder for the selected type, such as "What went wrong? What did you expect to happen?".

That placeholder is the only prompt the message box shows, and it disappears as soon as someone types. For your own forms, prefer a visible label above the field and keep the placeholder as an example. A visible label also helps people with low vision or with memory difficulties, who may forget what a box is for.

The rating question is the label of the star group: the stars sit in a `role="group"` element named by the visible question, so a screen reader announces the question before the five buttons named "1/5" to "5/5". Each star is a toggle button with `aria-pressed`, and only the selected star reports itself as pressed, so the announcement says which rating is chosen. Pressing the selected star again clears the rating.

## Color contrast on the button

The widget uses your accent color for the floating button and the send button, with white text on both. The contrast depends on the color you choose, and the widget does not check it. Aim for at least 4.5 to 1 for normal text, which is the WCAG AA level for body text. A bright yellow or light pastel usually fails with white text, while a deep blue or deep green usually passes.

Check the footer as well. The brand line at the bottom of the panel uses a mid gray on white that meets the 4.5 to 1 level. It is part of the widget, so you cannot change its color with an attribute.

## What the Escuta Produto widget handles, and what is left to you

The widget covers the basics so you can focus on your own page:

- Every control is a real button, and the type buttons expose `aria-pressed`.
- The star rating is a labelled group, and the selected star reports itself as pressed.
- The message field has an accessible name that follows the selected type.
- Escape closes the panel and focus returns to the element that opened it.
- With the reduced-motion setting on, the panel opens without the slide animation and the floating button no longer lifts on hover.
- The footer text meets the 4.5 to 1 contrast level.

Two things stay with you. First, the contrast of your accent color, because the widget uses whatever `data-color` you pass. Second, the panel is non-modal and has no focus trap. That suits a short form that people may want to leave halfway, but if your page needs a modal pattern, open the widget from your own button and test the flow with a screen reader.

## Check the Escuta Produto widget with a keyboard and a screen reader

Run these steps on your own install, not on a demo page:

1. Load the page and press Tab until the floating button has focus. Confirm the focus outline is visible.
2. Press Enter, then Tab through the type buttons, the message, the stars, the email field and the send button in order.
3. Press Escape and confirm focus returns to the button you started from.
4. Turn on a screen reader, such as VoiceOver or NVDA, and listen to the dialog name, the type buttons and the error message when you send an empty form.
5. Check the color contrast of the button with a contrast checker.

For setup details, see the [widget docs](/docs/widget). For the wording of the trigger that sits in front of the panel, read [what a feedback button should say](/resources/feedback-button-copy). If your page is on a phone, [designing a feedback widget for mobile screens](/resources/feedback-widget-mobile-design) covers touch targets and the keyboard overlap, which affects the same users.

## Set up an accessible trigger in Escuta Produto

Keep the floating button for most sites, since it is the most predictable control. On pages where a bug report has a clear place, such as a settings screen, use `data-trigger="none"` and add a real button with `data-escuta-open="bug"`. Give the button text that describes the action, check its contrast, and repeat the keyboard test on that page before you ship.

## Frequently asked questions

### What makes a feedback widget accessible?

Real buttons for every control, labelled fields, a dialog name, focus that returns to the trigger after closing, and enough contrast on the button color. Test with the keyboard first, then with a screen reader.

### Should a feedback form trap keyboard focus?

Not for a short feedback form. A non-modal panel lets visitors leave with Tab, which is acceptable here. A focus trap helps only when the panel blocks the page, and it adds risk if you get it wrong.

### How do I check the contrast of my feedback button?

Use a contrast checker with your accent color and white text. Aim for at least 4.5 to 1 for normal text. The Escuta Produto widget does not check contrast, so you need to check the color you pick.

---

# Why embeddable widgets use Shadow DOM

> Shadow DOM keeps a widget's styles and markup separate from the page, so site CSS does not break the widget and the widget does not restyle the site. It does not isolate global JavaScript, document events or inherited properties, so a widget still has to reset and scope those.

Source: https://escutaproduto.com/resources/shadow-dom-for-widgets
Last updated: 2026-10-09

## Why a widget cannot trust the host page's CSS

A feedback widget runs on someone else's website. That site may load a CSS reset, a framework stylesheet, a design system or a set of global rules such as `button { background: red }` that were written for a different part of the app. Any of them can change the look of a widget that shares the same page. The widget's own styles can cause the same damage in reverse, by restyling buttons, inputs and headings the site owner built on purpose.

Shadow DOM is the browser feature that solves this. It gives a component its own DOM subtree with its own styles, so the rules on either side stop at the boundary.

## What Shadow DOM isolates

When the Escuta Produto script runs, it creates a host element with the attribute `data-escuta-produto` and attaches an open shadow root to it. Every visible part of the widget lives inside that root: the floating button, the panel, the form, the thank you screen and the styles that go with them.

Two things follow. A site rule such as `button { border-radius: 0 }` does not reach the widget's buttons, because the selector does not match elements inside another shadow tree. And the widget's own rules, such as its font stack and its input borders, do not change the rest of the page. Both directions are isolated.

## What Shadow DOM does not isolate

Shadow DOM is a boundary for the DOM and for most styles. It is not a sandbox, and several things cross it:

- **Inherited properties.** Properties such as `color` and `font-family` pass from the host into the shadow tree unless something resets them. The widget sets `:host{all:initial}` on its host and sets its own font family on every element inside.
- **Custom properties.** They always cross the boundary. The widget reads its accent color from a `--c` custom property that it sets on the host.
- **Global JavaScript.** The widget exposes `window.EscutaProduto`, and any script on the page can call it. Shadow DOM does nothing to protect it.
- **Document events.** The widget listens for Escape and for clicks on elements with `data-escuta-open` on the document. Events from inside the shadow tree bubble up, so page scripts can observe them.
- **Stacking.** A shadow root does not lift its contents above the rest of the page. The widget uses very high `z-index` values on its fixed elements so the panel stays on top of your page's own layers.
- **Access to the root.** A root opened with `mode: "open"` can be read by page code through `shadowRoot`. Open mode is what testing tools need. It is not a security boundary, so never put a secret in a widget and assume the shadow root hides it.

These are not bugs in Shadow DOM. They are the boundaries of what it does, and a good widget reset and scope exactly these properties.

## Theming across the boundary

A widget that wants to be themed from outside should expose a small, documented surface. The Escuta Produto widget has three.

1. **Attributes on the script tag.** `data-color`, `data-position`, `data-locale` and `data-trigger` control the common choices, and they are read once when the script loads.
2. **The `--c` custom property.** The widget sets it inline on the host from `data-color`. Because inline styles beat normal stylesheet rules, a normal rule in your CSS will not override it. Change `data-color` instead.
3. **The `part` attribute.** The floating button has `part="button"`, so you can style it from your own stylesheet with `::part()`:

```css
[data-escuta-produto]::part(button) {
  border-radius: 8px;
  letter-spacing: 0.02em;
}
```

Rules from the outer page that target `::part` win over the widget's own rules for normal declarations, which is why this works. Only the floating button is exposed. The panel, the form and the stars are not, so you cannot restyle them this way. If you need a different panel, build your own form on the REST API.

## Testing and automation through shadow roots

Plain DOM queries do not look inside a shadow root. `document.querySelector("button")` will never return a widget button, and a snippet such as `document.querySelector(".fab")` returns `null`. To reach the floating button in a script you have to go through the host: `document.querySelector("[data-escuta-produto]").shadowRoot`, then query inside it.

Some browser test tools, Playwright among them, pierce open shadow roots when you use their own locators, so a test that finds the feedback button by its visible text can work without extra code. Other tools need explicit steps. Check your tool's documentation before you write selectors by hand.

A second practical point: the widget mounts once, on the body. If your app replaces the contents of the body during navigation, the host element can disappear. The widget does not watch for that and mount itself again. Check that the button is still there after a route change, and load the script again if it is not.

## Where the Escuta Produto widget draws the line

The widget uses Shadow DOM for what it should isolate: its markup, its styles and its fonts. It leaves the global `window.EscutaProduto` object, the document events and the custom property boundary open, because the public API needs them. You get a small set of attributes, the `part` hook on the button and a JavaScript API with `open`, `close`, `identify` and `setMetadata`.

For setup steps and every attribute, see the [widget docs](/docs/widget). For the security side of loading a third-party script, read [content security policy for third-party widgets](/resources/content-security-policy-widgets). For how the panel behaves with a keyboard, see [how to make a feedback widget accessible](/resources/accessible-feedback-widget).

## How to check the boundary with the Escuta Produto widget

Load the page and run three checks. First, change a global button style in your own CSS and confirm the widget button keeps its look. Second, call `EscutaProduto.close()` from your console and confirm the panel closes, which shows the global API is reachable. Third, navigate between two routes in your app and confirm the floating button is still on the page. If the second check works and the third one does not, you know exactly where the boundary is on your site.

## Frequently asked questions

### Why do embeddable widgets use Shadow DOM?

Shadow DOM gives the widget its own markup and styles, so the host page's CSS cannot break it and the widget cannot restyle the page. It is the standard way to keep a third-party component visually separate.

### Does Shadow DOM stop a widget from affecting my JavaScript?

No. Shadow DOM isolates markup and most styles, but global objects, document-level events and custom properties still cross the boundary. A well-built widget exposes a small public API and nothing more.

### How can I style a widget that uses Shadow DOM?

Use the attributes the widget documents, such as its accent color and position, and use the part selector for elements that expose one. In the Escuta Produto widget, the floating button has a part, so you can restyle it from your own stylesheet.

---

# Content Security Policy for third-party widgets

> A feedback widget needs script-src to load its script, connect-src to send submissions to its API, and style-src to apply its styles. A strict style-src without unsafe-inline can block the widget's injected stylesheet, so test with your real policy before release.

Source: https://escutaproduto.com/resources/content-security-policy-widgets
Last updated: 2026-10-09

## What a Content Security Policy does to a widget

A Content Security Policy is a response header that tells the browser where a page may load scripts, styles, images and network requests from. Sites use it to block injected code. A third-party feedback widget is exactly the kind of code a policy can stop, because it loads from another domain and talks to that domain when someone sends feedback.

The rules apply per directive. If your policy has no entry that allows the widget, the browser refuses the request and logs the violation. The page keeps working, but the feedback button may do nothing, or the widget may appear without its styles.

## Which directives the widget needs

The Escuta Produto widget touches three directives:

| Directive | Why the widget needs it |
| --- | --- |
| `script-src` | To load `widget.js` from escutaproduto.com with the script tag on your page. |
| `connect-src` | To send each submission with a `fetch` call to the API on the same origin as the script. |
| `style-src` | To apply the styles the widget writes into its own shadow root. |

The widget makes no other network request on your page. It loads no fonts, no images and no second script. It uses the system font stack, and its only icon is an emoji in the button text. So if your policy has `img-src` or `font-src` rules, you do not need to add escutaproduto.com to them.

The API address is built from the origin of the script tag, so `connect-src` must include the same origin as `script-src`. If you load the script from a different host, both entries must match that host, and the submission will go to that host too.

## Write the entries for your own domain

Here is a policy that allows the widget and keeps the rest of the page strict. Adjust the `'self'` entries to match what your site already needs:

```text
Content-Security-Policy: script-src 'self' https://escutaproduto.com; connect-src 'self' https://escutaproduto.com; style-src 'self' 'unsafe-inline'
```

If your site sets its policy with a meta tag instead of a header, use the same directives in the `content` attribute. Meta-tag policies cannot set `frame-ancestors`, so keep that directive in the header if you use it.

Note that `default-src` is a fallback. If you have `default-src 'self'` and no `script-src`, the browser will block the widget script, because `'self'` does not include escutaproduto.com. Add the explicit entries rather than relying on the fallback.

## Style-src is the one people miss

The widget writes a `style` element into its shadow root. A style element is governed by `style-src`, and under a policy that lacks `'unsafe-inline'` the browser blocks it. The widget then shows unstyled elements, or a panel with no layout, while the script itself still runs.

You have three options, in order of preference:

1. Keep `'unsafe-inline'` in `style-src` if your policy allows it. It is the simplest and the most reliable choice for this widget.
2. Use a hash. The CSS text depends on the `data-position` setting, so a hash works only for one position and breaks when the value changes. Hashes are fragile for this widget.
3. Use a nonce. The widget does not set a nonce on the element it creates, so a nonce in your policy will not allow it.

Be honest about the trade-off. Allowing `'unsafe-inline'` for styles loosens the policy, but inline styles are a smaller risk than inline scripts. Your script rules are where the strong protection matters, and those stay strict.

## Use a nonce only on your own script tag

Many sites use a nonce to allow their own inline scripts. If your policy has no host allowlist for scripts and relies on nonces alone, the script tag that loads `widget.js` must carry the nonce, because the widget cannot add one for itself:

```html
<script nonce="YOUR_NONCE" src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

The widget itself has no inline script and no `eval`. Everything it does runs from the file it loads, so the nonce on that one tag is enough.

## Debug a blocked request

When something fails, open the browser console and read the violation. It names the directive that blocked the request, such as `script-src`, `connect-src` or `style-src`, and the URL. Match that to the table above and add the missing entry.

There are two separate failures that look similar from the visitor's side. A CSP block stops the request before it leaves the browser, so nothing reaches the server. A server refusal arrives as a response. For example, if the page's origin is not on your allowed-origins list, the API answers with a 403, and the widget shows the error message under the send button. The fix there is in the product settings, not in your policy.

Roll out changes in report-only mode first. The header `Content-Security-Policy-Report-Only` takes the same directives but does not block anything. It logs what would have been blocked, so you can see the widget's requests before you enforce the policy.

## Set up the policy for Escuta Produto

Follow this order on a staging copy of your site:

1. Add escutaproduto.com to `script-src` and `connect-src`, and decide how to handle `style-src`.
2. Load a page with the widget and open the panel. Check the console for violations.
3. Send a test message from the panel and confirm it appears in the inbox.
4. Add your production origin to the allowed origins in the product settings, or the submission will return a 403.
5. Only then move the policy from report-only to enforced.

Server-side calls to the [REST API](/docs/api) do not pass through a browser policy, so they need no entry. For setup steps and every attribute, see the [widget docs](/docs/widget). For the wider question of how a widget is isolated from your page, read [why embeddable widgets use Shadow DOM](/resources/shadow-dom-for-widgets). For the performance cost of loading the script, see [how to keep a feedback widget from slowing your site](/resources/feedback-widget-performance).

## Frequently asked questions

### Which Content Security Policy entries does a feedback widget need?

Script-src to load the widget file, connect-src to send submissions to the API on the same origin, and style-src to apply the widget's styles. Add the widget's host to the first two, and check the third against your policy.

### Why does a feedback widget look unstyled under my policy?

The widget writes its styles into an inline style element. A style-src rule without unsafe-inline blocks that element, so the widget loads but its styles do not apply. Allow inline styles or test the change before you enforce it.

### How do I find out what my Content Security Policy is blocking?

Open the browser console on the page and read the violation messages. Each one names the blocked directive and the URL. Running the policy in report-only mode first lets you see the blocks without breaking the page.

---

# Feedback widgets, GDPR and LGPD

> The Escuta Produto widget collects the message, type, optional rating and contact details the visitor types, the page URL, and any metadata your code passes. It sets no tracking cookies. This is not legal advice, so have a qualified person review your privacy notice.

Source: https://escutaproduto.com/resources/feedback-widget-gdpr-lgpd
Last updated: 2026-10-09

## This is not legal advice

This article describes what one feedback widget does in the browser and on the server. It is a product explanation, not legal advice. GDPR (the European General Data Protection Regulation) and LGPD (Brazil's Lei Geral de Proteção de Dados) apply to your own situation in ways that depend on where you and your visitors are, what you do with the data and what else your site collects. Read your privacy notice with a qualified lawyer or data protection professional before you publish it. Nothing here replaces that review.

## What the widget sends, field by field

The widget sends one JSON payload each time someone presses the send button. These are the fields, and where each value comes from:

| Field | Source | Required |
| --- | --- | --- |
| `message` | Typed by the visitor, at least two characters | Yes |
| `kind` | Bug, idea, praise or other, chosen by the visitor or set by your trigger | Yes, defaults to idea in the panel |
| `rating` | One to five stars, if the visitor picks one | No |
| `email` | Typed by the visitor, or taken from your identify call | No |
| `name` | Taken from your identify call only | No |
| `pageUrl` | The full address of the page, including any query string | Sent automatically |
| `metadata` | Values from setMetadata and extra keys from identify | Only if you pass them |
| `website` | The hidden honeypot field, normally empty | Sent automatically |

The server adds two more values from the request. It saves the browser's user agent, which the browser sends as a request header, and the country, which the server derives from the request. The widget script does not send a user agent field in the payload, so do not describe it as doing so.

Each field is there for a reason. The message and kind are the feedback. The email is optional, so the visitor can choose whether you can reply. The page URL tells you where the problem happened.

## Watch the page URL

The widget sends `location.href`, the full address of the current page. That includes everything after the question mark. If your app puts a token, an invitation code or an email address in the address, the widget will send it with the feedback. Clean up those parameters before you install the widget on the page, or move sensitive values out of the address. The same caution applies to the hosted feedback page, where `?email=` prefills the email field.

## What the widget stores on the visitor's device

The widget script sets no cookies and writes nothing to `localStorage` or `sessionStorage`. That is what people mean when they say the widget has no tracking cookies. The widget itself does not identify returning visitors, and it does not load analytics, fonts or other third-party scripts. After the script loads, the only request it makes is the one that sends the feedback. That request is cross-origin and uses the browser's default credential handling, so the browser does not attach your site's cookies to it.

Your site may still set cookies for other reasons, such as login sessions or your own analytics. Those belong in your privacy notice and your cookie policy, separately from the widget.

## Data you add with identify and metadata

Your code can attach more information. The identify call takes an object with an email, a name and any other keys. The email and name fill the form fields. Every other key, such as an account ID, becomes metadata on the feedback item. The setMetadata call replaces the metadata object directly, and metadata is limited to 4 KB of JSON.

This is useful and it is also where the most personal data tends to slip in. Pass the fields you actually use to answer the feedback, such as an account ID or a product version, and leave out anything you would not want in an inbox. Every value you pass travels with each feedback item sent while it is set, so a value that looks harmless on one page can still reveal more than you intended.

## Data minimization in practice

Both laws ask you to collect what you need for the purpose you state. For feedback, a practical version of that rule looks like this:

1. Keep the message required and the email optional, as the widget does.
2. Ask for a rating only if you will use it.
3. Pass an account ID in metadata, not a full name and a phone number, unless you need both to reply.
4. Strip tokens and personal addresses from page URLs before the widget loads.
5. Close items that no longer need an answer, using the Closed status in the inbox.

Each step reduces the amount you have to explain in the privacy notice.

## Write the privacy notice around the real fields

Your notice should match the table above. List the fields the widget collects, say why you collect each one, and say who in your team can read the inbox. Describe the optional fields as optional. Name the places the data goes: Escuta Produto stores it, and your notification webhook may post an excerpt to a Slack or Discord channel you control. If you use the REST API from your own servers, describe that flow too.

Escuta Produto does not record sessions, take screenshots or record the screen, so your notice does not need to describe those. Keep the notice accurate to the fields you actually send. A notice that describes features you do not use is as much of a problem as one that leaves out features you do.

For setup, the [widget docs](/docs/widget) list the options and the metadata behavior, and the [notifications docs](/docs/notifications) explain what a webhook message contains. For how a widget loads and what it sends on the page, see [content security policy for third-party widgets](/resources/content-security-policy-widgets). For the handling side, read [how to triage customer feedback](/resources/how-to-triage-customer-feedback).

## How Escuta Produto keeps the data small

The widget is built to collect a short list of fields, and the inbox is built to stay private to your team. Each product has its own inbox. Each item has internal notes that visitors never see. You can export a product's feedback as UTF-8 CSV and delete a product from its settings. Those are the controls you have, and your privacy notice should describe them accurately, not as guarantees you have not checked with your own advisers.

## Frequently asked questions

### Is this article legal advice for GDPR or LGPD?

No. It describes what the feedback widget collects and stores, so you can write your own privacy notice. Have a qualified lawyer or data protection professional review your notice and your legal basis before you publish it.

### Does the feedback widget use tracking cookies?

The widget script sets no cookies and writes nothing to local or session storage. Your own site may still set cookies for login or analytics, and those need their own disclosure in your cookie policy.

### What personal data does a feedback widget send?

The message, the type, an optional rating, an optional email, the page URL and any metadata your code passes. The server also records the browser user agent and the country. Limit each optional field to what you need to reply.

---

# How to protect a feedback form from spam

> Combine a hidden honeypot field, an allowed-origins list, rate limits and size limits. Together they catch much of the automated noise without making real customers solve puzzles. Add a CAPTCHA only after these simpler controls fail, because it also blocks people.

Source: https://escutaproduto.com/resources/feedback-form-spam-protection
Last updated: 2026-10-09

## Why feedback forms attract spam

A public feedback form is an open door. Anyone who finds the endpoint can post to it, and some of them post links, fake reviews or scripts that waste your time. The risk is not that spam breaks anything. The risk is that it fills the inbox until real customers get buried and your team stops reading.

Spam usually arrives in one of three ways: a bot that fills every input on a page, a script that calls the API directly from outside a browser, or a person who pastes the same text over and over. Each needs a different control, so one measure is rarely enough.

## Use a honeypot field, not a puzzle

A honeypot is a form field that real people never see and never fill in. The Escuta Produto widget includes one. It is an input named `website`, positioned far off screen, with `tabindex` set to `-1`, `aria-hidden` set to true and `autocomplete` turned off. A visitor using a keyboard or a screen reader skips it, and a visitor who only sees the panel never fills it in.

Bots that fill every input they find will fill it in too. The widget sends its value with every submission, so any value there is a signal. That is the whole idea: the field costs real people nothing and gives automated submissions a place to reveal themselves.

Two limits apply. A honeypot does nothing against a script that calls your API directly and ignores the form. And a bot that is smart enough to skip hidden fields will get past it. The honeypot is one layer, not the defense.

If you build your own form instead of using the widget, copy the same pattern. Give the field a name that looks tempting, such as `website` or `company`, hide it with CSS rather than `type="hidden"`, and reject any submission where it has a value.

## Allowed origins and their limits

Each product in Escuta Produto has a list of allowed origins. Only these sites can submit feedback through the widget. If a submission arrives from a site that is not on the list, the API answers with a 403 and an origin-not-allowed error, and nothing is saved.

This check is useful because it stops casual abuse. Someone who copies your widget snippet to a site you do not own will be refused. But know its limit. The origin is a header that a browser sends. A script running outside a browser can set any origin it likes. Treat the allowed list as a filter for abuse that starts in a browser, not as proof that a submission came from a real visitor.

Keep the list accurate. Add every production domain and staging domain you use, and remove old ones. If the widget stops working after a domain change, the allowed list is the first place to look. Also check the widget docs for the exact origins to add, including any subdomains you use.

## Rate limits and size limits

Escuta Produto limits each IP address to 10 requests per minute per product. A visitor who sends more gets a 429 response, which the widget shows as an error. A real visitor rarely sends ten messages in a minute, so the limit rarely touches real people, but it stops a loop from flooding the inbox.

Size limits protect the server and the inbox. The API refuses a request body over 16 KB with a 413 response. In the widget, the message box stops at 5,000 characters, and the email box stops at 254. A message that long is almost never feedback, and the cap keeps a single submission small.

Rate limits work per IP address, so a shared office network can hit the limit faster than a single home connection. If that happens, slow the flow down with clearer wording, not a looser limit.

## Why CAPTCHAs cost more than they stop

A CAPTCHA is the usual first idea, and it is often the wrong one for feedback. A puzzle asks every visitor to do work before their message counts. People on phones, people with low vision, people who use assistive technology and people who are simply in a hurry all pay that cost. Someone who gives up after one failed puzzle is a customer you lost, and you never see them in the inbox.

CAPTCHAs also do not stop a determined spammer reliably. Solving services exist, and automated tools can work around many puzzles. So the trade is poor: real customers pay the cost of every puzzle, while a motivated spammer can still get through.

A practical rule: keep the form free of puzzles, use the honeypot, the origin list and the rate limit, then watch the inbox. Only add a CAPTCHA if a specific abuse pattern survives those controls, and only on the page where it happens.

## How Escuta Produto handles spam

The widget does not include a CAPTCHA. It combines the controls described above: the honeypot in the form, the allowed origins per product, the rate limit per IP address and product, and the size limits on the message and the request body. Each product also has a key that can only create feedback, never read it, so a leaked key cannot expose your inbox. If a key is abused, rotate it in the product settings and update the snippet on your site.

For setup, the [widget docs](/docs/widget) show the snippet and the allowed origins. The [REST API docs](/docs/api) list the status codes you should handle in your own form, including the 429 and 413 responses. For the message itself, read [how to triage customer feedback](/resources/how-to-triage-customer-feedback), which covers closing spam with a short internal note so it stays closed. For how the widget handles visitor data, read [feedback widgets, GDPR and LGPD](/resources/feedback-widget-gdpr-lgpd).

Spam will still reach you sometimes. That is normal. Set a weekly routine that closes obvious spam, keeps the inbox short and leaves real messages for a proper read. The controls reduce the noise, and the routine keeps the rest manageable.

## Frequently asked questions

### What is a honeypot field in a feedback form?

A honeypot is a hidden input that real visitors never fill in. Bots tend to fill every field they find, so a value in the honeypot is a sign of automated submission. The Escuta Produto widget sends this value with every feedback submission.

### Why is a CAPTCHA a poor choice for a feedback form?

A puzzle makes every visitor do extra work, and that cost falls on people using phones or assistive technology. Spammers can often pay to solve them. Start with a honeypot, an allowed-origins list and rate limits, and add a CAPTCHA only where abuse persists.

### What does an allowed origins list do for a feedback widget?

It limits which websites can submit through the widget, so a copied snippet on an unknown site is refused. It filters browser-based abuse. It does not prove who sent a submission, because non-browser clients can set their own origin header.

---

# How to keep a feedback widget from slowing your site

> Load the widget with defer, keep it free of dependencies, and avoid network requests until a visitor sends feedback. The Escuta Produto widget is one script of about 5 KB compressed with no fonts or images. Measure your pages with and without it before you call it cheap.

Source: https://escutaproduto.com/resources/feedback-widget-performance
Last updated: 2026-10-09

## Every third-party script has a cost

A feedback widget is a third-party script, and every third-party script competes with your own content for the browser. The browser must download the file, parse it, run it and draw anything it adds. On a slow phone connection or an older device, that work can delay the moment a visitor can read or tap the page. The widget's job is small, but the cost is paid on every page where you install it, so it is worth checking.

The good news is that a widget that does little on load can be cheap. The question to answer is not "is the widget fast" but "what does the widget do on my pages, and when".

## Load the script with defer

Add the `defer` attribute to the script tag. It tells the browser to download the file while it keeps parsing the HTML, and to run the script after the document is parsed:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

Without `defer`, a script in the head can block the parser until the file arrives. With `defer`, the page content is already in place when the widget starts. The widget finds its own script tag through `document.currentScript`, and it falls back to a query for the script if that is not available, so the attributes still apply.

If you need the widget to load after everything else, insert the script yourself once the page has finished loading:

```js
window.addEventListener("load", () => {
  const script = document.createElement("script");
  script.src = "https://escutaproduto.com/widget.js";
  script.dataset.key = "pk_your_product_key";
  document.body.appendChild(script);
});
```

The trade-off is that the feedback button appears a moment later. For most sites that is fine, because nobody needs the button in the first half second.

## Keep the payload small

The Escuta Produto widget is a single file of about 5 KB compressed before any server compression. It has no dependencies, so the browser does not download a framework, a component library or a second script to run it. Its styles are part of the same file and are written into its own shadow root.

Small matters, but so does what the file does at load time. The widget builds its markup, attaches a shadow root and mounts one element on the body. The panel starts hidden, and it becomes visible only when someone opens it. A hidden panel costs little to draw, so the cost stays low on pages where nobody opens the widget.

Options that do not change the file size include `data-trigger="none"`, which only hides the floating button. Hiding the button saves visual clutter, not download time. Judge the widget by the file and the work it does, not by what it looks like.

## Make no requests until someone sends

After the script loads, the widget makes no further network request until a visitor submits the form. There are no analytics calls, no tracking pixels, no font downloads and no image requests. The only request is the POST that sends the feedback, and it happens after a click.

This is worth checking in your own network panel. Load a page with the widget, leave it alone, and look at the requests the browser made. You should see the script and nothing else from the widget's domain. If you see more, the extra requests come from your own code or from another tool on the page.

## Avoid the preflight request

When a browser sends a cross-origin request with a custom content type such as `application/json`, it first sends a preflight request with the OPTIONS method to ask permission. That is a second round trip before the real message goes out. The widget sends its body as `text/plain` with a UTF-8 charset. That content type counts as a simple request, so the browser skips the preflight and sends the message directly.

The widget also sets `keepalive: true` on the request. That flag lets the browser finish the request if the visitor navigates away right after sending, which matters for a form people submit and leave. Neither choice changes what the server receives. The body is still the same JSON, and the server reads it as the widget sends it.

## Measure the impact before and after

Do not rely on a feeling. Measure one page with and without the widget, using the same device profile and network settings each time. Run a Lighthouse audit or a WebPageTest run, and compare these results:

1. Total Blocking Time, which shows how long the main thread was busy.
2. The time until the page's main content first appears on screen.
3. The number of requests and total bytes on the page.
4. The Performance panel in Chrome DevTools, which shows any long task the widget adds at load.

Run each test three times and use the median, because one run can vary a lot. If the numbers move in a way you can see on a real phone, act on it. If they do not, the widget is probably not your bottleneck, and the next step is to look at your own images and scripts.

## What the Escuta Produto widget loads and when

The widget loads once, with the script tag you add to your page. It appends one host element to the body, then waits. It sends a request only when a visitor sends feedback. It does not poll the server, does not fetch settings on load and does not load anything from a third party. The panel and the form are built in memory and stay hidden until someone opens them.

For attributes and the snippet, see the [widget docs](/docs/widget). If the script loads on every page, check your policy as well, using [content security policy for third-party widgets](/resources/content-security-policy-widgets). For how the widget stays separate from your page's styles, read [why embeddable widgets use Shadow DOM](/resources/shadow-dom-for-widgets).

## Set up the Escuta Produto widget without slowing your pages

Install the script with `defer` on the pages where feedback makes sense, such as the product itself, rather than on every marketing page. Measure one page before and after. If you have a slow page that does not need feedback, leave the widget off it, or use the delayed loading pattern above on the pages where the button matters less. Those choices keep the widget useful without adding weight to the pages where it adds no value.

## Frequently asked questions

### Does a feedback widget slow down my website?

A small widget with defer has a modest effect, mostly from the download and the work done at load. Measure one page with and without it, using the same device and network, to see the real impact on your site.

### What does the defer attribute do on a script tag?

It downloads the script while the page is still being parsed and runs it after the HTML is read. The page content appears without waiting for the script to run, which helps the first view.

### Why does the feedback request avoid a preflight?

The widget sends its body as text/plain, which is a simple content type. The browser then skips the extra permission request it would send for a JSON body, so the message goes out in one round trip.

---

# How to collect feedback in multiple languages

> The widget picks English or Portuguese from the browser language, and data-locale overrides that choice. Visitors can write in any language, and the inbox keeps their text as written. For other interface languages, build your own form on the REST API.

Source: https://escutaproduto.com/resources/multilingual-feedback
Last updated: 2026-10-09

## Two languages in the box, and what that means

The Escuta Produto widget ships with two sets of interface text: English and Portuguese. Every label, placeholder, error message and thank you message comes in both. There is no other language in the widget, and you cannot add one with an attribute. That limit shapes the rest of this article.

Language has two parts. The interface is the text around the form, such as the title, the type names and the send button. The message is what the visitor writes, and that can be in any language. The widget controls the first part. Your visitors control the second, and the inbox keeps their text as they wrote it.

## How the widget picks a language

The widget reads its language once, when the script loads. It uses the `data-locale` attribute if you set one. If not, it uses the browser language. If the value starts with `pt`, the widget shows Portuguese. For anything else, it shows English. Because the check looks only at the first two letters, `pt-BR`, `pt-PT` and plain `pt` all give the same Portuguese text.

That means a visitor in Brazil whose browser is set to English sees English, and a visitor in Portugal whose browser is set to Spanish sees English too, since Spanish is not one of the two sets. The rule is simple, but it can surprise you, so check the result on a browser set to each language you care about.

## Override the language with data-locale

When you know the language of your page, say so. Set `data-locale` on the script tag, so the widget matches the page regardless of the visitor's browser setting:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-locale="pt-BR" defer></script>
```

Use this on a Portuguese page that people will open from any country. Use the same attribute on an English page if your visitors use Portuguese browsers. The value is not case sensitive, so `PT-br` works as well.

If your site has both languages, you have two options. The simplest is one widget per language, with each page's script tag set to its own locale. The other is to let the browser decide and accept that some visitors will see the other language. Pick the first one if the page text is clearly in one language.

## Read feedback written in other languages

Your inbox does not translate messages. A message written in Portuguese arrives as Portuguese, and a message in German arrives as German. The dashboard shows the text as written, and the CSV export keeps it in UTF-8, so accented letters and other scripts survive. The export opens correctly in Excel and Google Sheets.

For your own reading, use the translation tool you already trust, or ask a teammate who reads the language. Copy the translation into an internal note on the item, so the next person sees it. Keep the original text in the message field, because the visitor wrote that and a translation can lose a detail.

Treat a translation as a reading aid, not as the record. When you reply to a customer, reply in the language they wrote in, and check the reply with someone who reads it fluently.

## Hosted pages and email links

The hosted feedback page takes a language parameter too. A link such as the one below opens the page in Portuguese, whatever the visitor's browser setting:

```text
https://escutaproduto.com/f/your-product-slug?lang=pt
```

Use the hosted page in emails and in printed materials, where no script can run. The `lang` parameter accepts `pt` or `en`. For a third language, neither the widget nor the hosted page can show your interface text, so the next section covers the option that does work.

## When two languages are not enough

If your interface needs a language other than English or Portuguese, build your own form and send the feedback through the REST API. The API takes a JSON body with the product key, the message, the kind, the rating and the optional contact fields, and it returns the new item's ID. Your form can use any language and any layout, because you write every label yourself.

Browser calls to the API need your site's origin on the product's allowed origins list, or the API answers with a 403. Calls made from your own server usually send no browser origin header, so they normally need no list entry. Keep the form small, follow the status codes in the [REST API docs](/docs/api), and handle the 429 response with a friendly message that asks the visitor to try again in a minute.

Mix the two approaches carefully. A widget for the default languages and a custom form for a third language can share one inbox, because both write to the same product. Give your custom form a clear label so your team can tell where items came from, using the metadata field.

## Set up both languages in Escuta Produto

Start by checking the language your page actually uses. For each page, decide whether the widget should match the page with data-locale, or follow the visitor's browser. Then open the page in both languages, once with a Portuguese browser and once with an English one, and confirm the panel shows what you expect. Send one test message in each language and confirm it appears in the inbox, with the text intact and the page URL attached. For the full list of attributes, see the [widget docs](/docs/widget). For the wording of the button itself, read [what a feedback button should say](/resources/feedback-button-copy), and for the mobile layout of a bilingual panel, see [designing a feedback widget for mobile screens](/resources/feedback-widget-mobile-design).

## Frequently asked questions

### Which languages does the Escuta Produto feedback widget support?

The widget has built-in text in English and Portuguese. Any browser language that starts with pt shows Portuguese, and everything else shows English. You can set the language with the locale attribute on the script tag.

### Can visitors write feedback in a language other than the widget's?

Yes. The message box accepts any text, and the inbox keeps it as written. The CSV export is UTF-8, so it opens correctly in Excel and Google Sheets. Translation is something your team does when reading.

### How do I collect feedback in a third language?

Build your own form in that language and send the feedback to the REST API with your product key. Browser calls need your site listed in the allowed origins, while calls from your own server usually need no origin entry.

---

# Matching a feedback widget to your brand and dark mode

> Set your brand color with data-color and check that white text on the floating button meets contrast guidelines. The widget panel stays light on dark sites, so choose a color that stands out on both, and restyle the button with ::part when you need more control.

Source: https://escutaproduto.com/resources/feedback-widget-branding
Last updated: 2026-10-09

## Set the accent color with data-color

The accent color is the one setting that carries your brand into the widget. Set it with the `data-color` attribute on the script tag. The value can be any CSS color, such as a hex code, an `rgb()` value or a named color:

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" data-color="#0f766e" defer></script>
```

If you leave it out, the widget uses an indigo default. The color goes to the floating button, the selected feedback type, the focus ring on the text box and the send button. The star ratings use a fixed amber, and the thank you check uses your accent. Pick one color and check it in every state, not only on the closed button.

Keep one thing in mind. The hosted feedback page uses the accent color from the product settings, while the widget uses `data-color`. If you want the same color in both places, set it in both. They do not sync.

## Check contrast on the floating button

The button text is white, and the send button text is white too. White text on a light accent fails contrast, and the widget does not check the color for you. Aim for at least 4.5 to 1 for normal text, which is the WCAG AA level for body text. A bright orange, a yellow or a pale pastel usually fails. A deep blue, a deep teal or a deep purple usually passes.

Test the actual color, not the swatch in your design file. Paste the hex code into a contrast checker against white, then look at the button on a real phone in daylight. A color that passes on a monitor can look washed out outdoors.

## Why the panel stays light on dark sites

The panel is always white with dark text, and the widget does not follow the visitor's dark mode setting. Its border radius, shadow and text colors are fixed, and the only color you control is the accent. On a dark site this creates a bright rectangle in the corner, which some teams find jarring.

A white panel keeps the form readable no matter what the page behind it looks like, but it is still a trade-off. If your brand is dark, you have three options:

1. Choose an accent color that stands out on your dark background but keeps white text readable on the button.
2. Move the floating button to the side that suits your layout with `data-position`, so the bright button does not sit beside a dark control.
3. Hide the floating button with `data-trigger="none"` and open the panel from a button in your own dark header or menu, styled to match.

The panel itself stays white in all three cases. That is a known limit of the widget, and it is worth stating in your own internal style guide so the next designer does not try to fix it with a stylesheet.

## Position, overlap and the trigger

The floating button sits 20 pixels from the bottom and the chosen side. Set `data-position="left"` if the right side already holds a chat launcher, a cookie banner or a navigation control. The widget has no offset option, so placement is a choice between two corners, not a fine adjustment.

If neither corner works, the cleanest answer is to remove the floating button and build your own trigger. Use `data-trigger="none"` and give any element the `data-escuta-open` attribute. The element can be a text link in your footer, a menu item or a styled button. Keyboard focus returns to that element when the panel closes, so the flow still works.

## Restyle the button with ::part

The floating button has `part="button"`, so you can adjust it from your own stylesheet without touching the widget. Outer styles that target `::part` win over the widget's built-in rules for normal declarations. A small example:

```css
[data-escuta-produto]::part(button) {
  border-radius: 8px;
  font-weight: 700;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
```

Use this to match corner radius, weight and shadow to your buttons. Keep the change small, and keep the text readable. Only the floating button has a part, so you cannot restyle the panel, the stars or the form this way.

## Keep the copy consistent

The widget's copy is fixed. The title reads "Send us feedback", the subtitle reads "What's on your mind? We read every message." and the footer shows the brand name "Escuta Produto". You cannot change those strings or remove the footer with an attribute. For Portuguese pages, set the locale and the widget switches to its Portuguese copy.

If the widget's voice does not match your brand, the honest fix is to replace the trigger and build your own form on the REST API. That keeps the copy and the layout yours, and the feedback still lands in the same inbox. Until then, choose the color and position carefully, and keep the surrounding copy friendly so the two voices sit well together. For the words on the trigger itself, read [what a feedback button should say](/resources/feedback-button-copy).

## Brand the Escuta Produto widget step by step

Work through the widget in this order. First, pick the accent color and set it with `data-color`. Second, check the contrast of white text on that color. Third, load the site in a dark mode browser setting and look at the panel next to your page. Fourth, decide between the left and right corners, or hide the button. Fifth, if you want a finer style, add the `::part` rule above. Then set the same accent in the product settings for the hosted page, so your links and widget match. For every attribute, see the [widget docs](/docs/widget). For how the widget stays isolated from your styles, read [why embeddable widgets use Shadow DOM](/resources/shadow-dom-for-widgets).

## Frequently asked questions

### Does the Escuta Produto feedback widget support dark mode?

The panel is always white with dark text and does not follow the visitor's dark mode setting. You can choose an accent color that stands out on dark pages, move the button to the other side, or hide the button and open the panel from your own dark menu.

### How do I change the color of the feedback button?

Set the data-color attribute on the script tag to any CSS color, such as a hex code. Check that white text on that color meets a contrast of at least 4.5 to 1, because the widget does not check it for you.

### Can I restyle the feedback button beyond its color?

Yes, for the floating button only. It exposes a part named button, so a stylesheet rule using the part selector can change its corner radius, font weight and shadow. The panel and form cannot be restyled this way.

---

# Escuta Produto vs Canny

> Escuta Produto is the better choice when you want customer feedback kept private, sorted into one inbox across several products and sent to Slack or Discord. Canny suits teams that want a public board where users vote and follow a roadmap. Pick by whether feedback should stay with your team or be shared publicly.

Source: https://escutaproduto.com/resources/escuta-produto-vs-canny
Last updated: 2026-10-09

## The short answer

For most teams that run one or more products and want to read feedback privately, Escuta Produto is the better choice. It collects bugs, ideas and praise into one inbox, with a filter for each product, and it publishes nothing to your customers.

Canny is a public feedback board with a roadmap. Users post ideas, vote on them and follow status changes. That model suits teams that want an open conversation with their community. If you want the opposite, a private inbox your team triages each week, Escuta Produto is built for that job.

## What a public voting board does well

A public board puts ideas where everyone can see them. People vote, comment and follow requests, and a roadmap shows what you intend to build. That visibility helps when a community shapes the product, when you want a public record of progress, or when users should see that others share their request.

The board is shared space, so every request and vote is visible to the people who use it. Some teams want that. Others want a request to reach the team first, before anyone else sees it.

## Why a private inbox fits many teams

Escuta Produto keeps feedback out of public view. Each item has a type (bug, idea, praise or other), a status (new, planned, in progress, done or closed), an optional rating from 1 to 5 stars, and private internal notes that only your team sees.

Private collection suits bug reports that contain account details, feedback about unfinished work, and any situation where a public vote count should not set your priorities. Escuta Produto also saves the page URL and browser automatically, so a report arrives with the context you need to reproduce it.

## Feedback across several products

Public boards are usually set up per product, each with its own address and audience. Escuta Produto works differently. One dashboard lists your products, each product has its own inbox with filters by status and type, and each product can send alerts to its own Slack or Discord webhook.

For a founder, studio or agency with several products, that means one weekly review covers everything. Each product uses its own key, so feedback always lands in the right inbox.

## How the two compare

| | Escuta Produto | Public voting board tool |
| --- | --- | --- |
| **Focus** | Private feedback inbox for one or more products | Public board with voting and a roadmap |
| **Where feedback is collected** | Floating widget, hosted feedback page, REST API | A public board that users post to and vote on |
| **Setup** | One script tag, or a REST call from your backend | A hosted board, linked from or embedded in your site |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## Who reads the feedback and who acts on it

In a private inbox, the people who read feedback are the people who act on it. Support staff, product managers and developers see the same list, and a status change tells everyone where an item stands. Internal notes stay inside the team, so a half-formed idea or a sensitive bug report can be discussed before anyone else sees it.

On a public board, the readers include customers, prospects and the wider community. That turns the board into a conversation with people outside the company. It can build trust when you are open about what you are working on, and it means every status change is seen beyond your team. Both approaches are valid. The right one depends on how much of the process you want visible.

## Who should choose which

Choose Escuta Produto if your goal is to hear from customers privately, triage everything in one inbox and keep your priorities under your control. It is a focused tool. It does not run public voting boards or public roadmaps. If you need those, that is not what Escuta Produto is for, and a public board tool such as Canny is the better match.

## What to ask before you pick

Answer these questions before you decide:

- Should customers see each other's requests and vote in public?
- Do you need a public roadmap that customers can follow?
- Do bug reports include account details or other information you would not post publicly?
- Do you run more than one product and want a single place to review all of them?
- Do you want new feedback to reach Slack or Discord as it arrives?

If the first two answers are yes and the rest are no, a public board fits better. If the last three are yes, Escuta Produto is the stronger choice.

## Setting up Escuta Produto for your products

Once you know a private inbox fits your team, setup takes a few steps:

1. Open the product in the Escuta Produto dashboard and copy its key. Keys start with pk_.
2. Add the widget script to your site with the key in data-key. The script is about 5 KB compressed and has no dependencies.
3. Set allowed origins in the product settings, so only your own sites can submit through the widget.
4. Add a Slack or Discord webhook, so each new item reaches your team when it arrives.

The [widget docs](/docs/widget) cover every option, including colors, position and language. The [notifications docs](/docs/notifications) explain the webhook alerts. For background on the collection side, read [what is a feedback widget](/resources/what-is-a-feedback-widget) and [how to collect customer feedback](/resources/how-to-collect-customer-feedback).

## Frequently asked questions

### Is Escuta Produto a replacement for a public feedback board?

Not in the sense of a public voting board. Escuta Produto collects feedback privately and sorts it into one inbox for your team. If you want users to vote on ideas in public and follow a roadmap, a public board tool is built for that job.

### Can one inbox hold feedback for several products?

Yes. Each product has its own key and widget, the dashboard lists all of them, and the inbox has a filter per product. Each product can also send alerts to its own Slack or Discord webhook.

### Can customers see other people's feedback in Escuta Produto?

No. Escuta Produto has no public voting board and no public roadmap. Submitted feedback stays in your dashboard. The hosted feedback page is for sending feedback and is not indexed by search engines.

### How do I get feedback out of Escuta Produto?

Export a product's feedback as a CSV file. The export is UTF-8 and opens correctly in Excel and Google Sheets, so you can count themes, share a summary or keep a copy of your data.

---

# Escuta Produto vs Featurebase

> Escuta Produto is the better choice when you want a small, dependency-free widget and one private inbox for all your products, with CSV export and Slack or Discord alerts. Featurebase is an all-in-one feedback and roadmap suite. Choose the tool that matches how much of the feedback process you want in one place.

Source: https://escutaproduto.com/resources/escuta-produto-vs-featurebase
Last updated: 2026-10-09

## The short answer

Escuta Produto is the better choice for most teams that want to collect customer feedback without adding a large tool to their product. It gives you a small widget, a REST API and one private inbox that holds bugs, ideas and praise for every product you run.

Featurebase is an all-in-one feedback and roadmap suite. It brings feedback boards, roadmaps and related modules into one product. That breadth suits teams that want to run the whole public feedback process in a single place. If you want a focused inbox that your team triages each week, Escuta Produto keeps the scope small on purpose.

## Two different sizes of tool

The most useful question is how much of the process you want in one tool. Some teams want a complete feedback suite with public boards, roadmaps and more. Other teams only want a reliable way to collect feedback from inside their product, sort it and act on it.

Escuta Produto takes the second path. It does one thing in detail: collect, triage, export and alert. A suite takes the first path and covers a wider surface. Neither approach is wrong. The right size depends on the people who will use the tool every week.

## What an all-in-one feedback suite covers

A suite like Featurebase is built around shared spaces. Customers post ideas, vote, comment and follow a roadmap, and your team manages those boards from one admin area. This model works well when the community is part of how you decide what to build, and when you want the request, the discussion and the status to live together.

A suite covers more ground, so its setup spans more modules. That is expected for a tool of that scope, and it is worth weighing against how much of that scope your team will use.

## What a lightweight widget and inbox covers

Escuta Produto gives you three ways to collect feedback. The floating widget is about 5 KB compressed, has no dependencies and adds a Feedback button to your pages. The hosted feedback page lives at a product address and is not indexed by search engines. The REST API accepts feedback from your own backend or apps, and an OpenAPI 3.1 file describes it.

Everything lands in one inbox with a filter per product. Each item has a type (bug, idea, praise or other), a status (new, planned, in progress, done or closed), private internal notes, and the page URL and browser saved automatically. The dashboard shows a 30-day chart and the average rating for each product.

## How the widget sits on your page

The widget loads with one script tag. It renders inside a Shadow DOM, so its styles stay separate from your page's styles, and your page's styles do not change its look.

```html
<script src="https://escutaproduto.com/widget.js" data-key="pk_your_product_key" defer></script>
```

You can change the accent color with `data-color`, move the button with `data-position` and set the language with `data-locale` (English or Portuguese). If you already have a menu or help link, set `data-trigger` to `none` and open the form from any element with `data-escuta-open`.

## How the two compare

| | Escuta Produto | All-in-one feedback suite |
| --- | --- | --- |
| **Focus** | Private feedback inbox for one or more products | Feedback boards, roadmaps and related modules |
| **Where feedback is collected** | Floating widget, hosted feedback page, REST API | Public or branded boards and portals |
| **Setup** | One script tag, plus optional identify call | Workspace setup across several modules |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## Keeping the feedback process small

A small tool keeps the weekly steps few. You open the inbox, read new items, set statuses, write a note when you decide and close what you will not act on. The same steps apply to every product, because the inbox filters by product rather than creating a separate space for each one.

Small also means fewer moving parts on your site. The widget loads with defer, renders its own styles inside a Shadow DOM and adds one floating button. If you later need public boards or a roadmap, you can add a tool for that job without changing how you collect feedback. Escuta Produto stays focused on the collection and triage step, and it exports everything as CSV when you want to combine it with other data.

## Who should choose which

Choose Escuta Produto when you run one or more products, want feedback in a private inbox and prefer a small tool with a clear job. It does not include public voting boards, public roadmaps or surveys. If you need those, that is not what Escuta Produto is for, and an all-in-one suite is the better match.

Choose a feedback suite when a public community is central to your product process and you want boards, roadmaps and feedback in one place.

## Setting up the widget and inbox

Escuta Produto keeps setup short. Copy the key for your product, add the script to your site, then identify logged-in users so their name and email fill the form. The [widget docs](/docs/widget) list every option, and the [API docs](/docs/api) cover server-side submissions.

If your product is built with Next.js, the [Next.js guide](/docs/nextjs) shows where to place the script. For a broader view of what to collect, read [what is in-app feedback](/resources/what-is-in-app-feedback) and [feedback widget best practices](/resources/feedback-widget-best-practices).

## Frequently asked questions

### Is Escuta Produto a full replacement for a feedback suite?

Not if you need public boards, roadmaps and community voting. Escuta Produto is a focused tool for collecting, triaging and exporting private feedback across your products. It is a strong fit when the inbox is the main need.

### How big is the Escuta Produto widget?

The widget is about 5 KB compressed, has no dependencies and renders inside a Shadow DOM so its styles stay separate from your page. It adds a floating Feedback button, which you can hide with data-trigger set to none.

### Can I send feedback from my own backend instead of the widget?

Yes. The REST API accepts a POST request with the public product key and a message, plus optional fields such as kind, rating, email and metadata. It returns a 201 response with the new item id, and an OpenAPI 3.1 file describes every field.

### Can I get feedback out of Escuta Produto for analysis?

Yes. Export a product's feedback as CSV. The file is UTF-8 and opens correctly in Excel and Google Sheets, so you can count themes or share a summary with your team.

---

# Escuta Produto vs Usersnap

> Escuta Produto is the better choice when you want a customer feedback inbox that collects bugs, ideas and praise across several products, with statuses, notes and CSV export. Usersnap is built around visual bug reporting for QA and website teams. Pick by whether you need customer feedback for product decisions or annotated visual reports for testing.

Source: https://escutaproduto.com/resources/escuta-produto-vs-usersnap
Last updated: 2026-10-09

## The short answer

Escuta Produto is the better choice when your main goal is to hear from customers and sort what they say. It collects bugs, ideas and praise into one private inbox, with a filter for each product, statuses your team sets and a CSV export you can take anywhere.

Usersnap is built around visual bug reporting. Its approach centers on annotated screen feedback for QA and website teams. That focus helps when testers and reviewers need to point at a problem on the screen. If your priority is customer feedback for product decisions, Escuta Produto keeps that work in one inbox.

## When a screenshot is the report

Some problems are easier to show than to describe. A misaligned button, a broken layout on one browser or a confusing step in a checkout is clearer with a picture. Visual bug reporting tools are designed around that moment. A tester or reviewer marks up the page, and the report arrives with the visual context attached.

That approach fits QA, staging reviews and teams that test websites before release. It is less about what customers wish the product did and more about what is broken right now.

## What visual bug reporting is built for

Visual tools focus on the person who finds a defect and needs to show it clearly. They suit a small group of reporters who work on the same site, and they help developers reproduce problems quickly.

If your main audience is that group, a visual tool matches the work. Feedback from paying customers, prospects and users who never open a bug tracker is a different job.

## What a feedback inbox is built for

Escuta Produto is built for the second job. Customers send feedback from a floating widget, a hosted page or your own backend through the REST API. Every item arrives with a type, a status, a page URL, a browser and an optional rating. Your team adds private internal notes, closes duplicates and moves items through new, planned, in progress, done and closed.

Each product has its own filtered inbox, and each product can post new items to its own Slack or Discord webhook. The 30-day chart and average rating show whether feedback is rising or falling after a release.

## Feedback from customers versus reports from testers

Customer feedback and tester reports overlap, but they answer different questions. A tester asks whether the page is broken. A customer asks whether the product is useful, what they expected and what they would change.

Escuta Produto does not capture screenshots or screen recordings. If your main need is an annotated screen capture attached to every report, that is not what Escuta Produto is for. If your main need is the customer voice across several products, that is the job it does well.

## How the two compare

| | Escuta Produto | Visual bug reporting tool |
| --- | --- | --- |
| **Focus** | Customer feedback inbox for bugs, ideas and praise | Visual bug reports for QA and website teams |
| **Where feedback is collected** | Floating widget, hosted feedback page, REST API | Visual capture from a widget on the page |
| **Setup** | One script tag, plus optional identify call | Widget install, often with reviewer roles |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## What a report needs to be useful

A useful report answers four questions: what the person expected, what happened, where it happened and who sent it. Visual tools answer the first two with pictures and annotations. A feedback inbox answers the third and fourth with the page URL, the browser and the identity of a logged-in user, and it captures the first two in the message and type the person chooses.

Escuta Produto asks for a type (bug, idea, praise or other), a message of at least two characters and an optional rating. The form stays short on purpose, so the customer has little to fill in and the team gets enough to decide what happens next. A run of one-star bug reports after a release is a signal even when each message is short, and the 30-day chart makes that run easy to spot.

## Who should choose which

Choose Escuta Produto if you want a private inbox for customer feedback across several products, with statuses, internal notes and alerts that reach Slack or Discord. Choose a visual bug reporting tool if your daily work is marking up pages for testers and developers.

Some teams use both. A visual tool handles defects found during testing, and the feedback inbox handles what customers ask for and say about the product.

## Setting up customer feedback in Escuta Produto

To start collecting customer feedback, add the widget to your site with the product key in data-key. The [widget docs](/docs/widget) list the options, including colors, position, language and the trigger setting. Set allowed origins in the product settings so only your own sites can submit through the widget.

Then connect a Slack or Discord webhook, so each new item reaches your team when it arrives. The [notifications docs](/docs/notifications) explain the alert format. For a wider view of the collection side, read [how to collect customer feedback](/resources/how-to-collect-customer-feedback) and [what is a feedback inbox](/resources/what-is-a-feedback-inbox).

## Frequently asked questions

### Does Escuta Produto capture screenshots?

No. Escuta Produto does not capture screenshots or screen recordings. It saves the page URL and the browser automatically with each item, which gives useful context without an image. If you need annotated screen captures, that is a different job.

### Is Escuta Produto good for bug reports from customers?

Yes. Customers can choose the bug type in the widget, describe what happened and leave an optional rating. Each report arrives with the page URL and browser, and your team can move it through statuses and add private notes.

### Can I use Escuta Produto alongside a visual bug reporting tool?

Yes. Many teams use a visual tool for defects found during testing and an inbox for what customers ask for and say. Each tool handles the kind of input it was built for, and they do not depend on each other.

### Where does feedback go after a customer submits it?

It goes into the product inbox in your dashboard. If you set a Slack or Discord webhook, a short alert with the type, sender, rating, excerpt and dashboard link is posted to that channel as well.

---

# Escuta Produto vs Userback

> Escuta Produto is the better choice when you want a minimal widget and an open REST API that feed one private inbox across several products. Userback is a broader visual feedback platform built around annotated reports. Choose by whether you want a small collection layer for customer feedback or a wider visual feedback workflow.

Source: https://escutaproduto.com/resources/escuta-produto-vs-userback
Last updated: 2026-10-09

## Minimal widget or broad visual platform

Escuta Produto and Userback both help teams gather feedback from people who use a product. The difference is the size of the tool. Escuta Produto is a minimal collection layer: a small widget, an API and one inbox. Userback is a broader visual feedback platform, with annotated reports and a workflow built around them.

The right choice depends on what you want to build around the feedback. A small layer fits teams that already have their own process and want a reliable way to receive input. A broader platform fits teams that want the capture, review and reporting steps in the same tool.

## What a visual feedback platform adds

A visual platform adds features around the moment a person points at something on the page. Reporters can mark up what they see, add context and send it into a review process. That suits product teams that review many visual issues, designers who want to see the exact element, and QA teams that compare reports with a release.

If those steps are the heart of your workflow, the extra surface area is useful. It also means more to learn and configure before the first report arrives.

## Where a small widget and API fit

Escuta Produto keeps the surface small. The floating widget is about 5 KB compressed, has no dependencies and renders inside a Shadow DOM, so its styles stay separate from your page. The hosted feedback page gives people a link to send feedback without installing anything. The REST API accepts feedback from your own backend or apps.

Every item then lands in one inbox with a filter per product. Each item has a type (bug, idea, praise or other), a status (new, planned, in progress, done or closed), an optional rating and private internal notes. The page URL and browser are saved automatically.

## Building on the REST API

If you want feedback to start from your own code, the REST API accepts a JSON body with the public product key and a message of at least two characters. The other fields are optional. A successful request returns a 201 response with the new id.

```bash
curl -X POST https://escutaproduto.com/api/v1/feedback \
  -H "Content-Type: application/json" \
  -d '{"key":"pk_your_product_key","kind":"bug","message":"Export button does nothing on Safari"}'
```

The public key can only create feedback. It cannot read the inbox, so it is safe to use from a client when you need to. The API allows 10 requests per minute per IP and product, and the request body is limited to 16 KB. An OpenAPI 3.1 file at /openapi.json describes every field.

Handle errors on your side. A 429 response means the product has sent too many requests within a minute, so back off and retry later. A 404 means the key is unknown, which usually points to a typo in your configuration. A 400 means a required field is missing or too short, such as a message under two characters.

## How the two compare

| | Escuta Produto | Visual feedback platform |
| --- | --- | --- |
| **Focus** | Minimal widget and API feeding a private inbox | Annotated visual feedback and review workflow |
| **Where feedback is collected** | Floating widget, hosted feedback page, REST API | Widget with visual capture on the page |
| **Setup** | One script tag, or a REST call from your backend | Widget install plus workflow configuration |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## Who sends the feedback

Feedback arrives from people with different levels of context. Customers know what they wanted but not how the product is built. Your team knows the codebase but may not notice what users find confusing. A small inbox holds both kinds of input side by side, and the filter per product keeps a mobile app's feedback separate from the web app's.

When your product also has a backend, the REST API lets you send feedback from the place where the context already exists. A failed export job, a cancelled account flow or a support ticket can each create an item with metadata that explains the situation. That is context a widget alone cannot see, and it arrives in the same inbox as everything else.

Keep metadata to fields you will read or filter later, such as the app version or the screen a person was on. A short, consistent set of keys keeps the inbox easy to scan and the CSV export easy to sort.

## Who should choose which

Choose Escuta Produto when you want a small layer that collects customer feedback, feeds one inbox for all your products and fits into your own backend. It does not capture screenshots or screen recordings, and it does not run a visual review workflow. If those are your priority, a visual feedback platform is the better match.

Choose a visual platform when annotated, visual reports are central to how your team works every day.

## Installing the Escuta Produto widget and API

Start with the widget. Copy the key for your product from the dashboard and add the script to your site. The [widget docs](/docs/widget) list the options for color, position, language and trigger. Identify logged-in users with their email and name so the form fills in for them.

Add the REST API when you want feedback from the backend, such as a cancellation flow or a support form. The [API docs](/docs/api) show the request format and the error responses. For background on which moments to ask, read [feedback after first success](/resources/feedback-after-first-success) and [how to collect customer feedback](/resources/how-to-collect-customer-feedback).

## Frequently asked questions

### Is Escuta Produto a visual feedback tool?

No. Escuta Produto is a feedback inbox. It collects written feedback with type, status, rating, page URL and browser, but it does not capture screenshots or screen recordings. It is built for collecting and triaging customer feedback across several products.

### Can I send feedback without the widget?

Yes. The REST API accepts feedback with the public product key and a message, and returns a 201 response with the new id. It works from your backend, a server action or an app, and an OpenAPI 3.1 file describes the request format.

### How many feedback requests can the API accept?

The API accepts up to 10 requests per minute per IP address and product. Larger request bodies are refused with a 413 response once they pass 16 KB, so keep messages focused and send extra detail as metadata when needed.

### Can one widget setup cover several products?

Each product has its own key and widget, and all of them appear in one dashboard with a filter per product. Each product can also post alerts to its own Slack or Discord webhook.

---

# Escuta Produto vs Marker.io

> Escuta Produto is the better choice for customer-facing feedback: bugs, ideas and praise from users, sorted in one private inbox across products. Marker.io is built for internal visual bug reporting by teams and QA, with reports sent into issue trackers. Choose by who files the report and where it needs to go next.

Source: https://escutaproduto.com/resources/escuta-produto-vs-marker-io
Last updated: 2026-10-09

## Who files the report

The first question is who will file the report. In a visual bug reporting tool, the reporter is usually a team member: a developer, a QA tester, a designer or a product manager reviewing a staging site. In a customer feedback inbox, the reporter is a user of your product, a visitor or a customer who has no idea how your tracker works.

That difference shapes everything else. Internal reporters know the vocabulary and the tools. Customers need a simple form, a clear choice of type and a way to say what they hoped would happen.

## Visual bug reporting for teams and QA

Marker.io fits teams that test and review a website or app before and after release. A reporter marks up the page, adds details and sends the report onward. Connected issue trackers then carry the work forward, so the report becomes a ticket with the context attached.

That workflow suits a team that already lives in an issue tracker and wants defects to reach it quickly. If your team works that way every day, a visual tool matches the process.

## Customer feedback for product decisions

Escuta Produto is built for the customer side. The floating widget and hosted feedback page collect bugs, ideas and praise from users. Each item has a type, a status (new, planned, in progress, done or closed), an optional rating and the page URL and browser saved automatically.

Your team triages the inbox, adds private internal notes and closes duplicates. Each product has its own filter and can post new items to a Slack or Discord webhook. The 30-day chart and average rating show whether feedback is rising after a release, and a CSV export lets you analyze the results in a spreadsheet.

## Issue tracker sync versus a feedback inbox

Issue trackers and feedback inboxes do different jobs. A tracker turns a defect into work for engineers. An inbox holds what customers say, so you can decide what to build and tell people when it ships.

Escuta Produto does not sync with Jira, Linear or GitHub. If your main need is for every report to become a tracker ticket automatically, that is not what Escuta Produto is for. Its alerts go to Slack or Discord, and its export gives you the full dataset as CSV, which you can import wherever you need it.

## How the two compare

| | Escuta Produto | Visual bug reporting tool |
| --- | --- | --- |
| **Focus** | Customer feedback inbox for bugs, ideas and praise | Internal visual bug reports for teams and QA |
| **Where feedback is collected** | Floating widget, hosted feedback page, REST API | Visual capture from the page by team members |
| **Setup** | One script tag, plus optional identify call | Widget install and connection to your trackers |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## How reports move through a team

Visual reports move through a team in a short loop. A reporter marks the problem, a developer reproduces it, the fix goes out and the reporter checks the result. That loop is tight, and it suits the people in it, because everyone knows the codebase and the release schedule.

Customer feedback moves on a longer loop. Someone reads the item, decides whether it matters, may ask the customer a question and only then builds the change or closes the item. The status field and internal notes exist for that longer loop. When you reply to a customer, you write from your own email, which keeps the conversation personal.

## Who should choose which

Choose Escuta Produto when the reporters are your customers and you want their bugs, ideas and praise in one private inbox across all your products. Choose a visual bug reporting tool when the reporters are your team and each report should become a ticket in your tracker.

Many teams need both. Use a visual tool for internal QA and an inbox for what customers send you.

## How to set up Escuta Produto for customer feedback

Start with the widget. Copy the product key, add the script to your site and set allowed origins so only your own sites can submit. The [widget docs](/docs/widget) cover the options, and the [hosted page docs](/docs/hosted-page) explain the link you can share in emails and help content.

Then add a Slack or Discord webhook. The [notifications docs](/docs/notifications) describe the alert, which includes the type, product, rating stars, sender, a short excerpt and a dashboard link. For the process around these items, read [how to triage customer feedback](/resources/how-to-triage-customer-feedback) and [what is feedback triage](/resources/what-is-feedback-triage).

Before you publish the widget, set allowed origins so only your own sites can submit, then send one test item from a staging site. Check that the item arrives with the page URL and browser attached, and that the alert reaches your Slack or Discord channel. Those two checks catch most setup mistakes in a few minutes. Once the first items arrive, read a full week of them before you change any settings, so your routine rests on real feedback rather than assumptions.

## Frequently asked questions

### Does Escuta Produto create tickets in Jira or Linear?

No. Escuta Produto has no Jira, Linear or GitHub sync. Its alerts go to a Slack or Discord webhook, and its CSV export lets you move feedback into any tool you use for tracking work.

### Is Escuta Produto meant for internal QA reports?

It is built for feedback from customers and users. Internal testers can use it, but its strength is the customer inbox with statuses, private notes and a filter per product. For annotated visual reports from testers, a visual bug tool is the better match.

### How do customers send feedback to Escuta Produto?

They can use the floating Feedback button added by the widget, a hosted feedback page for the product, or a request your backend sends through the REST API. The form asks for a type, a message, an optional rating and an email field when the user is not identified.

### Can I analyze Escuta Produto feedback outside the dashboard?

Yes. Export the product's feedback as a CSV file. The UTF-8 export opens correctly in Excel and Google Sheets, so you can sort by type or status, count repeats and share a summary with your team.

---

# Escuta Produto vs Hotjar Feedback

> Escuta Produto is the better choice when you want a dedicated feedback inbox for bugs, ideas and praise across several products, with statuses, notes and alerts. Hotjar is a behavior analytics suite that includes feedback alongside heatmaps and recordings. Choose by whether you need a feedback workflow or a wider view of how people use your pages.

Source: https://escutaproduto.com/resources/escuta-produto-vs-hotjar-feedback
Last updated: 2026-10-09

## Analytics first or feedback first

Hotjar and Escuta Produto both put a feedback option in front of people who use a site, but they start from different questions. A behavior analytics suite starts with what people do: where they click, how they scroll and how they move through a page. Feedback is one part of that picture. Escuta Produto starts with what people say and makes the feedback itself the product.

If your first question is "where do people get stuck?", an analytics suite is the natural starting point. If your first question is "what do our customers want us to fix or build?", a dedicated inbox is more direct.

## What a behavior analytics suite shows you

Analytics suites bring several views together. Heatmaps show where attention goes, recordings show individual sessions, and a feedback widget lets visitors leave a comment on a page. Each part answers a different question about behavior, and having them in one place helps teams that want to compare what people do with what they say.

That breadth is useful when a product or marketing team wants one tool for many questions. It also means feedback sits next to other data rather than being the center of the workflow.

## Where a dedicated inbox helps

A dedicated inbox puts feedback at the center. Escuta Produto collects bugs, ideas and praise from a floating widget, a hosted page or your backend. Every item has a type, a status (new, planned, in progress, done or closed), an optional rating, private internal notes and the page URL and browser saved automatically.

The inbox has a filter per product, so teams that run several products review each one in its own view. The dashboard shows a 30-day chart and the average rating, and a CSV export lets you analyze the data in a spreadsheet or share it with your team.

## Reading feedback next to behavior data

Feedback often makes more sense next to what people did. A complaint about a checkout step is easier to act on when you can see where visitors drop off. Escuta Produto does not record sessions or show heatmaps, so it cannot place a complaint inside a replay for you. If you need replays, pair a replay or analytics tool with the inbox.

The page URL saved with each item is the bridge. Filter by page, look up the same URL in your analytics tool and compare the two views for that page.

## How the two compare

| | Escuta Produto | Behavior analytics suite with feedback |
| --- | --- | --- |
| **Focus** | Dedicated feedback inbox for one or more products | Heatmaps, recordings and feedback in one suite |
| **Where feedback is collected** | Floating widget, hosted feedback page, REST API | Feedback widget on the site |
| **Setup** | One script tag, plus optional identify call | Analytics tracking plus the feedback widget |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## Turning a page into a question

A useful pattern starts from the page. Take the URL of a page that gets many complaints, look at it in your analytics tool and note what visitors do there. Then read the feedback items that came from that URL in the Escuta Produto inbox. Often the two views point at the same problem: a form that asks for too much, a button that looks clickable but is not, or an explanation that answers no question.

Write the finding as an internal note on the feedback item, so the next person who reads it sees the link between the complaint and the behavior. Keep the note factual and short. Over a few weeks, those notes become a record of which pages cause the most friction and why.

## Who should choose which

Choose Escuta Produto when feedback is the main input to your roadmap, when you run several products and want each product's inbox separate, and when you want bugs, ideas and praise triaged by your team every week. It does not run heatmaps, session replays or screenshots. If those are your priority, that is not what Escuta Produto is for.

Choose a behavior analytics suite when you want to watch how people use the site and collect feedback as one part of that view.

## Adding a feedback inbox to your analytics stack

If you already use an analytics tool, Escuta Produto can sit beside it. Add the widget with your product key, identify logged-in users and keep the analytics tracking as it is. The [widget docs](/docs/widget) explain the options, and the [API docs](/docs/api) show how to send feedback from your backend.

For the routine that turns feedback into decisions, read [how to triage customer feedback](/resources/how-to-triage-customer-feedback). For the practical side of collecting feedback on each page, read [feedback widget best practices](/resources/feedback-widget-best-practices).

Before you roll the widget out across the site, try it on a few key pages. Confirm the Feedback button does not cover controls on a phone, and send one test item to make sure the alert reaches your Slack or Discord channel. Small checks like these save time later, and they make sure the first real feedback you receive arrives intact.

## Frequently asked questions

### Does Escuta Produto record user sessions?

No. Escuta Produto does not record sessions, show heatmaps or capture screenshots. It collects written feedback with the page URL and browser saved automatically, and it keeps that feedback in a private inbox.

### Should I replace my analytics tool with Escuta Produto?

Not necessarily. Escuta Produto is a feedback inbox, not an analytics suite. Many teams keep their analytics tool for behavior data and use Escuta Produto for the feedback itself, comparing the two by page URL.

### Can I see which pages feedback comes from?

Yes. Each item saves the page URL it was sent from, and the inbox filters let you review feedback by status and type. You can export the full list as CSV to compare feedback with page-level data.

### How do I add the feedback widget next to an analytics script?

Add the Escuta Produto script with your product key in data-key, typically in the same place as your other scripts. Use defer so it loads after the page, and keep your analytics script as it is.

---

# Escuta Produto vs Google Forms and Typeform

> Escuta Produto is the better choice for feedback that arrives from inside your product, with the page URL, browser and product already attached and every item in one inbox. Google Forms and Typeform are general form builders that suit surveys and structured questionnaires. Choose by whether people should answer a set of questions or tell you what happened where they were.

Source: https://escutaproduto.com/resources/escuta-produto-vs-google-forms
Last updated: 2026-10-09

## Forms are a place people go

A form builder works by sending people to a form. You share a link, embed a page or drop a block into an email, and the person fills it in somewhere else. That works well for questionnaires, event registrations and structured surveys, where you want every person to answer the same questions in the same order.

Escuta Produto works differently. The Feedback button sits inside your product, so people send a note from the screen where the problem or idea came up. The question changes from "fill in this form" to "tell us what just happened here".

## What a form builder does well

Google Forms and Typeform are general tools, and they do their job well. You design questions, choose the answer types, collect responses in a sheet or dashboard and analyze the results. They are good for research with a fixed set of questions, for quick polls among a known group and for any task where the questions matter more than the place the answer came from.

Use a form builder when you want the same questions asked of everyone, such as a satisfaction survey after onboarding or a short interview screener.

## Why in-context feedback arrives with more detail

Feedback sent from inside a product arrives with context that a standalone form does not capture. Escuta Produto saves the page URL and the browser automatically, so you know where the person was. If you identify logged-in users, their name, email and any metadata you choose travel with the item. You can also add your own values with setMetadata, up to 4 KB of JSON, to record things such as the feature a person was using. Keep private data out of metadata unless your team needs it.

That context turns a vague "it is slow" into a report with a page and browser attached. Your team can reproduce the issue, see who sent it and reply with the right information.

## Where a form still makes sense

Forms still win in several cases. When you want a structured survey, when you need the same fields for every respondent, or when feedback comes from people who are not using your product, a form builder is the better fit. Escuta Produto is not a survey tool and does not run NPS campaigns or survey logic. If you need those, that is not what Escuta Produto is for.

## How the two compare

| | Escuta Produto | Google Forms or Typeform |
| --- | --- | --- |
| **Focus** | In-app feedback inbox for bugs, ideas and praise | General form and survey builder |
| **Where feedback is collected** | Floating widget inside the product, hosted page, REST API | A form link, an embed or a page you publish |
| **Setup** | One script tag, plus optional identify call | Build the form, then share or embed the link |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## Questions to ask before you choose

Ask who the respondent is. If they are customers using your product, an in-app widget reaches them where the problem is. If they are people you invited, such as beta testers or interview candidates, a form with a clear purpose and a short list of questions works better.

Ask what you will do with the answers. Structured questions suit counts and comparisons. Open feedback suits discovery, where you want the wording people actually use. Many teams keep both: a form for a research round every few months and the widget for the everyday flow of bugs, ideas and praise.

## Who should choose which

Choose Escuta Produto when feedback should come from inside your product, you want the page and browser attached, and you run one or more products with a filter for each. Choose a form builder when you need a set of structured questions answered by a defined group of people.

Many teams use a form for research and the widget for everyday feedback. The two cover different moments.

## Moving from a shared form to an in-app widget

If you currently send people to a shared form for product feedback, start with one entry point. Add the widget to the page where most feedback appears, copy the product key into the script and keep the form for research. The [widget docs](/docs/widget) list the options for position, color, language and the trigger setting.

For a wider view of where to ask, read [how to collect customer feedback](/resources/how-to-collect-customer-feedback) and [what is a feedback widget](/resources/what-is-a-feedback-widget). The [hosted page docs](/docs/hosted-page) explain the link you can share when people have no product screen to start from.

Keep the old form link working while you move. Add a line to your help content that says where feedback goes now, and read both sources in the same weekly review until the old form stops getting responses. That way no request is lost during the switch, and you can compare the early results from both channels. Tell customers in your release notes that the widget is now the place to send product feedback, and keep the research form for structured questions, so each tool keeps a clear job.

## Frequently asked questions

### Can Escuta Produto replace Google Forms for surveys?

No. Escuta Produto is a feedback inbox, not a survey tool. It does not run surveys, NPS campaigns or survey logic. It collects what people write from inside your product, with the page and browser attached, and keeps it in one inbox.

### What context does an Escuta Produto item include?

Each item saves the page URL and the browser automatically. The server adds the country. If you identify a logged-in user, their name and email fill the form, and any other values you set are stored as metadata.

### Can people send feedback without opening my product?

Yes. The hosted feedback page has its own address for each product and can be shared in emails or help content. It is not indexed by search engines, so it works as a direct link rather than a public page.

### Can I export feedback to a spreadsheet?

Yes. Export a product's feedback as a CSV file. It is UTF-8 and opens correctly in Excel and Google Sheets, so you can sort, filter and count the responses yourself.

---

# Escuta Produto vs Intercom for product feedback

> Escuta Produto is the better choice when your goal is a focused inbox for product feedback across several products, with statuses, private notes and CSV export. Intercom is a customer messaging platform for conversations, help and outreach. Choose by whether you need an ongoing conversation with each customer or a clear place to collect and triage product feedback.

Source: https://escutaproduto.com/resources/escuta-produto-vs-intercom
Last updated: 2026-10-09

## Messaging platform or feedback inbox

Intercom and Escuta Produto both talk to customers inside a product, but they are built for different conversations. A customer messaging platform is built for conversations: live chat, help content, support and messages sent to people as they use the product. Escuta Produto is built for one kind of input: feedback that customers choose to send, which your team triages as a list.

The practical question is whether you want an ongoing dialogue with each customer or a single place where product feedback waits to be sorted. The answer decides which tool belongs at the center of your routine.

## What a customer messaging platform covers

A messaging platform treats each customer as a person in a thread. Support questions, onboarding messages, help articles and outbound messages all live in that same tool. Teams that want one place for every customer contact often choose this model, because the conversation carries the full history.

That breadth suits support teams and customer success teams who answer questions all day. It also means the tool is shaped around the conversation, which is a different starting point from a feedback list.

## What a focused feedback inbox covers

Escuta Produto collects feedback from three places: a floating widget, a hosted feedback page and a REST API that your backend can call. Each item has a type (bug, idea, praise or other), a status (new, planned, in progress, done or closed), an optional rating, private internal notes and the page URL and browser saved automatically.

The inbox has a filter per product, a 30-day chart, counts by type and the average rating. A CSV export gives you the whole list for analysis, and each product can post new items to a Slack or Discord webhook. The tool does one job, and the list stays readable.

## Keeping conversations and feedback apart

Many teams end up with feedback spread across chat threads, email and support tickets. A message about a bug in the middle of a support conversation can get lost, and a product idea buried in a long thread is hard to count. Keeping feedback in its own inbox makes it easier to see patterns across customers.

You can keep both. Support handles questions in the conversation tool, and product feedback goes to the inbox where the team triages it. Escuta Produto does not reply to customers for you and does not send automatic emails, so you answer from your own email, and the status on each item tells you whether it is still open.

## How the two compare

| | Escuta Produto | Customer messaging platform |
| --- | --- | --- |
| **Focus** | Focused inbox for product feedback across products | Conversations, help content and customer messaging |
| **Where feedback is collected** | Floating widget, hosted feedback page, REST API | Chat and messages inside the product |
| **Setup** | One script tag, plus optional identify call | Messenger install and conversation settings |
| **Data export** | CSV export in UTF-8, opens in Excel and Google Sheets | Check the vendor's documentation |
| **Notifications** | One Slack or Discord webhook per product | Check the vendor's documentation |

Positioning as of October 2026; check each vendor's site for current details.

## Questions that belong in a feedback inbox

Some messages are product feedback even though they arrive as support. A customer who writes that a report is hard to find, or that a workflow takes too many steps, has told you something about the product. Those messages are easy to lose inside a support thread, where the next reply matters more than the pattern behind it.

Pull those items into a product inbox with a type and a status, and read them as a group each week. The CSV export gives you the same list in a spreadsheet, so a pattern across support and in-app feedback becomes visible without anyone rereading every thread.

## Who should choose which

Choose Escuta Produto when you want a clear, private place to collect bugs, ideas and praise, triage them each week and export them for analysis. It does not run live chat, help centers or outbound messages. If you need those, that is not what Escuta Produto is for.

Choose a customer messaging platform when conversations with each customer are the core of your support and growth work, and feedback is one input among many.

## Adding a feedback inbox next to your support tools

If you already run a messaging tool, add the Escuta Produto widget beside it rather than instead of it. Copy the product key, add the script and identify logged-in users, so their name and email fill the form. The [widget docs](/docs/widget) list every option, and the [API docs](/docs/api) describe how to send feedback from your backend.

For guidance on when to ask for feedback, read [collecting feedback after a support ticket](/resources/feedback-after-support-ticket) and [how to ask for feedback during onboarding](/resources/onboarding-feedback). For the routine that follows, read [how to triage customer feedback](/resources/how-to-triage-customer-feedback).

Agree on who owns the product inbox. A named person or a small rotation reads new items each week, sets their statuses and replies to customers from their own email. Without an owner, even a well-chosen tool goes stale, and feedback waits in a list that nobody opens. Decide early what counts as a feature request, a bug and a question, so the same kind of message gets the same label every week.

## Frequently asked questions

### Does Escuta Produto have live chat?

No. Escuta Produto does not run live chat, help centers or outbound messages. It collects product feedback through a widget, a hosted page and a REST API, and keeps it in a private inbox for your team to triage.

### Can Escuta Produto replace a customer messaging platform?

No, they cover different jobs. A messaging platform manages conversations with customers. Escuta Produto collects and triages product feedback. Many teams use both, with the messaging tool for support and the feedback inbox for product input.

### Does Escuta Produto send emails to customers?

No. Escuta Produto does not send automatic emails to customers. You reply from your own email. Each item has a status, so you can see which requests you have already answered.

### How do I connect Escuta Produto to my existing tools?

The integration options are Slack and Discord incoming webhooks, one per product, plus the REST API for sending feedback from your backend. A CSV export lets you move the full list into a spreadsheet or another system.

---

# Should you build your own feedback widget?

> Build your own feedback widget if collecting feedback is part of the product you sell or you have unusual requirements. Otherwise, adopting a tool such as Escuta Produto saves you from building storage, spam protection, rate limits, an inbox, alerts and exports. The widget is the easy part; the backend and the routine around it are where the work lives.

Source: https://escutaproduto.com/resources/build-vs-buy-feedback-widget
Last updated: 2026-10-09

## The short answer

Build your own feedback widget only when collecting feedback is itself the product you are making, or when you have requirements that no existing tool meets. For most product teams, the form is the smallest part of the job. The rest, meaning storage, spam protection, rate limits, an inbox, alerts and exports, takes longer than the form and needs care for as long as the product runs.

Escuta Produto is a ready-made option for that second group. It gives you a 5 KB (compressed) widget, a REST API, one inbox for all your products and Slack or Discord alerts, so your time goes to the product itself.

## What building a feedback widget really involves

A first version is quick. A text box, a submit button and a POST request to your server can work in an afternoon. The trouble starts after the first submission. Someone has to store the message, show it to the team, decide what happens to spam and make sure the widget works on every page that needs it.

Each of those steps is small on its own. Together they become a second product that needs maintenance, testing and documentation.

## Storage, spam and rate limits

A feedback endpoint is a public door. Anyone who finds it can send messages, so you need limits and checks from the start. You need to decide how many requests one address can make, how large a message can be, which sites may submit, and how to catch bots that fill in hidden fields. You also need to store the results safely and make sure one product's feedback never shows up in another's view.

Escuta Produto handles these parts with allowed origins for each product, a limit of 10 requests per minute per IP and product, a hidden honeypot field and size limits. Those are useful details, but they are also details you would need to design, test and keep working if you built them yourself.

## Notifications, statuses and export

An inbox is more than a table. Your team needs statuses (new, planned, in progress, done and closed), a way to add private notes, filters by type and status, search and a view of what came in recently. You also need alerts that reach the channel your team already reads, and an export for analysis.

Escuta Produto includes those pieces: a per-product inbox with filters, internal notes, a 30-day chart, counts by type, the average rating, Slack and Discord alerts and CSV export in UTF-8. If you build them, each one becomes a feature to maintain.

## When building your own makes sense

Building is the right call in a few cases. If feedback collection is a product you sell to your own customers, you need the code to behave exactly your way. If your data rules are unusual and an external store would not meet them, you need control over where the data lives. And if your team enjoys this kind of infrastructure, the work can be a good use of time.

Be honest about the maintenance. A widget you own is also a widget you must keep working, secure and up to date.

## When adopting Escuta Produto makes more sense

Adopting a tool makes more sense when the feedback is a means to an end. You want to hear from customers, decide what to build and tell people when it ships. In that case, the backend and the inbox are a distraction.

Escuta Produto runs on Cloudflare Workers with data in Cloudflare D1, and it works across English and Portuguese out of the box. It fits teams with several products because each one has its own key, its own filter and its own alert channel.

## How the trade-offs compare

| | Build your own | Adopt Escuta Produto |
| --- | --- | --- |
| **Starting point** | Write the form, endpoint and storage | Add one script tag and copy the product key |
| **Spam and limits** | Design, build and maintain them | Allowed origins, rate limits, honeypot and size limits included |
| **Inbox** | Build statuses, filters, notes and search | Statuses, notes, filters and a 30-day chart included |
| **Alerts and export** | Build the integrations and exports | Slack or Discord webhook and CSV export included |
| **Control** | Full control of code and data | Your data stays in one inbox, exported on request |

Positioning as of October 2026; check each vendor's site for current details.

## Deciding for your team

Ask three questions. Is collecting feedback a product you sell or a job you must do well? Do you have data rules that no outside tool can meet? Would your team rather spend the next month on the product than on the feedback backend? If the first two answers are no and the third is yes, adopting a tool is the simpler path.

## Adopting Escuta Produto without building the backend

Start with the widget. Copy the key for your product, add the script with defer and set allowed origins so only your sites can submit. The [widget docs](/docs/widget) cover the options, and the [API docs](/docs/api) describe how to send feedback from your own server.

Then connect a Slack or Discord webhook so the team sees new items as they arrive. The [notifications docs](/docs/notifications) explain the alert format. To decide what the widget should collect, read [what is a feedback widget](/resources/what-is-a-feedback-widget) and [what is a feedback inbox](/resources/what-is-a-feedback-inbox).

## Frequently asked questions

### Is building a feedback widget a bad idea?

No. It is a reasonable choice when collecting feedback is part of your product or your data rules rule out outside tools. The risk is underestimating the backend, which includes storage, spam protection, rate limits, an inbox, alerts and exports.

### What is the hardest part of a feedback widget?

The backend. The form takes an afternoon, but the endpoint has to resist spam, limit requests, store messages safely, show them to the team and keep working as the product changes. Those parts need ongoing attention.

### How does Escuta Produto protect against spam?

It uses allowed origins for each product, so only your sites can submit through the widget. It also applies a limit of 10 requests per minute per IP and product, adds a hidden honeypot field and enforces size limits on messages.

### Can I switch to a tool later if I start by building my own?

Yes, as long as you keep the data in a format you can export. Exporting the feedback as CSV is a practical way to move the history, and the new widget can replace the old form on the same pages.

---

# Feedback widget vs a mailto link

> A mailto link opens an email client with an address filled in, which is quick to add but loses context and mixes product feedback into a personal inbox. A feedback widget sends structured feedback with the page URL, browser, type and status attached, and keeps it in one inbox. Use a mailto link for a simple contact address; use a widget when feedback is a routine.

Source: https://escutaproduto.com/resources/feedback-widget-vs-mailto-link
Last updated: 2026-10-09

## Why a mailto link is tempting

A mailto link is the simplest feedback tool you can add to a website. It is one line of HTML, it needs no backend and it works in almost any browser. Click it and the visitor's email app opens with your address already in the To field.

That simplicity is why many products start with one. It answers a real need: a visitor wants to reach you, and an email address is the most familiar way to do it. The question is what happens after the message arrives.

## What gets lost in a mailto message

A mailto message is only as good as the text the person types. They may not say which page they were on, which browser they used, which account they had or what they clicked just before the problem. Your team then spends time asking follow-up questions, and some people never reply.

Context is what makes feedback actionable. A bug report that says "the export button does nothing" is hard to reproduce. The same report with the page URL, the browser and the account type is much easier to act on.

## Inbox noise and how it grows

Email is built for conversation. Feedback sent to a shared or personal mailbox sits alongside invoices, support questions, newsletters and partner requests. Each message needs someone to read it, decide what it is and file it somewhere. Without a routine, the list grows and the useful messages get buried.

There is also the question of ownership. When a message lands in one person's inbox, the rest of the team does not see it. When two people reply to the same request, you end up with duplicated effort or a customer who hears two different answers.

## What a feedback widget sends instead

A feedback widget adds a structured form to the page. The visitor chooses a type (bug, idea, praise or other), writes a message and can give a rating from 1 to 5 stars. The widget saves the page URL and browser automatically, so the report arrives with context.

Escuta Produto stores each item in one inbox with a filter per product. Items have statuses (new, planned, in progress, done or closed), private internal notes and an optional email field that is hidden when the user is identified. The widget is about 5 KB compressed and has no dependencies, so the page does not grow much.

## A quick comparison

| | Mailto link | Escuta Produto widget |
| --- | --- | --- |
| **Context** | Only what the person types | Page URL and browser saved automatically |
| **Structure** | Unstructured text in an email | Type, rating, message and email fields |
| **Where it lands** | A personal or shared mailbox | One inbox with a filter per product |
| **Tracking** | Manual, by moving or flagging emails | Statuses, internal notes and a 30-day chart |
| **Export** | Copy and paste from your email client | CSV export in UTF-8, opens in Excel and Google Sheets |

Positioning as of October 2026; check each vendor's site for current details.

## What a widget does not replace

A widget does not replace a person who can answer a question. Some visitors want to talk about a contract, a partnership or a refund. Those messages belong in a conversation with a named person who replies. Keep a contact address for them, and make the contact page say where each kind of message should go.

The widget also does not reply for you. Escuta Produto does not send automatic emails to customers, so each answer comes from your own address. That keeps the human part of the relationship where it belongs, and the widget handles the steady stream of product feedback.

## When a mailto link is enough

A mailto link is a fine choice when you want a general contact address, when messages are rare and personal, or when the conversation is the point. A small business that wants a person to answer a question about a service may never need a widget.

If you want a steady stream of product feedback, the balance changes. Add the widget where feedback belongs, keep the email address for direct contact and make sure the two do not collide in one shared mailbox.

## Replacing the mailto link with Escuta Produto

Start with the page where people most often write to you about the product. Add the widget script with your product key in data-key, and set allowed origins so only your own sites can submit. If you want a link instead of a floating button, set data-trigger to none and open the form from any element with data-escuta-open, such as a Feedback link in your footer.

Here is the current mailto link you might be replacing:

```html
<a href="mailto:hello@example.com?subject=Feedback">Send feedback</a>
```

The [widget docs](/docs/widget) explain the trigger and open options in full. The [hosted page docs](/docs/hosted-page) describe a link you can use in emails when the visitor has no product screen to start from. For the wider process, read [how to collect customer feedback](/resources/how-to-collect-customer-feedback) and [what is feedback metadata](/resources/what-is-feedback-metadata).

Test the change on a staging site before you publish it. Submit one item, check that the page URL and browser appear in the inbox, and confirm the Feedback button does not block your main navigation on a phone. A few minutes of checking makes the switch from email to a widget feel routine for your team and your visitors.

## Frequently asked questions

### Why is a feedback widget better than a mailto link?

A widget sends structured feedback with the page URL and browser saved automatically, and it lands in one inbox with statuses and notes. A mailto link relies on the person typing the context and places the message in an email client that the rest of the team may not see.

### Does the widget work without a contact email?

Yes. The email field is optional and is hidden when a user is identified through the widget. Visitors can send feedback with just a type and a message, and your team can reply later if an address is provided.

### Can I keep a mailto link on my site alongside the widget?

Yes. Many teams keep a general contact address for direct questions and use the widget for product feedback. Keeping them separate stops feedback from mixing with support and sales mail in one shared inbox.

### How do I move old feedback emails into an inbox?

Start with new feedback in the widget. For older emails, copy the useful items into internal notes on a new Escuta Produto item, or summarize them in a spreadsheet and export that. Use the status field to mark what you have already handled.
