# Identify Users

> Link tracked activity to your own user IDs and see real names in your dashboard


import { Callout, CodeBlock, Tab, Tabs } from "@/components/docs";

By default, Databuddy tracks visitors with an anonymous device ID. Calling `identify()` links that activity to a user ID from your own system, so the same person is recognized across sessions and devices — and your dashboard shows real names and emails instead of anonymous visitors.

<Callout type="warn">
  User identification processes personal data. Make sure you have a lawful
  basis (typically consent), disclose it in your privacy policy, and only send
  traits you are allowed to process.
</Callout>

## Quick Start

<Tabs items={['React / Next.js', 'Script Tag', 'Node.js']}>
<Tab value="React / Next.js">
Call `identify()` when your auth state resolves — on every page load is fine, repeat calls are deduplicated to one request per session.

<CodeBlock language="tsx">
  {`import { clearProfile, identify } from "@databuddy/sdk";

function IdentifyUser() {
  const { data: session, isPending } = authClient.useSession();

  useEffect(() => {
    if (isPending) {
      return;
    }
    if (session?.user) {
      identify(session.user.id, {
        email: session.user.email,
        name: session.user.name,
      });
    } else {
      clearProfile();
    }
  }, [session, isPending]);

  return null;
}`}
</CodeBlock>
</Tab>
<Tab value="Script Tag">
The same methods live on the global object once the script loads.

<CodeBlock language="js">
  {`// On login or signup
window.databuddy.identify("user_12345", {
  email: "jo@acme.com",
  name: "Jo Doe",
  plan: "pro",
});

// On logout
window.databuddy.clearProfile();`}
</CodeBlock>
</Tab>
<Tab value="Node.js">
Backends can identify users with an API key that has the `track:events` scope for the target website. Pass the client's anonymous ID (readable in the browser via `getAnonymousId()`) to link the device's activity.

<CodeBlock language="ts">
  {`import { Databuddy } from "@databuddy/sdk/node";

const client = new Databuddy({
  apiKey: process.env.DATABUDDY_API_KEY,
  websiteId: "your-website-id",
});

await client.identify({
  profileId: user.id,
  anonymousId: cookies.get("did"),
  traits: { email: user.email, plan: "pro" },
});`}
</CodeBlock>
</Tab>
</Tabs>

<Callout type="info">
  Pass an opaque, stable ID from your database (max 128 characters, stored
  verbatim) — not an email address. Emails belong in traits.
</Callout>

## How It Works

- Every visitor has an anonymous ID stored on their device.
- `identify(profileId, traits)` links that ID to your user and attaches the user ID to every subsequent event, including earlier pageviews from the same session.
- The profile ID persists in localStorage across reloads. Call `clearProfile()` on logout to return to anonymous tracking.
- Identified users appear on the **Users** page with their name and email, and collapse into one person across all their devices.

## Traits

Traits are flat key/value metadata about the user. Values must be strings, numbers, booleans, or `null` — nested objects are rejected. Limits: 50 keys, 2KB serialized.

| Trait | Behavior |
|-------|----------|
| `username` | Primary display name in the dashboard |
| `name` | Fallback display name when `username` is absent |
| `email` | Shown under the user's name; lowercased |
| anything else | Stored on the profile and shown in the user detail view |

Display names and emails are encrypted at rest (AES-256-GCM). Custom traits are stored as regular values so you can filter and segment by them.

Update traits later without re-identifying, and remove one by setting it to `null`:

<CodeBlock language="ts">
  {`import { setTraits } from "@databuddy/sdk";

setTraits({ plan: "enterprise", trial_ends_at: null });`}
</CodeBlock>

## Revenue Attribution

If you use the [Stripe or Paddle integration](/docs/Integrations/payments), include the user ID in your payment metadata and transactions are attributed to the identified user:

<Tabs items={['Stripe', 'Paddle']}>
<Tab value="Stripe">
<CodeBlock language="ts">
  {`await stripe.paymentIntents.create({
  amount,
  currency,
  metadata: {
    databuddy_profile_id: user.id,
  },
});`}
</CodeBlock>
</Tab>
<Tab value="Paddle">
<CodeBlock language="ts">
  {`{ custom_data: { profile_id: user.id } }`}
</CodeBlock>
</Tab>
</Tabs>

## API Reference

### `identify(profileId, traits?)`

Links the browser to a user ID. Persists across sessions, attaches to every subsequent event. Safe to call on every page load — repeat calls with the same ID and no traits send one request per session.

### `setTraits(traits)`

Merges traits into the identified user's profile. Requires a prior `identify()` — otherwise a no-op that warns in debug builds.

### `clearProfile()`

Forgets the identified user. The anonymous ID is kept, so subsequent activity is tracked anonymously again.

### `getProfileId()`

Returns the currently identified user ID, or `null` when anonymous.
