Skip to content

Privacy & data

This page describes exactly what the Patchrooms widget collects, where that data lives, and who can read it. Everything here is grounded in how the widget and server actually behave — not aspirational policy.

When someone files a report, the widget attaches a small context object describing the page the report was filed from, plus whatever content the reporter chose to add (text, a screenshot, a voice note, a quoted selection).

The widget reads a handful of values from the browser and attaches them to every report:

FieldSourceExample
urlwindow.location.hrefhttps://app.example.com/settings?tab=billing
pathnamewindow.location.pathname/settings
hostwindow.location.hostapp.example.com
userAgentnavigator.userAgentMozilla/5.0 …
languagenavigator.languageen-US
viewportwindow.innerWidth/innerHeight{ width: 1440, height: 900 }
devicederived from UA + widthdesktop / tablet / mobile
timestampclient clock2026-06-03T12:00:00.000Z
consoleErrorscaptured console.error linesup to the last 10 messages

Console errors. If you opt in to console capture, the widget installs a console.error hook that keeps a ring buffer of the last 10 error messages on the page. Each message is truncated to 2000 characters. These are attached so a report can show what went wrong in the browser at the time. Only console.error is captured — console.log, console.warn, and console.info are not.

These are common worries, so to be explicit:

  • No automatic DOM scraping. The widget does not serialize your page’s HTML, form values, or input fields.
  • No automatic screenshots. A screenshot is only attached when the reporter explicitly takes one inside the widget.
  • No DOM selectors by default. A CSS selector is only stored when the reporter explicitly picks an element (a “selection” block — see below).
  • No cookies or session tokens. The ingest endpoint that receives reports runs with open CORS and no cookies — it is authenticated only by your public project key.
  • No keystroke logging, no network interception, no analytics beacons.

Beyond the page context, a report is a list of blocks. There are four kinds, and each only exists because the reporter added it:

Text

A free-form message. Up to 10,000 characters. Whatever the reporter types.

Screenshot

A PNG or JPEG image, up to 2 MB. Taken by the reporter from inside the widget.

Voice note

An audio clip (WebM or MP4), up to 10 MB and 5 minutes. Recorded on demand.

Selection

A quoted snippet of on-page text (up to 2,000 chars), with the element’s URL and CSS selector so it can be located later.

A selection block is the only place a CSS selector is stored, and it is stored only when the reporter actively selects an element. It records the quoted text, the page URL, and a selector string for that element.

Screenshots and voice notes are uploaded as binary blobs to Google Cloud Storage. The flow is:

  1. The widget uploads the file to the ingest endpoint, which writes it to the project’s GCS bucket and records a small metadata document (project, bucket, object key, MIME type, byte size) in the database.

  2. The report itself is then submitted referencing those blobs. The server verifies each blob belongs to the project before binding it to the report.

Text and selection blocks are stored inline with the report; only screenshots and audio become GCS objects.

Access is scoped to the organization that owns the project. There is no per-report sharing and no public link.

  • Any member of the organization (owner, admin, or member) can view every report in every project belonging to that org.
  • Screenshots and audio are served only through a session-authenticated dashboard route that checks the viewer is a member of the owning organization before streaming the file. Blob responses are marked same-origin so other sites can’t embed them.
  • The public ingest endpoint can write reports (with your project key) but cannot read anyone else’s data back.

Each report carries a status you can move through as you triage it: new, triaged, in-progress, and closed. Closing a report keeps it in the project; it does not remove the data.

Orphaned uploads. If a screenshot or audio file is uploaded but its report is never submitted (for example, the reporter abandons the widget), the file stays unbound. A cleanup job removes unbound blobs — both the GCS object and its metadata — once they are older than 24 hours.

Your application can attach extra attributes to reports (custom fields, and any additional metadata the host app chooses to pass). Anything you supply this way is stored alongside the report and is visible to org members exactly like the rest of the report. Treat it the same as any other field: don’t push secrets, access tokens, or personal data you wouldn’t want a teammate to read.

The comment box, voice notes, and screenshots are exactly as private (or not) as the rest of your report: visible to everyone in your organization, stored on our servers, and not deletable by you today. Use that as your guide.

Safe and useful:

  • Steps to reproduce, expected vs. actual behavior.
  • What you were trying to do and what broke.
  • Non-sensitive identifiers (a public order number, a feature name).

Avoid pasting or capturing:

  • Passwords, API keys, session tokens, or 2FA codes.
  • Full payment-card numbers or banking details.
  • Personal data about third parties you have no right to share.
  • Anything you would not want every member of your organization to see.