Yak Docs
Customization

End-user identity

Pass a signed user identity into the widget and Yak will persist conversations against that user. When the same user returns, the widget shows a history pane so they can resume a previous chat or start a new one.

This is opt-in. If you don't pass a user, the widget behaves exactly as it always has — conversations live only in the browser and nothing is persisted server-side.

How it works

  1. Your backend signs the user id with your application's API secret using HMAC-SHA256.
  2. The signed identity is passed to <YakProvider> in the browser.
  3. The widget forwards { id, hash } to Yak on every request that touches that user's data.
  4. Yak verifies the hash against the secret, upserts the end-user record, and persists messages against a conversation owned by that user.

The secret never leaves your server. The hash proves the browser is acting on behalf of a real account from your system — without it, anyone with an end-user's id could impersonate them.

This is the same identity-verification model used by Intercom and similar widget vendors. Only the user id is signed, so the identity is tamper-proof.

Set it up

Grab your API secret

Open your application's Customer settings and reveal the End-user identity card. Copy the secret somewhere safe — treat it like any other backend credential.

Store it as an environment variable on your server (never expose it to the browser):

YAK_API_SECRET=...

Sign the user id on your server

Compute an HMAC-SHA256 hex digest of the user id using the secret:

import crypto from "node:crypto";

const userHash = crypto
  .createHmac("sha256", process.env.YAK_API_SECRET!)
  .update(currentUser.id)
  .digest("hex");
import hmac, hashlib, os

user_hash = hmac.new(
    os.environ["YAK_API_SECRET"].encode(),
    current_user.id.encode(),
    hashlib.sha256,
).hexdigest()
require "openssl"

user_hash = OpenSSL::HMAC.hexdigest(
  "SHA256",
  ENV["YAK_API_SECRET"],
  current_user.id,
)
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "os"
)

mac := hmac.New(sha256.New, []byte(os.Getenv("YAK_API_SECRET")))
mac.Write([]byte(currentUser.ID))
userHash := hex.EncodeToString(mac.Sum(nil))

Send the hash to the browser however you already pass user data (server-rendered HTML, a session API, etc.).

Pass the identity into the widget

Supply { id, hash } when you set up the widget. React/Next take it as a prop; the other SDKs take it as an option to createYakProvider.

import { YakProvider } from "@yak-io/react"; // or "@yak-io/nextjs/client"

<YakProvider
  appId={process.env.NEXT_PUBLIC_YAK_APP_ID!}
  user={{
    id: currentUser.id,
    hash: currentUser.yakHash, // from your server
  }}
>
  {children}
</YakProvider>
import { createYakProvider } from "@yak-io/vue";

const yak = createYakProvider({
  appId: "your-app-id",
  user: { id: currentUser.id, hash: currentUser.yakHash },
});
import { createYakProvider } from "@yak-io/svelte";

const yak = createYakProvider({
  appId: "your-app-id",
  user: { id: currentUser.id, hash: currentUser.yakHash },
});
import { createYakProvider } from "@yak-io/angular";

const yak = createYakProvider({
  appId: "your-app-id",
  user: { id: currentUser.id, hash: currentUser.yakHash },
});
import { createYakProvider } from "@yak-io/nuxt";

const yak = createYakProvider({
  appId: "your-app-id",
  user: { id: currentUser.id, hash: currentUser.yakHash },
});

That's it. Open the widget and you'll see a new history icon in the header — click it to browse past conversations or start a new one.

Updating the user (login / logout)

Your end-user may sign in or out without a full page reload. Keep the widget's identity in sync so conversations follow the right account.

  • React / Next.js are declarative — render <YakProvider> with the new user prop (or omit it / pass undefined on logout) and the provider re-threads the identity for you.
  • The Vue / Svelte / Angular / Nuxt SDKs return a handle from createYakProvider; call setUser() from your own reactivity (a Vue watch, Svelte $effect, or Angular effect) whenever the signed-in user changes. Pass undefined to log out.
import { watch } from "vue";

watch(currentUser, (u) => {
  yak.setUser(u ? { id: u.id, hash: u.yakHash } : undefined);
});
$effect(() => {
  yak.setUser($currentUser ? { id: $currentUser.id, hash: $currentUser.yakHash } : undefined);
});
// `currentUser` is a signal; re-run whenever it changes
effect(() => {
  const u = currentUser();
  yak.setUser(u ? { id: u.id, hash: u.yakHash } : undefined);
});
import { watch } from "vue";

watch(currentUser, (u) => {
  yak.setUser(u ? { id: u.id, hash: u.yakHash } : undefined);
});

Switching to a different id (or logging out) rotates the stored session token, so one user's session is never reused under another account. The first sign-in after anonymous browsing keeps the session, so the visitor's pre-login conversation is claimed by their new account.

The user prop

FieldTypeRequiredDescription
idstringYesStable user id from your system. HMAC-protected.
hashstringYesHex HMAC-SHA256 of id signed with your application's API secret.

What the end-user sees

When user is set and the hash verifies:

  • A clock icon appears in the chat header.
  • Clicking it opens a slide-over panel listing conversations, most recently started first.
  • Selecting a conversation re-hydrates the chat with the persisted messages — the user can keep talking right where they left off.
  • A "New" button starts a fresh conversation without losing the old ones.
  • Each conversation has a trash icon for removing it (and its messages) permanently.

When user is not set, none of this UI shows and no data is persisted.

Anonymous mode

If you don't pass user, the widget works exactly as before:

  • Conversations live only in the browser.
  • Nothing is written to Yak's database against an identifiable user.
  • The history pane is hidden.

You can mix the two modes inside the same application — for example, anonymous chat on a public marketing page and signed-in chat in your dashboard.

Rotating the secret

If you suspect the secret has leaked, rotate it from the dashboard. Rotation invalidates every previously signed hash, so deployed integrations will fail until you re-deploy with the new value. Plan the rotation alongside a deploy of your server code.

To force every current visitor onto a fresh session without touching your identity signing (no redeploy needed), use Sign out all sessions instead — see Session revocation.

Security model

  • What's signed: only the user id. A holder of the secret can produce a valid identity for any user id, so keep the secret on the server.
  • Where verification happens: every Yak endpoint that touches user data (/api/chat when user is present, the voice session and transcript endpoints, and the conversation history endpoints) re-verifies the hash and the user's ownership of the conversation. There are no session cookies — the signed identity rides along on each request.

For the broader security context, see the Security page.

On this page