Docs Identify users

Identify users

Tell Capless who is signed in, and every event and session replay from that browser carries their user id from then on, across page loads, until they sign out. Then you (or your agent) can ask "show me jane's last sessions" or "what did user_42 click before cancelling?".

capless("identify", user.id, { email: user.email, name: user.name }); // signed in
capless("reset");                                                      // signed out

Three rules cover every auth library:

  • Signed in: call identify with your auth provider's stable user id (not the email; emails change). Calling it on every page load is fine: repeat calls for the same user send nothing.
  • Signed out: call reset. It does nothing unless a user was identified, so it's safe on every signed-out page. Signing out starts a new session.
  • Traits (the third argument) are stored with the first identify of that user in that browser. Send only what your privacy policy covers. Capless does not strip them.

If your code can run before the tag has loaded, add the stub once, before the first call:

window.capless = window.capless || function () { (capless.q = capless.q || []).push(arguments) };

React helper

The React guides below use this small hook. Put it in your app once:

// use-capless-user.ts
"use client";
import { useEffect } from "react";

type CaplessUser = { id: string; email?: string | null; name?: string | null };

function capless(...args: unknown[]) {
  const w = window as any;
  w.capless = w.capless || function () { (w.capless.q = w.capless.q || []).push(arguments) };
  w.capless(...args);
}

/** undefined while the auth library is loading, null when signed out. */
export function useCaplessUser(user: CaplessUser | null | undefined) {
  useEffect(() => {
    if (user === undefined) return;
    if (user) capless("identify", user.id, { email: user.email ?? undefined, name: user.name ?? undefined });
    else capless("reset");
  }, [user === undefined, user?.id]);
}

Render the component from each guide once, inside your auth provider (for example in the root layout).

Hexclave

"use client";
import { useUser } from "@hexclave/next"; // or @hexclave/react
import { useCaplessUser } from "./use-capless-user";

export function CaplessIdentify() {
  const user = useUser(); // null when signed out
  useCaplessUser(user && { id: user.id, email: user.primaryEmail, name: user.displayName });
  return null;
}

Clerk

"use client";
import { useUser } from "@clerk/nextjs"; // or @clerk/clerk-react
import { useCaplessUser } from "./use-capless-user";

export function CaplessIdentify() {
  const { isLoaded, user } = useUser();
  useCaplessUser(!isLoaded ? undefined : user && { id: user.id, email: user.primaryEmailAddress?.emailAddress, name: user.fullName });
  return null;
}

Auth.js (NextAuth)

Auth.js leaves the user id out of the session by default; add it in your config:

// auth.ts
callbacks: {
  session({ session, token }) {
    if (token.sub) session.user.id = token.sub; // with database sessions: session.user.id = user.id
    return session;
  },
},
"use client";
import { useSession } from "next-auth/react";
import { useCaplessUser } from "./use-capless-user";

export function CaplessIdentify() {
  const { data: session, status } = useSession();
  const u = session?.user;
  useCaplessUser(status === "loading" ? undefined : u?.id ? { id: u.id, email: u.email, name: u.name } : null);
  return null;
}

Supabase Auth

import { createClient } from "@supabase/supabase-js";

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
supabase.auth.onAuthStateChange((_event, session) => {
  const u = session?.user;
  if (u) capless("identify", u.id, { email: u.email });
  else capless("reset");
});

Firebase Authentication

import { getAuth, onAuthStateChanged } from "firebase/auth";

onAuthStateChanged(getAuth(), (u) => {
  if (u) capless("identify", u.uid, { email: u.email, name: u.displayName });
  else capless("reset");
});

Auth0

import { useAuth0 } from "@auth0/auth0-react";
import { useCaplessUser } from "./use-capless-user";

export function CaplessIdentify() {
  const { isLoading, user } = useAuth0();
  useCaplessUser(isLoading ? undefined : user?.sub ? { id: user.sub, email: user.email, name: user.name } : null);
  return null;
}

With the Next.js SDK, use its useUser() the same way (user.sub).

Better Auth

"use client";
import { authClient } from "@/lib/auth-client";
import { useCaplessUser } from "./use-capless-user";

export function CaplessIdentify() {
  const { data, isPending } = authClient.useSession();
  useCaplessUser(isPending ? undefined : data?.user ? { id: data.user.id, email: data.user.email, name: data.user.name } : null);
  return null;
}

WorkOS AuthKit

"use client";
import { useAuth } from "@workos-inc/authkit-nextjs/components";
import { useCaplessUser } from "./use-capless-user";

export function CaplessIdentify() {
  const { user, loading } = useAuth();
  useCaplessUser(loading ? undefined : user ? { id: user.id, email: user.email, name: [user.firstName, user.lastName].filter(Boolean).join(" ") } : null);
  return null;
}

Kinde

"use client";
import { useKindeBrowserClient } from "@kinde-oss/kinde-auth-nextjs";
import { useCaplessUser } from "./use-capless-user";

export function CaplessIdentify() {
  const { user, isLoading } = useKindeBrowserClient();
  useCaplessUser(isLoading ? undefined : user ? { id: user.id, email: user.email, name: [user.given_name, user.family_name].filter(Boolean).join(" ") } : null);
  return null;
}

Amazon Cognito (Amplify)

import { getCurrentUser } from "aws-amplify/auth";
import { Hub } from "aws-amplify/utils";

async function syncUser() {
  try {
    const u = await getCurrentUser();
    capless("identify", u.userId, { email: u.signInDetails?.loginId });
  } catch {
    capless("reset"); // not signed in
  }
}
syncUser();
Hub.listen("auth", ({ payload }) => {
  if (payload.event === "signedIn") syncUser();
  if (payload.event === "signedOut") capless("reset");
});

Server-rendered apps (Rails, Django, Laravel, Express, PHP…)

Render one of these lines into every page, after the stub, with the id escaped as a JavaScript string by your template engine:

<script>
  window.capless = window.capless || function () { (capless.q = capless.q || []).push(arguments) };
  capless("identify", "{{ current_user.id }}"); /* when signed in */
  capless("reset");                             /* when signed out */
</script>

On a strict Content Security Policy, give the script your page's nonce.

Anything else

Find the place your app learns "someone signed in" and "someone signed out" (a callback, an event, a session hook) and call identify and reset there, as above.

Find a user's sessions

  • Capless app: Session replays → click a user, or filter by user id.
  • API: GET /v1/replays?userId=user_42 lists that user's recordings (last 7 days); GET /v1/replays/{session}/{tab} returns one.
  • MCP: list_replays with userId; then open the recording in the Capless app.
  • Events: read $web.events with the filter entry.userId == "user_42".

Events from before they signed in carry the same anonymousId. The identify event links the two: read entry.type == "identify" && entry.userId == "user_42", then read events with entry.anonymousId in the ids you found.

This page as Markdown

← Web SDK · Streams & cursors →