Reference data

The catalog routes return the identifiers that the rest of the API asks for: connections (WhatsApp numbers), departments, agents, official API message templates, labels and closing reasons, plus information about the key itself. They are all read-only and available to any active key.

Overview

Before sending a message from a specific number, transferring to a department, assigning an agent or closing with a reason, the integration needs the ids of those records. The seven routes on this page exist for that:

RouteReturnsUsed in
chave/infoThe key itself and the serverConnection test, scope check and time zone
conexoes/listarWhatsApp numbersconexao_id in sends, service request opening, filters and templates
departamentos/listarDepartmentsdepartamento_id in transfers, service request opening, service orders, schedule, filters and metrics
atendentes/listarAgentsatendente_id in transfers and opening; responsavel_id in service orders and schedule
modelos/listarOfficial API templatesmodelo_id in mensagens/enviar_modelo
etiquetas/listarConversation labelsconversas/etiquetas and the etiqueta_id filter
motivos/listarClosing reasonsmotivo_id in conversas/encerrar
  • Scope: none. Any active key can call them, even with every permission turned off.
  • Method: GET (POST is also accepted).
  • Pagination: none. Each route returns the whole list, which at an internet service provider (ISP) usually holds from a few to a few dozen items.
  • Counting: catalog calls count toward the key's usage and per-minute limit, like any other call.

The PHP and Python examples on this page use the chatisp() function shown in Address and authentication: it builds the URL, sends the key in the header and returns the decoded response.


Key information

GET /api_publica.php?r=chave/info

Shows the key that made the call (name, scopes and per-minute limit) and server data (API version, time zone, clock and plan). It is the best route to test the address and the key, and for the integration to check at startup that it has the scopes it needs.

Parameters: none.

terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=chave/info" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
$info = chatisp('chave/info');
$faltando = array_diff(['mensagens_enviar', 'tickets_gerir'], $info['chave']['escopos']);
if ($faltando) {
    die('Turn on in the key: ' . implode(', ', $faltando) . PHP_EOL);
}
Python
info = chatisp("chave/info")
faltando = {"mensagens_enviar", "tickets_gerir"} - set(info["chave"]["escopos"])
if faltando:
    raise SystemExit(f"Turn on in the key: {', '.join(faltando)}")
response (HTTP 200)
{
  "ok": true,
  "chave": {
    "id": 3,
    "nome": "ERP financeiro",
    "prefixo": "cisp_7f3a9…",
    "escopos": ["mensagens_enviar", "conversas_ler", "atendimentos_abrir"],
    "cota_minuto": 60
  },
  "servidor": {
    "api_versao": "1",
    "fuso_horario": "America/Sao_Paulo",
    "agora": "2026-10-01T09:30:12-03:00",
    "plano": "PREMIUM"
  }
}
FieldTypeDescription
chave.idintegerKey identifier in the panel.
chave.nomestringThe Name given to the key. It is the name shown in the history of the actions it performs.
chave.prefixostringThe beginning of the key, the same as shown in the panel list. The API never returns the full key.
chave.escoposlistScopes turned on, in the order of the screen. See the table in Key permissions.
chave.cota_minutointegerPer-minute limit. 0 = no limit.
servidor.api_versaostringAPI version, the same as in the X-ChatISP-Api-Versao header.
servidor.fuso_horariostringServer time zone (IANA name). Dates without a time zone sent to the API are read in it.
servidor.agorastringServer clock in ISO 8601. Useful to measure the offset from your system's clock.
servidor.planostringName of the current plan (PREMIUM, GOLD, ENTERPRISE, CUSTOM, or TRIAL during the trial).

Errors: only the access errors common to all routes (autenticacao, chave_invalida, plano, cota_excedida).


Connections (WhatsApp numbers)

GET /api_publica.php?r=conexoes/listar

Lists all registered connections, connected or not, sorted by name. Each connection is a WhatsApp number linked to ChatISP by QR Code or by the official Meta API (see WhatsApp connections).

Parameters: none.

terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=conexoes/listar" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
// One number = one line: drop the non-main leg of twin connections
$numeros = array_filter(chatisp('conexoes/listar')['conexoes'], fn($c) => $c['principal']);
foreach ($numeros as $c) {
    echo "{$c['id']}  {$c['nome']}  {$c['telefone']}  {$c['estado']}\n";
}
Python
# One number = one line: drop the non-main leg of twin connections
for c in chatisp("conexoes/listar")["conexoes"]:
    if c["principal"]:
        print(c["id"], c["nome"], c["telefone"], c["estado"])
