---
name: justonecx-integration
description: Add the JustOneCX SDK (product tours, announcements, surveys, live chat) to a web app. Use when the user asks to install JustOneCX, identify users, track events, or show tours/surveys from JustOneCX.
---

# JustOneCX integration

Full docs: https://docs.justonecx.qd.je (all pages as one file: https://docs.justonecx.qd.je/llms-full.txt).

## Values you need

- `publicKey`: starts with `pk_`. The user copies it from the JustOneCX dashboard, **Settings → Developers**. Ask the user for it. Never invent one. Use the placeholder `pk_your_workspace_key` if they have not given it.
- `apiUrl`: `https://api.justonecx.qd.je`. Always pass it. The default is `http://localhost:8000` and breaks production.

The public key is not secret. It is safe in client code and in git.

## Steps

1. **Detect the project type.** Read `package.json` and the entry files.
   - No bundler or plain HTML, WordPress, Webflow, Shopify: use the script tag.
   - Otherwise: use the npm package.
2. **Install.**
   - Script tag, before `</body>`:
     ```html
     <script
       src="https://app.justonecx.qd.je/sdk/justonecx-sdk.global.js"
       data-public-key="pk_your_workspace_key"
       data-api-url="https://api.justonecx.qd.je"
     ></script>
     ```
     Add `data-track-route-changes="true"` if the site is a single-page app.
   - npm: `npm install @justonecx/sdk`. If the install fails with 404, the package is not published yet. Use the script tag instead.
3. **Call `init` once, in the browser only, after mount.** Pass `trackRouteChanges: true` in single-page apps.
   - React/Next.js: client component with `useEffect(() => void init(options), [])`. Add `"use client"` in the App Router.
   - Vue: after `app.mount()`. Nuxt: `plugins/justonecx.client.ts`.
   - Angular: constructor of the root component, guarded by `isPlatformBrowser`.
   - Svelte/SvelteKit: `onMount` in the root layout.
   - Solid: `onMount`. Astro: a client `<script>`.
4. **Identify the user** after sign-in: `identify(user.id, { plan, role })`. Optional: `group(account.id, { plan })`.
5. **Reset on sign-out**: `await reset()`.
6. **Track events** the user asks for: `track("event_name", { key: value })`.

## Rules

- Never call `init()` during server-side rendering. It needs `window`.
- Call `identify`, `group`, `track` only after `init()`. Before it they log an error and do nothing.
- Use snake_case event names, for example `completed_checkout`.
- Traits and properties are flat objects of strings, numbers, booleans.
- Do not ask for, store or print the **identity secret**. It belongs only on the user's server.
- Do not edit existing auth or routing code beyond adding the calls above.

## Identity verification (recommended before production)

If the user enables identity verification, the server must sign the user id:

```js
import { createHmac } from "node:crypto";
const userHash = createHmac("sha256", process.env.JUSTONECX_IDENTITY_SECRET)
  .update(user.id)
  .digest("hex");
```

Pass it in the browser: `identify(user.id, traits, { userHash })`. For accounts: `group(account.id, traits, { accountHash })`.

## Tell the user to do manually

- Add the site's origin (for example `https://www.example.com` and `http://localhost:3000`) to **Settings → Developers → Domains & CORS**.
- Create and publish tours, announcements and surveys in the dashboard. The SDK shows them automatically.

## Check your work

- Open the app. The browser console shows no `[JustOneCX]` errors.
- If a CORS error appears, the origin is missing from the allow-list.
