# Contacts overview

> Contacts are durable records of the people your agents talk to — name, email, phone, linked Stripe accounts, and your own custom attributes — scoped per agent and keyed by a stable external ID. Search, sort, edit, and manage them from the dashboard or the API.

**Contacts** are durable records of the people your agents interact with. Where a conversation is a single exchange, a contact is the person behind it — a profile you can recognize across conversations, enrich with your own data, and look up from the dashboard or the API.

Each contact carries core profile fields, optional linked Stripe accounts, and any [custom attributes](/docs/contacts/custom-attributes) you define. Together they give your agent and your team context: who this customer is, what plan they're on, what their order history is — whatever you choose to store.

> **CONTACTS ARE SCOPED PER AGENT:** A contact belongs to one agent (its "chatbot"). The same person talking to two of your agents is two separate contact records — which keeps each agent's contact list clean and scoped to who it actually serves.

## What a contact holds

| Field | Description |
| --- | --- |
| External ID | A stable, caller-supplied identifier, unique per agent. This is how you reference a contact and how upserts match — typically your own user ID, customer ID, or email. |
| Name | The contact's display name. |
| Email | The contact's email address. |
| Phone number | The contact's phone number. |
| Stripe accounts | One or more linked Stripe accounts (label, Stripe ID, Stripe email) for billing context. |
| Custom attributes | Your own typed fields — plan tier, lifetime value, signup date, VIP flag, and so on. See Custom attributes. |
| Created / updated | Timestamps maintained automatically. |

> **CHOOSE A STABLE EXTERNAL ID:** The external ID is the key everything hangs off — it must be unique per agent and shouldn't change. Use your own customer ID or user ID. If you only have email, email works, but a permanent internal ID is more robust.

## Why contacts matter

- **Recognize returning customers** — tie conversations to a known person rather than treating every chat as anonymous.
- **Give the agent context** — custom attributes (plan, status, order history) let the agent and your team personalize responses.
- **Power your help desk** — when a conversation escalates, your team sees who they're talking to in the [Help Desk](/docs/help-desk/overview).
- **Sync with your systems** — push contacts in from your app or store via the [Contacts API](/docs/api/v1/contacts-create), and keep them current.

## Managing contacts in the dashboard

Each agent has a **Contacts** tab with a searchable, sortable, paginated table. From it you can:

- **Search** across contacts and **sort** by any core column (name, email, phone, external ID, created/updated).
- **Choose columns** — show or hide core fields and any of your non-archived custom attributes.
- **Add or edit** a contact with typed inputs that match each attribute's type.
- **Import** contacts in bulk from CSV or JSON.
- **Manage attributes** — define, archive, and restore the custom fields on this agent.
- **Adjust rows per page** to scan large lists efficiently.

*[image: The Contacts tab: a sortable, searchable table of contacts with columns for name, email, phone, and custom attributes, plus Add, Import, and Manage attributes controls.]*

## Creating and updating contacts

Add contacts four ways:

1. **Manually** — Use Add on the Contacts tab and fill in the fields, including typed inputs for any custom attributes.
2. **In bulk** — Import a CSV or JSON file. See [Importing contacts](/docs/contacts/importing).
3. **Via the API** — Create or upsert contacts programmatically from your own app or store. See the [Contacts API](/docs/api/v1/contacts-create).
4. **Automatically, from identity verification** — When a signed-in visitor is identified in the widget, Bookbag auto-creates and keeps the contact up to date — no separate API call. See below.

> **UPSERT BY EXTERNAL ID:** Creating a contact whose external ID already exists for that agent updates the existing record rather than duplicating it. This makes imports and API syncs safe to re-run — the external ID is the match key.

## Automatic contacts from identity verification

The easiest way to keep contacts current is to let the widget do it. When your site identifies a signed-in visitor with [identity verification](/docs/developers/identity-verification) — `window.bookbag('identify', { user_id, user_hash, user_metadata })` — Bookbag verifies the signature and **upserts the contact whose `external_id` equals `user_id`**: it fills name, email, and phone from `user_metadata`, turns any extra metadata keys into [custom attributes](/docs/contacts/custom-attributes), and updates the same record on return visits.

> **USER_ID IS THE EXTERNAL ID:** Identity verification ties a conversation to a person, and the verified `user_id` is used as the contact's `external_id`. Use your own stable customer id for both and everything — chats, the contact record, and your CRM — lines up automatically.

## How the agent uses contacts

A contact isn't just a record your team reads — on a **verified** conversation the agent receives the matched contact's details and uses them to personalize the chat: greeting the customer by name, tailoring answers to their plan or status, and not re-asking for information it already has. Your custom attributes (plan, lifetime value, VIP, …) come along as context.

Contacts also feed your [actions](/docs/actions/overview): an action can reference the verified customer with `{{contact.email}}`, `{{contact.name}}`, `{{contact.external_id}}`, `{{contact.phone}}`, and `{{contact.attr.<key>}}` — so a "look up my orders" or "update my address" action runs against the right person.

> **ONLY VERIFIED IDENTITIES PERSONALIZE:** Personalization and auto-sync happen only for a verified `user_hash`. An anonymous visitor — or a request with a bad signature — is treated as anonymous and never creates or alters a contact, so a forged id can't read another customer's details.

## Stripe accounts

A contact can carry one or more linked **Stripe accounts**, each with a label, Stripe ID, and Stripe email. This gives your team billing context right alongside the contact — useful when a conversation turns into a billing or subscription question.

## Contacts and the API

The dashboard and the public API operate on the same contacts. Programmatic access uses your API key and is scoped to the agent (chatbot) the key belongs to — you can create, list, update, and delete contacts and manage custom attributes. See the [Contacts API reference](/docs/api/v1/contacts-create) for endpoints, payloads, and authentication.

> **CHECK:** Included on every plan. Contacts aren't gated behind a higher tier — they're part of Bookbag out of the box.

## What's next

- [Custom attributes](/docs/contacts/custom-attributes) — Define your own typed fields — plan, status, LTV, and more.
- [Importing contacts](/docs/contacts/importing) — Bring contacts in from CSV or JSON in bulk.
- [Contacts API](/docs/api/v1/contacts-create) — Create, list, update, and delete contacts programmatically.
- [Collect leads](/docs/actions/collect-leads) — Capture new contacts straight from a conversation.