response (HTTP 200)
{
  "ok": true,
  "conexoes": [
    {
      "id": 1,
      "nome": "Atendimento",
      "tipo": "qr",
      "estado": "conectada",
      "telefone": "5544999990000",
      "nome_conta": "Fibra Exemplo",
      "gemea_id": 2,
      "principal": true
    },
    {
      "id": 2,
      "nome": "Atendimento (API oficial)",
      "tipo": "api_oficial",
      "estado": "conectada",
      "telefone": "5544999990000",
      "nome_conta": "Fibra Exemplo",
      "gemea_id": 1,
      "principal": false
    },
    {
      "id": 4,
      "nome": "Comercial",
      "tipo": "qr",
      "estado": "desconectada",
      "telefone": "5544999991111",
      "nome_conta": "Fibra Exemplo Vendas",
      "gemea_id": null,
      "principal": true
    }
  ]
}
FieldTypeDescription
idintegerThe conexao_id used in the other routes.
nomestringName given to the connection in the panel.
tipostringqr (connected by the app's QR Code) or api_oficial (official Meta API).
estadostringconectada, conectando, desconectada or erro. Only conectada connections send messages.
telefonestringThe WhatsApp number, digits only. May be empty on a connection that has not been paired yet.
nome_contastringWhatsApp profile name (what the customer sees).
gemea_idinteger or nullOn a twin connection, the id of the other leg of the same number. null on simple connections.
principalbooleanWhether this is the leg where the number's new conversations start. Always true on simple connections.

Errors: only the access errors.

Twin number: QR and official API on the same number

The same WhatsApp number can be linked to ChatISP both ways at once: by QR Code (the phone app) and by the official Meta API (in coexistence mode). In the panel, this pair appears as a single channel; in the API, it appears as two connections that point to each other through gemea_id. Rules that apply to the integration:

  • Both legs are the same number. To list numbers, consider only the connections with principal: true, as in the example above.
  • New conversations start on the main leg, which is the QR one when it exists. The conexao_id that appears in conversations and webhooks is, in general, the main leg's.
  • Sending: ChatISP chooses the leg that delivers each message, just as it does in the panel. If one leg goes down, the other keeps sending: the call only responds conexao_desconectada when both are offline.
  • Templates: only the official API leg sends templates. In mensagens/enviar_modelo and modelos/listar, you can pass the id of either leg: ChatISP uses the official leg of the same number.
  • Filters: in conversas/listar, filtering by the conexao_id of either leg returns the conversations of the whole number.

When to pass conexao_id

SituationWithout conexao_id
A single connected number (the twin pair counts as one number)The API uses that number. You can omit it.
More than one connected numberThe API uses the Sending number of the API key (Integrations, API keys). Key without a Sending number: HTTP 400 conexao_id_obrigatorio, specify which number to use.
No connected numberHTTP 400 sem_conexao.
mensagens/enviar_modeloThe rule above applies only to official API connections: with a single official number connected, you can omit it.
Sending to an existing conversation (conversa_id or protocolo)Not needed: the message goes out from the conversation's number.

Even with a single number today, store the conexao_id in the integration settings and always send it. That way, when the ISP connects a second number (sales, billing), the integration keeps working without any change.


Departments

GET /api_publica.php?r=departamentos/listar

Lists all departments, active and inactive, in the order defined in the panel (see Departments and queue).

Parameters: none.

terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=departamentos/listar" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
// Name => id map of the active departments
$deps = [];
foreach (chatisp('departamentos/listar')['departamentos'] as $d) {
    if ($d['ativo']) $deps[$d['nome']] = $d['id'];
}
$suporte = $deps['Suporte técnico'] ?? null;
Python
deps = {d["nome"]: d["id"] for d in chatisp("departamentos/listar")["departamentos"] if d["ativo"]}
suporte = deps.get("Suporte técnico")
response (HTTP 200)
{
  "ok": true,
  "departamentos": [
    { "id": 1, "nome": "Suporte técnico", "ativo": true,  "padrao": true },
    { "id": 2, "nome": "Financeiro",      "ativo": true,  "padrao": false },
    { "id": 3, "nome": "Comercial",       "ativo": true,  "padrao": false },
    { "id": 5, "nome": "Retenção",        "ativo": false, "padrao": false }
  ]
}
FieldTypeDescription
idintegerThe departamento_id used in the other routes.
nomestringDepartment name.
ativobooleanAn inactive department does not receive transfers or service requests: using it responds departamento_invalido. It appears in the list because older conversations may be linked to it.
padraobooleantrue on the department that receives new conversations (the Department for new conversations option, in Settings). None is marked when the option is empty or points to an inactive department.

Errors: only the access errors.


Agents

GET /api_publica.php?r=atendentes/listar

Lists the panel users in alphabetical order, with access profile, departments, presence and break. Deleted users do not appear; inactive users appear with ativo: false.

Parameters: none.

terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=atendentes/listar" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
// Who in department 1 is online and not on a break right now
$livres = array_filter(chatisp('atendentes/listar')['atendentes'], fn($a) =>
    $a['ativo'] && $a['online'] && !$a['em_pausa'] && in_array(1, $a['departamentos'], true));
Python
# Who in department 1 is online and not on a break right now
livres = [a for a in chatisp("atendentes/listar")["atendentes"]
          if a["ativo"] and a["online"] and not a["em_pausa"] and 1 in a["departamentos"]]
response (HTTP 200)
{
  "ok": true,
  "atendentes": [
    {
      "id": 7,
      "nome": "Ana Ribeiro",
      "perfil": "Atendente",
      "ativo": true,
      "departamentos": [1, 2],
      "online": true,
      "em_pausa": false
    },
    {
      "id": 4,
      "nome": "Carlos Mendes",
      "perfil": "Supervisor",
      "ativo": true,
      "departamentos": [1],
      "online": true,
      "em_pausa": true
    }
  ]
}
FieldTypeDescription
idintegerThe atendente_id (transfer, service request opening, filters) and the responsavel_id (service orders and schedule).
nomestringUser name.
perfilstringName of the access profile (see Agents, profiles and permissions). Empty if the user has no profile.
ativobooleanAn inactive user cannot receive conversations, service orders or appointments: using it responds atendente_invalido.
departamentoslist of integersIds of the departments the user works in.
onlinebooleanWhether the user had the panel open in the last 2 minutes.
em_pausabooleanWhether the user is online with a break in progress (lunch, rest). Always false for offline users.

Errors: only the access errors.

Transferring directly to an agent through the API works even if the agent is offline or on a break: the conversation stays with that agent. If the goal is for someone available to pick it up right away, transfer to the department and let the ChatISP distribution choose.


Message templates (official API)

GET /api_publica.php?r=modelos/listar

Lists the message templates of the official Meta API connections, sorted by name and language. An approved template is the only way to reach the customer outside the 24-hour window. The list reflects the last sync with Meta, done with the Sync with Meta button on the connection's Templates card (see WhatsApp connections): a template created or approved at Meta only appears here after syncing.

ParameterTypeRequiredDescription
conexao_idintegernoOnly the templates of this connection. It can be the id of either leg of a twin number: the QR leg answers with the official leg's templates. Without the parameter, the templates of all connections are returned.
terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=modelos/listar&conexao_id=2" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
// Only the ones the API can send
$enviaveis = array_filter(chatisp('modelos/listar', ['conexao_id' => 2])['modelos'], fn($m) => $m['enviavel']);
foreach ($enviaveis as $m) {
    echo "{$m['id']}  {$m['nome']} ({$m['idioma']}): {$m['variaveis']} variable(s)\n";
}
Python
modelos = chatisp("modelos/listar", {"conexao_id": 2})["modelos"]
for m in (m for m in modelos if m["enviavel"]):
    print(m["id"], m["nome"], m["idioma"], m["variaveis"])
response (HTTP 200)
{
  "ok": true,
  "modelos": [
    {
      "id": 12,
      "conexao_id": 2,
      "nome": "aviso_manutencao",
      "idioma": "pt_BR",
      "categoria": "UTILITY",
      "status": "APPROVED",
      "corpo": "Olá, {{1}}! Haverá manutenção programada na sua região em {{2}}, das {{3}}. Pedimos desculpas pelo transtorno.",
      "variaveis": 3,
      "cabecalho": null,
      "enviavel": true,
      "atualizado_em": "2026-09-28T14:02:11-03:00"
    },
    {
      "id": 15,
      "conexao_id": 2,
      "nome": "fatura_disponivel",
      "idioma": "pt_BR",
      "categoria": "UTILITY",
      "status": "APPROVED",
      "corpo": "Olá, {{1}}. Sua fatura de {{2}} está disponível. Vencimento: {{3}}.",
      "variaveis": 3,
      "cabecalho": "DOCUMENT",
      "enviavel": false,
      "atualizado_em": "2026-09-28T14:02:11-03:00"
    }
  ]
}
FieldTypeDescription
idintegerThe modelo_id for mensagens/enviar_modelo.
conexao_idintegerThe official API connection the template belongs to.
nomestringTemplate name at Meta. Also accepted in enviar_modelo through the modelo parameter, together with idioma.
idiomastringLanguage code at Meta, for example pt_BR. The same name may exist in several languages.
categoriastringCategory at Meta: UTILITY, MARKETING or AUTHENTICATION. It defines the conversation price charged by Meta.
statusstringStatus at Meta, as returned by Meta (for example APPROVED, PENDING, REJECTED). Only APPROVED can be sent.
corpostringBody text, with the variables in the format {{1}}, {{2}}...
variaveisintegerHow many variables the body has. In enviar_modelo, the variaveis list must have this number of values, in order.
cabecalhostring or nullTemplate header type: null (no header), TEXT, or media (IMAGE, VIDEO, DOCUMENT...).
enviavelbooleantrue when the API can send the template: approved and without a media header. Templates with a media header respond modelo_com_midia when sent.
atualizado_emstringDate of the last sync of this template.

Errors:

errorHTTPWhen
conexao_invalida400The conexao_id provided does not exist.
parametro_invalido400conexao_id is not an integer.

A QR connection without an official leg returns an empty list, with no error: numbers connected only by QR do not use templates.


Labels

GET /api_publica.php?r=etiquetas/listar

Lists the conversation labels registered in the panel (see Catalogs), in the order defined there.

Parameters: none.

terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=etiquetas/listar" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
$etiquetas = array_column(chatisp('etiquetas/listar')['etiquetas'], 'id', 'nome');
// labels the conversation as "Visita técnica" without removing the other labels
chatisp('conversas/etiquetas', ['conversa_id' => 18342, 'adicionar' => [$etiquetas['Visita técnica']]], 'POST');
Python
etiquetas = {e["nome"]: e["id"] for e in chatisp("etiquetas/listar")["etiquetas"]}
# labels the conversation as "Visita técnica" without removing the other labels
chatisp("conversas/etiquetas", {"conversa_id": 18342, "adicionar": [etiquetas["Visita técnica"]]}, "POST")
response (HTTP 200)
{
  "ok": true,
  "etiquetas": [
    { "id": 3, "nome": "Visita técnica", "cor": "#f59e0b" },
    { "id": 1, "nome": "Cliente novo",   "cor": "#10b981" },
    { "id": 6, "nome": "Cancelamento",   "cor": "#ef4444" }
  ]
}
FieldTypeDescription
idintegerId used in conversas/etiquetas and in the etiqueta_id filter of conversas/listar.
nomestringLabel name.
corstringHexadecimal color (#rrggbb), the same as in the panel.

Errors: only the access errors.


Closing reasons

GET /api_publica.php?r=motivos/listar

Lists the closing reasons registered in the panel (see Catalogs), active and inactive, in the order defined there.

Parameters: none.

terminal
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=motivos/listar" \
  -H "Authorization: Bearer $CHATISP_CHAVE"
PHP
$motivos = array_column(array_filter(chatisp('motivos/listar')['motivos'], fn($m) => $m['ativo']), 'id', 'nome');
chatisp('conversas/encerrar', ['conversa_id' => 18342, 'motivo_id' => $motivos['Problema resolvido']], 'POST');
Python
motivos = {m["nome"]: m["id"] for m in chatisp("motivos/listar")["motivos"] if m["ativo"]}
chatisp("conversas/encerrar", {"conversa_id": 18342, "motivo_id": motivos["Problema resolvido"]}, "POST")
response (HTTP 200)
{
  "ok": true,
  "motivos": [
    { "id": 1, "nome": "Problema resolvido",  "ativo": true },
    { "id": 2, "nome": "Dúvida esclarecida",  "ativo": true },
    { "id": 4, "nome": "Cliente não respondeu", "ativo": true },
    { "id": 7, "nome": "Engano",              "ativo": false }
  ]
}
FieldTypeDescription
idintegerThe motivo_id for conversas/encerrar.
nomestringReason name. It is the text that appears in motivo_encerramento on conversations and in the motivo field of the conversa_encerrada webhook.
ativobooleanAn inactive reason cannot be used to close (motivo_invalido), but it still appears on older conversations.

Errors: only the access errors.


Store and refresh the catalogs

Catalogs change rarely: departments, labels and reasons only change when someone edits the records in the panel. Best practices:

  • Cache them in your system and refresh at intervals (for example, every hour) or when the integration starts. Querying the catalog on every send wastes the per-minute limit.
  • Prefer ids over names. Names can be edited in the panel at any time; the id stays the same. If your configuration uses names (as in the examples above), resolve the id at startup and warn when a name is not found.
  • Handle record errors (departamento_invalido, atendente_invalido, motivo_invalido, etiqueta_invalida) as a signal to reload the catalog: something was deactivated or deleted in the panel.
  • Presence changes all the time. The online and em_pausa fields of atendentes/listar are valid for the moment of the query; do not cache them.
  • So does the connection status. Before a large send, check in conexoes/listar that the number is conectada.