Contacts
Reference for the contact routes: the list of everyone who has ever talked to your service desk, with search and incremental sync, the lookup of a contact with all of their conversations, and changing the name the team sees. Includes the phone grouping rule (with and without the ninth digit), examples in curl, PHP and Python, and the errors for each route.
Overview
ChatISP has no separate contact registry to maintain: a contact is the set of all conversations with the same phone number, on any of the company's WhatsApp numbers. It is the same view as the panel's Contacts screen (every contact that has ever reached the service desk, with their conversation history). A contact appears as soon as the first conversation with that phone number is created, whether because the customer wrote in or because of a send or open through the API.
| Route | Method | Key permission | Summary |
|---|---|---|---|
contatos/listar | GET or POST | Contacts | Paginated list, with search by name or phone and a recent activity filter. |
contatos/consultar | GET or POST | Contacts | One contact, with the identified customer, the totals and the latest 50 conversations. |
contatos/renomear | POST | Contacts | Saves the contact's name, in all of their conversations and in future ones. |
Typical cases: feeding the CRM with every number that has ever contacted the internet service provider (ISP), showing in the ERP how many conversations the subscriber has had, correcting the customer's name in ChatISP from the official record.
The Contacts permission stands on its own: a key with only Look up conversations cannot list contacts (403 permissao), and vice versa. The key sees the contacts of the whole company; the history restriction of agent profiles does not apply to the API.
One contact per phone number
The phone number is the contact's identity. Two details matter for integrators:
- With and without the ninth digit are the same contact. The WhatsApp of some Brazilian mobile phones still arrives with 8 digits after the area code. ChatISP merges
5544999990000and554499990000into a single contact, in the list, in the lookup and in the rename. You can search by either form. - All connections together. Conversations with the same phone number on different company WhatsApp numbers (for example, the support number and the sales number) form a single contact.
The returned telefone field is the one from the contact's most recent conversation, in the form WhatsApp delivered it, digits only and with the country code. When storing it in your system, normalize both Brazilian formats to the same key, as ChatISP does, so you do not duplicate the customer.
| Phone numbers in conversations | Contacts in the API |
|---|---|
5544999990000 and 554499990000 | Just one (Brazilian mobile, with and without the ninth digit). |
5544999990000 on support and on sales | Just one (same phone, different connections). |
351912345678 (Portugal) | Numbers from other countries are not grouped: each form is a separate contact. |
Contact object
{
"telefone": "5544999990000",
"nome": "Maria Souza",
"atendimentos": 7,
"abertos": 1,
"primeiro_contato_em": "2025-03-14T16:20:05-03:00",
"ultimo_contato_em": "2026-10-01T09:41:03-03:00",
"cliente": { "nome": "MARIA APARECIDA SOUZA", "origem": "IXC" }
}| Field | Type | Description |
|---|---|---|
telefone | string | Phone number of the contact's most recent conversation, digits only, with the country code. |
nome | string | Name as shown in the conversation: the one saved by the team (or by contatos/renomear) or, without it, the WhatsApp profile name. Empty when unknown. |
atendimentos | integer | Total conversations (protocols) of the contact, open and closed, including notices sent. |
abertos | integer | How many of these conversations are open right now. Only in contatos/listar. |
primeiro_contato_em, ultimo_contato_em | date and time | Opening of the first conversation and latest activity (last message or, with no messages, the opening of the most recent conversation). Only in contatos/listar. |
cliente | object or null | The customer identified in the ERP or in an integrated external system, as shown on the customer card during the conversation: nome (the one in the record) and origem (the name of the ERP or external system that identified them). null when no conversation with the contact ever identified a customer. |
tickets | integer | Total service orders recorded for this phone number, in any stage (including completed ones). Only in contatos/consultar. |
nome and cliente.nome are different things: the first is what the team calls the person on WhatsApp (it may be a nickname, or the name of whoever uses the phone); the second is the account holder in the ERP. See Customer record.
List contacts
/api_publica.php?r=contatos/listar
Paginated list of contacts, from the one with the most recent activity to the oldest.
Permission: Contacts.
| Parameter | Type | Description |
|---|---|---|
busca | string | Up to 120 characters. Searches anywhere in the contact name (the one in the conversations and the one saved by the team) and, when the text has digits, anywhere in the phone number. Maria, souza and 99999 all work. |
atualizados_desde | date | Only contacts with activity (conversation opened or message exchanged) from that moment on. Accepts ISO 8601, YYYY-MM-DD or YYYY-MM-DD HH:MM:SS; without a time zone, the server's time zone applies. |
pagina, limite | integer | Pagination: pagina starting at 1; limite from 1 to 100, default 50. |
Syncing with the CRM
- On the first load, go through all pages without filters.
- On the following ones, use
atualizados_desdewith the time of the previous run minus a small margin, and store each contact by the phone key (normalized with and without the ninth digit, as in One contact per phone number). - For the details of a changed contact (identified customer, service orders, conversations), call
contatos/consultar.
The list is ordered by latest activity. If messages arrive while you are going through the pages, a contact may move up to page 1 and you may skip another one. When syncing, the margin in atualizados_desde fixes this on the next run.
# search by name curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/listar&busca=souza" \ -H "Authorization: Bearer $CHATISP_CHAVE" # contacts with activity since yesterday, 100 per page curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/listar&atualizados_desde=2026-09-30&limite=100" \ -H "Authorization: Bearer $CHATISP_CHAVE"
<?php // full load of contacts into the CRM $pagina = 1; do { $ch = curl_init('https://atendimento.seuprovedor.com.br/api_publica.php?' . http_build_query([ 'r' => 'contatos/listar', 'limite' => 100, 'pagina' => $pagina, ])); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CHATISP_CHAVE')], ]); $r = json_decode(curl_exec($ch), true); curl_close($ch); foreach ($r['contatos'] as $c) { gravarNoCrm($c['telefone'], $c['nome'], $c['atendimentos']); // your code } $pagina++; } while ($pagina <= $r['paginacao']['paginas']);
import os, requests r = requests.get("https://atendimento.seuprovedor.com.br/api_publica.php", params={ "r": "contatos/listar", "busca": "99999", "limite": 20, }, headers={"Authorization": "Bearer " + os.environ["CHATISP_CHAVE"]}, timeout=30) for c in r.json()["contatos"]: titular = c["cliente"]["nome"] if c["cliente"] else "(no record)" print(c["telefone"], c["nome"], titular, c["abertos"])
{
"ok": true,
"contatos": [
{
"telefone": "5544999990000",
"nome": "Maria Souza",
"atendimentos": 7,
"abertos": 1,
"primeiro_contato_em": "2025-03-14T16:20:05-03:00",
"ultimo_contato_em": "2026-10-01T09:41:03-03:00",
"cliente": { "nome": "MARIA APARECIDA SOUZA", "origem": "IXC" }
},
{
"telefone": "554498887777",
"nome": "Paulo",
"atendimentos": 1,
"abertos": 0,
"primeiro_contato_em": "2026-09-30T18:02:44-03:00",
"ultimo_contato_em": "2026-09-30T18:10:12-03:00",
"cliente": null
}
],
"paginacao": { "pagina": 1, "limite": 50, "total": 2, "paginas": 1 }
}Route-specific errors: parametro_invalido (limite or pagina outside the accepted range), data_invalida.
Look up a contact
/api_publica.php?r=contatos/consultar
Returns a contact with the identified customer, the total conversations and service orders, and the 50 most recent conversations, newest first. Use it to build the subscriber profile in the ERP or CRM: "this customer has contacted the service desk 7 times; the last one was today, about slow speeds".
Permission: Contacts.
| Parameter | Type | Required | Description |
|---|---|---|---|
telefone | string | Yes | Contact's phone number, in any form (with or without 55, with or without the ninth digit). Same rule as the other routes. |
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/consultar&telefone=44999990000" \ -H "Authorization: Bearer $CHATISP_CHAVE"
<?php $ch = curl_init('https://atendimento.seuprovedor.com.br/api_publica.php?' . http_build_query([ 'r' => 'contatos/consultar', 'telefone' => '44999990000', ])); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CHATISP_CHAVE')], ]); $r = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status === 404) { echo 'This phone number has never contacted the service desk.', PHP_EOL; } else { echo $r['contato']['atendimentos'], ' conversations, ', $r['contato']['tickets'], ' tickets', PHP_EOL; foreach ($r['conversas'] as $c) { echo $c['protocolo'], ' ', $c['resumo']['titulo'] ?? '(no summary)', PHP_EOL; } }
import os, requests r = requests.get("https://atendimento.seuprovedor.com.br/api_publica.php", params={"r": "contatos/consultar", "telefone": "44999990000"}, headers={"Authorization": "Bearer " + os.environ["CHATISP_CHAVE"]}, timeout=30) if r.status_code == 404: print("No history") else: dados = r.json() for c in dados["conversas"]: print(c["protocolo"], c["estado"], c["motivo_encerramento"])
{
"ok": true,
"contato": {
"telefone": "5544999990000",
"nome": "Maria Souza",
"cliente": { "nome": "MARIA APARECIDA SOUZA", "origem": "IXC" },
"atendimentos": 7,
"tickets": 2
},
"conversas": [
{
"id": 18342,
"protocolo": "20261001-0042",
"conexao_id": 1,
"telefone": "5544999990000",
"nome": "Maria Souza",
"estado": "encerrada",
"aguardando_cliente": false,
"departamento_id": 1,
"departamento": "Technical support",
"atendente_id": 7,
"atendente": "Carlos Lima",
"nao_lidas": 0,
"motivo_encerramento": "Resolved",
"resumo": {
"titulo": "No internet after power outage",
"texto": "The customer reported no internet since the morning. The agent found the ONU without signal and scheduled a technical visit for the afternoon."
},
"criada_em": "2026-10-01T09:12:40-03:00",
"ultima_mensagem_em": "2026-10-01T09:41:03-03:00",
"encerrada_em": "2026-10-01T09:42:15-03:00"
}
]
}contato.atendimentoscounts all conversations with the phone number; theconversaslist brings at most the 50 most recent. For older ones, useconversas/listarwithtelefoneand pagination.- Conversations come in the conversation format, without the
etiquetasfield (look up a single conversation to get them). contato.telefoneandcontato.nomecome from the most recent conversation.
Route-specific errors: parametro_obrigatorio (telefone missing), telefone_invalido, nao_encontrado (404: the phone number has never had a conversation).
Rename a contact
/api_publica.php?r=contatos/renomear
Saves the contact's name, like the name edit an agent makes in the panel. Use it to replace the WhatsApp nickname ("Mom", "Joe from the farm") with the name from the ERP record, so the team knows who they are talking to.
Permission: Contacts.
| Parameter | Type | Required | Description |
|---|---|---|---|
telefone | string | Yes | Contact's phone number. It must have at least one conversation. |
nome | string | Yes | From 1 to 120 characters; leading and trailing spaces are removed. |
Effect
- In the panel, immediately: the name applies to all conversations with the phone number, open and closed, on all of the company's WhatsApp numbers and in both forms of the number (with and without the ninth digit). The conversation list, kanban, history and Contacts screen show the new name.
- In future conversations: every new conversation with this phone number starts with the saved name. It takes precedence over the WhatsApp profile name and over the
nomeparameter of the send routes and ofatendimentos/abrir. - The name is also used wherever automatic messages use
{nome}(for example, the closed service request message). - The customer's record in the ERP is not changed: it still appears in
cliente.nome.
curl -s -X POST "https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/renomear" \
-H "Authorization: Bearer $CHATISP_CHAVE" \
-H "Content-Type: application/json" \
-d '{"telefone": "44999990000", "nome": "Maria Aparecida Souza"}'<?php $ch = curl_init('https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/renomear'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('CHATISP_CHAVE'), 'Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode(['telefone' => '44999990000', 'nome' => 'Maria Aparecida Souza']), ]); $r = json_decode(curl_exec($ch), true); curl_close($ch);
import os, requests r = requests.post("https://atendimento.seuprovedor.com.br/api_publica.php", params={"r": "contatos/renomear"}, headers={"Authorization": "Bearer " + os.environ["CHATISP_CHAVE"]}, json={"telefone": "44999990000", "nome": "Maria Aparecida Souza"}, timeout=30) print(r.status_code, r.json())
{"ok": true, "telefone": "5544999990000", "nome": "Maria Aparecida Souza"}The returned telefone is the one from the contact's most recent conversation. Route-specific errors: parametro_obrigatorio (telefone missing), telefone_invalido, nome_invalido (empty or over 120 characters), nao_encontrado (404: the phone number has never had a conversation; to register a new number, open a service request or send a message first).
Errors by route
In addition to the general errors (rota, autenticacao, chave_invalida, plano, cota_excedida, permissao, json_invalido), described in Error codes:
error | HTTP | listar | consultar | renomear |
|---|---|---|---|---|
metodo | 405 | yes | ||
parametro_invalido | 400 | yes | yes | yes |
data_invalida | 400 | yes | ||
parametro_obrigatorio | 400 | yes | yes | |
telefone_invalido | 400 | yes | yes | |
nome_invalido | 400 | yes | ||
nao_encontrado | 404 | yes | yes |