Security & privacy

The widget runs on your users' browsers, on your domain. This page is the full account of what it can do, what it sends, and how to lock it down.

Your project key is public

Keys starting pk_ are publishable. They sit in your page source where anyone can read them, and that is fine, by design. A project key can do exactly one thing: submit feedback to that project.

A project key canA project key cannot
Post a new piece of feedback
Read the widget's own appearance settings
Read any feedback, yours or anyone else's
See themes, members, or account data
Change any setting

API keys are different

Keys starting sk_, created under Settings → Developers, read your feedback and themes. Those are secret. Never put one in front-end code, and rotate it immediately if it leaks.

Lock it to your domains

Add your domains under Settings → Projects. Once the list is non-empty, submissions are only accepted from those origins, so somebody who copies your key out of your page source cannot post with it from theirs.

An empty list means any origin, which is the right default for getting started and the wrong one to stay on. Subdomains are matched, so acme.com covers app.acme.com.

Server-side calls and the allowlist

A request from your backend has no Origin header, and a project with an allowlist refuses those rather than waving them through, since a missing header is trivially forged. If you post from a server or a mobile app, use a project with an empty allowlist for that traffic.

Content Security Policy

If your site sends a CSP header, the widget needs two entries: one to load the script, one to reach the API. Without them the browser blocks it silently and the only trace is a console error.

Content-Security-Policy
script-src  'self' https://www.usevoicebox.dev;
connect-src 'self' https://www.usevoicebox.dev;

That is the whole list. The widget loads no fonts, no images, and no third-party code, so font-src, img-src and frame-src need nothing added.

Its styles live inside a shadow root and are injected as a stylesheet, so a style-src without 'unsafe-inline' can still block them, leaving an unstyled panel rather than no panel. If yours is strict, allow the widget origin there too.

It cannot break your page

  • Style isolation. Everything renders in a Shadow DOM root with :host { all: initial }. Your CSS cannot reach in, including !important rules on *, and nothing we ship leaks out.
  • No globals but one. The script defines window.Voicebox and nothing else. It does not patch built-ins, add polyfills, or touch fetch.
  • Fails quietly. If our API is unreachable the widget does not render and throws nothing into your error tracking. A feedback tool having a bad day must never take a customer's site with it.
  • Never blocks. Loaded async and mounted on an idle callback, so it renders after your page is interactive, not before.

What actually gets sent

On submit, and only on submit. Nothing is transmitted while somebody is merely browsing your site.

one submission
{
  "body":     "the message they wrote",
  "type":     "IDEA | ISSUE | PRAISE | QUESTION | OTHER",
  "rating":   1-5, or null if you turned the scale off,
  "email":    only if they chose to type one,
  "pageUrl":  the page they were on when they sent it,
  "referrer": document.referrer, if any,
  "locale":   navigator.language,
  "metadata": whatever you passed to identify()
}

There are no cookies, no localStorage, no fingerprinting, and no cross-site tracking of any kind. The widget cannot see your other pages, your users' sessions, or anything they type outside its own textarea.

What reaches the AI provider

Three fields, and no others: the message body, the type they picked, and the rating. The email address and every identity trait are held back by construction, not by a filter that could be forgotten, so the model never receives anything that names a person.

You can switch analysis off entirely under Settings → General. Feedback is still collected, stored and shown, it simply is not scored or grouped, and nothing leaves for a model at all.

Spam and abuse

  • No captcha. A honeypot field and a timing check catch scripted submissions without making a real person identify traffic lights.
  • Rate limited per IP, so one source cannot flood a project.
  • Origin checked against your allowlist on every request.

Retention and your users' rights

Submitter IP addresses are kept for seven days, purely to make the rate limit work, and then deleted automatically. Everything else is kept until you delete it.

You can export everything your workspace holds as JSON, or delete the workspace and all of it, from Settings → General, without asking us. For the contractual side, see the Data Processing Agreement and the Privacy Policy.

You are the controller

Feedback your users submit is your data. We process it on your instructions to run the service, and we do not sell it, share it, or use it to train anything.