Skip to main content
Once the script loads, the widget exposes one global function:
Everything here is optional — the widget works fully with just the script tag.
CommandArgumentsWhat it does
identify{ userId?, email?, name? } · { jwt } · nullAttach the current user’s identity to their feedback (or null to clear on logout).
triggerOpen the widget from your own UI.
config{ launcher?, confirmation? }Choose who owns the injected launcher and confirmation UI.
oneventName, callbackListen for submit, submit_error, or close.
destroyRemove the widget and its listeners from the page.

Identify your users

Tell Produck who’s giving feedback so each submission ties back to a real user instead of being anonymous. Call identify once you know who they are — typically right after login.
All three fields are optional — send whatever you have.

Pass a login token (JWT)

Already holding the user’s session token? Hand it over and we read the standard sub / email / name claims for you — one line for Supabase, Auth0, Clerk, Firebase, or Cognito:
We only read the claims (the signature is not verified), so this labels feedback inside your own project — it is not an authentication check.

Server-rendered pages (no JavaScript)

If your backend already knows the user at render time, set data-user-* on the script tag and skip identify entirely. data-user-jwt works the same way if you’d rather pass a token:

Clear identity on logout

If you need to identify a user before the script finishes loading, queue the call first. The widget replays anything queued the moment it boots, so nothing is lost:

Open the widget from your own UI

Wire up your own “Give feedback” button with trigger:

Listen for events

React when a user submits or dismisses the widget — for example, to show a thank-you toast.
EventFires when
submitFeedback is successfully submitted. May fire after close when a failed submission is retried.
submit_errorA submission fails. Payload: { error, retry } — call retry() to resubmit the same feedback (re-fires submit on success).
closeThe widget surface closes — after a submit, a cancel, or a failed submit.

Bring your own UI

The widget injects two surfaces you can take over. Each is an ownership setting, not a boolean, so it stays meaningful as more options arrive:
SurfaceValuesDefault
launcherdroplet · nonedroplet (the floating duck button)
confirmationtoast · nonetoast (the post-submit success/error toast)
Set none on a surface to suppress the built-in version and render your own. Turning a surface off never removes capability — trigger, submit, and submit_error keep working.
Set the same thing with no JavaScript using data-* attributes on the script tag:
To call config before the script finishes loading, queue it with the same stub shown above for identify — the widget replays it on boot.
The keyboard shortcut (⌘/Ctrl + Shift + Z) still opens the widget when launcher is none — it’s an invisible trigger, not a visible surface.

Clean up on logout

Call destroy to remove the widget when a user’s session ends. On shared devices, this keeps the next user from seeing the previous one’s identity.