Contactos

Referencia de las rutas de contactos: la lista de todas las personas que ya hablaron con la central, con búsqueda y sincronización incremental, la consulta de un contacto con todas sus atenciones y el cambio del nombre que ve el equipo. Con el criterio de agrupación por teléfono (con y sin el noveno dígito), ejemplos en curl, PHP y Python y los errores de cada ruta.

Visión general

En ChatISP no existe un registro de contactos separado que mantener: el contacto es el conjunto de todas las conversaciones de un mismo teléfono, en cualquier número de WhatsApp de la empresa. Es la misma vista de la pantalla Contactos del panel (todos los contactos que ya pasaron por la central, con el historial de atenciones). Un contacto aparece en cuanto se crea la primera conversación con ese teléfono, ya sea porque el cliente escribió o por un envío o una apertura por la API.

RutaMétodoPermiso de la claveResumen
contatos/listarGET o POSTContactosLista paginada, con búsqueda por nombre o teléfono y filtro de actividad reciente.
contatos/consultarGET o POSTContactosUn contacto, con el cliente identificado, los totales y las últimas 50 conversaciones.
contatos/renomearPOSTContactosGuarda el nombre del contacto, en todas sus conversaciones y en las próximas.

Casos típicos: alimentar el CRM con todos los números que ya buscaron al proveedor de internet, mostrar en el ERP cuántas atenciones tuvo el suscriptor, corregir en ChatISP el nombre del cliente a partir del registro oficial.

El permiso Contactos es independiente: una clave solo con Consultar conversaciones no lista contactos (403 permissao), y viceversa. La clave ve los contactos de toda la empresa; la restricción de historial de los perfiles de agente no se aplica a la API.


Un contacto por teléfono

El teléfono es la identidad del contacto. Dos detalles importan para quien integra:

  • Con y sin el noveno dígito son el mismo contacto. El WhatsApp de algunos celulares brasileños todavía llega con 8 dígitos después del código de área. ChatISP une 5544999990000 y 554499990000 en un solo contacto, tanto en la lista como en la consulta y en el cambio de nombre. Puede buscar por cualquiera de las dos formas.
  • Todas las conexiones juntas. Las conversaciones del mismo teléfono en números de WhatsApp diferentes de la empresa (por ejemplo, el número de soporte y el comercial) forman un solo contacto.

El campo telefone devuelto es el de la conversación más reciente del contacto, en la forma en que WhatsApp lo entregó, solo dígitos y con el código de país. Al guardarlo en su sistema, normalice los dos formatos brasileños a la misma clave, como hace ChatISP, para no duplicar al cliente.

Teléfonos en las conversacionesContactos en la API
5544999990000 y 554499990000Uno solo (celular brasileño, con y sin el noveno dígito).
5544999990000 en soporte y en comercialUno solo (mismo teléfono, conexiones diferentes).
351912345678 (Portugal)Los números de otros países no se agrupan: cada forma es un contacto.

Objeto contacto

contacto (como viene en 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" }
}
CampoTipoDescripción
telefonestringTeléfono de la conversación más reciente del contacto, solo dígitos, con el código de país.
nomestringNombre como aparece en la atención: el guardado por el equipo (o por contatos/renomear) o, sin él, el nombre del perfil de WhatsApp. Vacío cuando es desconocido.
atendimentosenteroTotal de conversaciones (protocolos) del contacto, abiertas y cerradas, incluidos los avisos enviados.
abertosenteroCuántas de esas conversaciones están abiertas ahora. Solo en contatos/listar.
primeiro_contato_em, ultimo_contato_emfecha y horaApertura de la primera conversación y última actividad (último mensaje o, sin mensajes, la apertura de la conversación más reciente). Solo en contatos/listar.
clienteobjeto o nullEl cliente identificado en el ERP o en un sistema externo integrado, como aparece en la tarjeta del cliente durante la atención: nome (el del registro) y origem (el nombre del ERP o del sistema externo que lo identificó). null cuando ninguna conversación del contacto llegó a identificar un cliente.
ticketsenteroTotal de órdenes de servicio registradas para ese teléfono, en cualquier etapa (incluidos los concluidos). Solo en contatos/consultar.

nome y cliente.nome son cosas diferentes: el primero es cómo el equipo llama a la persona en WhatsApp (puede ser un apodo, o el nombre de quien usa el teléfono); el segundo es el titular del registro en el ERP. Vea Ficha del cliente.


Listar contactos

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

Lista paginada de los contactos, del que tuvo actividad más reciente al más antiguo.

Permiso: Contactos.

ParámetroTipoDescripción
buscastringHasta 120 caracteres. Busca en cualquier parte del nombre del contacto (el de las conversaciones y el guardado por el equipo) y, cuando el texto tiene dígitos, en cualquier parte del teléfono. Maria, souza y 99999 funcionan.
atualizados_desdefechaSolo contactos con actividad (conversación abierta o mensaje intercambiado) a partir de ese momento. Acepta ISO 8601, AAAA-MM-DD o AAAA-MM-DD HH:MM:SS; sin zona horaria, vale la zona horaria del servidor.
pagina, limiteenteroPaginación: pagina a partir de 1; limite de 1 a 100, predeterminado 50.

