---
title: "Contacts"
description: "The people Raíz talks to, with their phone numbers, emails and identity documents; imports and duplicates, contact profiles, and do-not-contact rules."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.getarbol.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Contacts

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](/en/developers/raiz/data/#definitions) 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`](/api/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:

```bash
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`](/api/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`](/api/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`](/api/raiz/contacts/count/) counts the same matches, up to 10,000.

## Import from a CSV

[`raiz.contacts.import`](/api/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).

```csv
given_name,family_name,whatsapp,email,preferred_site
Ana,Ejemplo,+12025550143,ana.ejemplo@example.com,north
Luis,Prueba,+12025550178,luis.prueba@example.com,south
```

The 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](/en/developers/raiz/data/#tasks) at once. [`raiz.tasks.get`](/api/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`](/api/raiz/tasks/cancel/) stops the import after its current batch, and a failed import resumes with [`raiz.tasks.retry`](/api/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`](/api/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`](/api/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](/en/developers/raiz/campaigns/#whatsapp-templates), 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`](/api/raiz/profiles/create/), [`raiz.profiles.get`](/api/raiz/profiles/get/), [`raiz.profiles.list`](/api/raiz/profiles/list/) and [`raiz.profiles.update`](/api/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`](/api/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`](/api/raiz/suppressions/list/) lists the rules, filtered by `kind`, by `q`, or by `phone` for the rules that forbid that number.

Source: https://docs.getarbol.com/en/developers/raiz/contacts/index.mdx
