@lionchat/mcp-server 1.24.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -403,6 +403,7 @@ Antes de escolher a ferramenta, cheque se a intenção bate com a coluna da dire
403
403
  | Intenção do usuário | Tool certa | Cuidado |
404
404
  |---|---|---|
405
405
  | Listar / ver não lidas | `lionchat_conversations_list` (`conversation_type: 'unread'`) ou `lionchat_conversations_meta` | NÃO use `lionchat_conversations_unread` — é AÇÃO DE ESCRITA (marca como não-lida) |
406
+ | Ver **uma empresa** (quem trabalha la, anotacoes, conversas) e ligar/tirar contatos | `lionchat_companies_show` + `lionchat_companies_contacts_list` / `_create` / `_destroy`, `lionchat_companies_notes_list`, `lionchat_companies_conversations_list` | Ligar pela ficha: `lionchat_contacts_update` com `company_id` (null tira). Cargo da pessoa = `additional_attributes.job_title`. Escolher a empresa troca o texto `company_name` pelo nome dela |
406
407
  | Achar a conversa **de uma pessoa/empresa** (nome, telefone, e-mail, CPF/CNPJ, nº da conversa) | `lionchat_search_list` (`/search/conversations`) ou `lionchat_contacts_search` | RÁPIDO. **NÃO use `lionchat_conversations_search`**: apesar do nome, ele é o legado que procura dentro das mensagens e leva **15 a 22 s** (medido em produção 11/08). O sufixo `_list` aqui é histórico — `lionchat_search_list*` **são buscas**, não listagens; a regra "não baixe tudo com `*_list`" vale para `lionchat_<recurso>_list` |
407
408
  | Achar **onde foi dito** algo (palavra dentro da conversa) | `lionchat_search_list_2` (`/search/messages`) | RÁPIDO: índice trigram próprio e teto de tempo em duas fases (2 s / 2 s desde 04/09 — antes a 2ª fase herdava 14 s e o pior caso da seção era 16 s). Estourou as duas, o payload volta com `message_search_interrupted: true` e a lista VAZIA — isso NÃO quer dizer que não existe, quer dizer que não deu tempo: tente de novo. É a substituta certa do `lionchat_conversations_search` |
408
409
  | Não sei se o termo é nome de alguém ou algo dito no papo | `lionchat_search_search` (`/search`) | Cobre os dois numa chamada. Opção mais segura quando a intenção é ambígua |
@@ -436,7 +437,7 @@ Antes de escolher a ferramenta, cheque se a intenção bate com a coluna da dire
436
437
  | **Mudar o modo de distribuição da caixa** (Rodízio × Equilibrado) | `lionchat_inboxes_update` (`auto_assignment_config.assignment_order`) | Só vale quando a caixa NÃO tem política vinculada (com política, a política manda). O jsonb é SUBSTITUÍDO, não mesclado: releia o `auto_assignment_config` atual e reenvie as outras chaves junto, senão apaga o `assign_offline_agents` e o freio |
437
438
  | **Criar/atualizar time** | `lionchat_teams_create` / `_update` | **Preencha `description`**: desde 03/08 é ela que o AI Agente lê pra decidir o roteamento por time. Descreva QUANDO mandar pra lá, não o que o time é |
438
439
  | **Trocar um atendente por outro** ("fulano saiu, entrou ciclano") | `lionchat_agents_replace` (`POST /agents/{id}/replace`, body `agent: {email, name, role}`; `inherit_memberships` default `true`) | Faz os dois passos de uma vez: cria o novo (passa pelo limite de vagas do plano por 30 min), manda a carteira inteira (conversas, cards, tarefas, agendamentos) e exclui o antigo em segundo plano. Responde **202** com `new_agent` — o antigo some da lista quando a transferência termina (`replacing: true` em `agents_list` enquanto roda). **Confirme antes** (seção 7a: é exclusão). Recusas 422: substituir a si mesmo, agente de suporte, e-mail já membro, e o **único administrador confirmado só troca por usuário já confirmado** (convide e espere aceitar). `agents_destroy` recusa quem está ENTRANDO numa substituição viva. Admin-only |
439
- | **Pausar a automação no meio** (esperar atribuição/card antes da próxima ação) | ação `wait` em `lionchat_automation_rules_create` / `_update` | Segundos, teto 300. Acima disso é caso de FlowBuilder. Ver `lionchat://docs/conversation-flows` |
440
+ | **Pausar a automação no meio** (esperar atribuição/card antes da próxima ação) | ação `wait` em `lionchat_automation_rules_create` / `_update` | Segundos, teto 300. Acima disso é caso de FlowBuilder. Formato objeto `[{seconds, only_if_open, only_if_no_reply}]` confere na volta se a conversa segue aberta e se ninguém respondeu (25/09). Ver `lionchat://docs/conversation-flows` |
440
441
  | **Buscar contato/conversa/card** | `lionchat_search_*`, `lionchat_contacts_search` | **Termo com letra exige 3+ caracteres** (dígito é isento). Menos que isso volta VAZIO sem erro — não conclua que o registro não existe. Ver `lionchat://docs/api-conventions` |
441
442
  | **Migrar conversas** de uma caixa WhatsApp pra outra (ex.: QR Code → Oficial) | `lionchat_inboxes_inbox_migration_list` (prévia) → `_execute` → acompanhar status | Admin-only e IRREVERSÍVEL: sempre mostre os números da prévia e confirme (seção 7a). Grupos não vão pro canal oficial — ficam na caixa de origem |
442
443
  | Qualquer coisa de Ligação WP (Wavoip) | **INTEGRAÇÃO REMOVIDA em 12/08/2026** — não há tool nem tela | A Wavoip saiu do produto: a aba "Ligação WP" **não existe mais**. NUNCA orientar o usuário a "Configurações da caixa → Ligação WP". Para voz em caixa QR Code, o caminho hoje é o **LionCalls**. As ligações antigas seguem no histórico (`provider: "wavoip"`); as gravações delas ficavam no servidor da Wavoip e vão sumir — ao tentar ouvir, o painel mostra "Esta gravação não está mais disponível", e isso é esperado, não é defeito. |
@@ -9,6 +9,7 @@ Padrões de autenticação, paginação, filtros, formatos e tratamento de erros
9
9
  | Tipo | Header / Param | Quando usar |
10
10
  |---|---|---|
11
11
  | `api_access_token` | Header `api_access_token: <token>` | API principal (você usa isto via MCP) |
