---
name: justonecx-integration
description: Add the JustOneCX SDK (product tours, announcements, surveys, live chat, roadmap and changelog) 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.com (all pages as one file: https://docs.justonecx.com/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.com`. 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.com/sdk/justonecx-sdk.global.js"
       data-public-key="pk_your_workspace_key"
       data-api-url="https://api.justonecx.com"
     ></script>
     ```
     Add `data-track-route-changes="true"` if the site is a single-page app.
   - npm: `npm install @justonecx/sdk`.
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** on every page load after sign-in (not only on the login event; order relative to `init()` doesn't matter): `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 })`.

## Roadmap, ideas and changelog

When the user wants customers to see a roadmap, vote on ideas, or read release notes, there are three options. Ask which one.

- **Hosted page** (no code): `https://boards.justonecx.com/<workspace-slug>/roadmap`. The user finds links in the dashboard under **Roadmap & Changelog**. Just link to it.
- **Embed in the user's site**: call `init()` first, then `mountRoadmap(element)`, `mountIdeas(element)` or `mountChangelog(element)` after mount and `handle.destroy()` on unmount. Script tag: `<justonecx-roadmap>`, `<justonecx-ideas>`, `<justonecx-changelog>`. Use `routing: "path"` plus a catch-all route only if the user wants real URLs; the default keeps the open idea in the query string.
- **In-app What's new**: put `data-justonecx-whats-new` on the user's own button, or call `openChangelog()`. `getUnreadChangelogCount()` gives the badge number. The user turns the widget on in the dashboard settings.

If the MCP server is available, call `get_board_embed_snippet`, `get_whats_new_snippet` and `generate_board_sign_on_code` instead of writing this by hand.

Rules:
- A private board or a hosted page needs the user signed in on the server side: build a sign-on link (`jocx_token`) with the Secret key. Never put the secret in client code.
- Import only from the package root `@justonecx/sdk`. Internal subpaths are not public API.
- A custom domain needs the Scale plan and two DNS records the user adds themselves (TXT `_justonecx.<host>` and a CNAME). Tell the user; do not try to do it.

## Rules

- Never call `init()` during server-side rendering. It needs `window`.
- `identify`, `group` and `track` called before `init()` are queued and replayed when `init()` runs.
- 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 **Secret key**. 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_SECRET_KEY)
  .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.
