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.

RotaMétodoPermissão da chaveResumo
contatos/listarGET ou POSTContatosLista paginada, com busca por nome ou telefone e filtro de atividade recente.
contatos/consultarGET ou POSTContatosUm contato, com o cliente identificado, os totais e as últimas 50 conversas.
contatos/renomearPOSTContatosSalva 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 5544999990000 e 554499990000 num 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 conversasContatos na API
5544999990000 e 554499990000Um só (celular brasileiro, com e sem o nono dígito).
5544999990000 no suporte e no comercialUm 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

contato (como vem em contatos/listar)
{
  "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" }
}
CampoTipoDescrição
telefonestringTelefone da conversa mais recente do contato, só dígitos, com o código do país.
nomestringNome como aparece no atendimento: o salvo pela equipe (ou por contatos/renomear) ou, sem ele, o nome do perfil do WhatsApp. Vazio quando desconhecido.
atendimentosinteiroTotal de conversas (protocolos) do contato, abertas e encerradas, incluindo os avisos enviados.
abertosinteiroQuantas dessas conversas estão abertas agora. Só em contatos/listar.
primeiro_contato_em, ultimo_contato_emdata e horaAbertura da primeira conversa e última atividade (última mensagem ou, sem mensagens, a abertura da conversa mais recente). Só em contatos/listar.
clienteobjeto ou nullO 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.
ticketsinteiroTotal 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

GET POST /api_publica.php?r=contatos/listar

Lista paginada dos contatos, do que teve atividade mais recente para o mais antigo.

Permissão: Contatos.

ParâmetroTipoDescrição
buscastringAté 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_desdedataSó 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, limiteinteiroPaginação: pagina a partir de 1; limite de 1 a 100, padrão 50.

Sincronizar com o CRM

  1. Na primeira carga, percorra todas as páginas sem filtro.
  2. Nas seguintes, use atualizados_desde com 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).
  3. 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.

terminal
# 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
<?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']);
Python
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"])
resposta JSON (HTTP 200)
{
  "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

GET POST /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âmetroTipoObrigatórioDescrição
telefonestringSimTelefone do contato, em qualquer das formas (com ou sem 55, com ou sem o nono dígito). Mesma regra das outras rotas.
terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/consultar&telefone=44999990000" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
<?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;
    }
}
Python
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"])
resposta JSON (HTTP 200)
{
  "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.atendimentos conta todas as conversas do telefone; a lista conversas traz no máximo as 50 mais recentes. Para as anteriores, use conversas/listar com telefone e paginação.
  • As conversas vêm no formato de conversa, sem o campo etiquetas (consulte uma conversa para tê-las).
  • contato.telefone e contato.nome vêm da conversa mais recente.

Erros específicos: parametro_obrigatorio (faltou telefone), telefone_invalido, nao_encontrado (404: o telefone nunca teve conversa).


Renomear contato

POST /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âmetroTipoObrigatórioDescrição
telefonestringSimTelefone do contato. Precisa ter pelo menos uma conversa.
nomestringSimDe 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 nome das rotas de envio e de atendimentos/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.
terminal
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
<?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);
Python
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())
resposta JSON (HTTP 200)
{"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:

errorHTTPlistarconsultarrenomear
metodo405sim
parametro_invalido400simsimsim
data_invalida400sim
parametro_obrigatorio400simsim
telefone_invalido400simsim
nome_invalido400sim
nao_encontrado404simsim