Sincronizar con el CRM

  1. En la primera carga, recorra todas las páginas sin filtro.
  2. En las siguientes, use atualizados_desde con la hora de la ronda anterior menos un pequeño margen, y guarde cada contacto por la clave del teléfono (normalizado con y sin el noveno dígito, como en Un contacto por teléfono).
  3. Para el detalle de un contacto modificado (cliente identificado, órdenes de servicio, conversaciones), llame a contatos/consultar.

El orden de la lista es por la última actividad. Si llegan mensajes mientras recorre las páginas, un contacto puede subir a la página 1 y usted saltarse otro. En la sincronización, el margen en atualizados_desde lo resuelve en la ronda siguiente.

terminal
# búsqueda por nombre
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=contatos/listar&busca=souza" \
  -H "Authorization: Bearer $CHATISP_CHAVE"

# quienes tuvieron actividad desde ayer, 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 de los contactos para el 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']); // su 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 "(sin registro)"
    print(c["telefone"], c["nome"], titular, c["abertos"])
respuesta 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 }
}

Errores específicos: parametro_invalido (limite o pagina fuera de lo aceptado), data_invalida.


Consultar contacto

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

Devuelve un contacto con el cliente identificado, el total de atenciones y de órdenes de servicio y las 50 conversaciones más recientes, de la más nueva a la más antigua. Sirve para armar la ficha del suscriptor en el ERP o en el CRM: "este cliente ya habló 7 veces con la central; la última fue hoy, por lentitud".

Permiso: Contactos.

ParámetroTipoObligatorioDescripción
telefonestringSíTeléfono del contacto, en cualquiera de las formas (con o sin 55, con o sin el noveno dígito). Misma regla de las otras rutas.
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 teléfono nunca habló con la central.', PHP_EOL;
} else {
    echo $r['contato']['atendimentos'], ' atenciones, ', $r['contato']['tickets'], ' OS', PHP_EOL;
    foreach ($r['conversas'] as $c) {
        echo $c['protocolo'], ' ', $c['resumo']['titulo'] ?? '(sin resumen)', 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("Sin historial")
else:
    dados = r.json()
    for c in dados["conversas"]:
        print(c["protocolo"], c["estado"], c["motivo_encerramento"])
respuesta 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": "Soporte técnico",
      "atendente_id": 7,
      "atendente": "Carlos Lima",
      "nao_lidas": 0,
      "motivo_encerramento": "Resuelto",
      "resumo": {
        "titulo": "Sin internet después de un corte de energía",
        "texto": "La clienta informó falta de internet desde la mañana. El agente identificó la ONU sin señal y programó una visita técnica para la 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 cuenta todas las conversaciones del teléfono; la lista conversas trae como máximo las 50 más recientes. Para las anteriores, use conversas/listar con telefone y paginación.
  • Las conversaciones vienen en el formato de conversación, sin el campo etiquetas (consulte una conversación para obtenerlas).
  • contato.telefone y contato.nome vienen de la conversación más reciente.

Errores específicos: parametro_obrigatorio (falta telefone), telefone_invalido, nao_encontrado (404: el teléfono nunca tuvo conversación).


Renombrar contacto

POST /api_publica.php?r=contatos/renomear

Guarda el nombre del contacto, como la edición de nombre que el agente hace en el panel. Úselo para cambiar el apodo de WhatsApp ("Mamá", "Pepe del campo") por el nombre del registro del ERP, para que el equipo sepa con quién está hablando.

Permiso: Contactos.

ParámetroTipoObligatorioDescripción
telefonestringSíTeléfono del contacto. Debe tener al menos una conversación.
nomestringSíDe 1 a 120 caracteres; los espacios en los extremos se eliminan.

Efecto

  • En el panel, en el momento: el nombre pasa a valer en todas las conversaciones del teléfono, abiertas y cerradas, en todos los números de WhatsApp de la empresa y en las dos formas del número (con y sin el noveno dígito). La lista de conversaciones, el kanban, el historial y la pantalla Contactos muestran el nombre nuevo.
  • En las próximas conversaciones: toda conversación nueva de ese teléfono ya nace con el nombre guardado. Este prevalece sobre el nombre del perfil de WhatsApp y sobre el parámetro nome de las rutas de envío y de atendimentos/abrir.
  • El nombre se usa también donde los mensajes automáticos usan {nome} (por ejemplo, el mensaje de atención cerrada).
  • El registro del cliente en el ERP no se modifica: sigue apareciendo en 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())
respuesta JSON (HTTP 200)
{"ok": true, "telefone": "5544999990000", "nome": "Maria Aparecida Souza"}

El telefone devuelto es el de la conversación más reciente del contacto. Errores específicos: parametro_obrigatorio (falta telefone), telefone_invalido, nome_invalido (vacío o de más de 120 caracteres), nao_encontrado (404: el teléfono nunca tuvo conversación; para registrar un número nuevo, abra una atención o envíe un mensaje antes).


Errores por ruta

Además de los errores generales (rota, autenticacao, chave_invalida, plano, cota_excedida, permissao, json_invalido), descritos en Códigos de error:

errorHTTPlistarconsultarrenomear
metodo405sí
parametro_invalido400sísísí
data_invalida400sí
parametro_obrigatorio400sísí
telefone_invalido400sísí
nome_invalido400sí
nao_encontrado404sísí