Broken Here

Project settings

Origins, the error prompt, masking, internal sites and request bodies.

A project's settings live in its Settings tab, beside Inbox and Site check, in seven sections:

SectionWhat is in it
SetupThe checklist a new project starts from, the install code, and whether the widget has loaded
GeneralThe name, this is an internal site, and deleting the project
Sites & accessThe site URL, allowed origins and blocked origins
Capture & privacyYour API, request bodies, redacted keys and masked elements
TrackersAsana, ClickUp, GitHub Issues, GitLab, Jira, Linear, monday.com, Notion, Trello and the help desks (Crisp, Freshdesk, Front, HubSpot, Intercom, Zendesk), each with its Send instantly switch
GitHub & coding agentGitHub first, then the coding agent that works on the repository it reaches
Auto-mergeThe project's switch for letting small fixes merge themselves on the Team and Agency plans, and what is still missing before it can

Setup is a checklist, not a second copy of the others: each step says where it stands and links to the one section that holds the setting. The one thing it saves itself is Allow on your site URL, which adds that site to the allowed origins in one press. The Settings tab opens on Setup while it has something to say (a step not done, a site refused, a tag gone quiet), and on General otherwise.

Allowed origins

Under Sites & access, one origin per line:

https://acme.com
https://admin.acme.com

Only these origins may send reports with your key. Leave it empty and reports are accepted from anywhere, which is fine while you are testing and wrong once the site is public.

An origin is the scheme, the host and the port, with no path and no trailing slash. Paste a whole URL if that is easier; saving reduces it to its origin, and the box shows you what was stored. A bare acme.com is read as https://acme.com.

Several environments

The same app usually runs in several places at once (a dev server, a review app per pull request, a staging site, production) and they are one project. An entry may carry one *:

https://acme.com
https://*.preview.acme.com
https://*-abc123xyz-ew.a.run.app
http://localhost:*

In a host, * stands for part of one label and never a dot: *.preview.acme.com matches pr-12.preview.acme.com and not a.b.preview.acme.com, nor preview.acme.com itself. As a port it stands for any port, which is what a dev server needs. The scheme is exact either way, so trusting https:// does not trust http://.

A pattern is only as safe as the part that is not a *. On Cloud Run the project hash goes in the fixed part and the service name in the wildcard, because anyone with a Google account can name a service admin, and nobody else has your hash. A wildcard over a whole provider (*.vercel.app, *.netlify.app, *.a.run.app, *.run.app, *.com) describes every site on it and is refused, and the settings page names the line it did not save. So is a label under a provider whose fixed part is not a token of your own: *app.vercel.app is anyone's evilapp. Under a provider, put the * at a - and keep at least eight characters of your own beside it, such as a project hash: *-abc123xyz-ew.a.run.app. Under a domain you own, pr-*.admin.hemea.com needs none of that. What counts as a provider comes from the public suffix list, the list browsers use to decide which sites are the same site.

A pattern under a provider accepts reports and is never handed a team member's session. On vercel.app or netlify.app anyone can open a site whose name fits *-acme-team.vercel.app, and nothing in a pattern says which providers allow that. Sign-in from the widget works on an origin written out whole and on a pattern under a domain you own. Each environment is its own browser origin, and the browser keeps origins apart, so a member signs in once per review app.

Your API

List the URL prefixes of your own backend under Capture & privacy. Setup's optional Point it at your API step links there.

https://api.acme.com
https://acme.com/api

Each line is an origin, with the same * as allowed origins and the same refusals, and optionally the path your backend lives under. A bare api.acme.com is read as https://api.acme.com, and a line that is not saved is named on the page. A line covers what is under it: https://acme.com/api covers /api/orders and not /apiary, and https://api.acme.com does not cover https://api.acme.com.evil.io.

Two things use this list. The first is the error prompt. When a request to one of these fails while a visitor is on the page, a small notice appears in the widget's corner: Something failed on this page., with the request under it (POST /api/orders failed (500)), a Report it button and a × to dismiss it. Report it opens the form with that line already filled in as the summary, so the visitor can send it as it is or add a sentence first. A notice goes away by itself after 20 seconds, and once none is left the usual report button comes back.

Each failing request gets a notice of its own, up to five at once. One the visitor has dismissed or reported is not offered again until the page's address changes.

Not every failure is worth interrupting someone over:

PromptsDoes not prompt
No response at all, 5xx, 404, 405400, 401, 403, 409, 422, 429

A request refused on its merits is usually the interface working. Prompting on every failed form validation is how a prompt becomes something people dismiss without reading.

With the list empty, nobody is ever interrupted. A visitor pulled into a report by someone else's broken ad script is worse than no report at all.

The second is ownership. Requests to these origins count as your code when the widget decides what is worth keeping; see What gets captured.

Masked elements

Under Capture & privacy, list the CSS selectors whose contents are blurred before the screenshot is taken and left out of the text of any element a reporter points at:

.customer-name
#invoice-total

Password inputs are always masked, without asking. Blurring happens in the browser, before the image exists, not to the image afterwards.

Selectors apply to the page, to any same-origin frame inside it and inside any open shadow root, because the screenshot draws the contents of those too. A frame from another origin can be neither read nor drawn, so nothing from one reaches a report in the first place.

A selector that does not parse is skipped rather than thrown, so a typo cannot break capture. But it also masks nothing, and nothing says so. Check a real report after adding one.

This is an internal site

Under General, turn it on for a site only your team visits, such as an admin app or an internal tool. Reporters get one extra field, Add to an existing ticket. A link to a Notion page there adds the report to that page, and any other link is kept on the report as related.

Leave it off for anything a client can reach. The field is gated twice. The widget hides it, and the server drops the value whatever was posted.

Capture request bodies

This is under Capture & privacy, and it is off by default. Bodies are the one thing a report can carry that is genuinely your customers' data, so a project has to ask for them.

When you turn it on, four rules hold at once:

  • Failed requests only: no response, or status 400 and above
  • Your API only: the page's own origin or a line of Your API, never a third party, however much of a domain it shares with you
  • Redacted in the browser, before the report is sent. Any field whose name contains one of the words in Redacted keys becomes [redacted]. Redacting on the server would already be too late
  • Truncated to 2 kB

Headers and cookies are never captured, with or without this setting, and there is no way to turn them on.

Redacted keys

The list of words is yours. A new project starts with pass, token, secret, auth, key, cvv, card, iban, otp, session and cookie, one per line. A word matches anywhere in a field's name, case-insensitively, so pass covers password and passphrase, and key covers stripe_key and sort_key alike. Over-matching costs a line of a bug report, which is the cheaper mistake.

The same words apply with bodies on or off, in two more places: the parameters of the page's URL, and the keys of an object your code passes to console.warn or console.error. Text your code prints is kept as written, JSON inside it included. See What gets captured.

Add a word your API uses (ssn, dob), take one out that keeps hiding something you need, or replace the whole list. Words are taken literally, so a.b means that key and nothing else. An empty list redacts nothing. If you have changed the list and want the original back, Restore the default list puts it in the box; save to keep it.

On this page