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:
| Route | Returns | Used in |
|---|---|---|
chave/info | The key itself and the server | Connection test, scope check and time zone |
conexoes/listar | WhatsApp numbers | conexao_id in sends, service request opening, filters and templates |
departamentos/listar | Departments | departamento_id in transfers, service request opening, service orders, schedule, filters and metrics |
atendentes/listar | Agents | atendente_id in transfers and opening; responsavel_id in service orders and schedule |
modelos/listar | Official API templates | modelo_id in mensagens/enviar_modelo |
etiquetas/listar | Conversation labels | conversas/etiquetas and the etiqueta_id filter |
motivos/listar | Closing reasons | motivo_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
/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.
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=chave/info" \ -H "Authorization: Bearer $CHATISP_CHAVE"
$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); }
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)}")
{
"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"
}
}| Field | Type | Description |
|---|---|---|
chave.id | integer | Key identifier in the panel. |
chave.nome | string | The Name given to the key. It is the name shown in the history of the actions it performs. |
chave.prefixo | string | The beginning of the key, the same as shown in the panel list. The API never returns the full key. |
chave.escopos | list | Scopes turned on, in the order of the screen. See the table in Key permissions. |
chave.cota_minuto | integer | Per-minute limit. 0 = no limit. |
servidor.api_versao | string | API version, the same as in the X-ChatISP-Api-Versao header. |
servidor.fuso_horario | string | Server time zone (IANA name). Dates without a time zone sent to the API are read in it. |
servidor.agora | string | Server clock in ISO 8601. Useful to measure the offset from your system's clock. |
servidor.plano | string | Name 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)
/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.
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=conexoes/listar" \ -H "Authorization: Bearer $CHATISP_CHAVE"
// 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"; }
# 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"])
{
"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
}
]
}| Field | Type | Description |
|---|---|---|
id | integer | The conexao_id used in the other routes. |
nome | string | Name given to the connection in the panel. |
tipo | string | qr (connected by the app's QR Code) or api_oficial (official Meta API). |
estado | string | conectada, conectando, desconectada or erro. Only conectada connections send messages. |
telefone | string | The WhatsApp number, digits only. May be empty on a connection that has not been paired yet. |
nome_conta | string | WhatsApp profile name (what the customer sees). |
gemea_id | integer or null | On a twin connection, the id of the other leg of the same number. null on simple connections. |
principal | boolean | Whether 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_idthat 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_desconectadawhen both are offline. - Templates: only the official API leg sends templates. In
mensagens/enviar_modeloandmodelos/listar, you can pass the id of either leg: ChatISP uses the official leg of the same number. - Filters: in
conversas/listar, filtering by theconexao_idof either leg returns the conversations of the whole number.
When to pass conexao_id
| Situation | Without 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 number | The 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 number | HTTP 400 sem_conexao. |
mensagens/enviar_modelo | The 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
/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.
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=departamentos/listar" \ -H "Authorization: Bearer $CHATISP_CHAVE"
// 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;
deps = {d["nome"]: d["id"] for d in chatisp("departamentos/listar")["departamentos"] if d["ativo"]}
suporte = deps.get("Suporte técnico"){
"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 }
]
}| Field | Type | Description |
|---|---|---|
id | integer | The departamento_id used in the other routes. |
nome | string | Department name. |
ativo | boolean | An 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. |
padrao | boolean | true 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
/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.
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=atendentes/listar" \ -H "Authorization: Bearer $CHATISP_CHAVE"
// 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));
# 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"]]
{
"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
}
]
}| Field | Type | Description |
|---|---|---|
id | integer | The atendente_id (transfer, service request opening, filters) and the responsavel_id (service orders and schedule). |
nome | string | User name. |
perfil | string | Name of the access profile (see Agents, profiles and permissions). Empty if the user has no profile. |
ativo | boolean | An inactive user cannot receive conversations, service orders or appointments: using it responds atendente_invalido. |
departamentos | list of integers | Ids of the departments the user works in. |
online | boolean | Whether the user had the panel open in the last 2 minutes. |
em_pausa | boolean | Whether 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)
/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.
| Parameter | Type | Required | Description |
|---|---|---|---|
conexao_id | integer | no | Only 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. |
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=modelos/listar&conexao_id=2" \ -H "Authorization: Bearer $CHATISP_CHAVE"
// 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"; }
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"])
{
"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"
}
]
}| Field | Type | Description |
|---|---|---|
id | integer | The modelo_id for mensagens/enviar_modelo. |
conexao_id | integer | The official API connection the template belongs to. |
nome | string | Template name at Meta. Also accepted in enviar_modelo through the modelo parameter, together with idioma. |
idioma | string | Language code at Meta, for example pt_BR. The same name may exist in several languages. |
categoria | string | Category at Meta: UTILITY, MARKETING or AUTHENTICATION. It defines the conversation price charged by Meta. |
status | string | Status at Meta, as returned by Meta (for example APPROVED, PENDING, REJECTED). Only APPROVED can be sent. |
corpo | string | Body text, with the variables in the format {{1}}, {{2}}... |
variaveis | integer | How many variables the body has. In enviar_modelo, the variaveis list must have this number of values, in order. |
cabecalho | string or null | Template header type: null (no header), TEXT, or media (IMAGE, VIDEO, DOCUMENT...). |
enviavel | boolean | true 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_em | string | Date of the last sync of this template. |
Errors:
error | HTTP | When |
|---|---|---|
conexao_invalida | 400 | The conexao_id provided does not exist. |
parametro_invalido | 400 | conexao_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
/api_publica.php?r=etiquetas/listar
Lists the conversation labels registered in the panel (see Catalogs), in the order defined there.
Parameters: none.
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=etiquetas/listar" \ -H "Authorization: Bearer $CHATISP_CHAVE"
$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');
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"){
"ok": true,
"etiquetas": [
{ "id": 3, "nome": "Visita técnica", "cor": "#f59e0b" },
{ "id": 1, "nome": "Cliente novo", "cor": "#10b981" },
{ "id": 6, "nome": "Cancelamento", "cor": "#ef4444" }
]
}| Field | Type | Description |
|---|---|---|
id | integer | Id used in conversas/etiquetas and in the etiqueta_id filter of conversas/listar. |
nome | string | Label name. |
cor | string | Hexadecimal color (#rrggbb), the same as in the panel. |
Errors: only the access errors.
Closing reasons
/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.
curl -s "https://atendimento.seuprovedor.com.br/api_publica.php?r=motivos/listar" \ -H "Authorization: Bearer $CHATISP_CHAVE"
$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');
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"){
"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 }
]
}| Field | Type | Description |
|---|---|---|
id | integer | The motivo_id for conversas/encerrar. |
nome | string | Reason name. It is the text that appears in motivo_encerramento on conversations and in the motivo field of the conversa_encerrada webhook. |
ativo | boolean | An 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
onlineandem_pausafields ofatendentes/listarare valid for the moment of the query; do not cache them. - So does the connection status. Before a large send, check in
conexoes/listarthat the number isconectada.