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

# Security and data

> What the embed can reach on your behalf, how the tenant ID works, and what telemetry it collects.

This page is written for a security or privacy review.

> [!NOTE]
>
> Everything on this page applies equally whether the widget is added through the snippet or through the [WordPress plugin](https://docs.plastics.com/integrations/website-embed/wordpress-plugin.md). The plugin only loads the same hosted script described below; it does not change what the widget can reach or collect.

## How it talks to the backend

The widget is **anonymous**. There is no login step, no user account, and no session cookie involved. Instead:

1. Every request the widget makes is sent to a dedicated widget endpoint on `api.baseUrl`, never directly to the underlying service.
2. Your `tenantId` travels with each request as an `X-Tenant-Id` header, identifying which organization's catalog and branding to use.
3. That endpoint checks the requested path and method against a fixed allowlist before forwarding anything. Requests outside the allowlist are rejected.

The allowlist covers only what the widget's screens actually need:

* Starting and continuing a chat, reading chat messages, cancelling a request, and resolving a clarifying question
* Reading material records and datasheets
* Reading catalog reference data such as families, manufacturers, colors, brands, and property definitions
* Reading citations
* Listing countries and states for the quote form
* Submitting a guest quote
* Posting widget telemetry

> [!NOTE]
>
> The allowlist is the boundary. It contains only anonymous catalog reads, chat operations, and the guest quote submission, so account, order, project, and administrative endpoints are not reachable through the widget path.

## Cross-origin access and the tenant ID

The widget endpoints and the widget script are served with permissive cross-origin headers so the bundle can be loaded and called from your own domain. There is currently no per-origin restriction on the widget endpoints, so a request carrying your tenant ID is accepted from any origin.

> [!WARNING]
>
> Treat `api.tenantId` as a public identifier, not a credential. It is visible in the page source of any site running your embed, and on its own it does not prove where a request came from. It scopes which catalog and branding a request receives; it is not a secret and does not authenticate the caller.

## Streaming responses

Answers stream over Server-Sent Events. When a conversation starts, the widget opens a long-lived connection for that chat and receives a sequence of typed events:

| Event | Purpose |
| - | - |
| `status` | Progress through the four processing stages, with a human-readable label per stage. |
| `token` | Incremental answer text, appended to the message as it arrives. |
| `messages` | Full message-list snapshots, used to reconcile the conversation state. |
| `table` | A structured table to render inside the answer. |
| `pre_summary` | An early summary shown ahead of the full answer. |

Browsers cannot attach custom headers to an SSE connection, so on the streaming connection only, the tenant ID is passed as a query parameter instead of a header.

If the stream goes quiet while a response is still in flight, the widget falls back to periodically refetching the conversation, so a dropped connection degrades to slower updates rather than a stuck screen.

## Telemetry

### What is collected

The widget buffers two kinds of events and periodically sends them to your `api.baseUrl`:

**Timings.** Named performance milestones with a timestamp, such as when the bundle finished loading, when the interface rendered, when the streaming connection opened, when the first answer token arrived, and when the answer finished rendering.

**Actions.** Named interaction events with a small structured context. The events currently emitted are:

* A message-sent event, whose context records the chat identifier, whether files were attached, whether a shortcut was used, and which search mode was selected.
* A diagnostic trace event, whose context records internal trace identifiers for the completed answer, used to correlate a slow or failed response with backend logs.
* Error events, whose context records the error text and where in the widget it occurred.

Each batch is sent with the following envelope: your tenant ID, a session identifier, the current chat identifier, the timing and action lists, the **origin** of the page hosting the widget, the browser **user agent** string, and the flush timestamp.

### What is not collected

* **No message content.** The text a visitor types and the text the assistant replies with are never included in a telemetry event.
* **No identity.** There is no sign-in, so no user account, email, or name is attached to telemetry. The session identifier is a random value generated fresh each time the widget bundle loads on a page; it is not persisted, and it does not link one page view to another.
* **No third-party analytics.** The embedded bundle does not include the platform's product analytics SDK. Telemetry goes only to your configured `api.baseUrl`.

> [!NOTE]
>
> Personal data does reach the platform when a visitor voluntarily submits a quote request. That is the quote form's own payload, described in [Overview](https://docs.plastics.com/integrations/website-embed/overview.md#product-detail-and-quotes), and it is separate from telemetry.

### How it is sent and retained

Batches are debounced and sent in the background, and are also flushed when the visitor navigates away or closes the tab. The widget buffers at most a few hundred events of each kind at a time and discards the oldest beyond that, so a long session cannot grow unbounded.

On receipt, the platform records each timing and action as a structured log entry, tagged with tenant ID, session ID, origin, and user agent. Field lengths and list sizes are capped on the server, and payload keys that would collide with reserved log fields are dropped.


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