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

# Analytics events

> The metadata-only events that the widget sends to your own analytics tool.

The widget reports what visitors do, so you can send it to your own analytics tool. Each event holds metadata only. It never holds the text of a question, an answer, an email address or a note.

## Send events to your analytics tool

Pass a function as `onAnalyticsEvent` to `init`. The widget calls it for each event. The script tag has no attribute for this option, so start the widget from your code, as [Start the widget from your code](https://paperlinkai.com/docs/developers/install/script-tag.md#start-the-widget-from-your-code) explains.

```html theme={"dark"}
<script src="https://api.paperlinkai.com/widget.js" async></script>
<script>
  window.PaperlinkAIChatQueue = window.PaperlinkAIChatQueue || [];
  window.PaperlinkAIChatQueue.push(function (PaperlinkAIChat) {
    PaperlinkAIChat.init({
      apiKey: 'pl_live_...',
      onAnalyticsEvent: (event) => {
        analytics.track(event.name, event);
      },
    });
  });
</script>
```

If you keep the standard tag with `data-api-key`, use the legacy window events below.

If your function throws an error, the widget catches it and logs it to the browser console. A broken handler does not break the chat.

> [!TIP]
>
> If a page has more than one widget, use `onAnalyticsEvent`. Each widget has its own handler. The legacy window events can only tell the widgets apart through `apiBaseUrl`.

## Events

Each event has a `name` and the fields in this table.

| Event | Fields |
| - | - |
| `widget_opened` | none |
| `widget_closed` | none |
| `widget_session_created` | `turnstile`: `'passed'` or `'skipped'` |
| `widget_session_resumed` | none |
| `widget_session_failed` | `code` |
| `widget_message_submitted` | `source`: `'input'` or `'followup'`; `length` |
| `widget_answer_completed` | `tier`; `answered`; `citationCount`; `followupCount`; `firstTokenMs`; `totalMs` |
| `widget_answer_failed` | `code` |
| `widget_followup_clicked` | `index` |
| `widget_contact_shown` | `reason`: `'idk'` or `'error'` |
| `widget_contact_link_clicked` | `kind`: `'email'` or `'url'` |
| `widget_contact_sent` | `withEmail`; `withNote` |
| `widget_contact_failed` | `code` |
| `answer_feedback_submitted` | `rating`: `'up'` or `'down'`; `reason` |
| `widget_status_link_clicked` | `surface`: `'answer_error'` or `'banner'`; `code` |

`length` is a number. It does not include the question itself. `firstTokenMs` is `null` when no text arrived. `reason` in `answer_feedback_submitted` has a value only with a thumbs-down.

## Legacy window events

> **Window CustomEvents**
>
> The widget also dispatches four events on `window`. Each event carries the fields of the matching analytics event, plus `apiBaseUrl`, in `detail`.
>
> * `paperlink-widget:opened`
> * `paperlink-widget:closed`
> * `paperlink-widget:message-submitted`
> * `paperlink-widget:session-created`
>
> New code uses `onAnalyticsEvent` instead.

> **[API keys](https://paperlinkai.com/docs/developers/security/api-keys.md)**
>
> Learn how to create, rotate and revoke the key that the widget uses.


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