Busymate AI

Guides

Reconnaître les clients connectés

Permettez à l’assistant de savoir qui est connecté : publiez votre clé publique, faites signer par votre API une attestation de courte durée, et branchez getIdentity et refreshIdentity.

JWKSLaunch token120 sNonceUsed onceLoginLogoutSwitch
Sur cette page

Busymate AI recognizes your signed-in customers without sharing an account database. So the assistant can trust who is asking, your API signs a short-lived proof for each signed-in customer (a launch token — an ES256 JWT valid for 120 seconds with a one-time nonce), you publish the matching public key (JWKS) as the workspace's identity provider, and the widget calls getIdentity and refreshIdentity. bro then serves that customer's history and account tools — only theirs.

Two identities

  • Your team signs in to the Console with their own accounts to manage the workspace.
  • Your customers never get a platform account. Each launch carries a short-lived proof your product signed; its subject is your unchanging customer id. The claims prove who is chatting — they are not an account database.

1. Keys

Create an ES256 key pair. Publish the public key at https://yourdomain/.well-known/jwks.json with a kid; keep the private key on your server. ES256 is the reference algorithm; the allowed list is part of the provider registration.

2. Register the provider

Open Console → Identity (or call upsert_tenant_identity_provider) and enter:

FieldValue
Issueryour origin, for example https://yourdomain
JWKS URLhttps://yourdomain/.well-known/jwks.json
Audiencebusymate-ai
Workspace claimtenant_id, equal to your workspace id
Subject claimsub — the unchanging internal customer id
Max proof ageat most 120 seconds
Mint endpointthe URL of the endpoint from step 3

Save the draft, run the checks, publish. An incomplete sign-in setup blocks the release.

3. The mint endpoint

Your API exposes one endpoint that requires your own signed-in session and returns a freshly signed proof for that customer:

typescript
import { SignJWT, importJWK } from "jose";

// POST https://YOUR-PRODUCT-DOMAIN/api/bmai/identity — requires YOUR OWN logged-in product session.
app.post("/api/bmai/identity", requireSession, async (req, res) => {
  // Fresh on EVERY mint. Never persist the launch token/nonce in localStorage,
  // sessionStorage, cookies, React state, or module state: every assistant
  // launch consumes this pair exactly once.
  const nonce = typeof req.body?.nonce === "string" ? req.body.nonce : "";
  if (!/^[A-Za-z0-9_-]{32,200}$/.test(nonce)) return res.status(400).json({ error: "invalid_nonce" });
  const key = await importJWK(JSON.parse(process.env.AI_LAUNCH_PRIVATE_JWK), "ES256");
  const token = await new SignJWT({
      "tenant_id": "<your-tenant-id>", // Busymate AI's registered tenant claim
      nonce,                               // equals the sibling field below
      name: req.user.displayName,          // optional low-sensitivity display claim
    })
    .setProtectedHeader({ alg: "ES256", kid: process.env.AI_LAUNCH_KEY_ID })
    .setIssuer("https://YOUR-PRODUCT-DOMAIN (set when you register the provider)")
    .setAudience("busymate-ai")
    .setSubject(req.user.id)               // IMMUTABLE internal account id; never email/phone/session id
    .setJti(crypto.randomUUID())           // one-time (replay-protected)
    .setIssuedAt()
    .setExpirationTime("120s")               // <= registered max age (120s)
    .sign(key);
  res.set("Cache-Control", "no-store");
  res.status(201).json({ token, nonce, expiresIn: 120 });
});
  • The nonce arrives from the widget and must match ^[A-Za-z0-9_-]{32,200}$; the response echoes it.
  • Claims: iss, aud, sub, your workspace claim, nonce, a one-time jti, iat, exp within the registered max age.
  • Respond 201 with { token, nonce, expiresIn } and Cache-Control: no-store. Every launch consumes the pair exactly once.

4. Wire the widget

Define window.BusymateAI.getIdentity before the embed script loads. It returns a fresh { token, nonce } for a signed-in customer, or null for a signed-out one. Call refreshIdentity() after login, logout, token rotation and every account switch — it reloads the correct visitor or signed-in history. Never keep a token or nonce in storage, cookies or component state. The Console's Integration section renders the full embed snippet with your values.

5. Full-page open

For a hosted page instead of an embed, mint the same pair and open your address with the token and nonce in the URL fragment — never the query string, referrer or logs. The destination strips the fragment before the exchange. The hosted-handoff snippet in Integration shows the exact form; see also White-label SDK → Customer identity.

6. Acceptance checklist

Setup progress is evidence of configuration; it does not prove the flow works. Run this list before you call sign-in done:

text
REQUIRED AUTH + HISTORY ACCEPTANCE — Busymate AI / bro
Setup progress is configuration evidence; it does not prove this workflow.

[ ] Signed out: getIdentity returns null and the assistant remains anonymous.
[ ] Login without reloading the host page: call refreshIdentity(); the frame becomes identified.
[ ] Every getIdentity call returns a different nonce AND JWT jti. No launch token or nonce is persisted.
[ ] The subject is the same immutable internal Busymate AI account id across sessions/devices — never email, phone, browser id, or session id.
[ ] Send an identified message; refresh the host page; the same conversation reappear.
[ ] That refresh produces no launch_replayed, invalid_token, or silent anonymous fallback.
[ ] Logout: call refreshIdentity(); account data is unavailable and the frame is anonymous.
[ ] Login again as the same account: call refreshIdentity(); that user's identified history returns.
[ ] Switch to a second account: call refreshIdentity(); it cannot see the first user's history or data.

Do not rely on a post-message-only identify() flow. Use refreshIdentity() after login,
logout, access-token/session rotation, and every account switch.

Pitfalls

  • An email, phone number or session id as sub. Use the unchanging internal id.
  • A token or nonce kept in localStorage, a cookie or React state.
  • A proof older than the registered max age, or a missing kid.
  • Answering getIdentity for a signed-out customer with anything but null.

Verify

  1. Signed out: the assistant is a visitor session.
  2. Log in without reloading the page and call refreshIdentity(): the frame is identified.
  3. The same customer on a second device sees the same history.
  4. A second account cannot see the first account's history or data.

Next

Questions

Do you store my customers' accounts?

No. Each launch carries a proof your product signed; the subject is your id. Conversations are keyed to that subject inside your workspace.

Why does the proof expire in 120 seconds?

It is a launch proof, not a session. A fresh one is minted per launch and used once, so a leaked proof is useless within moments.

Which algorithm must I use?

ES256 is the reference. The algorithms your provider accepts are part of its registration, checked against your JWKS.

Can visitors still chat?

Yes, when guest access is on. Signed-out visitors get answers and guidance; account tools need a signed-in customer.