Contacts
One record per person your organization talks to, holding the numbers and addresses they reach you on, a name, tags, notes, and the attributes your organization defines.
This page is being rolled out. If Contacts isn't in your sidebar, it isn't enabled for your organization yet — contact support.
What a contact is
A contact is one record for one person your organization talks to. It holds:
- A name — a display name, or a first and last name. A contact with neither is listed by one of its identifiers instead.
- Identifiers — the phone numbers, email addresses, chat visitor ids, and external ids the person is recognized by. A contact with no identifiers can never be matched to a conversation.
- Tags — up to 50 short labels, 40 characters each, for your own grouping.
- Notes — free text, up to 4,000 characters.
- Attributes — a value for each field your organization has defined. See Attributes below.
- Linked records — the person's record in your Zendesk, once your organization has a Zendesk integration. See Linking a contact to Zendesk.
Names, tags, notes, and text attributes keep what you type, in any language or script. Tandem removes control characters and the invisible characters that override text direction, and turns a line break in a name or tag into a space.
Open Contacts → All Contacts to see the list, and click Edit on a row to open the contact in a sidebar. Everything in the sidebar is edited behind one Save: names, tags, notes, attribute values, and identifiers added or removed all go up together.
You create a contact from Add Contact, by importing a CSV file, or through the API, and a voice agent can create one for you. With Recognize callers on, a call matches the caller's number to a contact and creates one holding that number when nothing matches, and a returning website chat visitor is matched to a contact the same way. Each of those moves the contact's Conversations count and its Last Contact. Nothing is on until somebody turns that switch on for an agent — see Contacts on an agent.
A visitor arriving at your chat widget for the first time is matched to nothing and no contact is created for them, because the match runs on an id the widget left in their browser on an earlier visit. Text messages are matched to contacts only when Recognize texters is on; see Text messages.
Adding a contact by hand asks for a first name, a last name, a phone number, an email address, and tags. Any one of the first four is enough to save. The dialog deliberately asks for no attribute values; you fill those in on the contact itself.
Finding a contact
The list is ordered by Last Contact, most recent first, and pages with Previous and Next rather than by page number: the service returns a forward cursor and no total, so there is no count to jump to a page against. There is no sort control, because sorting the page in hand would misrepresent the rest of the list.
The search box above the list takes a name, a number, or an email address, and how it runs depends on what you type:
- A value that looks like an email address or a phone number is matched against identifiers as a prefix, so a partial number finds every contact whose number starts with it.
- Anything else is matched against names and tags as a substring, ignoring case, across every contact in your organization. A contact created or renamed a moment ago can take a short time to turn up in a name search. Searching by its phone number or email address finds it at once.
A name or tag search pages with Previous and Next like the list. A phone or email search returns one page.
A contact's conversations
The contact's sidebar lists its conversations under Conversations, newest first, on every channel. That includes calls to an IVR workflow from one of the contact's phone numbers, which are matched by number because a workflow does not look the caller up. Each row shows the channel, the start time in UTC, the duration, who handled the conversation, and its disposition when it has one. Click a row to open the conversation in Call Logs. Load More fetches older ones.
A workflow with Redact Data in Logging on (on the General tab of its settings) records no phone number for its conversations, so its calls and texts are not listed here. A conversation that reaches a voice or chat agent with Recognize callers on is still listed when the agent matches it to the contact.
A call matched only by number does not move the contact's Conversations count or its Last Contact in the list, so a contact can show conversations in its sidebar and a count of zero. The Activity section above Conversations shows when the contact was first and last seen, and is left out for a contact no conversation has recognized yet.
From Call Logs
In Insights → Call Logs, the Source column shows the contact's name above the number for a call from one of your contacts. Click the name to open that contact's sidebar on the Contacts page.
Text messages
With Recognize texters on, the texts your workflows send are matched to a contact by the other person's phone number. Texts your phone numbers receive are matched once inbound texting on your account has moved to the current messaging service; ask support whether yours has. A number no contact holds gets a new contact. A number added this way shows Text message as its source on the contact. Of the texts you send, only those sent by a workflow are matched. A text from a short code or an alphanumeric sender is not matched.
Recognize texters is one switch for the whole organization, under Admin → Contact Attributes → Settings, and it is off by default. Turning it off applies from the next text.
A text from the person that starts a new workflow conversation adds one to the contact's Conversations count and moves its Last Contact. Later texts in that conversation, texts your workflows send, and texts to a number with no workflow are matched without counting.
A text never waits on the match. It is sent or delivered whether or not the match finishes in time.
A texting conversation shows under the contact's Conversations when it was started by a text from one of the contact's phone numbers. That match is by number, so it does not depend on Recognize texters. Texting conversations recorded before Recognize texters was released carry no number and are not listed.
Texting in the US needs an approved 10DLC registration (Admin → 10DLC Registration, where your organization has it). Until yours is approved, there are no texts to match.
One number, one person
A phone number, email address, chat visitor id, or external id belongs to exactly one contact in your organization. That rule is what lets a conversation resolve to a single person, and it has a consequence worth knowing before you go looking for a bug.
Adding an identifier another contact already holds does not move it. On a contact that
already exists the save succeeds, but the value is parked on the contact you were editing
rather than attached to it: the row is badged Belongs to another contact and reads
already on <contact id> beneath. Both records then carry a Possible duplicate badge. To
genuinely move a number, remove it from the contact that holds it and save, then add it to the
other one. That second save attaches the value and clears the parked row. When the two records
are the same person, merging them moves every identifier at once.
Add Contact parks nothing. Creating a contact with a number or address another contact already holds is refused outright, the dialog reports the refusal, and no record is created. So free the value on the contact that holds it first, or create the contact without it and add the identifier afterwards.
That is why a shared line (a household phone, a reception desk) shows up as one contact until you create the second contact without the number, remove the number from the first, and then add it to the second.
You can add three kinds of identifier by hand:
- Phone — rewritten to its international form when you leave the field, which is the form stored and compared. A number Tandem cannot dial is refused before you can add it.
- Email — case-insensitive.
- External ID — an id from another system, written
system:id(for examplecrm:12345). Both halves are required. A system startingzendesk.is reserved for a link to a person in Zendesk, writtenzendesk.<subdomain>:<user id>with the subdomain alone and the user id in digits (for examplezendesk.acme:381920001). Any other value under that system is refused. Linked Records writes these for you; see Linking a contact to Zendesk.
A chat visitor id cannot be typed. It is minted by the chat widget in the visitor's browser and means nothing outside it, so the widget is the only thing that ever sets one.
One save adds up to 20 identifiers and removes up to 20. A removal is staged the same way an addition is: the row stays visible, struck through, with an undo button until you save.
Linking a contact to Zendesk
When your organization has a Zendesk integration, a contact's sidebar has a Linked Records section. A link points the contact at the same person in Zendesk, and Open in Zendesk takes you straight to their profile there, in a new tab.
To add a link:
- Open the person's profile in Zendesk and copy the link from the address bar, which looks like
https://<subdomain>.zendesk.com/agent/users/<user id>. The user id on its own works too. - In the contact's Linked Records, paste it into Zendesk user ID or profile link and click Add Link.
- Click Save. The link saves with the rest of the sidebar's changes.
With more than one Zendesk integration, a pasted profile link picks its own account. A bare user
id needs one chosen under Zendesk account first. A link to a Zendesk account your organization
has no integration for is refused with That link is for <subdomain>.zendesk.com, which isn't one of your Zendesk integrations.
With exactly one link, Open in Zendesk also sits in the sidebar's title bar. Opening a profile needs you to be signed in to Zendesk.
A link is stored as an external id, so it follows the same rules as any identifier. A Zendesk user belongs to one contact: linking one another contact already holds parks the link under Identifiers as Belongs to another contact and flags both contacts as possible duplicates. Merging two contacts keeps both of their links.
A link keeps working if the Zendesk integration it was added through is removed. Its row then reads Not connected. With no Zendesk integration left, new links can't be added until one is connected again.
Deleting a contact removes its links and changes nothing in Zendesk. The person's Zendesk record stays until you delete it there.
Your Zendesk users
When your organization has a Zendesk integration, the Contacts page can list the end-users in your Zendesk account as well as your contacts. Choose Contacts, Zendesk, or All beside the search box. All shows your contacts first and your Zendesk users below them, each with its own Previous and Next, and the search box searches both. With more than one Zendesk integration, a picker beside the switch chooses which account to list. It starts on the first by name.
The integration needs users:read (or read) under Scopes. Without it the list shows This Zendesk integration can't read users. Add users:read to its scopes and save it. See Connect it
in Tandem.
The Zendesk list is read-only:
- It shows end-users only. Your Zendesk agents and admins never appear, in the list or in a search.
- It shows each user's name, email, phone, and when they were last updated, and pages with Previous and Next.
- Clicking a row opens the user in a sidebar: their profile, notes, activity, and any custom fields that have a value. Open in Zendesk opens their profile there.
- In the Zendesk view, Add Contact and Import CSV are hidden. Nothing in the Zendesk list or its sidebar can be edited.
Every page, search, and user you open is read from Zendesk at that moment, and Tandem keeps none of it. Each one is a request against your Zendesk account's API rate limit, which your other Zendesk apps share. When Zendesk is rate-limiting the account, the list says so and offers Retry.
The search box takes a name, an email address, a phone number, or words from a user's notes. Zendesk can take a few minutes to make a new or changed user searchable, so a user who appears in the list may not turn up in a search yet. A search returns at most 10,000 users.
Everyone in your organization who can open Contacts can see your Zendesk end-users here, including their notes and custom fields. The list doesn't show which Zendesk users are linked to a contact.
Attributes
Attributes are the fields every contact can hold beyond a name and the ways to reach them. They are defined once for the whole organization under Admin → Contact Attributes, and every contact in the organization then shows one field per definition.
Each definition has:
- Key — lowercase letters, numbers, and underscores, starting with a letter, up to 40 characters. This is the name agents and the API use.
- Label — what the field is called on a contact, up to 80 characters.
- Kind — what sort of value it holds, which decides the control you get and what the service accepts. See the table below.
- Required — a marker only. The field is shown as required in the contact sidebar, and nothing blocks a save when it is empty.
- Agent can edit — marks the attribute as one an agent may fill in.
Agent can edit is what decides which attributes an agent may fill in during a conversation, and it governs writing only. An agent that recognizes the caller is told the attribute values on their record whether or not this is set; an agent whose Contacts card does not allow updates can write none of them. See Contacts on an agent.
The kinds
| Kind | What the service accepts |
|---|---|
| Full name | Any text |
| An email address, stored lowercase | |
| Phone | A number Tandem can dial, stored in international form |
| Postal address | Any text, on a multi-line field |
| Date of birth | A date, YYYY-MM-DD |
| Date | A date, YYYY-MM-DD |
| Postal code | Any text |
| Alphanumeric code | Any text |
| Number | A number |
| Yes / no | Yes, No, or no value |
| Free text | Any text, on a multi-line field |
| Choice | One of the options you list on the definition |
Seven of these are checked for shape: Email, Phone, the two date kinds, Number, Yes / no, and Choice. The other five — Full name, Postal address, Postal code, Alphanumeric code, and Free text — accept any non-empty text up to 500 characters, so Postal code and Alphanumeric code give you a labeled field rather than a validated one.
Saving the list
One Save replaces the whole list, so every row goes up together and one invalid row holds back every edit on the page. If somebody else saves a different list while yours is open, a warning appears above the editor: saving replaces theirs with what is on your screen, and reloading the page starts you from the current list instead.
Two limits and one rule to plan around:
- 50 attributes per organization, each Choice kind carrying up to 50 options.
- Text values are capped at 500 characters on a contact.
- A key cannot change after it is saved. The field locks itself once a definition has been stored, because every value on every contact is filed under that key: renaming it would orphan all of them at once. Give a definition the wrong key and the way out is to remove it and add another.
Removing a definition takes the field off the contact form and leaves the values alone.
They stay on each contact, read-only, under No longer defined, so nothing is lost if you
remove a definition by mistake. Each row there is identified by the attribute's key, since
the label went with the definition: remove "Loyalty Tier" and the row reads loyalty_tier. Add
the key back and the values are editable again.
Importing contacts
Import CSV, beside Add Contact, creates and updates contacts from a spreadsheet. It takes a CSV file and walks you through three steps: choose the file, match its columns to contact fields, then follow the import while it runs.
The file
- CSV in UTF-8, with a header row naming each column. In Excel, save as CSV UTF-8. Excel's
other CSV formats are not UTF-8, and a file saved in one is refused if it holds an accented
letter. Spreadsheet files such as
.xlsxare not accepted. - Comma or semicolon between fields. Tandem reads whichever the header row uses more.
- Up to 20 MB and 50,000 rows. A larger file is refused before it uploads. A file with more than 50,000 rows stops after the 50,000th; see When an import stops.
- Up to 500 columns, each name up to 200 characters. A file past either limit is refused before it uploads.
- No two columns can share a name. A column whose header cell is blank is ignored, so the empty columns Excel sometimes adds after the last one do no harm. A file whose first line is blank, or whose header names no columns at all, is refused.
Blank rows, and rows whose cells are all empty, are skipped. They still count when rows are numbered, so a row number in the report matches the row in your spreadsheet, but they do not count toward the 50,000.
Excel can turn a long number into scientific notation (1.55555E+10), so format phone columns
as text before you export. A cell saved that way is reported as an invalid phone number.
Matching columns
The second step lists each column with a sample value from the file. Choose the contact field it fills: Phone, Email, First name, Last name, Display name, Tags, one of your organization's attributes, or Ignore. Columns with common names, such as Phone, Mobile, Email, First name, Last name, Name, and Tags, are matched for you, ignoring case, spaces, and punctuation.
Start Import stays unavailable until at least one column is matched to Phone or Email, because those are what a row is matched to a contact by. Several columns can be Phone or Email (a mobile and an office number, say); every other field can be chosen for one column only.
How a cell is read:
- Phone: spaces, dashes, and parentheses are ignored. A number without a
+is read as North American when it has 10 digits, and as starting with its country code otherwise. - Tags: a cell can hold several tags separated by
;or,. - Number attributes: digits with a period as the decimal mark, such as
3.5. A comma as the decimal mark (3,5) or as a thousands separator (1,000) is not a number. - Yes / no attributes:
yes,y,true, or1, andno,n,false, or0, in any case. - Other attributes: the same values a contact accepts; see The kinds.
What each row does
Each row is matched to a contact by its phone numbers and email addresses.
- No contact matches: a new contact is created with the row's values.
- One contact matches: the import fills in only what the contact is missing. It sets a name or an attribute value only where the contact has none, adds tags the contact does not have (up to 50), and adds the row's other phone numbers and email addresses. It never changes a value a contact already has and never removes anything. Notes are not imported.
- Two contacts match (the phone number belongs to one contact and the email address to another): the contact holding the value in the leftmost of those columns is updated. The other value is parked on it as Belongs to another contact, and both contacts get the Possible duplicate badge. These rows are counted under Possible duplicates; merge the pair if they are one person.
Rows are imported in file order, so a later row that repeats an earlier row's phone number or email address updates the contact the earlier row created.
A cell that can't be used is skipped and reported, and the rest of the row is still imported: a phone number that can't be dialed, an email address that isn't valid, an attribute value of the wrong kind, a tag longer than 40 characters, or a name longer than 120. A row left with no usable phone number or email address is not imported and counts as Failed, as does a row whose contact could not be saved.
Following an import
The import runs on Tandem's side, and the dialog counts the rows imported so far. Closing the dialog doesn't stop it; open Import CSV again in the same browser tab to see its progress. After a page reload the dialog no longer shows it, though the import still finishes and its contacts appear in the list.
When it finishes, the dialog shows how many rows were Created, Updated, Possible duplicates, and Failed, and lists each row that had a problem by its row number and column. Row numbers are spreadsheet rows: the header is row 1, so the first contact is row 2. The list holds the first 500 problems, and never shows the value in the cell.
When an import stops
An import is not all or nothing. If it stops part way, the rows imported before it stopped are kept, and the dialog shows the reason:
file has more than 50,000 rows: the rows after the 50,000th were not imported. Import them as a separate file.ran out of time: an import runs for at most 15 minutes, and a large file can need longer.stopped responding: the import recorded no progress for 2 minutes.the file is not valid CSV (CsvError): a problem further into the file than the preview reads, such as a quote that is opened and never closed.the import stopped on an unexpected error, followed by the error's name in parentheses: import the file again, and contact support if it recurs.
Importing the same file again is safe. Rows that were already imported match their contacts and only fill in what is still missing, so the second run picks up the rows the first never reached.
Nothing stops a second import from starting while one runs, from another browser tab for example. Because an import only fills in what is missing, two overlapping imports of the same file add nothing twice.
Who can import, and what happens to the file
Import CSV is hidden unless you hold the administrator role, the same check deleting and merging use, and the API checks the same role. Every role Tandem issues today carries it.
The uploaded file is deleted when the import finishes successfully. The file of an import that stopped, or whose delete failed, is removed by storage automatically about 7 days after it was uploaded.
Possible duplicates
A contact carries a Possible duplicate badge, on its list row and at the top of its sidebar, when an identifier somebody tried to add to it already belonged to another contact. The pending row on the contact names the id of the record that holds the value.
Open a badged contact and its sidebar has a Possible duplicates section listing each contact it was flagged against, with that contact's identifiers and the value the two collided on. Opening it also tidies the pending rows: one whose named holder has since given the value up, or been deleted, is dropped.
Two records that share a number may be one person with two entries or two people on one line. Nothing in Tandem decides which; you do. If they are the same person, merge them. If they are not, remove the identifier from the record it does not belong to and add it to the one you are keeping; that save clears the pending row. Resolved by hand, the badge does not clear itself, so treat it as "these two were once in conflict" rather than as a live warning.
Merging two contacts
In the Possible duplicates section, click Merge into This Contact beside the other record. The contact you have open is the one kept, and the other is merged into it. The confirmation names both. Save or discard any edits in the sidebar first: the button is unavailable while the contact has unsaved changes. Merging:
- Moves every identifier from the other record to the one kept: phone numbers, email addresses, chat visitor ids, and external ids. A call from any of those numbers reaches the kept contact from then on.
- Combines notes and tags. The other record's notes are added below the kept contact's. If the two together run past 4,000 characters, the end is cut and the message confirming the merge says so. Tags are combined up to the 50-tag limit.
- Fills in attribute values the kept contact has none for. Where both have a value, the kept contact's stays.
- Brings the history along. The kept contact's Conversations list includes the other record's conversations, and on the kept contact's next call a voice agent that recognizes callers reads the caller memory filed under the other record too.
- Removes the other record from your contacts list. In Call Logs, a call that named it now names the kept contact, and clicking the name opens the kept contact.
Merging can't be undone. A merged contact can't be split back into two.
The button is hidden unless you hold the administrator role, the same check deleting uses, and every role Tandem issues today carries it. Merging clears the flag between the two records. A third contact that was flagged against the merged-away record keeps its badge, and its Possible duplicates section no longer lists that record.
Deleting a contact
Delete a contact from the trash icon on its row in the list, and confirm. The icon is hidden unless you hold the administrator role, and the API route checks the same role. Every role Tandem issues today (Owner, Admin, and Member) carries it, so in practice everyone in your organization can delete a contact. See Admin for what that gate does and does not keep out.
The confirmation has a checkbox, Also delete this person's conversation history (call transcripts and chat transcripts), checked by default. Clear it to remove a duplicate or test contact while keeping the record of what was said.
Deleting is not undoable. Whichever you choose, it removes:
- The contact record, including its name, tags, notes, and attribute values.
- Its identifiers, which frees each number, address, and id for another contact to claim.
- Every contact merged into it. What is left of each merged-away record goes too.
- What your voice agents remember about this person: the caller memory, both the organization-wide record and any per-agent one, for the contact, for every contact merged into it, for every phone number it held, and for every website chat visitor id it held.
- Its history in reporting: every earlier change to the contact, with the names, numbers, and addresses those changes carried. This goes when reporting processes the deletion, shortly after the contact is removed.
With the box checked, it also removes:
- Call records and transcripts: for every phone number it held, the disconnect-resume record, the call records, and the stored transcripts.
- Website chat conversations and their transcripts, for every chat visitor id it held.
The memory, and with the box checked the call records, transcripts, and chat conversations, are erased before the contact is removed. If that fails, the delete fails with it and the contact is still there, so you can retry rather than being left with a deleted record and its conversation data still in place. A retry is safe and picks up where the last one stopped.
A contact deleted while it is being merged into another can have its details kept on the contact it was merging into.
Reporting keeps its record of every call, text, and chat, along with the phone number it came in on. What reporting drops is the name and the link: the conversations that pointed at the contact no longer point at anything, and the numbers and addresses it held are removed there as well. What stays behind is a marker under the deleted contact's id, holding nothing about the person: no name, no numbers, no addresses. Reporting keeps it so that reprocessing an older event cannot rebuild the contact you deleted.
Neither choice erases what a workflow recorded about a conversation as it ran: its entry in reporting and the data it logged, which can include the text of messages.
Whichever you choose, deleting a contact does not promise to erase the text messages exchanged with its numbers; some or all of them can remain.
Deleting a contact changes nothing in Zendesk. A person linked under Linked Records keeps their Zendesk record until you delete it there.
API
Contacts have a public API at /v1/contacts: list and search, create, read, update, delete,
merge, read a contact's possible duplicates, read or replace the organization's attribute
definitions, read or change the organization's contact settings, and import a CSV file. A
delete erases the conversation history unless you send history=keep. See the
Contacts API reference,
Merge contacts for the merge, and
Start an import for the import. An import through
the API takes the same three calls the dialog makes: ask for an upload URL, PUT the file to
it, then start the import and read its progress until it finishes.