A contact is a person your organization talks to: a patient, or a relative who calls for one. Raíz keeps one record per person, with their names, the phone numbers, WhatsApp numbers and emails they use, their identity documents, a summary and what your agents learned about them. Every conversation with them, on any channel, links to that record, so your team and your systems see one history per patient. The Raíz Dashboard calls contacts People.
What a contact holds
| Field | What it is |
|---|---|
givenName, familyName |
Their names. fullName joins them. |
points |
Up to 20 ways to reach them. Each has a kind (phone, email, whatsapp, telegram, slack or web), an address (E.164 for a phone), an optional label, and preferred; when no point of a kind is preferred, the first one is. |
identifiers |
Up to 5 identity documents: a type code such as CC, CE, PA or TI, a number of capital letters and digits, and the issuing countryCode (CO by default). |
postalAddresses |
Up to 5, each with an optional use: home or work. |
summary |
A summary of the person. |
metadata |
Your own keys and values, which Raíz never interprets. |
facts |
The current value of each definition about them. |
profileName |
The name they show on WhatsApp, as WhatsApp sent it. Read-only. |
phone, email |
Their preferred phone and email. |
lastConversation, conversationCount, lastContactedAt |
Their latest conversation, and how much you’ve been in touch. |
Create, update and find
raiz.contacts.create adds one person. It isn’t idempotent, and when another contact already has one of the addresses or identity documents, it answers 409 ADDRESS_IN_USE or 409 IDENTIFIER_IN_USE and creates nothing, so search first. In this example, Ana Ejemplo is an invented person:
curl https://api.getarbol.com/v1/raiz/contacts \
--request POST \
--header "Authorization: Bearer $ARBOL_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"givenName": "Ana",
"familyName": "Ejemplo",
"points": [
{ "kind": "whatsapp", "address": "+12025550143" },
{ "kind": "email", "address": "ana.ejemplo@example.com" }
]
}'raiz.contacts.update changes a contact. Fields you leave out stay and null clears one, but points and identifiers are complete sets: listed items keep their id and verification, and missing ones are removed. Read the contact first and send its version as expectedVersion. Names and summaries you write this way are never overwritten by the AI.
raiz.contacts.list finds people by q: names, profile names, usernames, phones (even partial, or without the country prefix), emails, documents or the contact id, ignoring accents, case and number formatting. It also filters by channelType, agentId (people who talked with that agent), handling and lastContactedAfter, and sorts by created, name or last_contacted. raiz.contacts.count counts the same matches, up to 10,000.
Import from a CSV
raiz.contacts.import adds or updates many people at once from a UTF-8 CSV with a header row, separated by commas, semicolons or tabs. Send it as text in csv or base64-encoded in file.data, not both, up to 12 MB (about 100,000 rows).
given_name,family_name,whatsapp,email,preferred_site
Ana,Ejemplo,+12025550143,ana.ejemplo@example.com,north
Luis,Prueba,+12025550178,luis.prueba@example.com,southThe columns given_name, family_name, phone, whatsapp, email, document_type, document_number and summary fill the contact. A column whose header is the key of an active contact definition imports as a fact, as preferred_site would for a definition with that key; any other column is stored as metadata. countryCode (default CO) is the country of phone numbers written without +. A row whose phone, WhatsApp number, email or document matches an existing contact updates that contact; the others create new ones.
The import answers with a task at once. raiz.tasks.get shows its progress, processedRows of totalRows, and at the end how many contacts were created, updated and rejected, with the first 100 row errors. raiz.tasks.cancel stops the import after its current batch, and a failed import resumes with raiz.tasks.retry within 7 days.
Duplicates and history
When a duplicate contact is merged into another, the survivor absorbs it, and the duplicate’s id keeps working: raiz.contacts.get returns the survivor, raiz.conversations.list with that contactId lists the survivor’s conversations, and facts recorded with it go to the survivor.
expand=timeline on raiz.contacts.get adds what happened to the person, newest first: conversations, facts, changes and merges, including those of the duplicates it absorbed. Page through it with timelineCursor.
Contacts and conversations
Each conversation has a contactId, the person on the other side, and can have a subjectContactId, the person it’s about when that’s someone else. raiz.conversations.list with contactId finds both. To move a conversation to another contact, update its contactId with raiz.conversations.update; facts already recorded stay with the previous contact. A campaign recipient’s contact is the one it names, or else the contact that owns its number, created when there is none.
Contact profiles
A contact profile is a saved, named way to reach people, which other systems use by its id to start conversations. A voice profile calls with a voice agent, optionally with its own firstMessage and instructions. A whatsapp profile opens a conversation with an approved template, whose parameters can use variables such as {{patient_name}}, and its text agent answers the replies. Both can limit when contact starts with contactHours in a timeZone.
Manage profiles with raiz.profiles.create, raiz.profiles.get, raiz.profiles.list and raiz.profiles.update. A profile’s channelType never changes, and conversations started from one carry its profileId.
Do-not-contact rules
A suppression forbids contacting someone. Calls, WhatsApp messages and the web widget check these rules before contacting anyone, and an attempt they forbid fails with CONTACT_SUPPRESSED. raiz.suppressions.create adds a rule of one of four kinds, each with a reason that people will understand later:
kind |
Forbids |
|---|---|
phone |
One number, in phone, in E.164. |
phone_prefix |
Every number that starts with phonePrefix: a country code such as +57, or a range such as +57300. |
ip_network |
Web chat visitors from ipNetwork, an IPv4 or IPv6 address or CIDR network. |
country |
A country, in countryCode, matched against a phone number’s country or a web visitor’s location. |
The same rule twice answers 409 SUPPRESSION_EXISTS. raiz.suppressions.list lists the rules, filtered by kind, by q, or by phone for the rules that forbid that number.