# Visitor profile, tags and activity

> Send the visitor's plan, company, custom fields, tags and activity from your site. Your team sees them next to the conversation. Limits and privacy.

Your site can tell your team who is writing and what they just did. The team sees it in the inbox next to the conversation and on the contact page.

![The contact sidebar with the company, plan, tags and latest activity](https://hrc.lt/screens/en/inbox-contact-profile.desktop.1600.cb6b97bc.webp)

## What a visitor profile is

- **Profile:** name, phone, avatar, company, tags and up to 50 typed custom fields.
- **Activity:** a short history of what the visitor did (`track`).
- **Session values:** what matters for this conversation, sent with every message (`setContext`).

The profile belongs to the contact and stays between conversations. Session values belong to the messages. The two never mix.

```js
// After sign-in, with the hash your server computed
HrcWidget('identify', {
  externalId: String(customer.id),
  email: customer.email,
  name: customer.name,
  userHash: customer.supportHash,
  attributes: {
    plan: 'pro',
    signed_up_at: customer.createdAt,
    orders: customer.orderCount,
  },
  tags: customer.isVip ? ['vip'] : [],
});

// Later, on any page
HrcWidget('update', { attributes: { cart_items: 3, cart_total: 129.5 } });
HrcWidget('track', 'order-placed', { order_id: 'o-1042', total: 129.5 });
```

## Custom fields

| Rule        | Value                                                                                                        |
| ----------- | ------------------------------------------------------------------------------------------------------------ |
| Key         | Lower case letters, digits, `_` and `-`, starting with a letter, up to 40 characters. Upper case is lowered. |
| Types       | Text (1 to 255 characters), number, `true`/`false`, date.                                                    |
| Date        | A JS `Date`, or an ISO 8601 string (`2026-11-02`) under a key ending in `_at` or `_date`.                    |
| Count       | Up to 50 fields per contact.                                                                                 |
| Remove      | `null` or `''`.                                                                                              |
| Not allowed | Objects and arrays (company is its own field).                                                               |

Reserved keys (`id`, `email`, `name`, `phone`, `company`, `tags`, `language`, `user_hash`, anything starting with `hrc_`, and others) and keys that look like secrets (`password`, `secret`, `token`, `cvv`, `card_number`, `iban`, `api_key`) are refused.

## Name, phone, avatar and company

- `name` up to 120 characters.
- `phone` up to 32 characters with at least 5 digits. Shown to your team only, never used to contact anyone.
- `avatarUrl` `https:` only, up to 500 characters. Initials show when it fails to load.
- `company`: `{ id?, name, website?, plan? }`, one company per contact, `null` removes it.

## Tags and activity

Tags (`setTags`): up to 20 per contact, lower case letters, digits and hyphens, up to 32 characters.

Activity (`track`): a name of lower case letters, digits, `_`, `.` and `-`, up to 64 characters. Past tense reads best: `plan-upgraded`, `checkout.failed`. Up to 10 properties. The newest 100 events are kept, for up to 180 days. The sidebar shows the newest 5, the contact page all of them.

![The contact page with profile fields, their sources and the activity](https://hrc.lt/screens/en/contact-events.desktop.1600.d5200da9.webp)

Activity is for support ("what did they just do"), not for analytics.

## Who may change what

Every value has a source. A lower source never overwrites a higher one: the field is skipped and the widget prints one console warning.

| Source    | Who writes                                                      | Shown in the app               |
| --------- | --------------------------------------------------------------- | ------------------------------ |
| Team      | A member in the app, an AI agent over MCP or the REST API       | "set by the team"              |
| Your site | The SDK while the session is recognized with a valid `userHash` | "from your site"               |
| Browser   | The SDK in an anonymous session                                 | "from the browser, unverified" |

```js
// No sign-in: the values are kept as "from the browser, unverified"
HrcWidget('update', {
  attributes: { landing_page: 'pricing', utm_source: 'newsletter' },
});
```

`userHash` proves the id and the e-mail only. Send values that decide something (plan, balance, permissions) from your server through the [REST API](https://hrc.lt/en/docs/ai-agents/rest-api#trusted-profile): their source is then the team, and the browser cannot change them.

## When data is stored

Until the visitor sends a message, uploads a file or is recognized, nothing is stored in their browser or on the server. Until then profile changes and up to 20 events wait in the widget's memory and go out right after the session exists. A page reload drops them.

## Privacy

Profile values, tags and activity are personal data of the contact, and your company is their controller. Never send passwords, payment card data, health data or other special category data. Deleting a contact deletes its activity too.
