---
title: "User identification"
description: "Tell the web widget who your signed-in users are with a signed token, so your agent and your team know who they're talking to."
---

If your website has signed-in users, you can tell Chatvox who they are. Their conversations are then linked to a [contact](/docs/leads-and-contacts/managing-contacts) with their name, email, and phone, instead of an anonymous visitor.

Identification uses a token that **your server** signs with a secret only you and Chatvox know. This stops anyone from pretending to be one of your users.

:::warning
Keep the secret on your server. Never put it in frontend JavaScript, HTML, mobile apps, or a public repository.
:::

## How it works

1. **Get your agent's secret**

    In your agent, go to **Settings → Security**. Under **User identification**, select **Generate secret**, then **Copy**. The secret starts with `cvx_id_`.

    Only team members who can edit the agent can see and manage the secret.

    Store it in your server's environment variables, for example as `CHATVOX_IDENTITY_SECRET`.

2. **Sign a token on your server**

    Add an endpoint that only signed-in users can call. It returns a JWT signed with your secret using **HS256**.

3. **Pass the token to the widget**

    On your website, fetch the token and call `Chatvox.identify({ token })`.

## Token claims

| Claim | Required | Description |
| --- | --- | --- |
| `sub` | Yes | A stable, unique ID for the user in your system, such as their database ID. Must be a string. |
| `exp` | Yes | When the token expires, as a Unix timestamp. One hour is a good default. |
| `name` | No | The user's display name. |
| `email` | No | The user's verified email address. |
| `phone` | No | The user's verified phone number. |

## Server examples

**Node.js**

```bash
npm install jsonwebtoken
```

```js
import jwt from "jsonwebtoken";

app.get("/api/chatvox/identity", requireAuth, (req, res) => {
  const user = req.user;

  const token = jwt.sign(
    {
      sub: String(user.id),
      name: user.name,
      email: user.email,
      phone: user.phone,
      exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
    },
    process.env.CHATVOX_IDENTITY_SECRET,
    { algorithm: "HS256" },
  );

  res.json({ token });
});
```

**PHP (Laravel)**

```bash
composer require firebase/php-jwt
```

```php
use Firebase\JWT\JWT;
use Illuminate\Http\Request;

Route::get('/api/chatvox/identity', function (Request $request) {
    $user = $request->user();

    $token = JWT::encode([
        'sub' => (string) $user->id,
        'name' => $user->name,
        'email' => $user->email,
        'phone' => $user->phone,
        'exp' => now()->addHour()->timestamp,
    ], env('CHATVOX_IDENTITY_SECRET'), 'HS256');

    return response()->json(['token' => $token]);
})->middleware('auth');
```

**WordPress**

No extra libraries needed. Add your secret to `wp-config.php`:

```php
define( 'CHATVOX_IDENTITY_SECRET', 'cvx_id_...' );
```

Then add this to a small plugin or your theme's `functions.php`. It identifies logged-in users on every page and resets the chat when they log out, so you can skip [Identify the user in the browser](#identify-the-user-in-the-browser) below.

```php
/**
 * Signs a Chatvox identity token (HS256 JWT) for a WordPress user.
 */
function chatvox_identity_token( WP_User $user ) {
    $encode = function ( $data ) {
        return rtrim( strtr( base64_encode( $data ), '+/', '-_' ), '=' );
    };

    $header  = array( 'alg' => 'HS256', 'typ' => 'JWT' );
    $payload = array_filter( array(
        'sub'   => (string) $user->ID,
        'name'  => $user->display_name,
        'email' => $user->user_email,
        'phone' => get_user_meta( $user->ID, 'billing_phone', true ), // WooCommerce, if used
        'exp'   => time() + HOUR_IN_SECONDS,
    ) );

    $unsigned  = $encode( wp_json_encode( $header ) ) . '.' . $encode( wp_json_encode( $payload ) );
    $signature = hash_hmac( 'sha256', $unsigned, CHATVOX_IDENTITY_SECRET, true );

    return $unsigned . '.' . $encode( $signature );
}

add_action( 'wp_footer', function () {
    if ( ! is_user_logged_in() || ! defined( 'CHATVOX_IDENTITY_SECRET' ) ) {
        return;
    }

    $token = chatvox_identity_token( wp_get_current_user() );
    ?>
    <script>
      window.addEventListener('load', function () {
        if (!window.Chatvox) return;

        window.Chatvox.identify({ token: <?php echo wp_json_encode( $token ); ?> });

        // Clear the chat when the user logs out.
        document.addEventListener('click', function (event) {
          if (event.target.closest('a[href*="action=logout"]')) {
            window.Chatvox.resetUser();
          }
        });
      });
    </script>
    <?php
}, 100 );
```

:::warning
The token is printed into the page, so make sure your caching plugin or host doesn't cache pages for logged-in users. Most don't by default.
:::

## Identify the user in the browser

```js
const response = await fetch("/api/chatvox/identity");

if (response.ok) {
  const { token } = await response.json();
  window.Chatvox.identify({ token });
}
```

You can call `identify` at any time:

- **Before the chat is opened**: the widget keeps the token and sends it when the conversation starts.
- **During a conversation**: the widget sends it right away and links the current conversation to the user.

The token is kept in memory only. It's never saved to local storage, session storage, or cookies.

## Log out or switch users

When a user signs out, or your app switches to a different user, call:

```js
window.Chatvox.resetUser();
```

This clears the token and the saved conversation on that browser, so the next person starts as a new, anonymous visitor. Always call it on sign-out, especially on shared computers.

:::note
If a conversation is already linked to one user and a token for a different user arrives, Chatvox starts a new conversation instead of letting the second user see the first user's messages.
:::

## Rotating your secret

To replace your secret, go to **Settings → Security**, select **Rotate**, and confirm with **Rotate secret**. Tokens signed with the old secret stop working immediately, so update the environment variable on your server right away.

## Other channels

- **Public Chat** doesn't identify visitors automatically. See [Customer Identification](/docs/leads-and-contacts/customer-identification).
- **WhatsApp** identifies customers by their phone number, so no secret is needed. WhatsApp and web contacts aren't merged automatically, even if the email or phone matches.
