Contatos
Referência das rotas de contatos: a lista de todas as pessoas que já falaram com a central, com busca e sincronização incremental, a consulta de um contato com todos os atendimentos dele e a troca do nome que a equipe vê. Com o critério de agrupamento por telefone (com e sem o nono dígito), exemplos em curl, PHP e Python e os erros de cada rota.
Visão geral
No ChatISP não existe um cadastro de contatos separado para manter: o contato é o conjunto de todas as conversas de um mesmo telefone, em qualquer número de WhatsApp da empresa. É a mesma visão da tela Contatos do painel (todos os contatos que já passaram pela central, com o histórico de atendimentos). Um contato aparece assim que a primeira conversa com aquele telefone é criada, seja porque o cliente escreveu, seja por um envio ou abertura pela API.
| Rota | Método | Permissão da chave | Resumo |
|---|---|---|---|
contatos/listar | GET ou POST | Contatos | Lista paginada, com busca por nome ou telefone e filtro de atividade recente. |
contatos/consultar | GET ou POST | Contatos | Um contato, com o cliente identificado, os totais e as últimas 50 conversas. |
contatos/renomear | POST | Contatos | Salva o nome do contato, em todas as conversas dele e nas próximas. |
Casos típicos: alimentar o CRM com todos os números que já procuraram o provedor, mostrar no ERP quantos atendimentos o assinante teve, corrigir no ChatISP o nome do cliente a partir do cadastro oficial.
A permissão Contatos é própria: uma chave só com Consultar conversas não lista contatos (403 permissao), e vice-versa. A chave enxerga os contatos da casa inteira; a restrição de histórico dos perfis de atendente não se aplica à API.
Um contato por telefone
O telefone é a identidade do contato. Dois detalhes importam para quem integra:
- Com e sem o nono dígito são o mesmo contato. O WhatsApp de alguns celulares brasileiros ainda chega com 8 dígitos depois do DDD. O ChatISP junta
5544999990000e554499990000num contato só, tanto na lista quanto na consulta e na troca de nome. Você pode buscar por qualquer uma das duas formas. - Todas as conexões juntas. As conversas do mesmo telefone em números de WhatsApp diferentes da empresa (por exemplo, o número do suporte e o do comercial) formam um contato só.
O campo telefone devolvido é o da conversa mais recente do contato, na forma em que o WhatsApp o entregou, só dígitos e com o código do país. Ao gravar no seu sistema, normalize os dois formatos brasileiros para a mesma chave, como o ChatISP faz, para não duplicar o cliente.
| Telefones nas conversas | Contatos na API |
|---|---|
5544999990000 e 554499990000 | Um só (celular brasileiro, com e sem o nono dígito). |
5544999990000 no suporte e no comercial | Um só (mesmo telefone, conexões diferentes). |
351912345678 (Portugal) | Números de outros países não são agrupados: cada forma é um contato. |
Objeto contato
{
"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" }
}| Campo | Tipo | Descrição |
|---|---|---|
telefone | string | Telefone da conversa mais recente do contato, só dígitos, com o código do país. |
nome | string | Nome como aparece no atendimento: o salvo pela equipe (ou por contatos/renomear) ou, sem ele, o nome do perfil do WhatsApp. Vazio quando desconhecido. |
atendimentos | inteiro | Total de conversas (protocolos) do contato, abertas e encerradas, incluindo os avisos enviados. |
abertos | inteiro | Quantas dessas conversas estão abertas agora. Só em contatos/listar. |
primeiro_contato_em, ultimo_contato_em | data e hora | Abertura da primeira conversa e última atividade (última mensagem ou, sem mensagens, a abertura da conversa mais recente). Só em contatos/listar. |
cliente | objeto ou null | O cliente identificado no ERP ou num sistema externo integrado, como aparece no cartão do cliente durante o atendimento: nome (o do cadastro) e origem (o nome do ERP ou do sistema externo que o identificou). null quando nenhuma conversa do contato chegou a identificar um cliente. |
tickets | inteiro | Total de ordens de serviço registrados para esse telefone, em qualquer etapa (inclusive os concluídos). Só em contatos/consultar. |
nome e cliente.nome são coisas diferentes: o primeiro é como a equipe chama a pessoa no WhatsApp (pode ser um apelido, ou o nome de quem usa o telefone); o segundo é o titular do cadastro no ERP. Veja Ficha do cliente.
Listar contatos
/api_publica.php?r=contatos/listar
Lista paginada dos contatos, do que teve atividade mais recente para o mais antigo.
Permissão: Contatos.
| Parâmetro | Tipo | Descrição |
|---|---|---|
busca | string | Até 120 caracteres. Procura em qualquer parte do nome do contato (o das conversas e o salvo pela equipe) e, quando o texto tem dígitos, em qualquer parte do telefone. Maria, souza e 99999 funcionam. |
atualizados_desde | data | Só contatos com atividade (conversa aberta ou mensagem trocada) a partir desse momento. Aceita ISO 8601, AAAA-MM-DD ou AAAA-MM-DD HH:MM:SS; sem fuso, vale o fuso do servidor. |
pagina, limite | inteiro | Paginação: pagina a partir de 1; limite de 1 a 100, padrão 50. |
Sincronizar com o CRM
- Na primeira carga, percorra todas as páginas sem filtro.
- Nas seguintes, use
atualizados_desdecom o horário da rodada anterior menos uma pequena folga, e grave cada contato pela chave do telefone (normalizado com e sem o nono dígito, como em Um contato por telefone). - Para o detalhe de um contato alterado (cliente identificado, ordens de serviço, conversas), chame
contatos/consultar.
A ordem da lista é pela última atividade. Se chegarem mensagens enquanto você percorre as páginas, um contato pode subir para a página 1 e você pular outro. Na sincronização, a folga no atualizados_desde resolve isso na rodada seguinte.
# busca por nome curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/listar&busca=souza" \ -H "Authorization: Bearer $CHATISP_CHAVE" # quem teve atividade desde ontem, 100 por página 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 // carga completa dos contatos para o 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']); // seu código } $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 "(sem cadastro)" 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 }
}Erros específicos: parametro_invalido (limite ou pagina fora do aceito), data_invalida.
Consultar contato
/api_publica.php?r=contatos/consultar
Devolve um contato com o cliente identificado, o total de atendimentos e de ordens de serviço e as 50 conversas mais recentes, da mais nova para a mais antiga. Serve para montar a ficha do assinante no ERP ou no CRM: "este cliente já falou 7 vezes com a central; a última foi hoje, sobre lentidão".
Permissão: Contatos.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
telefone | string | Sim | Telefone do contato, em qualquer das formas (com ou sem 55, com ou sem o nono dígito). Mesma regra das outras rotas. |
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 'Este telefone nunca falou com a central.', PHP_EOL; } else { echo $r['contato']['atendimentos'], ' atendimentos, ', $r['contato']['tickets'], ' tickets', PHP_EOL; foreach ($r['conversas'] as $c) { echo $c['protocolo'], ' ', $c['resumo']['titulo'] ?? '(sem resumo)', 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("Sem histórico") 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": "Suporte técnico",
"atendente_id": 7,
"atendente": "Carlos Lima",
"nao_lidas": 0,
"motivo_encerramento": "Resolvido",
"resumo": {
"titulo": "Sem internet após queda de energia",
"texto": "A cliente relatou falta de internet desde a manhã. O atendente identificou a ONU sem sinal e agendou visita técnica para a tarde."
},
"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.atendimentosconta todas as conversas do telefone; a listaconversastraz no máximo as 50 mais recentes. Para as anteriores, useconversas/listarcomtelefonee paginação.- As conversas vêm no formato de conversa, sem o campo
etiquetas(consulte uma conversa para tê-las). contato.telefoneecontato.nomevêm da conversa mais recente.
Erros específicos: parametro_obrigatorio (faltou telefone), telefone_invalido, nao_encontrado (404: o telefone nunca teve conversa).
Renomear contato
/api_publica.php?r=contatos/renomear
Salva o nome do contato, como a edição de nome que o atendente faz no painel. Use para trocar o apelido do WhatsApp ("Mãe", "Zé do Sítio") pelo nome do cadastro do ERP, para a equipe saber com quem está falando.
Permissão: Contatos.
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
telefone | string | Sim | Telefone do contato. Precisa ter pelo menos uma conversa. |
nome | string | Sim | De 1 a 120 caracteres; espaços nas pontas são removidos. |
Efeito
- No painel, na hora: o nome passa a valer em todas as conversas do telefone, abertas e encerradas, em todos os números de WhatsApp da empresa e nas duas formas do número (com e sem o nono dígito). Lista de conversas, kanban, histórico e tela Contatos mostram o nome novo.
- Nas próximas conversas: toda conversa nova desse telefone já nasce com o nome salvo. Ele vale mais que o nome do perfil do WhatsApp e mais que o parâmetro
nomedas rotas de envio e deatendimentos/abrir. - O nome entra também onde as mensagens automáticas usam
{nome}(por exemplo, a mensagem de chamado encerrado). - O cadastro do cliente no ERP não é alterado: ele continua aparecendo em
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"}O telefone devolvido é o da conversa mais recente do contato. Erros específicos: parametro_obrigatorio (faltou telefone), telefone_invalido, nome_invalido (vazio ou acima de 120 caracteres), nao_encontrado (404: o telefone nunca teve conversa; para registrar um número novo, abra um atendimento ou envie uma mensagem antes).
Erros por rota
Além dos erros gerais (rota, autenticacao, chave_invalida, plano, cota_excedida, permissao, json_invalido), descritos em Códigos de erro:
error | HTTP | listar | consultar | renomear |
|---|---|---|---|---|
metodo | 405 | sim | ||
parametro_invalido | 400 | sim | sim | sim |
data_invalida | 400 | sim | ||
parametro_obrigatorio | 400 | sim | sim | |
telefone_invalido | 400 | sim | sim | |
nome_invalido | 400 | sim | ||
nao_encontrado | 404 | sim | sim |