API
Public feedback widget
Drop-in JavaScript widget to capture feedback and NPS scores from your own product directly into ProductOS, keyed by origin, no API token in the browser.
The ProductOS feedback widget is a small JavaScript snippet you drop into your own product to let users send feedback or an NPS score directly into a ProductOS workspace. No API token in the browser, no CORS setup, one script tag.
Want your own UI instead, or to submit from a server or a mobile app? Use the Feedback API. It takes the same publishable keys.
What it ships
- A floating launcher button (lower-right by default; draggable, and it snaps to the nearest edge of the viewport).
- A drawer with two modes: Feedback (a category chip plus text) and Score (NPS 0 to 10 with an optional comment).
- Keyboard shortcuts:
⌘/orCtrl+/toggles the drawer,Esccloses it,⌘↵orCtrl+↵submits, and in Score mode1to9,0(10) and-(0) pick a score. - Submissions go to
POST /api/v1/public/widget/feedback, authenticated by a workspace publishable key and checked against the key's allowed origins.
Issuing a publishable key
- Settings → Widget & API → Publishable keys → New key.
- Enter:
- Name: for your own records, e.g. "Marketing site".
- Allowed origins: comma-separated hosts, e.g.
acme.com, *.acme.com, localhost:3000. A host without a port matches any port;*.acme.commatches subdomains;*allows any origin (not recommended for production).
- Copy the
pk_live_*key. It's shown once. Unlike API tokens, it is safe to ship in the browser: it can only submit feedback, and only from the origins you allowed.
Installing
Drop this into your HTML:
<script
src="https://productos.ca/widget.js"
data-key="pk_live_xxxxxxxxxxxxx"
data-mode="feedback"
async
></script>The script mounts itself. data-mode picks the drawer that opens first: feedback or nps.
Programmatic setup
Skip data-key and call init yourself to theme the widget or identify the user:
<script src="https://productos.ca/widget.js" async></script>
<script>
window.addEventListener("load", () => {
ProductOSWidget.init({
key: "pk_live_xxxxxxxxxxxxx",
mode: "feedback", // or "nps"
theme: {
mode: "auto", // "light" | "dark" | "auto"
accent: "#5e6ad2",
radius: 8, // or "none" | "sm" | "md" | "lg"
},
launcher: { enabled: true, position: "br" }, // "br" | "bl" | "tr" | "tl"
});
// Attribute submissions to the signed-in user.
ProductOSWidget.identify({ userId: "usr_8842", email: "jane@acme.com", name: "Jane Doe", company: "Acme" });
});
</script>The theme also accepts background, foreground, muted, border and fontFamily. Identified users appear on the submission in ProductOS, so your team can follow up. Call ProductOSWidget.identify(null) on sign-out.
Opening from your own button
ProductOSWidget.open();
ProductOSWidget.close();Set launcher: { enabled: false } to hide the floating button and open the drawer only from your own "Send feedback" link. ProductOSWidget.destroy() removes the widget entirely.
Building your own UI
Don't build on /api/v1/public/widget/feedback: it's the widget's own endpoint. It only accepts browser requests from allowed origins, returns no ID, and has no safe retries. Use the Feedback API instead. It accepts the same fields, returns the submission's FDBK- reference, takes secret keys for server-side calls, and lets you read the status back.
Attribution
The drawer footer shows "Powered by ProductOS". Whether it can be hidden is controlled by ProductOS on the server, per workspace; contact support to remove it.
Privacy
- The widget calls ProductOS once when it loads (to read its configuration) and once per submission. Requests are sent without cookies.
- It keeps small pieces of state, such as where the launcher was dragged, in the page's localStorage.
- The submitter's IP is stored with the submission for abuse review. It isn't shown in the app.
- Identity passed to
identifyis visible to your team. Don't pass anything you wouldn't want a teammate with access to the workspace to see.
Next
- Feedback API: the same intake, with your own code.
- Feedback widget & API settings: managing keys and origins.
- Authentication: for non-widget API access.