12
+ | Chave de API como Bearer | `Authorization: Bearer <token>` (mesma chave de 24 caracteres do perfil) | Desde 26/09/2026, pra n8n, Make e ferramentas que só sabem mandar Bearer. Se os dois vierem, vale o `api_access_token` |
12
13
  | Devise Token Auth | `access-token` + `client` + `uid` | Dashboard web/mobile (não você) |
13
14
  | HMAC | `X-Webhook-Signature` | Webhooks de entrada |
14
15
  | OAuth Bearer | `Authorization: Bearer <token>` | MCP Remote (você usa isto) |
@@ -385,7 +385,7 @@ Como a coleta de dados/cenários funciona hoje (importante pra diagnosticar "IA
385
385
 
386
386
  As instruções aceitam variáveis Liquid `{{ }}` que chegam JÁ PREENCHIDAS com os dados reais do contato/conversa do atendimento — vale pra instrução base do assistente (`config.instructions`) e pra instrução de cada cenário (`scenario.instruction`). O texto é gravado como está (sem sanitização); cap de 20.000 chars na base.
387
387
 
388
- - Contato: `{{contact.name}}`, `{{contact.first_name}}`, `{{contact.last_name}}`, `{{contact.email}}`, `{{contact.phone}}`, `{{contact.cpf}}`, `{{contact.cnpj}}`, `{{contact.rg}}`, `{{contact.date_of_birth}}`, `{{contact.profession}}`, `{{contact.address.city}}` (e `.cep`/`.street`/`.number`/`.neighborhood`/`.state`...), `{{contact.custom_attribute.<chave>}}`.
388
+ - Contato: `{{contact.name}}`, `{{contact.first_name}}`, `{{contact.last_name}}`, `{{contact.email}}`, `{{contact.phone}}`, `{{contact.cpf}}`, `{{contact.cnpj}}`, `{{contact.rg}}`, `{{contact.date_of_birth}}`, `{{contact.profession}}`, `{{contact.company_name}}` (Empresa), `{{contact.job_title}}` (Cargo), `{{contact.address.city}}` (e `.cep`/`.street`/`.number`/`.neighborhood`/`.state`...), `{{contact.custom_attribute.<chave>}}`.
389
389
  - Conversa: `{{conversation.display_id}}`, `{{conversation.custom_attribute.<chave>}}`.
390
390
  - Variável sem valor sai vazia (não quebra). Texto SEM `{{` sai idêntico (preserva o cache do prompt) — só use variável quando agregar. Erro de digitação (filtro inválido, `{{` sem fechar) degrada suave: o pedaço com erro sai vazio e o resto das variáveis continua resolvendo (exceção: bloco `{% %}` sem fechar ainda cai pra texto cru).
391
391
  - Conta (2026-07): `{{account.custom_attribute.<chave>}}` (slogan, endereço...) AGORA resolve nas instruções e cenários — MAS só os atributos de conta que você DEFINIU (via `account_variables_create`); campos internos de faturamento e secrets NUNCA aparecem no autocomplete nem resolvem. O editor de Instruções/Cenários já sugere essas variáveis de conta ao digitar `{{`.
@@ -1001,3 +1001,50 @@ grupo, então o número entraria num grupo que nunca apareceria no painel. O cam
1001
1001
  a opção nas configurações da caixa e tentar de novo.
1002
1002
 
1003
1003
  A resposta do join_info vem no formato cru do motor, em PascalCase (`Name`, não `name`).
1004
+
1005
+ ## Gerenciar um grupo: permissões, pedidos para entrar, link e foto (26/09/2026)
1006
+
1007
+ Só em caixa de WhatsApp QR Code. O `group_id` é o id do WhatsApp **com o sufixo** (`120363...@g.us`),
1008
+ sempre como texto. Toda ação que muda o grupo exige que o número da caixa seja **admin do grupo**
1009
+ (403 se não for) — confira `my_role` em `lionchat_inboxes_waha_groups_show` antes.
1010
+
1011
+ **Permissões** — `lionchat_inboxes_waha_groups_update_2` aceita quatro chaves, todas no mesmo sentido
1012
+ (`true` = só admins / precisa aprovar):
1013
+
1014
+ | Chave | O que faz |
1015
+ |---|---|
1016
+ | `messages_admin_only` | só admins enviam mensagens |
1017
+ | `info_admin_only` | só admins editam nome, foto e descrição |
1018
+ | `members_add_admin_only` | só admins adicionam participantes |
1019
+ | `approval_required` | quem pede para entrar pelo link espera um admin aprovar |
1020
+
1021
+ - Mande só `true` ou `false` nas duas últimas — qualquer outra coisa é recusada com 422 (nunca vira `true`).
1022
+ - A resposta diz o que foi aplicado: com várias chaves, se uma falhar vem 422 com `applied` e `failed`.
1023
+ Informe à pessoa o que mudou e o que não mudou; não repita tudo de novo.
1024
+ - Para LER as duas últimas use `lionchat_inboxes_waha_groups_list_4`. `null` = o WhatsApp não respondeu
1025
+ agora (não é `false`). `approval_supported: false` = o servidor do WhatsApp daquela caixa ainda não
1026
+ tem aprovação de novos participantes: não ofereça a opção.
1027
+ - **Desligar `approval_required` com pedidos esperando:** o WhatsApp pode aprovar todos de uma vez.
1028
+ Liste os pedidos antes (`lionchat_inboxes_waha_groups_list_5`), diga quantos são e só desligue com
1029
+ confirmação explícita.
1030
+
1031
+ **Pedidos para entrar**
1032
+
1033
+ 1. `lionchat_inboxes_waha_groups_list_5` — lista. `supported: false` = servidor sem o recurso;
1034
+ `allowed: false` = o WhatsApp não deixou (o número não é admin do grupo).
1035
+ 2. `lionchat_inboxes_waha_groups_create_7` (aprovar) ou `_create_8` (recusar) com
1036
+ `participants: [{ id }]`, usando o `id` de cada pedido da lista.
1037
+
1038
+ - Aprovar coloca a pessoa no grupo e **aciona a pílula "entrou" e os fluxos "Entrou no grupo"**, igual a
1039
+ quem entra pelo link. Aprovar muitos de uma vez dispara um fluxo por pessoa.
1040
+ - A resposta traz `results` por pessoa e as listas **relidas** (`join_requests` e `participants`). Use
1041
+ essas listas para responder — não chame o `show` de novo (ele faz 6 perguntas ao WhatsApp e leva ~7 s).
1042
+ - 422 com as listas reais = o WhatsApp recusou (o pedido pode ter sido tratado pelo celular). Não repita.
1043
+ - Telefone do pedido só vem em `pn`. Quando vier `null`, o WhatsApp escondeu o número: diga isso; nunca
1044
+ apresente os dígitos do `id` (terminado em `@lid`) como telefone.
1045
+
1046
+ **Link de convite** — `lionchat_inboxes_waha_groups_create_4` gera um link novo e **mata o antigo**,
1047
+ inclusive o que fluxos e campanhas já enviaram. Confirme antes.
1048
+
1049
+ **Foto** — `lionchat_inboxes_waha_groups_update_3` troca; `lionchat_inboxes_waha_groups_destroy` remove
1050
+ (no WhatsApp e na lista de conversas do LionChat).
@@ -721,7 +721,7 @@ Todos os formatos abaixo foram conferidos linha a linha em `app/services/action_
721
721
  | `add_note_to_kanban_item` | `[{"funnel_id": 31, "text": "texto da nota"}]` — a chave e **`text`**, nao `note` |
722
722
  | `set_kanban_item_status` | `[{"funnel_id": 31, "status": "won"}]` — `won`, `lost` ou `open`. Status fora disso e ignorado |
723
723
  | `start_kanban_item_timer` / `stop_kanban_item_timer` | `[{"funnel_id": 31}]` |
724
- | `wait` | `[30]` — segundos. Ver "Acao Aguardar" abaixo |
724
+ | `wait` | `[30]` — segundos. Com conferência na volta: `[{"seconds": 120, "only_if_open": true, "only_if_no_reply": true}]`. Ver "Acao Aguardar" abaixo |
725
725
 
726
726
  **Acoes de Kanban:** todas precisam que a conversa ja tenha um card no funil informado. Sem card,
727
727
  a acao e pulada (fica so no log do servidor). Use `create_kanban_item` antes, na mesma regra.
@@ -756,6 +756,24 @@ conversas esperam ao mesmo tempo sem travar o atendimento. Na volta, o sistema c
756
756
  ainda existe e esta ligada, se a conversa ainda existe e se a caixa nao foi excluida — se algo
757
757
  disso mudou, ele simplesmente nao retoma.
758
758
 
759
+ **Conferir na volta (novo 2026-09-25).** Duas caixinhas opcionais, no formato OBJETO:
760
+
761
+ ```json
762
+ { "action_name": "wait", "action_params": [{ "seconds": 120, "only_if_open": true, "only_if_no_reply": true }] }
763
+ ```
764
+
765
+ | Chave | O que faz na volta |
766
+ |---|---|
767
+ | `only_if_open` | So segue se a conversa ainda estiver **aberta ou pendente**. Resolvida/adiada = o resto da regra NAO roda |
768
+ | `only_if_no_reply` | So segue se **ninguem respondeu** o cliente durante a espera. Conta qualquer mensagem PUBLICA de saida depois da pausa — atendente, IA, outra automacao ou mensagem automatica da caixa (saudacao, ausencia). Nota privada nao conta |
769
+
770
+ - Booleano de verdade (`true`/`false`); texto `"true"` tambem e aceito. Sem as chaves = retoma como sempre.
771
+ - O formato antigo (`[120]`) continua valendo e nao tem conferencia nenhuma.
772
+ - Quando a conferencia barra, o historico da execucao (`lionchat_automation_rules_list_1`) ganha o passo
773
+ `wait_check` com status `skipped` e o motivo. Nao e erro: e a regra fazendo o que foi pedido.
774
+ - Caso tipico: "manda o lembrete em 5 minutos **se ninguem tiver atendido ainda**" — sem `only_if_no_reply`,
775
+ o lembrete sai mesmo depois que o atendente ja respondeu.
776
+
759
777
  Lista autoritativa de nomes validos: `AutomationRule#actions_attributes` no backend. Nome de acao
760
778
  fora dessa lista e recusado com 422 ao salvar — isso a API valida. O que ela **nao** valida e o
761
779
  conteudo do `action_params`. `apply_kanban_checklist_template` existe no motor mas **nao** e uma
@@ -149,7 +149,12 @@ Company
149
149
  ├── id (PK)
150
150
  ├── account_id (FK)
151
151
  ├── name
152
- └── domain
152
+ ├── domain
153
+ ├── description
154
+ ├── contacts_count
155
+ ├── avatar (foto)
156
+ ├── additional_attributes (jsonb: legal_name, cnpj, phone, email, address{...}, social_profiles{...}; 2026-09-25)
157
+ └── last_activity_at (ultima vez que um contato da empresa teve atividade; 2026-09-25)
153
158
 
154
159
  AttributeChange (histórico de alterações de atributo — 2026-09-11)
155
160
  ├── id (PK)
@@ -173,9 +178,34 @@ não gera; chaves protegidas (rastreio `utm_*`/`gclid`, `waha_*`, `eclinica_*`,
173
178
  nascimento e documentos vêm mascarados (`masked: true`) para quem não é administrador. Retenção 180 dias.
174
179
 
175
180
  **`additional_attributes` vs `custom_attributes` no Contato:**
176
- - `additional_attributes` (jsonb): campos do sistema porém **EDITÁVEIS via API** (`permitted_params` permite `additional_attributes: {}`). Guarda chaves padrão como `city`, `company`, `country_code` — que são as chaves filtráveis em `lib/filters/filter_keys.yml` (tipo `additional_attributes`). NÃO é não-editável.
181
+ - `additional_attributes` (jsonb): campos do sistema porém **EDITÁVEIS via API** (`permitted_params` permite `additional_attributes: {}`). Guarda chaves padrão como `city`, `company_name` (o campo "Empresa" da ficha), `job_title` (o "Cargo", 2026-09-25) e `country_code`. NÃO é não-editável.
177
182
  - `custom_attributes` (jsonb): dado de negócio livre, definido pelo cliente via Atributos Customizados.
178
183
 
184
+ **Empresas (2026-09-25):** o contato pode estar ligado a UMA empresa (`company_id`). Ligar/tirar:
185
+ `lionchat_contacts_update` com `company_id` (empresa da mesma conta; vazio/null tira; se a chave nao for
186
+ enviada nada muda) ou `lionchat_companies_contacts_create` / `_destroy`. Regra do texto
187
+ `additional_attributes.company_name`: escolher a empresa faz ele virar o nome da empresa; tirar so apaga se
188
+ ele ainda era o nome dela; renomear a empresa so troca nos contatos que ainda tinham o nome antigo. **Empresa e
189
+ vinculo sao SEMPRE manuais** (25/09): o sistema NAO cria empresa nem vincula contato pelo dominio do e-mail.
190
+ **Teto: 100 contatos por empresa** — o 101o e recusado (422, "Esta empresa ja tem 100 contatos...") por qualquer
191
+ caminho (ficha, `lionchat_companies_contacts_create`, `lionchat_contacts_update`/`_create` com `company_id`);
192
+ tire alguem antes de colocar outro. O cargo da
193
+ pessoa na empresa e `additional_attributes.job_title`. A pagina da empresa junta os contatos
194
+ (`lionchat_companies_contacts_list`), as anotacoes (`lionchat_companies_notes_list`: as da PROPRIA empresa,
195
+ `kind: company`, + as dos contatos, `kind: contact`), as conversas (`lionchat_companies_conversations_list`,
196
+ filtradas pelo que o usuario pode ver), os documentos de todos os contatos (`lionchat_companies_documents_list`,
197
+ so leitura, cada item com `contacts[]` — o mesmo arquivo mandado a varios contatos vem UMA vez) e os compromissos
198
+ (`lionchat_companies_tasks_list`) e os cards do Kanban dos contatos (`lionchat_companies_kanban_items_list`, cada
199
+ card com `contacts[{id,name}]`, so os que o usuario ve no quadro). No card do quadro,
200
+ `conversation.contact.company_id`/`company_name` dizem a empresa do contato do card. Contratos dos contatos: `lionchat_signature_envelopes_list` com `company_id`.
201
+ Anotacao da empresa: `lionchat_companies_notes_create` / `_update` / `_destroy` (`{note:{content}}`; qualquer
202
+ atendente). Cadastro da empresa (25/09): `lionchat_companies_create`/`_update` aceitam
203
+ `additional_attributes` com `legal_name`, `cnpj` (validado; grava so digitos), `phone`, `email`,
204
+ `address{cep,street,number,complement,neighborhood,city,state,country}` e
205
+ `social_profiles{linkedin,facebook,instagram,telegram,tiktok,twitter,github}`. MESCLA: mande so o que muda;
206
+ valor vazio apaga aquela chave. Excluir empresa: so administrador; os contatos ficam, so deixam de estar na empresa (as anotacoes da
207
+ empresa vao junto).
208
+
179
209
  **Dados cadastrais (`additional_attributes.cadastral`):** CPF, CNPJ, RG, passaporte, nascimento,
180
210
  gênero, estado civil, profissão e endereço completo moram em `additional_attributes->cadastral`.
181
211
  São **IMUTÁVEIS por padrão** (primeira escrita vale) — alterar exige `force_update: true`, que SÓ
@@ -191,6 +221,7 @@ universal); a superfície REST de contatos não mudou.
191
221
  **Padrões de `source_id` por canal:**
192
222
  - Waha: `5511999999999@c.us` (1-on-1) ou `120363xxx@g.us` (grupo) ou `XXXX@lid` (LID)
193
223
  - WhatsApp Cloud: `5511999999999` (E.164 sem prefixo)
224
+ - ou o ID do WhatsApp da Meta (`CO.1046754121506176`) quando o cliente chegou SEM telefone (so nome de usuario). Nesse caso o contato nao tem `phone_number` e guarda `additional_attributes.social_whatsapp_user_id` / `social_whatsapp_user_name` (2026-09-25)
194
225
  - Email: o email mesmo
195
226
  - WebWidget: UUID gerado
196
227
 
@@ -335,6 +366,8 @@ Captain::AssistantResponse (FAQ)
335
366
  ├── question
336
367
  ├── answer
337
368
  ├── status (pending/approved/rejected)
369
+ ├── occurrences_count (int, default 1 — em quantas conversas DIFERENTES a mesma pergunta pendente
370
+ │ apareceu; soma so enquanto pendente. Lista: sort=occurrences = mais perguntadas primeiro, 2026-09-25)
338
371
  ├── embedding (vector, pgvector)
339
372
  └── documentable (polymorphic: Conversation que gerou)
340
373
 
@@ -384,6 +417,8 @@ e `send_conversion` (`[{destinations, event_names, value}]` — só administrado
384
417
  execução). Atributo protegido em `update_*_attribute` é recusado na execução (só no log). Executar
385
418
  (`lionchat_macros_execute`): até 100 conversas por chamada, a mesma conversa em 5 s é pulada em silêncio,
386
419
  30 execuções por minuto por usuário (429). A mensagem de `send_canned_response` sai assinada por quem executou.
420
+ Cada execução deixa na conversa a pílula "Macro: X executada por Fulano" (uma por conversa, 26/09) — vale
421
+ também pelo MCP, então quem executa aparece no histórico da conversa.
387
422
 
388
423
  ## Agenda / Tarefas / Booking
389
424
 
@@ -542,12 +577,12 @@ Atributos de sistema no CONTATO (prefixo `eclinica_`, protegidos, usáveis como
542
577
  |---|---|---|
543
578
  | `eclinica_cliente_id` | text | ID do paciente no e-Clínica (chave de matching) |
544
579
  | `eclinica_unit_id` / `eclinica_unit_name` | text | Unidade/filial que originou o evento |
545
- | `eclinica_idagenda` | text | ID do último agendamento |
546
- | `eclinica_data_consulta` | date | Data da consulta, ISO |
580
+ | `eclinica_idagenda` | text | ID do último agendamento (tela: "e-Clinica - ID do Ultimo Agendamento (ficha)") |
581
+ | `eclinica_data_consulta` | date | Data da consulta do ÚLTIMO aviso recebido, ISO (tela: "e-Clinica - Data da Ultima Consulta (ficha)"). **Nunca use na mensagem de LEMBRETE** — ver "Variáveis do lembrete" abaixo |
547
582
  | `eclinica_hora_consulta` | **time** | Hora da consulta 24h `"HH:MM"` (novo 2026-07-06) |
548
583
  | `eclinica_hora_final` | **time** | Hora final da consulta (novo 2026-07-27) |
549
- | `eclinica_link_confirmacao` | **link** | Link ecli.co para o paciente confirmar/cancelar a ÚLTIMA consulta (novo 2026-09-08; só existe quando a unidade aprendeu o padrão na aba Unidades). No LEMBRETE use a variável `{{link_confirmacao}}` do fluxo (ou `{{agendamento.link_confirmacao}}` na automação): é da consulta certa — a ficha aponta para a última, e quem tem duas marcadas receberia o link errado |
550
- | `eclinica_paciente_nome` | text | Nome do PACIENTE como está no cadastro da e-Clínica (novo 2026-09-09). O nome do contato pode ser o do WhatsApp (mãe que agenda o filho pelo celular dela) — por isso NUNCA use `{{contact.name}}` nas mensagens da clínica. Nos fluxos por evento: `{{ contact.custom_attribute.eclinica_paciente_nome \| default: contact.name \| primeiro_nome }}`; no LEMBRETE use a variável `{{ paciente_nome \| default: contact.name \| primeiro_nome }}` (por consulta) ou `{{agendamento.paciente_nome}}` na automação. `primeiro_nome` = primeira palavra com inicial maiúscula. |
584
+ | `eclinica_link_confirmacao` | **link** | Link ecli.co para o paciente confirmar/cancelar a ÚLTIMA consulta (novo 2026-09-08; só existe quando a unidade aprendeu o padrão na aba Unidades). No LEMBRETE use a variável `{{link_confirmacao}}` do fluxo (ou `{{eclinica_lembrete.link_confirmacao}}` na automação): é da consulta certa — a ficha aponta para a última, e quem tem duas marcadas receberia o link errado |
585
+ | `eclinica_paciente_nome` | text | Nome do PACIENTE como está no cadastro da e-Clínica (novo 2026-09-09). O nome do contato pode ser o do WhatsApp (mãe que agenda o filho pelo celular dela) — por isso NUNCA use `{{contact.name}}` nas mensagens da clínica. Nos fluxos por evento: `{{ contact.custom_attribute.eclinica_paciente_nome \| default: contact.name \| primeiro_nome }}`; no LEMBRETE use a variável `{{ paciente_nome \| default: contact.name \| primeiro_nome }}` (por consulta) ou `{{eclinica_lembrete.paciente_nome}}` na automação. `primeiro_nome` = primeira palavra com inicial maiúscula. |
551
586
  | `eclinica_status_agendamento` | text | `agendado` / `aguardando` (paciente chegou) / `no_show` / `atendido` / `desmarcado`. Vem do TIPO do evento, nunca da letra da situação. **Estado terminal do MESMO agendamento não volta pra `agendado`** (2026-08-20): a e-Clínica manda `agendamento_alterado` a cada edição da consulta, inclusive depois da chegada/atendimento, e isso NÃO reabre o status. Consulta com `eclinica_idagenda` DIFERENTE nasce `agendado` |
552
587
  | `eclinica_situacao` | text | Situação do agendamento no painel, **POR EXTENSO** (2026-08-21, legenda oficial da e-Clínica): AGUARDANDO, NA CADEIRA, PASSAR FINANCEIRO, AGENDAR RETORNO, ATENDIDO, CONFIRMADO, CONFIRMADO PELO LINK, CONFIRMADO PELA API, FALTA, DESMARCADO, CANCELADO PELO LINK, CANCELADO PELA API. Antes guardava a letra crua (`A`, `C`…). Código nunca visto aparece como veio |
553
588
  | `eclinica_compromisso` | text | Tipo da consulta — TEXTO LIVRE da recepção (ex: Consulta, Retorno) |
@@ -628,11 +663,32 @@ linhas ANTIGAS (até 17/09); e `fail_reason = fluxo_ainda_em_andamento_com_este_
628
663
  fluxo do aviso anterior ainda estava em andamento com aquele contato (típico de fluxo que aguarda o
629
664
  paciente clicar num botão) — pode ser reenviado com `lionchat_eclinica_reminder_history_reprocess`.
630
665
  O fluxo do lembrete recebe também `{{cliente_id}}` (código do paciente DAQUELA consulta na e-Clínica) e
631
- `{{agendatipo}}`; na automação, `{{agendamento.cliente_id}}` e `{{agendamento.agendatipo}}`. Ao consultar a
666
+ `{{agendatipo}}`; na automação, `{{eclinica_lembrete.cliente_id}}` e `{{eclinica_lembrete.agendatipo}}`. Ao consultar a
632
667
  agenda do paciente dentro do fluxo, use `{{cliente_id}}` do lembrete — a ficha do contato é uma por
633
668
  TELEFONE e guarda só o último paciente. Quem quiser "só um lembrete por dia" monta no próprio fluxo
634
669
  (condição num atributo da conversa com `{{data_consulta}}` + ação que grava o dia depois de enviar).
635
670
 
671
+ **Variáveis do lembrete — o nome certo (desde 2026-09-28).** Cada lembrete guarda a FOTOGRAFIA da consulta
672
+ dele. A FICHA do contato (`eclinica_data_consulta`, `eclinica_hora_consulta`, `eclinica_profissional`...) guarda
673
+ só a consulta do ÚLTIMO aviso que a e-Clínica mandou — quem marca o retorno no mesmo dia em que é atendido tem
674
+ a ficha sobrescrita e o lembrete sai com a data de OUTRA consulta (caso real: Big Odonto, 28/09). Na tela, os 9
675
+ campos de agendamento da ficha aparecem como "e-Clinica - ... Ultima Consulta (ficha)" (a chave não mudou).
676
+ - **Na AUTOMAÇÃO alvo de lembrete** (`reminder_config[].automation_id`), use SEMPRE
677
+ `{{eclinica_lembrete.<campo>}}`: `data_consulta`, `hora_consulta`, `profissional`, `compromisso`,
678
+ `paciente_nome`, `unidade`, `link_confirmacao`, `idagenda`, `cliente_id`, `agendatipo`, `cor`; no lembrete de
679
+ laboratório `data_prevista`, `data_entrega`, `data_referencia`; no de cobrança `vencimento`, `valor`,
680
+ `descricao`. Vale na mensagem, na nota interna, nos parâmetros do modelo de WhatsApp
681
+ (`processed_params.body`) e em título/descrição de tarefa. A data sai **DD/MM/AAAA** onde vira texto para a
682
+ pessoa; gravada em campo (atualizar atributo/campo do card) fica no formato interno `AAAA-MM-DD`. Filtro
683
+ Liquid (`{{ x | f }}`) NÃO funciona nesse nome. O nome antigo `{{agendamento.<campo>}}` continua funcionando
684
+ (automações antigas), mas não ensine mais.
685
+ - **No FLUXO nada muda**: continue com as variáveis soltas `{{data_consulta}}`, `{{hora_consulta}}`,
686
+ `{{link_confirmacao}}`, `{{paciente_nome}}`, `{{cliente_id}}`... Não use `eclinica_lembrete` em fluxo. Nos
687
+ fluxos de lembrete que conferem a consulta na e-Clínica antes de enviar, essas variáveis soltas são
688
+ CORRIGIDAS pelo próprio fluxo quando a consulta foi remarcada.
689
+ - Automação/fluxo disparado por EVENTO da e-Clínica (não lembrete) continua lendo a ficha — ali ela acabou de
690
+ ser gravada pelo próprio evento e está certa.
691
+
636
692
  ## Relatórios e Métricas
637
693
 
638
694
  ```
@@ -86,7 +86,8 @@ Mesmo shape de condição. 15/página.
86
86
  | attribute_key | Operadores |
87
87
  |---|---|
88
88
  | `id` (id interno do contato) | equal_to, not_equal_to |
89
- | `name`, `email`, `identifier`, `city`, `company`, `profession` | equal_to, not_equal_to, contains, does_not_contain |
89
+ | `name`, `email`, `identifier`, `city`, `company_name` (Empresa), `job_title` (Cargo), `profession` | equal_to, not_equal_to, contains, does_not_contain |
90
+ | `company` | **campo ANTIGO — nada grava nele, nunca casa. Use `company_name`** (2026-09-25). Continua aceito só para não quebrar regras antigas |
90
91
  | `phone_number` | + starts_with (busca por DDD/prefixo) — **corrigido na atualização de 26/07/2026**; em instalação anterior devolve SEMPRE lista vazia (virava igualdade exata). Vazio com prefixo que existe = instalação antiga. **`equal_to`/`not_equal_to` casam as DUAS formas do celular BR (com e sem o 9º dígito) desde 05/09/2026** — mande `+5541999887766` ou `+554199887766`, os dois acham a mesma ficha; `not_equal_to` exclui as duas. Termo que não é celular BR completo (fixo, estrangeiro, trecho) segue exato |
91
92
  | `cpf`, `cnpj`, `rg` (cadastral) | equal_to, not_equal_to, contains, does_not_contain, is_present, is_not_present |
92
93
  | `country_code` | equal_to, not_equal_to |
@@ -606,6 +606,8 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
606
606
 
607
607
  **Coerção por TIPO (2026-07-29):** em `attr_source: 'contact'|'conversation'`, o `attr_value` de atributo personalizado é coagido pelo tipo da definição antes de gravar: número/moeda/porcentagem viram numérico ("1.234,56" → 1234.56; inteiro fica inteiro), checkbox aceita sim/não/true/false e grava BOOLEANO, lista casa caixa-insensível e grava a opção canônica, data aceita DD/MM/AAAA ou ISO, hora aceita "14:30"/"14h30". Valor irreconhecível grava como veio e o passo mostra nota âmbar no histórico. Card NÃO coage (atributo de card não tem definição tipada no backend).
608
608
 
609
+ **Empresa e Cargo (2026-09-25):** `contact.company_name` e `contact.job_title` valem em CONDIÇÃO (`attr_source: 'contact'`, `attr_key` igual ao path), em filtro de regras e como variável (`{{contact.company_name}}`, `{{contact.job_title}}`). São SÓ LEITURA: a ação "Alterar atributo do contato" NÃO grava esses dois (o texto Empresa anda junto com a empresa vinculada — para ligar a empresa use `lionchat_contacts_update` com `company_id`).
610
+
609
611
  **Campos NATIVOS do contato (2026-07-29):** com `attr_source: 'contact'`, `attr_key` também aceita os paths canônicos `contact.name`, `contact.email`, `contact.cpf`, `contact.cnpj`, `contact.rg`, `contact.passport`, `contact.date_of_birth`, `contact.gender`, `contact.marital_status`, `contact.profession` e `contact.address.{cep,street,number,complement,neighborhood,city,state,country}` — gravam na ficha nativa do contato (não em custom_attributes). Regras: telefone/identifier/etiquetas NÃO são aceitos (mapa fechado — chave fora dele vira atributo personalizado comum); documentos, nascimento, gênero e **e-mail** só preenchem campo VAZIO; nome/profissão/estado civil/endereço sobrescrevem; valor em branco nunca grava; gênero aceita `m|f|o|na` e estado civil `solteiro|casado|uniao_estavel|divorciado|separado|viuvo`; data de nascimento em `dd/mm/aaaa` ou `aaaa-mm-dd`. Recusas aparecem no histórico de execução (passo 'skipped'/'error' com mensagem).
610
612
 
611
613
  **Campos NATIVOS do contato em CONDIÇÃO (2026-08-13):** o bloco de Condições e o filtro do gatilho de data passaram a LER os mesmos paths nativos (`contact.name`, `contact.cpf`, `contact.marital_status`, `contact.address.city`, ...) na fonte `contact` — antes só liam atributo personalizado e o campo da ficha voltava vazio, jogando tudo na saída padrão em silêncio. Use com `is_empty`/`is_not_empty` para montar "o CPF está preenchido?". Aceita a forma curta (`contact.cpf`) e a longa (`contact.cadastral.cpf`). **`contact.label` NÃO é aceito** em condição (consulta cara em caminho reativo). **Gênero e estado civil são listas fechadas**: compare com o valor canônico (`m|f|o|na`, `solteiro|casado|uniao_estavel|divorciado|separado|viuvo`) — comparar com "Masculino" nunca casa.
@@ -716,3 +716,34 @@ arquivo, aponta as colunas e ainda oferece uma planilha de exemplo pra baixar.
716
716
  Olhe `item_details.status_changed_at` do card: ele é comparado como **texto** contra a janela do
717
717
  relatório, e tem que terminar em `Z`. `2026-08-01T03:00:00Z` é 1º de agosto em Brasília; gravar
718
718
  `2026-08-01T00:00:00Z` joga a venda pra julho.
719
+
720
+ ---
721
+
722
+ ## Lançamento retroativo — marcar ganho/perda com data passada (2026-09-25)
723
+
724
+ Serve para o caso do dia a dia: o negócio fechou semanas atrás e só agora alguém registra. Sem isso, o
725
+ relatório contaria a venda no mês em que o registro foi feito, não no mês em que ela aconteceu.
726
+
727
+ Na tela: ao marcar **Ganho** ou **Perdido** aparece a chavinha **"Lançamento retroativo"**; ligando, abre
728
+ um campo de data. Pela API/MCP é a chave `retroativo_em` dentro de `item_details`, no
729
+ `lionchat_kanban_items_update`:
730
+
731
+ ```json
732
+ { "item_details": { "status": "won", "win_reason": "indicacao", "retroativo_em": "2026-08-05" } }
733
+ ```
734
+
735
+ | Regra | Detalhe |
736
+ |---|---|
737
+ | Quando vale | SÓ junto de um `status` `won`/`lost` que **muda** o status. Card que já está ganho ignora a data. |
738
+ | O que ela faz | Decide o carimbo `item_details.status_changed_at` — o que TODOS os relatórios contam. |
739
+ | O que ela NÃO faz | **Não é gravada no card.** Não muda conversão para Meta/GA4/Google Ads: essas continuam saindo com a data de hoje. |
740
+ | Data futura ou ilegível | Recusada com **422** (a regra vive no servidor, não na tela — API e MCP passam por ela). |
741
+ | Data anterior à criação do card | Permitida. |
742
+ | Só no atualizar | No `lionchat_kanban_items_create` a chave é **descartada**. Para importar venda antiga já fechada, use a Conciliação de vendas (seção acima), que foi feita para isso. |
743
+
744
+ **Corrigir uma data lançada errado:** marcar `won` de novo por cima **não muda nada** (o carimbo só é
745
+ escrito quando o status muda). Mande `status: "open"` — isso apaga o carimbo — e depois `won`/`lost`
746
+ com o `retroativo_em` certo.
747
+
748
+ A hora é montada em **UTC**, com o horário atual no dia escolhido. É o mesmo motivo do diagnóstico
749
+ acima: o relatório compara o carimbo como texto, então a data escolhida tem que ser a data contada.
@@ -111,6 +111,7 @@ conversa antiga que NÃO aparece no relatório de hoje (régua antiga por criaç
111
111
  | `lionchat_reports_list_3` | Resumo POR LABEL |
112
112
  | `lionchat_reports_list_4` | Resumo POR CANAL (channel) |
113
113
  | `lionchat_reports_list_5` | Série temporal / evolução (timeseries de um `metric`) |
114
+ | `lionchat_reports_drilldown` | As CONVERSAS por trás de UM ponto da série temporal (clique na barra do gráfico) |
114
115
  | `lionchat_reports_list_6` | Resumo do BOT (bot_summary) |
115
116
  | `lionchat_reports_list_7` | Exportação de AGENTES em CSV |
116
117
  | `lionchat_reports_list_8` | Exportação de INBOXES em CSV |
@@ -146,6 +147,7 @@ conversa antiga que NÃO aparece no relatório de hoje (régua antiga por criaç
146
147
  | "qual canal tem mais demanda", "comparar WhatsApp vs Email" | `lionchat_reports_list_2` (inbox) ou `lionchat_reports_list_4` (tipo de canal) |
147
148
  | "quantas conversas urgentes", "por etiqueta" | `lionchat_reports_list_3` |
148
149
  | "evolução dia a dia", "mês a mês", "gráfico de linha" | `lionchat_reports_list_5` |
150
+ | "quais conversas deram aquele pico de terça?", "quem são as 40 conversas do dia 12?" | `lionchat_reports_list_5` pra achar o ponto, depois `lionchat_reports_drilldown` |
149
151
  | "como tá o bot resolvendo", "% de handoff pra humano" | `lionchat_reports_list_6` ou `lionchat_reports_list_14` |
150
152
  | "exportar planilha de agentes/inboxes/labels/times" | `_7` / `_8` / `_9` / `_10` (CSV) |
151
153
  | "horário de pico", "quando tem mais demanda" | `lionchat_reports_list_13` |
@@ -233,6 +235,35 @@ Retorna pontos (data + valor) para UM `metric` por vez:
233
235
  - `business_hours`: `true` faz médias contarem só o horário de atendimento
234
236
  - `timezone_offset`: deslocamento de fuso (horas) — afeta como os pontos são agrupados por dia/hora
235
237
 
238
+ ### `lionchat_reports_drilldown` — As conversas por trás de um ponto do gráfico (2026-09-25)
239
+ **Use quando:** "quais conversas são essas 40 do dia 12?", "por que a terça teve pico?", "quem demorou
240
+ naquela semana?"
241
+
242
+ É o mesmo que clicar numa barra do gráfico nos Relatórios do painel. Conta com a **mesma régua** que
243
+ desenhou a barra — então a lista bate com o número do ponto (ex.: por etiqueta conta pela data em que a
244
+ etiqueta foi APLICADA, não pela criação da conversa).
245
+
246
+ Fluxo: chame `lionchat_reports_list_5` → pegue o `timestamp` do ponto → chame esta ferramenta com:
247
+ - `metric`: a MESMA da série. Aceita só `conversations_count`, `incoming_messages_count`,
248
+ `outgoing_messages_count`, `resolutions_count`, `avg_first_response_time`, `avg_resolution_time`,
249
+ `reply_time`. Métrica de bot ou fora da lista = 422 `invalid metric`.
250
+ - `bucket_timestamp`: o `timestamp` do ponto (Unix em segundos).
251
+ - `group_by`, `type`, `id`, `timezone_offset`, `business_hours`: os MESMOS usados na série — é isso
252
+ que define o começo e o fim do ponto. `group_by` diferente = outro recorte e a lista não bate.
253
+ - `since` / `until`: o período do relatório (recorta o primeiro e o último ponto quando o período começa
254
+ ou termina no meio de uma semana/mês).
255
+ - `page`: 25 conversas por página, mais recentes primeiro.
256
+
257
+ Resposta: `{ meta: { metric, since, until, page, per_page, total_count, visible_count, hidden_count },
258
+ payload: [{ id, display_id, status, created_at, contact_name, inbox_name, assignee_name, detail }] }`.
259
+ `detail` explica o porquê de cada conversa: `{count}` (quantas mensagens/eventos dela caíram no ponto) nas
260
+ contagens de mensagens e de resoluções, `{seconds}` nas médias de tempo, ou `null`. `display_id` é o número que aparece na
261
+ tela da conversa.
262
+
263
+ > A lista mostra **só as conversas que a pessoa do token pode abrir**. `hidden_count` diz quantas ficaram
264
+ > de fora por permissão — diga isso ao usuário em vez de concluir que "faltam conversas". Teto de 5.000
265
+ > conversas por ponto. Exige permissão de ver relatórios.
266
+
236
267
  ### `lionchat_reports_list_6` — Resumo do Bot
237
268
  **Use quando:** "como tá o bot resolvendo", "quantos handoffs pra humano"
238
269
 
@@ -574,7 +605,7 @@ Não é consulta livre: escolha um `widget_type` e preencha **só os campos daqu
574
605
  | `calls_report` | `dimension` (agent/inbox) · `scope_type` (só `inbox`, opcional — recorte por caixa, desde 27/08/2026) + `scope_id` · `time_range` — ligações / atendidas / não atendidas / não concluídas / tempo total / tempo médio | — |
575
606
  | `lead_origin` | `time_range` | `liontrack` |
576
607
  | `agent_report` | `dimension` (agent/team/inbox) · `scope_type`+`scope_id` · `columns[]` · `time_range` | — |
577
- | `eclinica_no_show` | `dimension` (unit / operator / date) · `time_range` · `no_show_over_scheduled` (true/false, nasce desligada) — faltas na agenda da e-Clínica: agendados / compareceram / faltaram / desmarcados / sem desfecho / % de falta (o rótulo da coluna declara a base) | `eclinica_integration` |
608
+ | `eclinica_no_show` | `dimension` (unit / operator / date) · `time_range` · `no_show_over_scheduled` (true/false, nasce desligada) · `only_first_visit` (true/false, nasce desligada) — faltas na agenda da e-Clínica: agendados / compareceram / faltaram / desmarcados / sem desfecho / % de falta (o rótulo da coluna declara a base) | `eclinica_integration` |
578
609
  | `eclinica_conversion` | `dimension` (unit / operator) · `time_range` · `count_retorno_as_consulta` (true/false, nasce LIGADA) — consultas que viraram procedimento: consultas / viraram procedimento (até 60 dias) / % / procedimentos / não classificado | `eclinica_integration` |
579
610
 
580
611
  **Os dois blocos da e-Clínica (09/09/2026)** leem os eventos da agenda (`eclinica_webhook_events`) e
@@ -594,9 +625,20 @@ só existem em conta com a integração ligada. Três regras que mudam o número
594
625
  - **`no_show_over_scheduled`** (true/false, nasce desligada — 10/09/2026): ligada, a % de falta passa a
595
626
  ser `faltaram / agendados`, e a unidade que não anota desfecho aparece como a melhor da rede; o rótulo
596
627
  da coluna declara qual base está valendo. Aceita `"true"`/`"false"` em texto; outro valor = 422.
628
+ - **`only_first_visit`** (true/false, nasce desligada — 28/09/2026, só no `eclinica_no_show`): ligada, a
629
+ tabela conta **só a consulta inicial** — retorno, sessão de tratamento e procedimento ficam de fora — e
630
+ o rótulo da primeira coluna troca de "Agendados" para "Consultas iniciais". A regra lê o TEXTO do
631
+ compromisso, porque a clínica escreve o tipo por extenso: começa com `CONSULTA` **e** não contém
632
+ `RETORNO`. Medido na conta 56 (01 a 24/09/2026): "CONSULTA INICIAL" 1.445 vezes e "CONSULTA DE RETORNO"
633
+ 192; Sorocaba tem 1.023 compromissos para 99 consultas iniciais. Use quando o pedido falar em primeira
634
+ consulta, consulta inicial, paciente novo ou primeiro atendimento. Aceita `"true"`/`"false"` em texto;
635
+ outro valor = 422.
597
636
  - no bloco de conversão, **a lista de nomes de procedimento foi abolida** (10/09/2026): começa com
598
637
  `CONSULTA` ou contém `RETORNO` (em qualquer posição) é consulta; **todo o resto que tem paciente é
599
- procedimento**. Antes, 1.961 agendamentos em 30 dias (13,8% da agenda) caíam em "Não classificado" só
638
+ procedimento**. Desde 28/09/2026 o `RETORNO` é testado ANTES do prefixo `CONSULTA`: até ali
639
+ "CONSULTA DE RETORNO" era lido como consulta NOVA (192 agendamentos em 24 dias, 9,8% do que o bloco
640
+ contava como consulta). Com `count_retorno_as_consulta` ligada o número não muda; só muda para quem a
641
+ desligou — e ali é o conserto do que a chavinha sempre prometeu. Antes, 1.961 agendamentos em 30 dias (13,8% da agenda) caíam em "Não classificado" só
600
642
  por escrita diferente do nome ("ONDAS 1/3" x "ONDAS DE CHOQUE").
601
643
  - **`count_retorno_as_consulta`** (true/false, nasce LIGADA — 10/09/2026): ligada, o bloco responde "de
602
644
  toda consulta atendida, quantas viraram tratamento"; desligada, "do paciente NOVO, quantos fecham" — o
@@ -629,7 +671,7 @@ IDs de etiqueta**, nunca nome de métrica).
629
671
  | `series` | `{series: [{timestamp, value}]}` | `timestamp` é unix em segundos. As duas métricas de tempo vêm em **segundos** e trazem também `count` por ponto |
630
672
  | `categories` | `{categories: [{label, value}]}` | `funnel_stages` vem **na ordem das etapas do funil**, nunca por valor — não reordene |
631
673
  | `scalar` | `{value, total, secondary}` | Use o `total` para separar "ninguém respondeu" de "zero legítimo". **SLA sem nenhum prazo aplicado devolve 100.0** — não anuncie "100% de cumprimento" sem olhar o total |
632
- | `table` | `{dimension, columns, rows, total}` | O `total` **não é a soma das linhas** (contagem própria por métrica). As linhas são os membros **atuais**: quem saiu da equipe some da linha e continua no total |
674
+ | `table` | `{dimension, columns, rows, total}` | O `total` **não é a soma das linhas** (contagem própria por métrica) e pode ser MENOR que ela — a mesma conversa atribuída a duas pessoas no período aparece nas duas linhas e conta uma vez no total. **Desde 28/09/2026 quem saiu da conta TEM linha**, marcada com `former: true` (antes sumia e ficava só no total). Cada linha leva `entity_id` (o código do atendente/equipe/caixa) — **use ele para identificar**, nunca o `label`: dois cadastros podem ter o mesmo nome visível. O total ainda pode ser MAIOR que a soma: ele inclui quem não tem linha (acesso de suporte, e quem saiu sem nome guardado) |
633
675
 
634
676
  ### Armadilhas que fazem o bloco sair errado
635
677