Runbook — Audiência do Sagazchat
Manual operacional para uma IA ou pessoa operar a Audiência (base de contatos) do Sagazchat sem depender de tentativa visual cega.
Manual operacional para uma IA ou pessoa operar a Audiência (base de contatos) do Sagazchat sem depender de tentativa visual cega.
Status: validado em 2026-05-11 na conta de exemplo (
user_id=39, empresaexemplo, app v4.6.0). Contato pré-existente — NÃO DELETAR:
Fulano Exemplo(id 97653, +55 98 8120-XXXX)Outros 3 contatos que existiam (
Cliente Exemplo,contato-exemplo,55119XXXXXXX) foram deletados pelo próprio administrador durante a auditoria (não por mim).
1. Onde fica
- Sidebar: Gestão › Audiência (item simples, link direto, sem submenu).
- Lista:
https://app.sagazchat.com/contacts - Detalhe/edição: modal sobre a lista — NÃO navega URL (URL continua
/contacts).
2. Lista /contacts
2.1 Cabeçalho
Botões no topo direito, da esquerda pra direita:
| Botão | Estilo | Ação |
|---|---|---|
| Remover todos | cinza | Modal de confirmação destrutivo (apaga TODOS os contatos + atendimentos relacionados) |
| Exportar | cinza | Dispara GET /contacts/export direto, sem modal — baixa arquivo |
| Importar | cinza com chevron ▾ | Dropdown com 2 opções: Importar XLS/XLSX, Importar do Whatsapp |
| Adicionar | verde | Modal “Especialista Virtual” (criar contato) |
Acima da tabela: campo Pesquisar... (busca por texto livre).
2.2 Tabela
Colunas:
| # | Coluna | Comportamento |
|---|---|---|
| 0 | ☐ (checkbox no header) | Marca/desmarca todos da página |
| 1 | Nome | Nome do contato. Mostra avatar (foto do WhatsApp) + ícone do canal (badge verde do WhatsApp). Quando o contato não tem nome, mostra o telefone como nome (ex: 55119XXXXXXX). Coluna sortable (⇅). |
| 2 | Formatado +55 (XX) XXXXX-XXXX para BR; cru pra internacionais (2349019879608) | |
| 3 | Texto ou Não informado se vazio | |
| 4 | Data de Inscrição | dd/mm/aaaa. Sortable (⇅). |
| 5 | Ações | Kebab ⋮ com 3 opções (ver § 4) |
Cada linha tem checkbox individual à esquerda do avatar.
Comportamento dos checkboxes ao selecionar (toolbar contextual com bulk actions?) — [a validar]. Não foi explorado nesta auditoria.
Endpoint da lista:
GET /contacts/?searchParam=&pageNumber=1
(Atenção: trailing slash /? — necessário.)
Filtro de busca usa searchParam=<texto>. Não foi validado contra quais campos a busca rastreia (nome+telefone+email óbvios; etiqueta provavelmente não — campo de etiqueta não aparece na UI da lista).
3. Criar contato
Botão + Adicionar abre modal cujo heading é “Especialista Virtual” (quirky — provável leftover de nome de componente; não é nome de feature).
Campos:
| Campo | Tipo | Detalhes |
|---|---|---|
| Nome | text (name="name") | Nome completo do contato |
| Telefone | tel (sem name) | Combobox de país (default Brasil 🇧🇷 +55) + número. Placeholder +55 (13) 91234 4321. |
text (name="email") | Opcional | |
| Data de nascimento | date (name="birthDate") | dd/mm/aaaa |
| Informações adicionais | Repeatable | Contador (0..N) + botão +. Clicar + adiciona um par extraInfo[N].name (Nome do campo) + extraInfo[N].value (Valor) |
Botões: Cancelar, Salvar.
Schema do POST:
{ name, phone, email, birthDate, extraInfo: [{name, value}] }—[a validar](não capturei o POST de criar nesta auditoria pra não popular a base).
4. Kebab da linha
Menu ⋮ na coluna Ações com 3 itens validados:
| Item | Ação | Observação |
|---|---|---|
| Editar contato | Abre o mesmo modal “Especialista Virtual” pré-preenchido | Equivalente a clicar no nome |
| Ir para o chat | Navega pra /live-chat-whatsapp (ou outro canal) abrindo a conversa do contato | Não validei navegação completa |
| Deletar | Modal de confirmação Deletar contato? Você tem certeza que deseja excluir o contato "<nome>"? Todos os atendimentos relacionados serão perdidos. Botões Cancelar / Deletar. Destrutivo. |
IMPORTANTE PARA IA: clicar no nome do contato abre o mesmo modal que “Editar contato” — não é navegação separada.
5. Detalhe/edição (modal)
Click no nome OU Editar contato no kebab disparam:
GET /contacts/<id>
Resposta carrega o modal com todos os campos preenchidos. Heading do modal mostra <Nome> + linha com <+55 (XX) XXXXX-XXXX>.
Campos do modal são os mesmos da criação (Nome, Telefone, E-Mail, Data de nascimento, Informações adicionais). Botões Cancelar/Salvar.
Endpoint PUT de salvar edição não capturado —
[a validar]. Provável:PUT /contacts/<id>.
6. Importar
Botão Importar abre dropdown com 2 opções.
6.1 Importar XLS/XLSX
Modal “Importar Lista de Contatos” com:
- Subtítulo (texto residual confuso):
Escolha o canal para iniciar a conversa com esse contato— não tem seletor de canal visível na tela; texto provavelmente leftover de versão anterior. - Link “Baixe aqui” pra modelo de planilha.
- Área drag-and-drop:
Solte o arquivo aqui, ou clique para selecionar/Formatos suportados: XLS e XLSX. <input type="file" name="file" accept=".xls,.xlsx">- Botões: Cancelar, Importar.
Endpoint POST do import não capturado (não enviei arquivo). Provável:
POST /contacts/importmultipart/form-data.
6.2 Importar do Whatsapp
Sem modal — clique dispara request direto:
POST /contacts/cell-import/all
Resposta não capturada. Hipótese: sincroniza TODOS os contatos do celular conectado ao WhatsApp não-oficial (instância). Provavelmente longo (pode levar minutos).
CUIDADO: ação importa massa de contatos. Validar antes em conta de teste se for usar em produção.
7. Exportar
Botão Exportar dispara direto:
GET /contacts/export
Sem modal de opções/filtros. Exporta a base inteira. Formato não confirmado (provável CSV ou XLSX).
Download não foi capturado pelo response listener — pode ter usado
Content-Disposition: attachmentque o navegador roteia direto pro disco.
8. Remover todos (DESTRUTIVO)
Botão Remover todos abre modal cujo heading é apenas “Deletar” (não “Remover todos”, quirky):
- Texto:
Tem certeza que deseja deletar TODOS os contatos? Todos os atendimentos relacionados serão perdidos. - Botões:
Cancelar/Deletar(vermelho).
JAMAIS clicar em Deletar sem ordem explícita e dupla confirmação do administrador.
Endpoint provável: DELETE /contacts/all ou similar — [a validar].
9. Ações seguras vs disparadoras (para IA)
Seguras (pode rodar sem confirmação):
GET /contacts/?searchParam=&pageNumber=1— listarGET /contacts/<id>— detalheGET /contacts/export— exportar (apenas baixa, não muda dados)- Abrir modal Adicionar/Editar e cancelar com Escape
- Abrir dropdown Importar (sem clicar nas opções)
- Abrir kebab e cancelar
Disparadoras (exigem ordem explícita do administrador):
POST /contacts(criar) —[a validar]endpointPUT /contacts/<id>(editar) —[a validar]DELETE /contacts/<id>(deletar individual)DELETE /contacts/all(deletar TODOS) — NUNCA sem dupla confirmaçãoPOST /contacts/cell-import/all(importar do WhatsApp) — pode trazer centenas de contatos sem revertendo- Botão Salvar em qualquer modal
10. Quirks e armadilhas
- Modal de criar contato se chama “Especialista Virtual” — confuso visualmente. Não é nome de feature, é leftover de naming do componente.
- Modal de remover todos tem heading só “Deletar” — sem mencionar que é massa. Ler o texto antes de clicar.
- “Importar do Whatsapp” não tem modal — clique imediato dispara
POST /contacts/cell-import/all. UX perigoso pra quem clica explorando. - Subtítulo do modal Importar XLS tem texto residual
"Escolha o canal para iniciar a conversa com esse contato"que não corresponde à função. - Click no nome do contato abre modal de edição — não navega pra tela dedicada. URL continua
/contacts. - Contato sem nome aparece como o número (ex:
55119XXXXXXX) — buscar contato por “nome” pode falhar se o usuário não nomeou. - Empresa “exemplo” no topo da sidebar ≠ contato “Fulano Exemplo” na lista — homonímia perigosa pra seletores que buscam por texto exato. Sempre filtrar pela tabela
<TR>ou wrapper de linha. - Trailing slash em
/contacts/?...— sem ele o endpoint pode falhar (/contacts?searchParam=pode redirecionar ou 404).
11. Snippets — comandos Playwright via CDP
Pressupõe sessão CDP ativa em
scripts/_out/cdp.txt.
11.1 Listar contatos
await page.goto('https://app.sagazchat.com/contacts', { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(2500);
// captura: GET /contacts/?searchParam=&pageNumber=1
11.2 Buscar por texto
await page.locator('input[placeholder="Pesquisar..."]').fill('Exemplo');
await page.waitForTimeout(1000);
// dispara GET /contacts/?searchParam=Exemplo&pageNumber=1
11.3 Kebab da linha (defensivo)
// procura o cell EXATO e usa wrapper de linha pra evitar acertar outro item
const kebab = await page.evaluate((nome) => {
const cell = Array.from(document.querySelectorAll('*')).find((el) => (el.textContent || '').trim() === nome && el.children.length === 0);
if (!cell) return null;
// sobe até wrapper de linha (<TR> com 5-19 children)
let row = cell;
for (let i = 0; i < 10; i++) {
if (!row.parentElement) break;
row = row.parentElement;
if (row.children.length >= 5 && row.children.length < 20) break;
}
const ic = Array.from(row.querySelectorAll('.MuiIconButton-root, button')).filter((b) => {
const r = b.getBoundingClientRect();
return r.width < 60 && r.height < 60;
});
const last = ic[ic.length - 1];
if (!last) return null;
const r = last.getBoundingClientRect();
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
}, 'Fulano Exemplo');
await page.mouse.click(kebab.x, kebab.y);
11.4 Abrir modal de edição
const coord = await page.evaluate((nome) => {
const cell = Array.from(document.querySelectorAll('*')).find((el) => (el.textContent || '').trim() === nome && el.children.length === 0);
if (!cell) return null;
const r = cell.getBoundingClientRect();
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
}, 'Fulano Exemplo');
await page.mouse.click(coord.x, coord.y);
// modal abre; GET /contacts/<id>
11.5 Fechar dialog defensivo
async function closeAll() {
for (let i = 0; i < 3; i++) {
const has = await page.evaluate(() => !!document.querySelector('[role="dialog"], .MuiDialog-paper, [role="menu"], .MuiMenu-paper, .MuiPopover-paper'));
if (!has) return;
await page.keyboard.press('Escape');
await page.waitForTimeout(400);
}
}
12. Como iniciar nova auditoria
- Garantir sessão CDP ativa (
scripts/_out/cdp.txt). - Confirmar empresa:
exemplono topo da sidebar. - Verificar contato
Fulano Exemplona lista — sentinela. Não deletar. - Pra criar contatos de teste, prefixar nome com
LAB-CONTATO-*para fácil identificação e cleanup posterior. - Pra confirmar comportamento de bulk actions (checkboxes), marcar 1+ contatos e ver se aparece toolbar contextual no topo. Documentar e atualizar § 2.2.
13. Pendências para próxima auditoria
- Bulk actions ao selecionar checkboxes — verificar se aparece toolbar contextual e quais ações disponíveis.
- POST de criar contato — schema do body completo (extraInfo, country code, etc).
- PUT de editar contato — endpoint e payload.
- POST do Importar XLS/XLSX — endpoint e structure multipart.
- Resposta de
POST /contacts/cell-import/all— síncrona ou job assíncrono? Como saber quando terminou? - Formato real do export (CSV ou XLSX)? Quais colunas?
DELETE /contacts/all— endpoint real (e payload se houver).- Itens da sidebar ainda não auditados: Conexões/integrations, Agendamentos, Campanhas, N8N, Prompts, Assistente Interno, Gerente de grupo.
14. Scripts criados
scripts/sagaz-audiencia-investigar.mjs— listing inicialscripts/sagaz-audiencia-detalhar.mjs— primeira tentativa (kebab pegou linha errada — abandonado, defeitos corrigidos no próximo)scripts/sagaz-audiencia-detalhar2.mjs— kebab + click nome via wrapper de linhascripts/sagaz-audiencia-acoes-topo.mjs— Exportar, Remover todos (cancela), Importar XLS, Importar WhatsApp, botão +