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

# Configure the SDK

> Every window global the Sleekplan SDK reads at load time: SLEEK_PRODUCT_ID, SLEEK_USER, SLEEK_SETTINGS, SLEEK_DATA, and SLEEK_COOKIE_DOMAIN, plus how the SDK stores its session

The SDK reads a handful of `window` globals while it boots. They are the only way to change behavior that happens *before* you can call a method: which workspace to load, who the visitor is, where the launcher sits, and what the first session request is allowed to return.

<Warning>
  Every global on this page must be assigned **before** the SDK script tag runs. The SDK reads them once during initialization, so assigning one later has no effect. The one exception is [`$sleek.sso`](/docs/authentication/single-sign-on), which the SDK calls on demand.
</Warning>

## Globals at a glance

| Global                       | Type   | What it does                                                                                                          |
| ---------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------- |
| `window.SLEEK_PRODUCT_ID`    | number | Required. The workspace the widget loads. Already set for you in the install snippet                                  |
| `window.SLEEK_USER`          | object | Identifies a user at boot, without waiting for `sleek:init`. Same shape as [`$sleek.setUser()`](/docs/sdk/user-setup) |
| `window.SLEEK_SETTINGS`      | object | Overrides host-page widget settings (launcher button, position) and carries `session` flags                           |
| `window.SLEEK_DATA`          | object | Overrides the product data handed to the widget itself. Advanced                                                      |
| `window.SLEEK_COOKIE_DOMAIN` | string | Forces the cookie domain, so one session is shared across subdomains                                                  |

## `window.SLEEK_PRODUCT_ID`

