# Privacy and cookies

This page lists what the Masheev chat widget and the headless SDK store in a visitor's browser, when they store it and for how long, and what you need to tell your visitors. You run the website and decide to offer the chat, so you are responsible for your site's privacy notice and cookie notice. Masheev processes the conversation on your behalf under the Data Processing Addendum.

## What the widget stores

The widget does not set cookies. It uses browser storage in the widget's own frame (`https://iframe.masheev.com`), which browsers keep separate from your site's storage.

| Key                                                           | Storage          | What it holds                                                                                              | When it is written                                             | How long it stays                                                                                             |
| ------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `widget_visitor_{inboxId}`                                    | `localStorage`   | A random visitor identifier issued by Masheev, a signed access grant for it, and the time it was last used | When the visitor starts a chat (see below). Never on page load | Deleted the next time the widget loads after 13 months without a chat. Each new chat session resets the clock |
| `widget_workflow_{inboxId}_{workflowId}`                      | `localStorage`   | The same, for a [conversation workflow](https://docs.masheev.com/workflows.md) in `sessionMode: "workflow"`                           | When the visitor starts the workflow                           | As above                                                                                                      |
| `masheev:scheduler-pending-intent:{inboxId}:{conversationId}` | `sessionStorage` | IDs of booking requests still waiting for a result                                                         | Only while a booking request is in progress                    | Removed when the request settles, or when the tab closes                                                      |

With `sessionMode: "ephemeral"` the widget stores no visitor identifier at all, and every page load starts a new conversation.

Earlier widget versions stored the identifier as plain text under `widget_visitor_{inboxId}` and the grant under `widget_visitor_{inboxId}_token`. The widget converts these to the format above the first time it reads them and deletes the `_token` key.

### When a chat starts

Loading a page with the widget only fetches the inbox's public appearance (colours, greeting, AI disclosure). No visitor identifier is created, sent or stored.

The identifier is created by Masheev's server and stored in the browser when the visitor starts a chat:

- `chat-widget` mode: when the visitor opens the chat.
- `prompt-input` mode: when the visitor opens the conversation or sends a first message.
- `embedded` mode: when the visitor first clicks, taps or types in the chat area. A browser that already holds an identifier for the inbox restores its conversation straight away.

Cloudflare Turnstile, which protects session creation from bots, also loads only at that point, not on page load. Refer to Cloudflare's documentation for what the challenge itself processes.

### Why there is no cookie banner step by default

The UK and EU rules on storage in a user's device (PECR regulation 6 in the UK, Article 5(3) of the ePrivacy Directive in the EU) require consent unless the storage is strictly necessary to provide a service the user asked for. Because the widget stores nothing until the visitor starts a chat, and the identifier is what lets the visitor continue that conversation, Masheev's view is that this storage falls within that exemption. This is a reasonable reading of the rules and of ICO guidance, not a ruling, and your own legal adviser should confirm it for your site. If you prefer to ask first, turn on `requireConsent`.

## Asking for consent first: `requireConsent`

With `requireConsent: true` the widget shows a consent step before any chat session starts, in every mode (`chat-widget`, `prompt-input` and `embedded`). Until the visitor ticks the box and selects **Start Chat**:

- no session is created and no visitor identifier is stored;
- a message typed in `prompt-input` mode waits and is sent only after the visitor agrees.

```ts
Masheev.init({
  inboxId: "inbox_abc",
  requireConsent: true,
  privacyUrl: "https://example.com/privacy",
});
```

The consent time is sent with the session request and kept on the contact record on Masheev's servers as evidence. The browser does not store a consent flag. When a returning visitor's browser still holds its identifier, the server confirms the earlier consent and the visitor is not asked again until the consent wording changes.

## Headless SDK

The headless SDK runs on your own site, so its storage is on your site's origin.

| Key                         | Storage                      | What it holds                                           | When it is written                                                                                  | How long it stays                                                                |
| --------------------------- | ---------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `masheev_visitor_{inboxId}` | Depends on `storage` (below) | Visitor identifier, signed access grant, last-used time | After the first session is created (`createSession()` or the first `sendMessage()`). Never on mount | Deleted when read after 13 months without use. Each new session resets the clock |

Choose where it is kept with the `storage` prop on `<MasheevProvider>`:

- `"persistent"` (default): `localStorage`, so a returning visitor continues the conversation.
- `"session"`: `sessionStorage`, cleared when the tab closes.
- `"none"`: nothing is stored. Every page load starts a new conversation.

Set `requireConsent` to stop any session until you call `giveConsent()` from `useMasheev()`, typically from your own consent dialog:

```tsx
<MasheevProvider inboxId="inbox_abc" storage="session" requireConsent>
  <Chat />
</MasheevProvider>;

function Chat() {
  const { hasConsent, giveConsent, sendMessage } = useMasheev();
  if (!hasConsent) return <button onClick={giveConsent}>I agree, start chat</button>;
  // ...
}
```

Turnstile loads only when a session is requested.

## Your privacy policy in the widget

The widget links to **your** privacy policy, because you decide why and how the chat is used. Add its address in the dashboard under the chat inbox's settings (**Privacy policy URL**, `https://` only). An active chat inbox's settings cannot be saved without it. You can also pass `privacyUrl` in the embed code, which takes precedence.

If no address is set, the widget does not link to Masheev's policy. It shows "This chat is provided by {your business name} using Masheev" instead.

## AI disclosure

Visitors are told they are chatting with AI before the conversation starts:

- The widget shows the disclosure (for example "You're chatting with Alex, an AI assistant for Acme Corp.") above the conversation, before the greeting, as a note screen readers announce first.
- Every widget footer starts with "AI-powered ·" when an AI agent answers.
- AI-written messages carry `data-ai-generated="true"` in the page.
- If you build your own interface with the headless SDK or the API, Masheev adds the disclosure to the start of the first AI reply in each conversation. `POST /api/widget/session` also returns `disclosure` whenever an AI agent is configured, resumed sessions included. Send `disclosureRendered: true` in that request only if your interface shows the disclosure before the conversation itself; the reply is then left unchanged.

## What to tell your visitors

As the business offering the chat, your privacy notice should cover:

- that your site offers a chat answered by an AI assistant, with your team able to take over;
- what visitors may share in the chat and what you use it for;
- that Masheev processes the conversation for you as your service provider;
- how long you keep conversations and how visitors can ask for access or deletion;
- the browser storage listed on this page, in your cookie or storage notice.

Keep the privacy policy URL in the inbox settings pointing at that notice.
