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

# User & Chat Identification

> How user.id and context.id improve ad targeting, frequency capping, and monetization.

Two ids identify who is chatting and in which conversation:

* **`user.id`** — optional. A stable ID enables user-based ad controls. See [`user`](https://docs.thrad.ai/api/api-reference/bid-request-v2#user).
* **`context.id`** — optional. A chat ID enables controls for that conversation. See [`context`](https://docs.thrad.ai/api/api-reference/bid-request-v2#context).

When you send `user.id`, keep it stable for at least the whole session, and across sessions wherever your privacy policy allows. Omitting it skips user-based retargeting, campaign repeat limits, per-user abuse limits, and user-linked transcript storage. Contextual matching still works, as does chat pacing when `context.id` is supplied. We do not generate an ID.

The IDs support these features:

* **Frequency capping** — limits how often the same user sees the same campaign: by default at most 20 times a day, and not for 24 hours after they click it. Needs a persistent `user.id`; a fresh id each session resets these limits. With `context.id` too, a campaign is not shown twice in the same chat within 2 minutes.
* **Ad pacing** — controls how often ads appear within a conversation. Uses `context.id`.
* **Better targeting** — DSPs can optimize bids on user-level signals. Needs a persistent `user.id`.
* **Accurate analytics** — your dashboard reflects real unique users and conversations.

```json theme={null}
{
  "context": {
    "id": "chat_8f2c1a",
    "chat": {
      "messages": [
        {
          "role": "user",
          "content": "What shoes are good for trail running?"
        }
      ]
    }
  },
  "user": {
    "id": "user_5d1e9b"
  }
}
```

## `context.id`

The optional **chat id**: a unique id for the current conversation. If supplied, it must be non-blank and 1–100 characters. Use the **same `context.id`** for every request in one chat, and a new one when the user starts a new conversation. Chat repeat checks also need `user.id`.

Without it you still get ads, but we can't pace ads or avoid repeats within a conversation. We don't generate a chat ID for you.

**Rules:**

* One `context.id` per conversation, not per user
* Store it in `sessionStorage` (browser) or generate it per conversation (server-side)
* Reset it when the user starts a new chat

### Browser Example

```javascript theme={null}
function getContextId() {
  const STORAGE_KEY = 'thrad_context_id';
  let contextId = sessionStorage.getItem(STORAGE_KEY);

  if (!contextId) {
    contextId = 'chat_' + crypto.randomUUID();
    sessionStorage.setItem(STORAGE_KEY, contextId);
  }

  return contextId;
}
```

### Server-Side Example

```python theme={null}
import uuid

def get_context_id():
    return f"chat_{uuid.uuid4()}"
```

## `user.id`

An optional anonymous id for the user. It is yours: send whatever key identifies the person in your own system. It never has to mean anything to us, and it is never matched against another publisher's ids. Supplied IDs must be non-blank and at most 100 characters. You can omit the entire `user` block when you have no other user fields to send. Age checks still apply when an age is sent without an ID.

**Persistent (recommended):**

* Store it in `localStorage`, a cookie, or your own user store
* Reuse it across sessions so DSPs can build better bidding signals
* Use anonymous UUIDs — never PII (email, name, phone, etc.)

**Per session:**

* Generate one UUID when the session starts and reuse it for every request in that session
* Frequency capping and pacing still work within the session; cross-session targeting won't apply
* Never mint a new id per request: that defeats capping entirely and looks like invalid traffic

### Browser Example — Persistent

```javascript theme={null}
function getUserId() {
  const STORAGE_KEY = 'thrad_user_id';
  let userId = localStorage.getItem(STORAGE_KEY);

  if (!userId) {
    userId = 'user_' + crypto.randomUUID();
    localStorage.setItem(STORAGE_KEY, userId);
  }

  return userId;
}
```

### Browser Example — Per session

```javascript theme={null}
function getUserId() {
  const STORAGE_KEY = 'thrad_user_id';
  let userId = sessionStorage.getItem(STORAGE_KEY);

  if (!userId) {
    userId = 'user_' + crypto.randomUUID();
    sessionStorage.setItem(STORAGE_KEY, userId);
  }

  return userId;
}
```

> [!NOTE]
>
> Publishers who persist `user.id` across sessions see **higher fill rates and CPMs** than those who don't. We recommend it where your privacy policy allows.

## `user.extended_ids`

`user.id` names the person only within your app. To name them across publishers, send hashed identifiers in [`user.extended_ids`](https://docs.thrad.ai/api/api-reference/bid-request-v2#user):

```json theme={null}
{
  "user": {
    "id": "user_5d1e9b",
    "extended_ids": {
      "emails_sha256": [
        "6db61e6dcbcf2390e4a46af426f26a133a3bee45021422fc7ae86e9136f14110"
      ],
      "phone_numbers_sha256": [
        "758fbf68945f21c416814c539ab578876c8d98fb69e6da692def92cd52417fe0"
      ]
    }
  }
}
```

Up to 3 of each, no repeats. Hash them exactly like this, then send the lowercase 64-character SHA-256 hex:

| | normalize | example |
| - | - | - |
| **Email** | trim spaces, lowercase | `"  Alex@Example.COM  "` → `alex@example.com` |
| **Phone** | keep the country code; remove spaces, `(`, `)`, `.`, `-`, a leading `+` and leading zeros | `"+1 (415) 555-2671"` → `14155552671` |

Anything that isn't a lowercase SHA-256 hex is refused, so an unhashed address never reaches us. Only send these where your privacy policy allows.

## Device and location

Identity isn't the only thing that decides whether a campaign can bid. **Send `device.geo.country`, `device.type` and `device.os` on every request.** Without the user's country, every campaign that targets a country is skipped, and device-targeted campaigns can't match without `type` and `os`. `device.ip` and `device.user_agent` are only fallbacks. See [`device`](https://docs.thrad.ai/api/api-reference/bid-request-v2#device).

## Next steps

> [card] [Impression Billing](https://docs.thrad.ai/api/guides/billing-viewability.md)
>
> Make sure every ad you show gets counted and paid.

> [card] [Best Practices](https://docs.thrad.ai/api/guides/best-practices.md)
>
> Timeouts, rate limits, errors, and fill rate.

Need exact fields? The [API Reference](https://docs.thrad.ai/api/api-reference/bid-request-v2.md) has every request and response.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.