The numeric ID of your workspace. It comes pre-filled in the snippet on [Settings → Widget](https://app.sleekplan.com/settings/widget); leave it as it is.

```html theme={"system"}
<script type="text/javascript">
  window.$sleek = [];
  window.SLEEK_PRODUCT_ID = 12345678;
</script>
```

## `window.SLEEK_USER`

Identify the visitor at boot instead of calling `$sleek.setUser()` after the `sleek:init` event. The object takes exactly the same fields as [`setUser()`](/docs/sdk/user-setup), and the SDK applies it while it initializes, so the identity is in place before the widget can be opened.

```javascript theme={"system"}
window.SLEEK_USER = {
  mail: 'jane@example.com',
  id: '1398',
  name: 'janesmith',
  token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' // server-generated SSO JWT
};
```

Use this when your page is server-rendered and already knows who is signed in. If identity only becomes available later (after an async auth call, for example), call `$sleek.setUser()` instead.

## `window.SLEEK_SETTINGS`

Two independent things live under this global.

### Host-page widget overrides

Any top-level key is merged over your workspace's saved **widget** settings. These overrides only affect what the SDK renders on your page: the launcher button and the position of the widget panel. They do not change anything inside the widget itself.

```javascript theme={"system"}
// Hide the launcher on this page, and pin the panel to the right
window.SLEEK_SETTINGS = {
  enable_button: false,
  position: { widget: 'right', button: 'bottom' }
};
```

| Key               | Type                                             | Effect                                                                         |
| ----------------- | ------------------------------------------------ | ------------------------------------------------------------------------------ |
| `enable_button`   | boolean                                          | `false` skips building the launcher button entirely                            |
| `position.widget` | `"left"` \| `"right"`                            | Which side the widget panel and launcher sit on                                |
| `position.button` | `"top"` \| `"middle"` \| `"bottom"`              | Vertical placement of the launcher                                             |
| `button_text`     | string                                           | Label on the launcher                                                          |
| `button_size`     | `"default"` \| `"compact"`                       | Compact collapses the label until hover                                        |
| `button_icon`     | `"logo"` \| `"icon"` \| `"question"` \| `"none"` | Launcher icon: your product logo, the default smiley, a question mark, or none |
| `brand_color`     | string                                           | CSS color used for the launcher background                                     |

<Warning>
  The merge is shallow. Setting `position` replaces the whole object, so always pass both `widget` and `button`.
</Warning>

### `session` flags

The `session` object is forwarded verbatim to the session endpoint on every session call, including the page-change pings. It suppresses things the backend would otherwise push at the visitor unprompted, which is what you want on pages where an unexpected popup or badge would get in the way: a login screen, a checkout step, a full-screen editor.

```javascript theme={"system"}
window.SLEEK_SETTINGS = {
  session: {
    skip_notifications: true,
    skip_announcements: true
  }
};
```

<ParamField path="session.skip_notifications" type="boolean">
  When truthy, the session returns a zeroed notification count. The launcher badge and any `data-badge-*` elements stay empty, and no unread count is fetched.
</ParamField>

<ParamField path="session.skip_announcements" type="boolean">
  When truthy, the session never returns a changelog announcement, so no announcement popup opens on its own. Announcements you trigger yourself with [`$sleek.showPopup()`](/docs/sdk/popups) still work.
</ParamField>

<Note>
  `skip_notifications` and `skip_announcements` are the only recognized `session` keys. Anything else in the object is ignored.
</Note>

## `window.SLEEK_DATA`

Merged over the product data the SDK fetches from the backend, and, unlike `SLEEK_SETTINGS`, the merged result *is* what gets handed to the widget when it mounts. That makes it powerful and easy to break things with, so treat it as an escape hatch rather than a configuration surface.

```javascript theme={"system"}
// Override the avatar the launcher and widget render for this product
window.SLEEK_DATA = {
  product_img: 'https://example.com/assets/logo.png'
};
```

The merge is shallow, so replacing a nested object such as `product_settings` means supplying the whole thing. Prefer changing settings in [Settings → Widget](https://app.sleekplan.com/settings/widget) whenever you can.

## `window.SLEEK_COOKIE_DOMAIN`

By default the SDK derives its cookie domain from the last two labels of the current hostname, so `app.example.com` and `www.example.com` already share a session. Set this global when that default is wrong, for example on a multi-level domain or when you deliberately want to scope the session narrower.

```javascript theme={"system"}
// Share the session across every subdomain of example.co.uk
window.SLEEK_COOKIE_DOMAIN = 'example.co.uk';
```

Pass the bare domain without a leading dot; the SDK adds it.

## How the SDK stores its session

The SDK keeps two separate stores, and each picks its backing mechanism independently at runtime:

| Store                               | Holds                                           | Preference order                            |
| ----------------------------------- | ----------------------------------------------- | ------------------------------------------- |
| `_sleek_session` / `_sleek_product` | Visitor session and the signed-in user's token  | Cookie, then `localStorage`, then in-memory |
| `_sleek_storage`                    | Cached widget state and short-lived preferences | `localStorage`, then cookie, then in-memory |

If a store already contains data under one mechanism, the SDK keeps using that mechanism rather than switching, so an existing visitor's session survives a change in browser settings.

<Note>
  In restricted environments (private windows, aggressive tracking protection, blocked storage) both cookies and `localStorage` can be unavailable. The SDK falls back to an in-memory store and keeps working for the rest of the page load, but the session will not survive a reload and the visitor will be asked to sign in again.
</Note>

## Loading the SDK twice

The SDK is a singleton. If the script runs a second time on the same page it throws `$sleek already exist, make sure you do not load the $sleek SDK twice` and points `window.$sleek` back at the original instance, so the widget keeps working. Check for a duplicate script tag if you see that error, typically a tag manager and a hardcoded snippet both firing.

## Bots

The SDK inspects the user agent on boot and skips loading entirely for known crawlers (Googlebot, Bingbot, Lighthouse, social preview fetchers, and similar), logging `$sleek: skip the loading process for bots.` to the console. No settings request is made and no `$sleek` methods are available, which is expected and keeps the widget out of your page-speed scores.

<CardGroup cols={2}>
  <Card title="SDK overview" icon="js" href="/docs/sdk/overview">
    Every method on `$sleek`, from `open()` to `shutdown()`.
  </Card>

  <Card title="User setup" icon="user" href="/docs/sdk/user-setup">
    The field reference for `SLEEK_USER` and `$sleek.setUser()`.
  </Card>
</CardGroup>
