> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rumorly.io/llms.txt
> Use this file to discover all available pages before exploring further.

# JS snippet

> Ask the question inside your product with a 4 kB script.

For products that don't ask "how did you hear about us?" anywhere yet. The snippet shows a small card with your question and a few quick picks, and sends the answer along with your user's id so it can be matched to billing.

* About 4 kB gzipped, no dependencies.
* Renders in a Shadow DOM, so your CSS can't break it and its CSS can't leak into your page.
* Keyboard accessible: Escape closes it and focus stays inside the card while it's open.
* Never asks the same browser twice. Closing the card counts as an answer for that purpose.

[See it running on a demo page](https://rumorly.io/widget-demo). It uses a demo key, so nothing is stored.

> [!NOTE]
>
> The snippet and the REST API need the Growth plan or higher. The 14-day signup trial includes them.

## 1. Create a publishable key

In **Settings → API keys**, create a key of type **Publishable** and list every origin it may be used from, for example `https://app.yourproduct.com`. Publishable keys (`pk_live_…`) are safe in the browser because they can only:

* create responses (they can never read data),
* from the origins you listed (any other `Origin` gets `403`),
* at most 10 submissions in a burst, then one every 6 seconds, per key and visitor IP (`429` with `Retry-After` after that).

## 2. Add the script

```html theme={null}
<script src="https://api.rumorly.io/widget/rumorly.js" defer></script>
<script>
  window.addEventListener("load", function () {
    Rumorly.init({ publishableKey: "pk_live_…" });
    Rumorly.identify({ email: user.email, userId: user.id, name: user.name });
    Rumorly.ask({ trigger: "first-session-end" });
  });
</script>
```

Call `identify` as soon as you know who the user is. `userId` is sent as `external_id`, which is the most reliable way to match the answer to a Stripe customer (see [How revenue is matched](/concepts#how-revenue-is-matched)).

## Options

**publishableKey** `string` *required*

Your `pk_…` key.

**question** `string`

Overrides the question set in **Settings → General** (default "How did you hear about us?").

**options** `string[]`

The quick-pick chips. By default, up to six of your most-answered channels in respondent-friendly words ("ChatGPT or another AI", "A podcast"…). Respondents can always type their own answer.

**position** `"bottom-right" | "bottom-left"` (default: bottom-right)

Where the card appears.

**theme** `"light" | "dark" | "auto"` (default: light)

`auto` follows the visitor's operating-system setting.

**apiBase** `string`

Defaults to the origin the script was loaded from. Only set it if you self-host the script.

## When to ask

`Rumorly.ask()` takes one option, `trigger`:

| Trigger | Behaviour |
| - | - |
| `"now"` (default) | Shows the card right away. Good after onboarding or the first key action. |
| `"first-session-end"` | On desktop, shows the card when the pointer leaves through the top of the window (exit intent). On touch devices, after 45 seconds of visible time. |

`Rumorly.reset()` forgets that this browser was asked, which is useful while testing.

## What gets sent

Besides the answer and the identity you passed, the snippet sends the UTM parameters (`utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`), the referrer and the landing page **from the visitor's first visit**, stored in local storage at that visit. Reports use them to compare what customers said with what click-based attribution says. Every submission carries an `Idempotency-Key`, so a flaky connection never creates duplicates.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.