Once the script loads, the widget exposes one global function:
Everything here is optional — the widget works fully with just the script tag.
| Command | Arguments | What it does |
|---|
identify | { userId?, email?, name? } · { jwt } · null | Attach the current user’s identity to their feedback (or null to clear on logout). |
trigger | — | Open the widget from your own UI. |
config | { launcher?, confirmation? } | Choose who owns the injected launcher and confirmation UI. |
on | eventName, callback | Listen for submit, submit_error, or close. |
destroy | — | Remove 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:
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.
| Event | Fires when |
|---|
submit | Feedback is successfully submitted. May fire after close when a failed submission is retried. |
submit_error | A submission fails. Payload: { error, retry } — call retry() to resubmit the same feedback (re-fires submit on success). |
close | The 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:
| Surface | Values | Default |
|---|
launcher | droplet · none | droplet (the floating duck button) |
confirmation | toast · none | toast (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.