Appearance
Identity verification
What it is, in plain words
Your Public key (pk_…) is in your site's code, so anyone who opens your site can read it. Without more, anyone can then call identify("someone-else") and write to that person's profile, chat history and feedback.
Identity verification closes that gap. After a user signs in, your server signs their user id with your Secret key. JustOneCX checks the signature and ignores identify() calls that do not have a valid one.
Why you set it up on your server
Only a secret that never reaches the browser can prove who is signed in. That is why the Secret key must live in your server's environment, as JUSTONECX_SECRET_KEY. JustOneCX cannot place it there for you, and it must never go in browser code. You do this once per site. It is not per user and not per page.
Set it up, in this order
The order matters. If you turn on Require before your site sends the signature, all your existing users count as unverified.
- Copy the Secret key. Open Settings → Developers → Secret key. The full key is shown only once. If you missed it, regenerate it and copy the new one.
- Store it on your server only, as
JUSTONECX_SECRET_KEY(hosting dashboard, server.env, or CI secret). Add the empty nameJUSTONECX_SECRET_KEY=to.env.example. - Sign the user id on your server after login (code below), and send the result to the browser with the signed-in user, for example in your session payload.
- Pass it to the SDK in the browser, and deploy.
- Switch on Require. In Settings → Developers → Identity verification, turn on Require identity verification.
- Check it works. Sign in on your site, then look at the setup checklist (or ask your AI assistant to run
workspace_status). It should say verified sign-ins are arriving.
Sign on your server
The Secret key is the same key you use to send events from your server. One key, one place to rotate.
js
import { createHmac } from "node:crypto";
const userHash = createHmac("sha256", process.env.JUSTONECX_SECRET_KEY)
.update(String(user.id))
.digest("hex");python
import hmac, hashlib, os
user_hash = hmac.new(
os.environ["JUSTONECX_SECRET_KEY"].encode(), str(user.id).encode(), hashlib.sha256
).hexdigest()ruby
require "openssl"
user_hash = OpenSSL::HMAC.hexdigest("SHA256", ENV.fetch("JUSTONECX_SECRET_KEY"), user.id.to_s)php
$userHash = hash_hmac('sha256', (string) $user->id, getenv('JUSTONECX_SECRET_KEY'));Pass it to the SDK
ts
identify(user.id, { plan: user.plan }, { userHash });
group(account.id, { plan: account.plan }, { accountHash });accountHash is the HMAC of the account id, computed the same way. Sign exactly the same string you pass to identify().
Set it up with an AI coding assistant
If your assistant is connected to the JustOneCX MCP server, ask it: "Set up JustOneCX identity verification". It reads the exact steps from get_install_info, adds the server code and .env.example entry, and passes userHash to identify().
It never sees your Secret key, on purpose: you paste the key into your server environment yourself. It switches Require on only after your site sends userHash and you say yes, because that setting changes the live site.
If it does not work
| Symptom | Likely cause |
|---|---|
| Users show as unverified after you turned Require on | Your site does not send userHash yet, or it signs a different string than the user id it passes to identify() (a number vs a string, or an email vs an id). |
| Every hash is rejected | You signed with the Public key (pk_…) instead of the Secret key. |
| It worked, then stopped | The Secret key was rotated and the server still has the old one. |
| The hash is computed in the browser | Do not. The Secret key would be public. Compute it on the server. |
To test one hash, ask your assistant to verify a userId and the hash your server made. It tells you whether it matches, and never shows the expected value.
Roadmap and changelog
Identity verification also protects the boards. A private board only opens for a visitor whose identity was verified, and an unverified userId never counts. On a hosted page or custom domain use a sign-on link.
What happens without a valid hash
When verification is on, a call without a valid hash returns normally but is ignored. It cannot merge into another person's profile or change their traits.
Rotating the Secret key
Rotating the Secret key invalidates every hash you issued and every server event call that still uses the old key. Your server must use the new key for both event ingest and userHash. Copy the new key from the dashboard right after you rotate, deploy it, then check that identify calls verify.
If you used a separate identity secret before
Older workspaces had a second key, the identity secret. It still works until you rotate your Secret key, and the dashboard shows a banner while it does. After you rotate, only the Secret key is accepted.