> ## Documentation Index
> Fetch the complete documentation index at: https://developers.mihu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Chat Identification

> Match web chat visitors to their contact by e-mail or phone number, securely

**Chat Identification** tells the Mihu web chat widget who the visitor is — when your website already knows. Give the widget the customer's **e-mail or phone number** and Mihu matches the chat to their existing contact: the AI agent answers with their own details — name, appointments, history — and your team sees one continuous record instead of a new anonymous visitor.

E-mail and phone number are the two identifiers, and either one is enough. Use whichever your site knows the customer by.

It works with a signature your **server** creates. Nothing a visitor types into the chat is ever used to match them to an existing contact.

## How it connects to Contacts

Chat Identification is the link between your website's sign-in and the **Contacts** module in Mihu.

| You sign                    | Mihu compares it with                       | In Contacts            |
| --------------------------- | ------------------------------------------- | ---------------------- |
| The customer's e-mail       | the **E-mail** field of your contacts       | `john.doe@example.com` |
| The customer's phone number | the **Phone number** field of your contacts | `+14155550123`         |

* **A contact matches:** the chat opens on that contact. It shows up in their conversations next to their calls, WhatsApp messages and e-mails, and the AI agent can use what the contact record holds: name, appointments, notes, history.
* **No contact matches:** a new contact is created with the verified name and e-mail or phone number when the visitor sends their first message. The next visit is matched to it.
* **No valid signature:** the visitor is an anonymous web visitor and is never attached to an existing contact.

Contacts you import, create by API or collect on other channels are all matched the same way — there is nothing to map or sync. Keep the e-mail and phone number on the contact identical to what your site knows the customer by.

## How it works

1. You create an **identity secret** for your widget in Mihu. It lives on your server only.
2. When a signed-in customer loads a page, the page asks your server who they are. Your server signs their e-mail **or** phone number with the secret (HMAC-SHA256) and returns it with the result — the **hash**.
3. The page hands the e-mail or phone, the `hash` and optionally a `name` to the widget with `mihu.identify(...)`.
4. Mihu recomputes the signature. If it matches, the chat belongs to the contact with that e-mail or phone number.

| What the widget receives                                     | Result                                                                                  |
| ------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| A valid hash, and a contact with that e-mail / phone exists  | The chat opens on that contact, with personalised answers                               |
| A valid hash, no contact with that e-mail / phone yet        | A new contact is created with the verified name and e-mail / phone on the first message |
| No hash, a wrong hash, or the widget has no secret           | The visitor stays anonymous — never matched to an existing contact                      |
| The visitor types an e-mail or phone into a form or the chat | Never matched — typing proves nothing                                                   |

<Note>
  Identifying a visitor creates nothing by itself. The contact and the conversation appear in Mihu with the visitor's **first message**, so signed-in customers who never open the chat do not fill your inbox.
</Note>

## Before you start

* A published web chat widget, already embedded on your site with its script tag.
* A website with its own sign-in, and server-side code that knows the signed-in user's e-mail or phone number.

## 1. Create the identity secret

Open **Web Widgets**, select your widget and scroll down to **Ship it and update it**. The **Chat Identification** card sits next to the embed snippet — click **Create secret**.

The card then walks you through the same three steps as this page, with your secret and copy-ready code.

You get a value that starts with `wis_`. Store it where your server keeps its other secrets (an environment variable, a secrets manager).

<Warning>
  The secret must never reach the browser: not in page HTML, not in JavaScript, not in a mobile app bundle. Anyone who has it can sign any e-mail. If it leaks, click **Replace secret** — signatures made with the old one stop working at once.
</Warning>

## 2. Add an endpoint that signs the signed-in customer

The hash is the hex HMAC-SHA256 of **one identifier**, keyed with your identity secret:

| Identifier   | Value to sign                                                   | Example                |
| ------------ | --------------------------------------------------------------- | ---------------------- |
| E-mail       | lower-cased and trimmed                                         | `john.doe@example.com` |
| Phone number | international format, digits only — the leading `+` is optional | `+14155550123`         |

