Contacts: the people your agent knows, with custom attributes
Keep one record per person for each agent: verified visitors, leads, CSV imports and API contacts with custom attributes that server-side actions can read as {{contact.*}}.
A contact is a person your agent knows: their email, phone, name, their user id on your site, and up to 50 custom attributes of your own, such as a plan or an account manager. Each agent keeps its own contacts, on the Leads tab under Contacts. Server-side API actions can read them as {{contact.*}} placeholders. The AI itself never sees the attributes.
Where contacts come from
| Source | Shown as | What creates or changes the contact |
|---|---|---|
| Identity verification | Verified visitor | A visitor your site signed in chats with the agent. The contact gets their user id as External ID, the name and email your page passed, and metadata as attributes. |
| Lead form or contact details written in the chat | Lead | A lead is saved with an email or phone number that no contact has yet. |
| CSV import | Import | A row of your file. |
| REST API or MCP server | API | Your own systems create or update the contact. |
There is one contact per external ID and one per email address. Each source changes a contact only as far as it can be trusted:
- Verified visitors. The contact is found by the user id only. When the name, email or metadata your page passes changes, the contact follows: metadata keys that are valid attribute names (lower-case, see Attributes) become attributes, and other keys are left out. The email is taken only if no other contact has it. A verified visitor is never merged into an existing contact by email, because only the user id is signed: otherwise a signed-in visitor could claim someone else's contact by passing their email. The Last seen time follows their chats.
- Leads. A new email or phone number creates a contact. If the email or phone number already belongs to a contact that also came from a lead, its empty fields are filled in. A contact from a verified visitor, an import or the API is never changed by a lead, because anyone can type any email address. Visitors are never told whether a contact already existed.
- Imports, the API and the dashboard. These come from you, so their values replace what is stored.
Leads stay in Collected Leads as before. A contact is an extra record next to them, not a replacement.
Find and open a contact
The Contacts card lists the agent's contacts, most recently seen first, with the number used out of your plan's limit. Search by email, name, external ID or phone, or filter by source. Click a contact to open it:
- Its email, phone, name and external ID, and when it was first and last seen.
- All its attributes.
- Its conversations, up to the 20 most recent, each linking to the Conversations tab. These are the chats the contact was linked to, and for a verified visitor every chat with their user id.
Attributes
Attributes are your own fields on a contact, such as plan, account_manager or seats.
- A name starts with a lower-case letter and uses lower-case letters, digits and underscores, up to 40 characters.
email,name,phone,external_id,id,source,first_seen_at,last_seen_at,created_atandupdated_atare reserved.- A value is text up to 1,000 characters, a number or true/false. Through the dashboard and a CSV file, values are saved as text.
- A contact can have up to 50 attributes.
To edit them, open a contact, change the names and values, use Add attribute or the × beside one to remove it, and click Save attributes.
Import a CSV file
- On the Contacts card, click Import CSV and choose a
.csvfile of up to 5 MB and 10,000 rows. Commas, semicolons and tabs all work as separators. - intoCHAT reads the file and shows a preview without saving anything: how each column is used, the first rows, and how many contacts would be created, updated and skipped, with the reasons for the first 20 skipped rows.
- Click Import to save. The result shows how many contacts were created, updated and skipped.
How the file is read:
- The columns
external_id,email,phoneandnamefill in the contact. Column names are turned into snake_case first, soExternal IDandexternalIdboth work. - Every other column becomes an attribute:
Plan Tieris saved asplan_tier. A column name that can't become one, such as2024 spend, stops the import with a message naming it. - The columns
source,first_seen_at,last_seen_at,created_at,updated_atandidare ignored, so an exported file can be imported again. - Each row updates the contact with its
external_id, otherwise the contact with itsemail, otherwise it creates a new contact. A row whose email belongs to a contact with a different external ID is skipped. - An empty cell changes nothing. To remove an attribute, edit the contact.
- A row without an
external_idor anemailis skipped, and so is a row with an invalid email or phone number, a value over 1,000 characters, or one that would give a contact more than 50 attributes. - New contacts stop at your plan's limit; the rows beyond it are skipped with that reason.
Export a CSV file
Export CSV downloads the contacts that match the current search and filter: external_id, email, phone, name, source, first_seen_at, last_seen_at, then one column per attribute. A cell that starts with =, +, - or @ gets a leading apostrophe, so a spreadsheet app shows it as text and never runs it as a formula. Importing the file again removes the apostrophe.
Use contacts in actions
A server-side API action can use these placeholders in its URL, parameters, headers and body:
| Placeholder | Filled with |
|---|---|
{{contact.email}} | The contact's email |
{{contact.name}} | The contact's name |
{{contact.phone}} | The contact's phone number |
{{contact.external_id}} | The contact's external ID |
{{contact.<attribute>}} | One attribute, for example {{contact.plan}} |
GET https://api.example.com/accounts/{{contact.external_id}}?plan={{contact.plan}}
Which contact is used:
- For a visitor your site signed in and verified, the contact with their user id as external ID, and no other.
- For anyone else, the contact of the lead saved in this conversation, found by its email or phone number. A contact with an external ID is never used this way: it belongs to a user of your site, and only their verified chats can read it.
intoCHAT fills these in on its servers. The AI never sees them and can't choose them. If there is no contact, or the contact doesn't have one of the values the action uses, the action doesn't run. The agent is told the action needs details it doesn't have, in the same words whether or not a contact exists, so the visitor learns nothing about your contacts. Unlike {{user.*}}, nothing is ever sent empty.
- An unknown placeholder, such as
{{contact.Plan}}or{{contact.id}}, can't be saved. - Send test request in the editor has no visitor, so an action that uses
{{contact.*}}can't be tested there. - A client-side action never gets contact data, because its request is built in the visitor's browser.
- In the Playground, Test as a signed-in visitor uses the contact with that user ID, if you have one. Test identities never create contacts.
Delete a contact
Open the contact and click Delete contact. Its attributes go with it. Its leads and conversations stay. If the same verified visitor chats again, or the same email comes in as a new lead, a new contact is created. Deleting an agent deletes its contacts. Duplicating an agent doesn't copy them.
Who can do what
Everyone on your team can see contacts and export them. Editors, admins and the owner can edit attributes, import a file and delete contacts.
Limits
| Free | Starter | Pro | Enterprise | |
|---|---|---|---|---|
| Contacts per agent | 100 | 5,000 | 25,000 | 100,000 |
An import or an API call that would go over the limit is refused with a message naming the limit. New verified visitors and leads beyond it are simply not saved as contacts, and the chat isn't affected. After a downgrade, an agent keeps all its contacts and can still update them, but gets no new ones until it is under the limit.