# Install the Opskeet chat widget

Opskeet adds live chat with an AI agent, a help center and analytics to a website. Only the script is required; everything else is optional.

## Script (required)

Load it in the `<head>` of every page, in the shared layout or template. `PROJECT_ID` is the project's ID, already filled in when the code is copied from Opskeet.

```html
<script
  type="module"
  src="https://opskeet.com/app/widget/loader.js?id=PROJECT_ID"
></script>
```

- Don't change the URL or the ID, and don't bundle or self-host the file.
- Load it on every page, even where the chat isn't wanted, and call `Opskeet.hide()` there instead. Pages without it are missing from analytics and can't show new replies.
- Single-page apps need nothing extra: page changes are picked up on their own.
- Next.js (App Router), in `app/layout.tsx` inside `<body>`:

  <!-- prettier-ignore -->
  ```tsx
  import Script from "next/script"

  <Script
    type="module"
    src="https://opskeet.com/app/widget/loader.js?id=PROJECT_ID"
    strategy="afterInteractive"
  />
  ```

- WordPress: a plugin like WPCode, Code Snippets → Header & Footer → Header. Classic themes: `header.php`, before `</head>`.
- Webflow: Site settings → Custom code → Head code, then publish.
- Shopify: Online Store → Themes → ⋯ → Edit code → `layout/theme.liquid`, before `</head>`.

It shows as installed in Opskeet as soon as it loads on the site.

## Coming from another chat

Replacing Intercom, Crisp, Tawk.to, Chatwoot, Help Scout Beacon, Front Chat or Gleap? Remove its snippet and add the one above. Nothing else to set: the site's existing calls to the old chat drive Opskeet instead, unless the old chat is still on the page. Users it signs the old way stay verified once the old identity secret is saved in Settings → Import & export. Move to the calls below when convenient. Bringing the old tool's data over: [Switch to Opskeet](https://opskeet.com/docs/switch.md).

## Identify signed-in users

On sites with accounts, the chat can know who's signed in: their name, email and company show in the inbox, and their chats follow them across devices.

### Signed token (recommended)

Make an HS256 JWT on the server, signed with the project's identity secret. The secret is in Opskeet under Settings → Install → Identify signed-in users. Keep it in a server environment variable; never put it in the page.

Claims:

| Claim           |                                                                                    |
| --------------- | ---------------------------------------------------------------------------------- |
| `user_id`       | Required. Their stable ID, as a string.                                            |
| `exp`           | Required. At most 24 hours ahead.                                                  |
| `email`, `name` | Optional.                                                                          |
| `company`       | Optional. `{ id, name, domain }`; other keys (like `seats`) become company fields. |
| anything else   | Strings, numbers and true/false become contact fields (like `plan`).               |

```js
import jwt from "jsonwebtoken"

jwt.sign(
  {
    user_id: user.id,
    email: user.email,
    name: user.name,
    plan: user.plan,
    company: {
      id: org.id,
      name: org.name,
      domain: org.domain,
      seats: org.seats,
    },
  },
  process.env.OPSKEET_IDENTITY_SECRET,
  { algorithm: "HS256", expiresIn: "1h" }
)
```

Make a fresh token on each page load and put it on the page before the script:

```html
<script>
  window.OpskeetUser = { jwt: "TOKEN_FROM_YOUR_SERVER" }
</script>
```

Once the page has loaded:

```js
Opskeet.identify({ jwt }) // someone signs in without a reload, or a fresh token before the last expires
Opskeet.update({ plan: "pro" }) // changes details, keeps the token
Opskeet.logout() // on every sign-out, so the next person on that browser never sees their chats
```

A bad or expired token is refused; the browser console says why. Once tokens work, Settings → Install → Require a token refuses anyone without one.

### Without a server

Pass details instead of a token:

```js
window.OpskeetUser = {
  id: "123",
  email: "ana@example.com",
  name: "Ana",
  company: { id: "acme" },
  plan: "pro",
}
```

These are kept but prove nothing, so chats stay on one browser, and `company` only joins a company that already exists. Use a token whenever there's a backend.

## Open the chat from the site's own buttons

Any element with `data-opskeet-open` opens the chat. A value is written into the message box for them, not sent.

```html
<button data-opskeet-open>Chat with us</button>
<button data-opskeet-open="I'd like a demo">Book a demo</button>
```

## JavaScript API

```js
Opskeet.open({ message }) // message is optional, written in, not sent
Opskeet.close()
Opskeet.toggle()
Opskeet.hide() // the launcher; the chat still opens from buttons and code
Opskeet.show()
Opskeet.openArticle("getting-started") // a help center article, by its address
Opskeet.searchHelp("refunds")
Opskeet.setLanguage("fr") // one of the project's languages
Opskeet.track("upgraded", { plan: "pro" })
const stop = Opskeet.on("unread", (count) => {}) // returns a function that stops listening
```

Events for `Opskeet.on`: `ready`, `open`, `close`, `unread` (count of conversations with something unread), `message` (`{ conversationId, author, ai }`, a new message from the team or the AI).

To call methods before the script has loaded, add this line first, then call them by name:

```js
window.Opskeet =
  window.Opskeet ||
  function () {
    ;(Opskeet.q = Opskeet.q || []).push(arguments)
  }
Opskeet("open")
```

To hide the launcher on every page, turn it off in Settings → Widget instead.

## Track events

`Opskeet.track(name, details)` shows on the contact's activity and in analytics, so the team sees what someone did before they wrote. It can also show an outbound message set to wait for that event. Name up to 50 characters; up to 20 details: text (up to 200 characters), numbers, true or false. Never send passwords, card numbers or other sensitive data.

## Chat page

Every project also has a chat page that needs no install: `https://opskeet.com/app/chat/PROJECT_ID`. Link to it from emails, apps or anywhere the script can't go.