Add one small route to your own server. It reads the customer from the **current session**, signs their e-mail (or phone number) and returns it as JSON.

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  const crypto = require('crypto');

  app.get('/mihu/identity', requireLogin, (req, res) => {
    const email = req.user.email.trim().toLowerCase();
    const hash = crypto
      .createHmac('sha256', process.env.MIHU_IDENTITY_SECRET)
      .update(email)
      .digest('hex');

    res.json({ name: req.user.name, email, hash });
  });
  ```

  ```python Python (Flask) theme={null}
  import hmac, hashlib, os

  @app.get("/mihu/identity")
  @login_required
  def mihu_identity():
      email = current_user.email.strip().lower()
      digest = hmac.new(
          os.environ["MIHU_IDENTITY_SECRET"].encode(),
          email.encode(),
          hashlib.sha256,
      ).hexdigest()
      return {"name": current_user.name, "email": email, "hash": digest}
  ```
</CodeGroup>

To identify by phone number, sign `req.user.phone` instead and return it as `phone`.

<Warning>
  The endpoint must only ever answer for the customer signed in to **that request**. Never read the e-mail or phone from the query string or the request body: an endpoint that signs whatever the browser sends lets anyone pose as any customer. Visitors who are not signed in get `401` and simply stay anonymous in the chat.
</Warning>

## 3. Pass the identity to the widget

Add this next to the widget's script tag. It asks your endpoint who is signed in and hands the answer to the widget. It can run as soon as the page loads: calls made before the widget is ready are queued and delivered once it is.

```html theme={null}
<!-- your existing embed snippet -->
<script src="https://YOUR-WORKSPACE-HOST/v1/widget.js" data-key="pk_live_..." async></script>

<script>
  window.mihu = window.mihu || { q: [], identify: function (u) { this.q.push(['identify', u]); } };

  fetch('/mihu/identity', { credentials: 'include' })
    .then(function (r) { return r.ok ? r.json() : null; })
    .then(function (user) { if (user) mihu.identify(user); });
</script>
```

The first line is a tiny stub: if the widget has not loaded yet it remembers the call, and the widget replays it. Copy the script tag itself from **Embed snippet** in your widget — it carries your workspace host and public key.

In a single-page app, run the same `fetch` again after the customer signs in.

### Server-rendered pages

If your server renders the HTML, you can skip the endpoint: compute the hash while rendering and print the values straight into the page for the signed-in customer.

```html theme={null}
<script>
  window.mihu = window.mihu || { q: [], identify: function (u) { this.q.push(['identify', u]); } };

  mihu.identify({
    name:  "John Doe",          // optional
    email: "john.doe@example.com",     // the e-mail you signed
    hash:  "9f2c…e41a"              // computed by your server for this customer
  });
</script>
```

| Field   | Required                 | Notes                                                                                                                              |
| ------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `email` | One of `email` / `phone` | The value your server signed; Mihu lower-cases it before checking                                                                  |
| `phone` | One of `email` / `phone` | The number your server signed. Spaces, dashes and brackets are ignored, and it matches the contact with or without the leading `+` |
| `hash`  | Yes                      | 64 hex characters, the signature of the e-mail **or** of the phone. `user_hash` is accepted as an alias                            |
| `name`  | No                       | Used when the contact has no name yet                                                                                              |

With a phone number instead of an e-mail:

```javascript theme={null}
mihu.identify({ name: "John Doe", phone: "+14155550123", hash: "…" });
```

One hash signs one identifier. If you pass both `email` and `phone`, Mihu uses the one the hash belongs to; the other is ignored.

The widget sends an identity once per chat, so calling `mihu.identify` on every page view is fine.

## Test it

1. Pick a contact that already exists in Mihu and compute the hash for their e-mail:

   ```bash theme={null}
   python3 -c "import hmac,hashlib; print(hmac.new(b'wis_your_secret', b'john.doe@example.com', hashlib.sha256).hexdigest())"
   ```

2. Open a page with your widget, open the browser console and run:

   ```javascript theme={null}
   mihu.identify({ email: "john.doe@example.com", hash: "PASTE_THE_HASH" });
   ```

3. Send a message in the chat. In Mihu the conversation appears on that contact.

## When the customer signs in during a chat

Call `mihu.identify(...)` again after sign-in. A running anonymous chat moves to the known contact immediately and keeps its messages.

## Security notes

* **Sign on the server, per request, for the signed-in user only.** Your endpoint takes the customer from the session — never an e-mail or phone number sent by the browser.
* The hash for an e-mail or phone number does not change until you replace the secret. Print it only into pages of the customer it belongs to.
* Replacing the secret is instant and safe: customers are simply anonymous until your server signs with the new one.
* Sites without a sign-in cannot use Chat Identification; their visitors chat anonymously, and the AI agent can still collect a name or an e-mail as ordinary conversation details.

## Troubleshooting

| Symptom                                           | Check                                                                                                                                                                                                                                                                          |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| The visitor stays anonymous                       | The e-mail was signed in lower case and trimmed (a phone number: digits only, international format); the secret is the current one (not replaced since); `hash` is the 64-character hex string, not Base64                                                                     |
| Works in the console, not on the page             | The block starts with the stub line from step 3 (the one that begins with `window.mihu =`); your endpoint returns `200` with `name`, `email` or `phone`, and `hash` for a signed-in customer (check the Network tab), and the `fetch` sends cookies (`credentials: 'include'`) |
| A new contact appears instead of the existing one | The existing contact's e-mail or phone number in Mihu differs from the one you signed (check the country code on phone numbers)                                                                                                                                                |
| Stopped working after **Replace secret**          | Update the secret on your server                                                                                                                                                                                                                                               |
