@lionchat/mcp-server 1.14.0 → 1.15.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.
@@ -347,6 +347,8 @@ Antes de QUALQUER ação que escreve, modifica, apaga ou envia, pare e avalie se
347
347
 
348
348
  **Como confirmar:** descreva o efeito em linguagem do usuário e aguarde o "sim". Ex.: *"Vou apagar o contato #312 — isso remove as 4 conversas dele também. Confirma?"*. Se faltar dado, peça só o que falta (1-2 perguntas), nunca um questionário.
349
349
 
350
+ **Ferramentas com trava dura de confirmação.** Algumas ferramentas **recusam a chamada** sem `confirm: true` — a recusa vem do conector, antes de tocar no sistema. Não é erro: é o pedido de OK. O caminho é sempre o mesmo — descreva ao usuário exatamente o que será criado/alterado/apagado, aguarde o "sim", e só então reenvie a MESMA chamada com `confirm: true`. Nunca mande `confirm: true` na primeira tentativa "pra economizar uma volta": a trava existe justamente porque a descrição sozinha não segurava. Hoje: `lionchat_custom_dashboards_create`, `_update` e `_destroy` (relatório personalizado aparece na tela do cliente; alterar substitui a lista de blocos e apagar não tem desfazer).
351
+
350
352
  **Aja sem perguntar apenas em operações idempotentes:** leitura, busca, resumo, classificação.
351
353
 
352
354
  **Excluir agente/usuário (`lionchat_agents_destroy`) — SEMPRE pergunte o destino primeiro.** Se o agente tem recursos vinculados (conversas, tarefas — inclusive as que ele **criou** —, cards do Kanban, campanhas, agendamentos), o sistema EXIGE um destino de reatribuição. Antes de excluir: consulte `lionchat_agents_assigned_resources` (o retorno inclui `tasks_created`, tarefas criadas por ele); se houver QUALQUER recurso, **pergunte pra qual agente transferir tudo** e passe `reassign_to_agent_id` no destroy. Nunca tente excluir sem o destino — o sistema recusa e o trabalho do agente ficaria órfão. Ao excluir, tudo passa pro agente escolhido; o excluído fica só no histórico.
@@ -523,6 +523,17 @@ ficar muda (2026-06-05).
523
523
  | `message_created` | Nova mensagem (incoming ou outgoing) |
524
524
  | `first_reply_created` | Primeira resposta humana |
525
525
 
526
+ ### Gatilhos de Agendamento (Booking nativo) — 2026-08-20
527
+
528
+ O Booking do LionChat (Agenda → aba Booking, link público, IA) também dispara flow pelo node
529
+ `start`: `booking_created`, `booking_cancelled`, `booking_rescheduled`, `booking_completed`.
530
+ Config opcional `{ booking_event_type_ids: ["44"], agent_ids: [], create_conversation: false }`
531
+ (vazio = todos; `create_conversation: true` cria a conversa na 1ª caixa do flow para quem nunca
532
+ conversou). Variáveis `{{booking.*}}` (data, hora, tipo, agente, links de remarcar/cancelar,
533
+ horário anterior no remarcado). Detalhe completo e regras no `lionchat://docs/flowbuilder-design-guide`
534
+ (seção "Gatilhos de AGENDAMENTO"). **Não confundir com os lembretes da e-Clínica** (integração
535
+ separada). Também NÃO são eventos de `automation_rule`.
536
+
526
537
  **Os gatilhos de Formulário público (`lead_form_completed`, `lead_form_milestone`,
527
538
  `lead_form_abandoned`) NÃO entram nessa tabela** — são gatilhos de **flow**, não eventos de
528
539
  automação. Não adianta criar `automation_rule` com esses nomes em `event_name`; o caminho é o node
@@ -400,13 +400,19 @@ Booking (agendamento confirmado)
400
400
 
401
401
  ## e-Clínicas (Efficient) — integração de clínicas
402
402
 
403
- 13 eventos de webhook, cada um mapeável a automação OU flow na tela Integrações > e-Clínica:
403
+ 15 eventos de webhook, cada um mapeável a automação OU flow na tela Integrações > e-Clínica
404
+ (o mapeamento é SÓ pela tela — não há ferramenta MCP que escreva `event_mapping`):
404
405
  `cliente_novo`, `agendamento_novo`, `falta`, `pagamento` (os 4 da doc oficial) +
405
406
  `agendamento_atendido`, `agendamento_alterado`, `agendamento_desmarcado`,
406
407
  `agendamento_transferido`, `cliente_baixa_pagamento`, `cliente_alteracao`,
407
- `cliente_inclusao_pagamento`, `controle_laboratorio_novo`, `controle_laboratorio_alterado`
408
+ `cliente_inclusao_pagamento`, `controle_laboratorio_novo`, `controle_laboratorio_alterado`,
409
+ `agendamento_aguardando`, `odontograma_aprovado`
408
410
  (capturados ao vivo, não documentados pelo e-Clínica).
409
411
 
412
+ Os 2 últimos são do perfil ODONTOLÓGICO (2026-08-20): `agendamento_aguardando` = a recepção marcou a
413
+ chegada do paciente na clínica (grava a hora da chegada e `eclinica_status_agendamento = aguardando`);
414
+ `odontograma_aprovado` = plano de tratamento aprovado (grava o cabeçalho do odontograma).
415
+
410
416
  Atributos de sistema no CONTATO (prefixo `eclinica_`, protegidos, usáveis como variável):
411
417
 
412
418
  | Chave | Tipo | Conteúdo |
@@ -417,8 +423,8 @@ Atributos de sistema no CONTATO (prefixo `eclinica_`, protegidos, usáveis como
417
423
  | `eclinica_data_consulta` | date | Data da consulta, ISO |
418
424
  | `eclinica_hora_consulta` | **time** | Hora da consulta 24h `"HH:MM"` (novo 2026-07-06) |
419
425
  | `eclinica_hora_final` | **time** | Hora final da consulta (novo 2026-07-27) |
420
- | `eclinica_status_agendamento` | text | agendado / no_show / atendido / desmarcado |
421
- | `eclinica_situacao` | text | Situação crua da agenda (age_situacao do webhook) |
426
+ | `eclinica_status_agendamento` | text | agendado / aguardando (paciente chegou) / no_show / atendido / desmarcado. **`aguardando` é transitório**: o próximo evento de agenda daquele paciente grava por cima (o último evento recebido é sempre o que vale) |
427
+ | `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 |
422
428
  | `eclinica_compromisso` | text | Tipo da consulta — TEXTO LIVRE da recepção (ex: Consulta, Retorno) |
423
429
  | `eclinica_agendatipo` | text | Tipo de Agendamento — NOME da lista fixa do painel, resolvido por unidade (novo 2026-07-28). **Filtrar sempre pelo NOME, nunca pelo número: os números colidem entre filiais** |
424
430
  | `eclinica_agendatipo_id` | text | Tipo de Agendamento — número cru (não comparável entre filiais) |
@@ -430,6 +436,17 @@ Atributos de sistema no CONTATO (prefixo `eclinica_`, protegidos, usáveis como
430
436
  | `eclinica_ultimo_pagamento` | text | Valor do último pagamento |
431
437
  | `eclinica_ultimo_pagamento_data` | date | Data do último pagamento/baixa (novo 2026-07-06) |
432
438
  | `eclinica_ultimo_pagamento_descricao` | text | Descrição da baixa (ex: CONSULTA INICIAL) |
439
+ | `eclinica_ultima_chegada_idagenda` | text | Agendamento em que o paciente chegou pela última vez (2026-08-20) |
440
+ | `eclinica_ultima_chegada_data` | date | Data da última chegada do paciente na clínica |
441
+ | `eclinica_ultima_chegada_hora` | **time** | Hora em que a recepção marcou a chegada (`age_horaguardando`, ex `15:06`) |
442
+ | `eclinica_odontograma_id` | text | ID do último odontograma/plano de tratamento aprovado (2026-08-20) |
443
+ | `eclinica_odontograma_aprovado_em` | date | Dia da aprovação, já no fuso da conta (o webhook manda em UTC) |
444
+ | `eclinica_odontograma_inicio` | date | Data de início do tratamento (`odo_datainicio`) |
445
+ | `eclinica_odontograma_situacao` | text | Código de situação como vem da e-Clínica (ex: `T`) |
446
+ | `eclinica_odontograma_valor` | text | Valor do plano — **pode vir `0.00`** quando a clínica não preenche |
447
+ | `eclinica_odontograma_profissional` | text | NOME do profissional do odontograma |
448
+ | `eclinica_odontograma_convenio_id` | text | Convênio do odontograma (número) |
449
+ | `eclinica_marcador_nome` | text | Marca que a clínica digita no fim do nome do paciente no e-Clínica (ex: `*`, `***`) — retirada do nome e guardada aqui (2026-08-20). Significado é da clínica |
433
450
  | `eclinica_laboratorio_data_prevista` | date | Previsão de entrega da medicação (novo 2026-07-23) |
434
451
  | `eclinica_laboratorio_data_moldagem` | date | Data do pedido da medicação |
435
452
  | `eclinica_laboratorio_data_entrega` | date | Data em que a medicação CHEGOU na unidade — é a data que dispara o aviso de retirada do flow |
@@ -245,7 +245,50 @@ payload=[
245
245
  **R6 — Cards parados (sem tarefa) com atributo do contato:**
246
246
  `kanban_items_filter funnel_id=37 task_filter=no_task contact_custom_attribute_filters="[{\"attribute_key\":\"ja_investe\",\"filter_operator\":\"equal_to\",\"values\":[\"ja_invisto\"]}]"`
247
247
 
248
+ **R7 — Pergunta de relatório que NENHUMA ferramenta responde pronta** (relatório personalizado
249
+ montado na hora, sem salvar nada). Ex.: *"quantos cards foram ganhos em cada etapa do funil 37 nos
250
+ últimos 30 dias?"*
251
+
252
+ ```json
253
+ custom_dashboards_preview_widget
254
+ widget_type=funnel_stages chart_type=bar funnel_id=37
255
+ date_basis=closed status=won time_range=last_30_days timezone_offset=-3
256
+ ```
257
+ Resposta: `{shape:"categories", categories:[{label:"Negociação", value:12}, ...]}` — **na ordem das
258
+ etapas do funil**, não por valor. Trocar `date_basis` muda a pergunta: `created` = "entraram no
259
+ funil", `moved` = "entraram na etapa", `closed` = "foram fechados".
260
+
261
+ **R8 — Tabela de produtividade por operador, com etiqueta e taxa de conversão:**
262
+
263
+ ```json
264
+ custom_dashboards_preview_widget
265
+ widget_type=agent_report chart_type=table dimension=agent
266
+ time_range=custom from=2026-08-01 to=2026-08-15 timezone_offset=-3
267
+ columns=[
268
+ {"metric":"leads"},
269
+ {"metric":"atendidos"},
270
+ {"metric":"label_count","label_id":5,"as":"Agendados"},
271
+ {"metric":"conversao","numerator":[5],"denominator":"leads","as":"% agendamento"}
272
+ ]
273
+ ```
274
+ > ⚠️ `numerator` é **lista de IDs de etiqueta** (`[5]`), nunca nome de métrica. Mandar
275
+ > `["label_count"]` faz a coluna de conversão **sumir em silêncio**.
276
+ > ⚠️ `time_range=custom` aceita no máximo **92 dias**, e só existe em `agent_report` e
277
+ > `conversations_timeseries`. Pedir "esse ano" volta 422 com a explicação.
278
+ > ⚠️ O `total` da tabela **não é a soma das linhas** — cada métrica tem contagem própria.
279
+
280
+ **R9 — Salvar isso como relatório na tela do cliente:** teste com o R8, mostre o número, confirme
281
+ com o usuário e só então:
282
+ ```json
283
+ custom_dashboards_create confirm=true
284
+ name="Produtividade da equipe"
285
+ widgets=[{"id":"prod-1", ...mesmo desenho do R8...}]
286
+ ```
287
+ Para **alterar** depois: leia com `custom_dashboards_list`, mexa no array e devolva-o **completo** —
288
+ `widgets` substitui a lista inteira, e preservar o `id` de cada bloco é o que mantém o vínculo.
289
+
248
290
  **Erros clássicos a evitar:** `includes` em labels (não existe — use equal_to); operador de
249
291
  número/data em texto; array-de-objetos em query (padrão B exige JSON-string); esquecer
250
292
  `custom_attribute_type` quando contato e conversa têm atributo de mesmo nome; esperar paginação
251
- no kanban_items_filter (não tem — estreite o filtro).
293
+ no kanban_items_filter (não tem — estreite o filtro); montar um `custom_dashboards_update` em cima
294
+ de uma listagem que veio cortada (apaga os blocos que não chegaram).
@@ -23,7 +23,7 @@ Como criar fluxos do LionChat (FlowBuilder) que abrem certinhos no canvas, sem n
23
23
  | Classificar intenção da mensagem | `ai` mode `intent` | - |
24
24
  | Analisar sentimento | `ai` mode `sentiment` | - |
25
25
  | Encerrar fluxo no meio | `action` com `key: 'deactivate_flow'` OU `exit_conditions` no nível do flow | - |
26
- | Distribuir aleatório entre branches | `randomizer` | - |
26
+ | Dividir na proporção entre branches (A/B) | `randomizer` | - |
27
27
  | Gerir grupo WhatsApp: criar/buscar, nome/foto/descrição, participantes, admins, convite (WAHA) | `update_group` (uma operação por bloco via `groupOperation`) | - |
28
28
  | Iniciar outro fluxo | `action` com `key: 'start_flow'` OU node `activate_flow` | - |
29
29
  | Encerrar ramo / definir retorno de ai_tool | `end` | - |
@@ -53,14 +53,18 @@ Todo node tem essa estrutura base:
53
53
  "position": { "x": 50, "y": 300 },
54
54
  "data": {
55
55
  "label": "Início",
56
- "triggers": [
57
- { "type": "message_received", "keywords": ["oi", "ola"], "match_type": "contains" }
56
+ "items": [
57
+ { "key": "message_received", "config": { "keywords": ["oi", "ola"], "match_type": "contains" } }
58
58
  ]
59
59
  }
60
60
  }
61
61
  ```
62
62
 
63
- **Triggers válidos:** `message_received`, `message_sent`, `conversation_created`, `conversation_resolved`, `conversation_reopened`, `label_added`, `label_removed`, `card_created`, `card_moved`, `conversation_attribute_changed`, `card_attribute_changed`, `contact_attribute_changed`, `date_trigger`, `campaign_trigger`, `cron`, `webhook`.
63
+ **ATENÇÃO — chave `items`, não `triggers` (corrigido 19/08/2026).** Os gatilhos vão em **`data.items`**, cada item no formato **`{ "key": "...", "config": { ...filtros... } }`**. O formato antigo (`data.triggers` + `type` + filtros soltos no topo) ainda é aceito pelo motor por compatibilidade, mas **NÃO deve ser usado em fluxo novo**: o editor do FlowBuilder lê apenas `data.items`, então um fluxo gravado no formato antigo **dispara normalmente mas abre com o bloco Início EM BRANCO no painel do cliente** — e um clique em "Concluir" naquele modal apagava o gatilho em silêncio. Foram 53 fluxos de 9 contas nesse estado até o conserto de 19/08. Alguns leitores do backend (`has_webhook_trigger?` do disparo por webhook, o filtro de Campanha de Fluxo, o gatilho LionTrack e o histórico de execução) também só entendem `items`.
64
+
65
+ **Triggers válidos:** `message_received`, `message_sent`, `conversation_created`, `conversation_resolved`, `conversation_reopened`, `team_changed`, `assignee_changed`, `label_added`, `label_removed`, `sla_missed`, `card_created`, `card_moved`, `card_won`, `card_lost`, `conversation_attribute_changed`, `card_attribute_changed`, `contact_attribute_changed`, `date_trigger`, `campaign_trigger`, `manual_trigger`, `webhook_received`, `page_track`, `group_participant_joined`, `group_participant_left`, `lead_form_completed`, `lead_form_milestone`, `lead_form_abandoned`, `booking_created`, `booking_cancelled`, `booking_rescheduled`, `booking_completed`.
66
+
67
+ **NÃO existem** os gatilhos `webhook` (o correto é **`webhook_received`**) nem `cron` (para disparo por data use `date_trigger`; para disparo em lote use `campaign_trigger` + Campanha de Fluxo). Até 19/08/2026 este guia listava os dois por engano — o fluxo 180 da conta 54 ficou ATIVO com `webhook` e **nunca disparou uma vez em 26 dias**, sem erro nenhum.
64
68
 
65
69
  **Trigger `message_sent` (novo 2026-06-11):** par do message_received, mas pra mensagens de SAÍDA — dispara quando atendente, celular (eco do WhatsApp) ou a própria IA/flow envia mensagem (nota privada NÃO dispara). Config: `keywords` (opcional) + `match_type` (`contains`|`exact`). Caso de uso clássico: "quando eu responder do celular, desligar a IA". ATENÇÃO: mensagem da IA também dispara — se a ação for desativar a IA, use keywords que só humanos digitam ou aceite que a primeira resposta da IA aciona o flow. Protegido por anti-loop (profundidade 5) e sessão única por conversa+flow; nunca alimenta `waiting_input`.
66
70
 
@@ -74,7 +78,7 @@ Todo node tem essa estrutura base:
74
78
 
75
79
  - `date_trigger` — **Gatilho de Data (novo 2026-07-10):** dispara quando uma DATA guardada na ficha do CONTATO chega (aniversário, data de exame, vencimento de plano). Modelo "agendamento de mensagem": a escrita da data já marca o disparo — NÃO existe varredura periódica. Só em flow `conversation` **individual** (a data é de um contato; grupo não tem). Config (item usa `config` ANINHADO, igual `webhook_received`/`attribute_changed`):
76
80
  ```json
77
- { "type": "date_trigger", "config": {
81
+ { "key": "date_trigger", "config": {
78
82
  "attr_key": "_date_of_birth", // "_date_of_birth" = Aniversário nativo; OU a chave de um atributo do contato do tipo Data (ex.: "data_exame")
79
83
  "offset_direction": "on", // "before" | "on" | "after"
80
84
  "offset_days": 0, // 0-365 (relevante só p/ before/after)
@@ -127,17 +131,19 @@ Receita completa: `flows_create`/`flows_update` com o item no start → `flows_l
127
131
  **Trigger `webhook` — Webhook Universal EMBUTIDO (novo 2026-06):** o flow pode ser disparado por um webhook próprio, criado automaticamente. Receita via API:
128
132
  1. Criar o flow normalmente (`flows_create`).
129
133
  2. `POST /custom_webhook_integrations` com `{ "custom_webhook_integration": { "flow_id": <id do flow> } }` — o sistema cria a integração embutida (idempotente: repetir retorna a mesma; nome automático "Flow: <nome>"; auto-mapeia todos os eventos → este flow) e retorna a URL única do webhook.
130
- 3. No node `start`, adicionar item `{ "type": "webhook_received", "config": { "integration_id": <id da integração> } }`.
134
+ 3. No node `start`, adicionar em `data.items` o item `{ "key": "webhook_received", "config": { "integration_id": <id da integração> } }`.
131
135
  4. Salvar o flow (`flows_update`) — o save sincroniza a ativação do webhook embutido (remover o item desativa a integração automaticamente).
132
136
  Webhooks embutidos NÃO aparecem na listagem de integrações standalone; excluir o flow destrói o webhook; duplicar o flow NÃO copia o gatilho embutido. Rate limit do endpoint público: 60/min por token.
133
137
 
138
+ **Gatilhos de AGENDAMENTO — Booking NATIVO do LionChat (2026-08-20):** `booking_created` (alguém marcou pelo link público, pela IA ou pelo painel), `booking_cancelled` (paciente cancelou pelo link, IA cancelou ou a equipe cancelou na Agenda), `booking_rescheduled` (a data/hora mudou — link do paciente, IA, editar/adiar na Agenda), `booking_completed` (equipe concluiu na Agenda; exige a Agenda unificada ligada na conta). **Não é e-Clínica.** Config (tudo opcional, vazio = todos): `booking_event_type_ids` (array de ids de tipo de agendamento, **STRING**), `agent_ids` (array de ids de usuário — o RESPONSÁVEL da tarefa), `create_conversation` (boolean, padrão `false`: só dispara para quem já tem conversa numa caixa do flow; `true` = cria a conversa na 1ª caixa do flow). Exemplo: `{ "key": "booking_created", "config": { "booking_event_type_ids": ["44"], "agent_ids": [], "create_conversation": true } }`. São INERTES a evento de conversa (quem dispara é o `Booking::FlowTriggerDispatcher`, por CONTA — caixa do flow só define onde a conversa é criada/reusada). Variáveis no flow: `{{booking.date}}` (DD/MM/AAAA), `{{booking.time}}`, `{{booking.weekday}}`, `{{booking.datetime}}` (ISO), `{{booking.type}}`, `{{booking.type_format}}` (presencial/videochamada), `{{booking.duration}}`, `{{booking.agent}}`, `{{booking.status}}` (confirmado/cancelado/remarcado/concluído), `{{booking.description}}`, `{{booking.cancel_url}}`, `{{booking.reschedule_url}}`, `{{booking.color}}`, `{{booking.id}}`; só no remarcado: `{{booking.previous_date}}`, `{{booking.previous_time}}`, `{{booking.previous_datetime}}`; `{{booking.meeting_url}}` NÃO vem no criado (nasce vazio). Data/hora vêm da TAREFA da Agenda (sempre atuais). Regras: o evento mais novo SUBSTITUI a rodada ativa do mesmo flow na mesma conversa (2ª consulta do mesmo paciente, ou cancelado chegando com o criado ainda esperando resposta); excluir a tarefa, reabrir cancelada ou desfazer conclusão NÃO disparam; adiar (snooze) conta como remarcado; tipo com confirmação/lembrete próprios ligados + `booking_created` = o paciente pode receber duas mensagens (a tela avisa). Colisão entre flows é por CONTA: mesmo kind + tipos se cruzando + agentes se cruzando (vazio = todos).
139
+
134
140
  **TRAVA DE GATILHO DUPLICADO (2026-06-16) — leia ANTES de ativar/criar flow ativo:** o sistema BLOQUEIA ter dois flows ATIVOS com o MESMO gatilho na MESMA inbox e mesmo `conversation_mode` (evita o evento disparar dois flows). Colisão = mesmo tipo de gatilho + config cruzando (mesmas keywords/funil/labels/url/etc) + inbox compartilhada + mesmo modo. EXCEÇÕES que podem coexistir: `webhook_received`, `manual_trigger` e `campaign_trigger` (este último porque não dispara por evento — é só uma autorização para a campanha).
135
141
  - Ao **ativar** (`flows_toggle` inativo→ativo) ou **criar já ativo**: qualquer conflito é barrado.
136
142
  - Ao **editar** um flow já ativo: só conflito NOVO é barrado (duplicados que já existiam são preservados).
137
143
  - A API responde **422** com `{ "error_code": "flow_trigger_conflict", "conflicts": [{ flow_id, flow_name, trigger_type, inbox_id, inbox_name }] }`.
138
144
  - Existe `POST /flows/check_conflicts` (mesma assinatura, NÃO salva) pra checar antes.
139
145
 
140
- **Como a IA deve agir:** antes de ativar um flow, confira via `flows_list` se já não há outro flow ativo no mesmo gatilho+inbox. Se receber 422 `flow_trigger_conflict`, NÃO fique reativando — EXPLIQUE o conflito ao usuário (nome do flow conflitante + caixa) e ofereça desativar o outro flow ou ajustar o gatilho/keywords.
146
+ **Como a IA deve agir:** antes de ativar um flow, confira via `flows_check_conflicts` (mesma assinatura do toggle/update, NÃO salva nada e devolve exatamente quem colide) se já não há outro flow ativo no mesmo gatilho+inbox — NÃO tente deduzir isso lendo o `flow_data` de cada item da `flows_list` (a lista é pra escolher/filtrar fluxos, não pra inspecionar blocos). Se receber 422 `flow_trigger_conflict`, NÃO fique reativando — EXPLIQUE o conflito ao usuário (nome do flow conflitante + caixa) e ofereça desativar o outro flow ou ajustar o gatilho/keywords.
141
147
 
142
148
  **Handles que SAEM:** `success`.
143
149
 
@@ -700,7 +706,9 @@ Corrigido em 30/07: o motor lê `percent` e aceita `weight` só como dado antigo
700
706
  `percent`** — randomizador criado com `weight` até roda, mas abre com as porcentagens EM BRANCO na
701
707
  tela do cliente (a tela lê só `percent`).
702
708
 
703
- **Modo `distribute_agents` (sorteio PONDERADO):** `data.mode: 'distribute_agents'` + `data.agents: [{ agent_id, percent }]` — sorteia UM agente por probabilidade (percentuais somando 100) e atribui automaticamente. Isso é SORTEIO, não rodízio: no curto prazo pode cair no mesmo agente várias vezes seguidas. Pra RODÍZIO EXATO (cada um na vez), use a AÇÃO `distribute_agents` no node `action` (ver seção 2.5) — não este modo.
709
+ **Divisão EXATA, não sorteio (desde 20/08/2026):** nos DOIS modos o bloco divide o que passa por ele na proporção configurada — 50/50 alterna A,B,A,B; 70/30 dá 7 de cada 10 intercalados. Contador por fluxo+bloco; mexer em agente/porcentagem zera a contagem. (Até 20/08 era cara-ou-coroa — 5 leads seguidos pra mesma pessoa num "50/50" era normal.)
710
+
711
+ **Modo `distribute_agents` (divisão PONDERADA):** `data.mode: 'distribute_agents'` + `data.agents: [{ agent_id, percent }]` — escolhe o agente da vez (percentuais somando 100) e atribui automaticamente. Diferença pra AÇÃO `distribute_agents` do node `action` (seção 2.5): a ação é rodízio SIMPLES (1,2,3 sem porcentagem); este modo aceita pesos diferentes (70/30). Com pesos iguais os dois dão o mesmo resultado.
704
712
 
705
713
  **Handles que SAEM:** o `id` de cada branch (`A`, `B`, ...). Em `mode: 'distribute_agents'` é `success`.
706
714
 
@@ -881,7 +889,7 @@ Sempre presente:
881
889
 
882
890
  | Variável | Conteúdo |
883
891
  |---|---|
884
- | `{{trigger.type}}` | chave do evento (`message_created`, `conversation_created`, `label_added`, `kanban_item_stage_changed`, `webhook_received`, `date_trigger`, `flow_campaign`, `manual`, `pagetrack_visited`…) |
892
+ | `{{trigger.type}}` | chave do evento (`message_created`, `conversation_created`, `label_added`, `kanban_item_stage_changed`, `webhook_received`, `date_trigger`, `flow_campaign`, `manual`, `pagetrack_visited`, `booking_created`, `booking_cancelled`, `booking_rescheduled`, `booking_completed`…) |
885
893
  | `{{trigger.name}}` | rótulo legível do MESMO evento ("Mensagem recebida", "Card mudou de etapa") — use este pra ESCREVER pro cliente, e o `type` pra COMPARAR em condição |
886
894
  | `{{trigger.activated_at}}` | quando disparou (ISO 8601) |
887
895
  | `{{trigger.kanban_item_id}}` / `{{trigger.funnel_id}}` | só quando veio do Kanban |
@@ -11,7 +11,7 @@
11
11
  ```json
12
12
  {
13
13
  "nodes": [
14
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "conversation_created" }] } },
14
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "conversation_created", "config": {} }] } },
15
15
  { "id": "n2", "type": "send_message", "position": { "x": 370, "y": 300 }, "data": { "label": "Saudação", "messageItems": [
16
16
  { "id": "m1", "type": "text", "content": "Oi {{contact.name|default:'tudo bem?'}} 👋 Como posso ajudar hoje?\n\n1️⃣ Vendas\n2️⃣ Suporte\n3️⃣ Financeiro" }
17
17
  ] } },
@@ -55,7 +55,7 @@
55
55
  ```json
56
56
  {
57
57
  "nodes": [
58
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "conversation_created" }] } },
58
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "conversation_created", "config": {} }] } },
59
59
  { "id": "n2", "type": "send_message", "position": { "x": 370, "y": 300 }, "data": { "label": "Pede nome", "messageItems": [
60
60
  { "id": "m1", "type": "text", "content": "Olá! Antes de continuar, qual seu nome completo?" }
61
61
  ] } },
@@ -99,7 +99,7 @@
99
99
  ```json
100
100
  {
101
101
  "nodes": [
102
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "message_received", "keywords": ["proposta","orçamento"], "match_type": "contains" }] } },
102
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "message_received", "config": { "keywords": ["proposta","orçamento"], "match_type": "contains" } }] } },
103
103
  { "id": "n2", "type": "send_message", "position": { "x": 370, "y": 300 }, "data": { "label": "B - Budget", "messageItems": [{ "id": "m1", "type": "text", "content": "Pra te ajudar melhor, qual a faixa de investimento que vocês têm em mente?\n\n1️⃣ Até R$ 5k\n2️⃣ R$ 5k a R$ 20k\n3️⃣ Acima de R$ 20k" }] } },
104
104
  { "id": "n3", "type": "wait_response", "position": { "x": 690, "y": 300 }, "data": { "label": "Budget", "waitTime": 60, "waitUnit": "minutes", "validation": "options", "acceptedOptions": ["1","2","3"], "saveTo": "contact_attr", "saveAttrKey": "budget_tier" } },
105
105
  { "id": "n4", "type": "send_message", "position": { "x": 1010, "y": 300 }, "data": { "label": "A - Authority", "messageItems": [{ "id": "m2", "type": "text", "content": "Você é o decisor ou tem outras pessoas envolvidas?" }] } },
@@ -150,7 +150,7 @@
150
150
  ```json
151
151
  {
152
152
  "nodes": [
153
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "conversation_resolved" }] } },
153
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "conversation_resolved", "config": {} }] } },
154
154
  { "id": "n2", "type": "wait", "position": { "x": 370, "y": 300 }, "data": { "label": "Aguarda 5 min", "waitTime": 5, "waitUnit": "minutes" } },
155
155
  { "id": "n3", "type": "send_message", "position": { "x": 690, "y": 300 }, "data": { "label": "Pede nota", "messageItems": [{ "id": "m1", "type": "text", "content": "Antes de fechar: de 1 a 5, como foi seu atendimento?\n\n5 = excelente\n1 = ruim" }] } },
156
156
  { "id": "n4", "type": "wait_response", "position": { "x": 1010, "y": 300 }, "data": { "label": "Nota", "waitTime": 60, "waitUnit": "minutes", "validation": "options", "acceptedOptions": ["1","2","3","4","5"], "saveTo": "conversation_attr", "saveAttrKey": "csat_score" } },
@@ -182,7 +182,7 @@
182
182
  ```json
183
183
  {
184
184
  "nodes": [
185
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "conversation_created" }] } },
185
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "conversation_created", "config": {} }] } },
186
186
  { "id": "n2", "type": "condition", "position": { "x": 370, "y": 300 }, "data": { "label": "Horário comercial?", "conditions": [
187
187
  { "id": "c1", "label": "Dentro do horário", "field": "{{now.hour}}", "operator": "greater_than", "value": "8", "valueType": "variable" }
188
188
  ] } },
@@ -210,7 +210,7 @@
210
210
  ```json
211
211
  {
212
212
  "nodes": [
213
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "message_received" }] } },
213
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "message_received", "config": {} }] } },
214
214
  { "id": "n2", "type": "ai", "position": { "x": 370, "y": 300 }, "data": {
215
215
  "label": "Classifica", "aiMode": "intent",
216
216
  "aiPrompt": "Classifique a intenção da última mensagem do cliente",
@@ -242,7 +242,7 @@
242
242
  ```json
243
243
  {
244
244
  "nodes": [
245
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "label_added", "label_names": ["follow-up"] }] } },
245
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "label_added", "config": { "label_names": ["follow-up"] } }] } },
246
246
  { "id": "n2", "type": "wait", "position": { "x": 370, "y": 300 }, "data": { "label": "Espera 7 dias", "waitTime": 7, "waitUnit": "days" } },
247
247
  { "id": "n3", "type": "send_message", "position": { "x": 690, "y": 300 }, "data": { "label": "Template re-engajamento", "messageItems": [
248
248
  { "id": "m1", "type": "whatsapp_template", "templateId": 42, "params": ["{{contact.name}}"] }
@@ -269,7 +269,7 @@
269
269
  ```json
270
270
  {
271
271
  "nodes": [
272
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "message_received" }] } },
272
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "message_received", "config": {} }] } },
273
273
  { "id": "n2", "type": "condition", "position": { "x": 370, "y": 300 }, "data": { "label": "Pede humano?", "conditions": [
274
274
  { "id": "c1", "label": "Sim", "field": "{{last_response}}", "operator": "contains", "value": "humano", "valueType": "variable" }
275
275
  ] } },
@@ -299,7 +299,7 @@
299
299
  ```json
300
300
  {
301
301
  "nodes": [
302
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "message_received", "keywords": ["agendar","reunião"], "match_type": "contains" }] } },
302
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "message_received", "config": { "keywords": ["agendar","reunião"], "match_type": "contains" } }] } },
303
303
  { "id": "n2", "type": "send_message", "position": { "x": 370, "y": 300 }, "data": { "label": "Pede data", "messageItems": [{ "id": "m1", "type": "text", "content": "Em que data você prefere? (formato DD/MM)" }] } },
304
304
  { "id": "n3", "type": "wait_response", "position": { "x": 690, "y": 300 }, "data": { "label": "Data", "waitTime": 30, "waitUnit": "minutes", "validation": "regex", "regexPattern": "^\\d{2}/\\d{2}$", "invalidMessage": "Formato inválido. Use DD/MM (ex: 25/05)", "maxRetries": 2, "saveTo": "variable", "saveVariable": "data_pref" } },
305
305
  { "id": "n4", "type": "api", "position": { "x": 1010, "y": 300 }, "data": {
@@ -333,7 +333,7 @@
333
333
  ```json
334
334
  {
335
335
  "nodes": [
336
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "label_added", "label_names": ["urgente"] }] } },
336
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "label_added", "config": { "label_names": ["urgente"] } }] } },
337
337
  { "id": "n2", "type": "action", "position": { "x": 370, "y": 300 }, "data": { "label": "Dispara webhook", "items": [
338
338
  { "key": "send_webhook", "config": {
339
339
  "url": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
@@ -364,7 +364,7 @@
364
364
  {
365
365
  "nodes": [
366
366
  { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [
367
- { "type": "webhook_received", "config": { "integration_id": 123 } }
367
+ { "key": "webhook_received", "config": { "integration_id": 123 } }
368
368
  ] } },
369
369
  { "id": "n2", "type": "action", "position": { "x": 370, "y": 300 }, "data": { "label": "Marca origem", "items": [
370
370
  { "key": "add_conversation_label", "config": { "labels": ["compra-confirmada"] } }
@@ -417,7 +417,7 @@
417
417
  ```json
418
418
  {
419
419
  "nodes": [
420
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "message_received", "keywords": ["orçamento"], "match_type": "contains" }] } },
420
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "message_received", "config": { "keywords": ["orçamento"], "match_type": "contains" } }] } },
421
421
  { "id": "n2", "type": "action", "position": { "x": 370, "y": 300 }, "data": { "label": "Marca", "items": [
422
422
  { "key": "add_conversation_label", "config": { "labels": ["pediu-orcamento"] } },
423
423
  { "key": "add_label", "config": { "labels": ["interessado-em-comprar"] } }
@@ -440,7 +440,7 @@
440
440
  ```json
441
441
  {
442
442
  "nodes": [
443
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "conversation_created" }] } },
443
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "conversation_created", "config": {} }] } },
444
444
  { "id": "n2", "type": "send_message", "position": { "x": 370, "y": 300 }, "data": { "label": "Oferece", "messageItems": [
445
445
  { "id": "m1", "type": "text", "content": "Quer receber nossas novidades por e-mail?" }
446
446
  ] } },
@@ -520,7 +520,7 @@
520
520
  {
521
521
  "nodes": [
522
522
  { "id": "start", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [
523
- { "type": "date_trigger", "config": {
523
+ { "key": "date_trigger", "config": {
524
524
  "attr_key": "_date_of_birth",
525
525
  "offset_direction": "on",
526
526
  "offset_days": 0,
@@ -561,7 +561,7 @@ funciona em conta com caixa **WhatsApp QR Code (`Channel::Waha`)** — a caixa v
561
561
  ```json
562
562
  {
563
563
  "nodes": [
564
- { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "triggers": [{ "type": "card_won", "funnel_ids": ["1"] }] } },
564
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "card_won", "config": { "funnel_ids": ["1"] } }] } },
565
565
  { "id": "n2", "type": "update_group", "position": { "x": 370, "y": 300 }, "data": {
566
566
  "label": "Cria grupo de onboarding",
567
567
  "groupOperation": "create",
@@ -526,3 +526,153 @@ Endpoints de relatório podem retornar **MUITOS** dados:
526
526
  - `csat_list` sem filtro → milhares
527
527
 
528
528
  **Sempre limite período** quando o usuário não foi específico. Se pediu "esta semana", use 7d. Se pediu "vamos ver tudo", confirme antes (ano inteiro pode ser muito).
529
+
530
+ ---
531
+
532
+ ## Relatórios personalizados — ler, calcular e montar (2026-08)
533
+
534
+ É a aba **Personalizados** dentro de Relatórios: o cliente monta blocos (gráficos e tabelas) e salva
535
+ num painel. Tudo aqui é **só administrador** — quem não for recebe **401** (não é problema de
536
+ conexão do conector; não mande reconectar).
537
+
538
+ | Ferramenta | Para quê |
539
+ |---|---|
540
+ | `lionchat_custom_dashboards_list` | Quais relatórios existem, já com os blocos dentro |
541
+ | `lionchat_custom_dashboards_widget_data` | O número de um bloco **já salvo** |
542
+ | `lionchat_custom_dashboards_preview_widget` | Monta um bloco **na hora** e calcula, sem salvar |
543
+ | `lionchat_custom_dashboards_create` | Cria um relatório que **aparece na tela do cliente** |
544
+ | `lionchat_custom_dashboards_update` | Altera (substitui a lista de blocos) |
545
+ | `lionchat_custom_dashboards_destroy` | Apaga, **sem desfazer** |
546
+
547
+ As três últimas **alteram o que o cliente vê** e só rodam com `confirm: true` — confirme com ele o
548
+ que exatamente vai mudar antes de reenviar.
549
+
550
+ ### `preview_widget` é a ferramenta mais poderosa deste guia
551
+
552
+ Ela responde pergunta de relatório que **nenhuma outra ferramenta cobre**, porque monta o recorte na
553
+ hora. Exemplo real: *"quantos cards foram ganhos em cada etapa do funil 12 nos últimos 30 dias"* —
554
+ nenhum `reports_*` responde; aqui é `funnel_stages` + `date_basis: closed` + `status: won`.
555
+
556
+ Não é consulta livre: escolha um `widget_type` e preencha **só os campos daquele tipo**.
557
+
558
+ ### Os 7 tipos de bloco
559
+
560
+ | Tipo | Campos | Recurso exigido |
561
+ |---|---|---|
562
+ | `conversations_timeseries` | `metric` (conversations, incoming_messages, outgoing_messages, resolutions, reopens, avg_first_response_time, avg_resolution_time) · `bucket` (day/week/month) · `scope_type`+`scope_id` · `time_range` | — |
563
+ | `entity_ranking` | `dimension` (agent/inbox/team/label) · `metric` (conversations, resolutions, avg_first_response_time, avg_resolution_time) · `time_range` | — |
564
+ | `csat_summary` | `time_range` | — |
565
+ | `sla_summary` | `time_range` | `sla` (Enterprise) |
566
+ | `funnel_stages` | `funnel_id` · `date_basis` (created/moved/closed/any) · `status` (all/open/won/lost) · `time_range` | `kanban_board` |
567
+ | `lead_origin` | `time_range` | `liontrack` |
568
+ | `agent_report` | `dimension` (agent/team/inbox) · `scope_type`+`scope_id` · `columns[]` · `time_range` | — |
569
+
570
+ **`time_range`:** `last_7_days`, `last_30_days`, `this_month`, `last_month` — e `custom` **só em
571
+ `conversations_timeseries` e `agent_report`**, exigindo `from`/`to`. **A janela não pode passar de 92
572
+ dias**, e esse teto é da janela, não de um tipo: pedir "esse ano" é recusado com a explicação.
573
+
574
+ **Colunas do `agent_report`:** `leads`, `atendidos`, `resolvidas`, `abertas`, `pendentes`, `adiadas`,
575
+ `label_count` (exige `label_id`) e `conversao` (exige `denominator` e `numerator`, que é **lista de
576
+ IDs de etiqueta**, nunca nome de métrica).
577
+
578
+ ### Como ler a resposta (campo `shape`)
579
+
580
+ | `shape` | Estrutura | Cuidado |
581
+ |---|---|---|
582
+ | `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 |
583
+ | `categories` | `{categories: [{label, value}]}` | `funnel_stages` vem **na ordem das etapas do funil**, nunca por valor — não reordene |
584
+ | `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 |
585
+ | `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 |
586
+
587
+ ### Armadilhas que fazem o bloco sair errado
588
+
589
+ 1. **`scope_type`/`scope_id` têm sentido OPOSTO por tipo.** No `conversations_timeseries` = "o
590
+ gráfico é só daquela entidade". No `agent_report` = "mostre só as linhas de quem é dessa
591
+ equipe/caixa". Confundir troca a conta inteira.
592
+ 2. **Campo fora da lista é descartado em silêncio.** Se um valor "não fez efeito", provavelmente o
593
+ nome do campo está errado.
594
+ 3. **Sempre envie `timezone_offset` em HORAS** (Brasília = `-3`). Sem ele o cálculo é em UTC e o
595
+ número não bate com o que o cliente vê na tela.
596
+ 4. **`update` substitui a lista de blocos inteira.** Leia com `list`, altere o array, devolva
597
+ completo — e preserve o `id` de cada bloco que já existia.
598
+ 5. **`list` não pagina.** Se a resposta vier com aviso de corte, **não** use aquela lista para montar
599
+ um `update`: você apagaria os blocos que não chegaram.
600
+ 6. **Recurso não liberado devolve 422 com `code: "feature_unavailable"`** — a resposta certa é "sua
601
+ conta não tem esse recurso", nunca "não há dados".
602
+
603
+ ### Fluxo recomendado para "me monta um relatório de X"
604
+
605
+ 1. `preview_widget` para **testar o desenho** e conferir que o número faz sentido.
606
+ 2. Mostre o número ao usuário e confirme que é o que ele quer.
607
+ 3. `create` com `confirm: true`, usando o mesmo desenho.
608
+
609
+ ---
610
+
611
+ ## Relatório personalizado como FONTE de um resumo escrito (2026-08-21)
612
+
613
+ O caso de uso: o cliente deixa um relatório montado na tela e pede *"me traz o relatório XPTO todo
614
+ dia"*. Em vez de garimpar dado em dez ferramentas, **leia o relatório salvo e escreva o texto**.
615
+
616
+ ### O passo a passo
617
+
618
+ 1. `lionchat_custom_dashboards_list` — ache o relatório pelo nome.
619
+ 2. `lionchat_custom_dashboards_list` (ou `_show`) devolve `widgets[]`, cada um com `id`, `title`,
620
+ `widget_type` e os params. **É o `title` que diz o que aquele bloco é** — sem ele, quatro blocos
621
+ do mesmo tipo chegam indistinguíveis.
622
+ 3. Para cada bloco: `lionchat_custom_dashboards_widget_data` com o `widget_id`.
623
+ **Mande sempre `timezone_offset`, lido do fuso DA CONTA** — `lionchat_account_show` devolve o
624
+ campo `timezone` (ex.: `America/Sao_Paulo`); converta para horas. **Não assuma -3.** O padrão da
625
+ plataforma é São Paulo, mas a conta pode ter outro: Mato Grosso do Sul é -4, e há cliente fora do
626
+ Brasil. Sem o fuso certo, o número não bate com a tela — e numa janela de UM DIA uma hora de
627
+ diferença joga o que aconteceu de madrugada para o dia errado.
628
+ 4. Para um resumo DIÁRIO, mande também **`time_range: 'yesterday'`**. Isso recalcula o bloco naquela
629
+ janela **sem alterar o relatório salvo** — o cliente continua vendo os 30 dias dele na tela.
630
+ 5. Escreva o texto a partir dos números. Não invente linha que o relatório não tem.
631
+
632
+ ### Regras que evitam relatório mentiroso
633
+
634
+ - **Bloco sem `title`**: diga o tipo e o período em vez de adivinhar ("Etapas do funil, últimos 30
635
+ dias"). Nunca chute de qual médico/equipe é.
636
+ - **Zero é resposta, não erro.** Etapa sem card, atendente sem ligação: escreva zero. Só diga "sem
637
+ dado" quando a chamada falhar de verdade.
638
+ - **Não some blocos de janelas diferentes.** Um bloco em 7 dias e outro em 30 no mesmo relatório é
639
+ comum; somar os dois inventa número.
640
+ - **`total` do shape `table` NÃO é a soma das linhas** em toda métrica — é contagem própria. Leia o
641
+ que vem, não recalcule.
642
+ - **Recorte e métrica não são negociáveis pelo agente**: `widget_data` só aceita mudar a JANELA
643
+ (`time_range`/`from`/`to`/`bucket`). Se o cliente pedir "o mesmo relatório só que por caixa", isso
644
+ é editar o relatório (com confirmação), não um override.
645
+
646
+ ### Escolher entre os dois blocos de funil (erro fácil)
647
+
648
+ | Pergunta | Bloco |
649
+ |---|---|
650
+ | "Quantos agendamentos aconteceram no período?" | `stage_entries` |
651
+ | "Como está o funil hoje / quantos estão em cada etapa?" | `funnel_stages` |
652
+
653
+ `funnel_stages` conta quem **está** na etapa; card que entrou e depois saiu some dele. Medido em
654
+ produção: **25% de diferença**. `stage_entries` lê o histórico de movimentação e conta **card
655
+ distinto** (quem volta pra etapa conta uma vez só).
656
+
657
+ **Limite do `stage_entries`**: o histórico só existe a partir de **12/07/2026** e tem retenção de
658
+ 365 dias. Período que alcance antes disso devolve MENOS do que aconteceu — avise em vez de
659
+ apresentar o número como completo.
660
+
661
+ ### Ligações (`calls_report`)
662
+
663
+ Colunas: Ligações · Atendidas · Não atendidas · **Não concluídas** · Tempo total · Tempo médio.
664
+
665
+ - "Não concluídas" são as que ficaram presas (tocando, em andamento, falhou, cancelada). **Não as
666
+ chame de "não atendidas"** — são coisas diferentes e a soma das três tem que fechar com o total.
667
+ - Tempo médio já vem calculado sobre as ATENDIDAS. Não recalcule dividindo pelo total.
668
+ - **Ligação sem atendente tem linha própria** e pode ser boa parte do volume (23% numa conta real).
669
+ Não a esconda ao resumir por pessoa.
670
+ - A tabela lista só quem teve ligação no período — atendente ausente da tabela fez zero.
671
+
672
+ ### Faturamento (`measure`)
673
+
674
+ `measure: 'value'` faz `funnel_stages`/`stage_entries` somarem o VALOR dos cards em vez de contar.
675
+ Combinado com `status: 'won'` + `date_basis: 'closed'`, dá o faturamento ganho da etapa no período.
676
+
677
+ **Cuidado ao escrever**: se os cards do cliente têm valor padrão (todos com o mesmo número), a soma
678
+ não é faturamento — é o padrão multiplicado. Se todos os valores forem idênticos, desconfie e diga.
@@ -1,3 +1,5 @@
1
+ export declare const CONFIRM_PARAM = "confirm";
2
+ export declare const CONFIRM_DESCRIPTION: string;
1
3
  export declare const FULL_RESPONSE_PARAM = "full_response";
2
4
  export declare const FULL_RESPONSE_DESCRIPTION: string;
3
5
  export declare const FULL_RESPONSE_TOOLS: Set<string>;
package/dist/sanitize.js CHANGED
@@ -53,6 +53,12 @@ const SLIM_KEYS = new Set([
53
53
  // AIDEV-NOTE: tools cujo PROPOSITO e o campo pesado — slim nao se aplica nelas
54
54
  // (segredos continuam redigidos mesmo assim).
55
55
  const SLIM_EXEMPT_SUBSTRINGS = ['whatsapp_templates', 'csat_template'];
56
+ // AIDEV-NOTE: [auditoria adversus A3] parametro do CONECTOR (nunca chega ao Rails) que carrega o
57
+ // OK do usuario pra ferramenta marcada com `confirm_required` no catalogo. Stripado antes do
58
+ // bucketing, igual full_response.
59
+ export const CONFIRM_PARAM = 'confirm';
60
+ export const CONFIRM_DESCRIPTION = 'OBRIGATORIO nesta ferramenta: ela ALTERA algo que o usuario ve na tela. Confirme com ele o que ' +
61
+ 'vai ser criado/alterado/apagado e so entao reenvie com confirm:true. Sem isso a chamada e recusada.';
56
62
  export const FULL_RESPONSE_PARAM = 'full_response';
57
63
  export const FULL_RESPONSE_DESCRIPTION = 'true = resposta COMPLETA, sem a poda de campos pesados (message_templates, working_hours, ' +
58
64
  'avatares, meta_history_import). Padrao false (resposta enxuta). Segredos sao SEMPRE censurados, ' +
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"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"}
package/dist/tools.js CHANGED
@@ -5,7 +5,7 @@ import { substitutePath, buildQueryString, formatResponse, separateParams, } fro
5
5
  import { readFileSync } from 'fs';
6
6
  import { resolve, dirname } from 'path';
7
7
  import { fileURLToPath } from 'url';
8
- import { FULL_RESPONSE_PARAM, FULL_RESPONSE_DESCRIPTION, supportsFullResponse, } from './sanitize.js';
8
+ import { FULL_RESPONSE_PARAM, FULL_RESPONSE_DESCRIPTION, CONFIRM_PARAM, CONFIRM_DESCRIPTION, supportsFullResponse, } from './sanitize.js';
9
9
  // AIDEV-NOTE: Map HTTP method to tool annotations for MCP client hinting
10
10
  function getToolAnnotations(method) {
11
11
  switch (method.toUpperCase()) {
@@ -141,7 +141,7 @@ function paramTypeToZod(param) {
141
141
  // AIDEV-NOTE: [2026-07-24] toolId opcional — tools curadas (sanitize.FULL_RESPONSE_TOOLS)
142
142
  // ganham o param full_response no schema (escape do slim). NAO injetar nas 682 (inflaria
143
143
  // o tools/list em ~20-30k tokens).
144
- function buildZodSchema(params, toolId) {
144
+ function buildZodSchema(params, toolId, confirmRequired = false) {
145
145
  const shape = {};
146
146
  let hasAccountIdInPath = false;
147
147
  for (const param of params) {
@@ -179,6 +179,12 @@ function buildZodSchema(params, toolId) {
179
179
  .optional()
180
180
  .describe(FULL_RESPONSE_DESCRIPTION);
181
181
  }
182
+ // AIDEV-NOTE: [auditoria adversus A3] ferramenta marcada no catalogo ganha o campo `confirm` —
183
+ // OPCIONAL no schema de proposito: quem cobra e o handler, com mensagem que explica o que fazer.
184
+ // Marcar required aqui devolveria erro seco de validacao, sem ensinar nada ao modelo.
185
+ if (confirmRequired) {
186
+ shape[CONFIRM_PARAM] = z.boolean().optional().describe(CONFIRM_DESCRIPTION);
187
+ }
182
188
  return z.object(shape);
183
189
  }
184
190
  // AIDEV-NOTE: Check if endpoint has any file-type params (unsupported in MCP v1)
@@ -187,7 +193,7 @@ function hasFileParam(params) {
187
193
  }
188
194
  // AIDEV-NOTE: Register a single API endpoint as an MCP tool
189
195
  function registerSingleTool(server, config, client, endpoint) {
190
- const schema = buildZodSchema(endpoint.params, endpoint.id);
196
+ const schema = buildZodSchema(endpoint.params, endpoint.id, endpoint.confirm_required);
191
197
  const annotations = getToolAnnotations(endpoint.method);
192
198
  const hasFile = hasFileParam(endpoint.params);
193
199
  // AIDEV-NOTE: Prepend file upload warning to description when endpoint needs file params
@@ -218,6 +224,22 @@ function registerSingleTool(server, config, client, endpoint) {
218
224
  }
219
225
  }
220
226
  }
227
+ // AIDEV-NOTE: [auditoria adversus A3] trava de confirmacao, ANTES de qualquer trabalho —
228
+ // ferramenta que altera o que o cliente ve na tela nao roda sem o OK explicito. Espelha a
229
+ // trava do conector remoto (runner.ts). A anotacao de destrutivo derivada do metodo HTTP
230
+ // nunca cobriu criar (POST) nem alterar (PATCH).
231
+ if (endpoint.confirm_required && params[CONFIRM_PARAM] !== true) {
232
+ return {
233
+ content: [
234
+ {
235
+ type: 'text',
236
+ text: `"${endpoint.id}" altera o que o usuario ve na tela (${endpoint.title}). ` +
237
+ 'Confirme com ele exatamente o que sera criado/alterado/apagado e reenvie com confirm:true.',
238
+ },
239
+ ],
240
+ isError: true,
241
+ };
242
+ }
221
243
  // AIDEV-NOTE: Multi-tenant — input account_id (if provided) overrides env LIONCHAT_ACCOUNT_ID.
222
244
  // Resolve and strip account_id BEFORE bucketing so it never lands in body/query for
223
245
  // endpoints that don't declare it as an explicit param.
@@ -232,6 +254,9 @@ function registerSingleTool(server, config, client, endpoint) {
232
254
  // default->body do separateParams vazaria o campo pro corpo enviado ao Rails.
233
255
  const fullResponse = params[FULL_RESPONSE_PARAM] === true;
234
256
  delete paramsWithoutAccount[FULL_RESPONSE_PARAM];
257
+ // AIDEV-NOTE: `confirm` tambem e flag do CONECTOR — stripar antes do bucketing, senao o
258
+ // catch-all default->body do separateParams mandaria o campo pro Rails.
259
+ delete paramsWithoutAccount[CONFIRM_PARAM];
235
260
  const { pathParams, queryParams, bodyParams } = separateParams(paramsWithoutAccount, endpoint.params, endpoint.body_wrapper);
236
261
  const path = substitutePath(endpoint.path, effectiveAccountId, pathParams);
237
262
  const queryStr = buildQueryString(queryParams);
@@ -307,7 +332,7 @@ function registerListCategoriesTool(server, allEndpoints) {
307
332
  // Helps LLMs build correct flow_data without hitting trial-and-error on
308
333
  // node types, action keys, source handles, etc.
309
334
  function registerFlowsSchemaReferenceTool(server) {
310
- const reference = `LIONCHAT FLOW BUILDER — SCHEMA REFERENCE (atualizado 2026-08-18)
335
+ const reference = `LIONCHAT FLOW BUILDER — SCHEMA REFERENCE (atualizado 2026-08-19)
311
336
 
312
337
  flow_data tem o formato Vue Flow: { nodes: [...], edges: [...] }.
313
338
 
@@ -332,7 +357,14 @@ flow_data tem o formato Vue Flow: { nodes: [...], edges: [...] }.
332
357
  ═══ NODE TYPES + source handles ═══
333
358
 
334
359
  ▸ start
335
- data: { label, triggers: [...] }
360
+ data: { label, items: [ {key:'<gatilho>', config:{...filtros...}} ] }
361
+ ATENCAO — a chave e "items" e o item usa "key"+"config" (corrigido 19/08/2026). NAO use
362
+ "triggers" com "type" e filtros soltos no topo: o motor ainda aceita esse formato antigo, mas o
363
+ EDITOR do FlowBuilder le SO "items" — fluxo salvo assim dispara normalmente e abre com o bloco
364
+ Inicio EM BRANCO no painel do cliente (53 fluxos de 9 contas ficaram assim ate 19/08), e um
365
+ clique em "Concluir" naquele modal apagava o gatilho em silencio. Alem do editor, o disparo por
366
+ webhook, o filtro de Campanha de Fluxo, o gatilho LionTrack e o historico de execucao tambem so
367
+ entendem "items". O app normaliza o formato antigo na gravacao, mas nao conte com isso.
336
368
  Triggers: message_received (keywords[] min 3 chars + match_type 'contains'|'exact' — dispara em
337
369
  QUALQUER msg do cliente que case), conversation_created/conversation_reopened (match_mode +
338
370
  keywords opcionais), conversation_resolved, label_added/label_removed (label_names[] — NAO
@@ -361,7 +393,7 @@ flow_data tem o formato Vue Flow: { nodes: [...], edges: [...] }.
361
393
  ativa na conversa; flow antigo sem config = todos os SLAs/qualquer prazo),
362
394
  date_trigger (NOVO 2026-07-10 — Gatilho de Data: dispara quando uma DATA do CONTATO chega,
363
395
  aniversario/exame; modelo agenda, sem varredura; SO flow individual). Item usa config ANINHADO:
364
- {type:'date_trigger', config:{ attr_key ('_date_of_birth'=aniversario nativo, OU chave de
396
+ {key:'date_trigger', config:{ attr_key ('_date_of_birth'=aniversario nativo, OU chave de
365
397
  atributo do contato tipo Data ex 'data_exame' — OBRIGATORIO), offset_direction 'before'|'on'|
366
398
  'after', offset_days 0-365, repeat_yearly bool (true=ignora ano/aniversario), send_time_source
367
399
  'fixed'|'attribute'|'variable' (+ send_time 'HH:MM' | send_time_attr_key | send_time_template
@@ -381,13 +413,19 @@ flow_data tem o formato Vue Flow: { nodes: [...], edges: [...] }.
381
413
  inbox_id pra confirmar -> campaigns_create com flow_id.
382
414
  lead_form_completed / lead_form_milestone / lead_form_abandoned (NOVOS 2026-08-15 — gatilhos
383
415
  de FORMULARIO PUBLICO, feature Formularios): disparados pelo formulario de captacao, NAO por
384
- evento de conversa (inertes no matcher). Item: {key:'lead_form_completed', lead_form_id:<id>}
385
- — lead_form_id pode ir no topo do item (caminho API/MCP) ou dentro de config (tela);
416
+ evento de conversa (inertes no matcher). Item: {key:'lead_form_completed', config:{lead_form_id:<id>}}
417
+ — desde 19/08 o app move pra dentro de "config" qualquer filtro que venha solto no topo do item,
418
+ entao mande sempre em config;
386
419
  lead_form_milestone aceita milestone_node_id (vazio = qualquer marco); lead_form_abandoned
387
420
  dispara pelo tempo de abandono configurado no formulario. SO flow individual (nunca grupo).
388
421
  O flow nasce com variaveis form_* + respostas planas (form_<slug_da_pergunta>). Ver tools
389
422
  lionchat_lead_forms_* e o resource formularios-publicos.
390
- cron, webhook. NOVO message_sent (2026-06-11): par do message_received pra mensagens de
423
+ manual_trigger (ativacao manual pelo atendente na sidebar), webhook_received (ver WEBHOOK
424
+ EMBUTIDO abaixo), page_track (LionTrack). NAO EXISTEM os gatilhos "cron" nem "webhook" — esta
425
+ referencia listava os dois por engano ate 19/08/2026 e um flow com "webhook" ficou ATIVO 26 dias
426
+ sem disparar UMA vez, em silencio; o nome certo e "webhook_received". Para disparo por data use
427
+ date_trigger; para disparo em lote, campaign_trigger + Campanha de Fluxo.
428
+ NOVO message_sent (2026-06-11): par do message_received pra mensagens de
391
429
  SAIDA (atendente, celular/eco, IA — nota privada NAO) com keywords+match_type; cuidado: acao
392
430
  'desativar IA' com esse trigger sem keywords = a propria resposta da IA dispara o flow.
393
431
  group_participant_joined / group_participant_left (NOVO 07/08 — 'Entrou no grupo' / 'Saiu do
@@ -400,7 +438,7 @@ flow_data tem o formato Vue Flow: { nodes: [...], edges: [...] }.
400
438
  PESSOA que entrou/saiu ('entrou no grupo -> manda no privado').
401
439
  WEBHOOK EMBUTIDO (Webhook Universal): 1) criar flow; 2) POST /custom_webhook_integrations com
402
440
  {custom_webhook_integration:{flow_id}} (idempotente, retorna URL unica); 3) flows_update com
403
- item {type:'webhook_received', config:{integration_id}} no data.items do start. Remover o item
441
+ item {key:'webhook_received', config:{integration_id}} no data.items do start. Remover o item
404
442
  desativa o webhook. Excluir flow destroi o webhook. Duplicar flow NAO copia o gatilho.
405
443
  VALIDACAO flow_trigger_conflict: criar/atualizar/ativar retorna HTTP 422
406
444
  {error_code:'flow_trigger_conflict', conflicts:[{flow_id, flow_name, trigger_type, inbox_id,
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,mFAAmF;AAEnF,OAAO,EAAE,CAAC,EAAc,MAAM,KAAK,CAAC;AAIpC,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAsCvB,yEAAyE;AACzE,SAAS,kBAAkB,CAAC,MAAc;IACxC,QAAQ,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;QAC7B,KAAK,KAAK;YACR,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QAC9E,KAAK,MAAM;YACT,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;QAChF,KAAK,OAAO,CAAC;QACb,KAAK,KAAK;YACR,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QAC/E,KAAK,QAAQ;YACX,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;QAC9E;YACE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;IAClF,CAAC;AACH,CAAC;AAED,8FAA8F;AAC9F,8EAA8E;AAC9E,iFAAiF;AACjF,MAAM,iBAAiB,GAA2B;IAChD,OAAO,EAAE,SAAS;IAClB,mBAAmB,EAAE,kBAAkB;IACvC,UAAU,EAAE,UAAU;IACtB,WAAW,EAAE,eAAe;IAC5B,mBAAmB,EAAE,SAAS;IAC9B,WAAW,EAAE,UAAU;IACvB,OAAO,EAAE,OAAO;IAChB,QAAQ,EAAE,QAAQ;IAClB,wBAAwB,EAAE,gBAAgB;IAC1C,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,QAAQ;IACjB,cAAc,EAAE,eAAe;IAC/B,UAAU,EAAE,eAAe;IAC3B,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,QAAQ;IACnB,QAAQ,EAAE,QAAQ;IAClB,kBAAkB,EAAE,OAAO;IAC3B,oBAAoB,EAAE,mBAAmB;IACzC,qBAAqB,EAAE,oBAAoB;IAC3C,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,cAAc;IACvB,oBAAoB,EAAE,aAAa;IACnC,kBAAkB,EAAE,eAAe;IACnC,WAAW,EAAE,kBAAkB;IAC/B,OAAO,EAAE,cAAc;IACvB,oBAAoB,EAAE,iBAAiB;IACvC,qBAAqB,EAAE,sBAAsB;IAC7C,qBAAqB,EAAE,iBAAiB;IACxC,eAAe,EAAE,aAAa;IAC9B,SAAS,EAAE,eAAe;IAC1B,iBAAiB,EAAE,qBAAqB;IACxC,SAAS,EAAE,gBAAgB;IAC3B,UAAU,EAAE,YAAY;IACxB,OAAO,EAAE,OAAO;IAChB,aAAa,EAAE,oBAAoB;IACnC,sBAAsB,EAAE,mBAAmB;IAC3C,qBAAqB,EAAE,kBAAkB;IACzC,sBAAsB,EAAE,cAAc;IACtC,yBAAyB,EAAE,mBAAmB;IAC9C,KAAK,EAAE,KAAK;IACZ,mBAAmB,EAAE,gBAAgB;IACrC,gBAAgB,EAAE,gBAAgB;IAClC,iBAAiB,EAAE,aAAa;IAChC,oBAAoB,EAAE,oBAAoB;IAC1C,eAAe,EAAE,eAAe;IAChC,mBAAmB,EAAE,iBAAiB;IACtC,iBAAiB,EAAE,iBAAiB;IACpC,yBAAyB,EAAE,qBAAqB;IAChD,UAAU,EAAE,UAAU;IACtB,wBAAwB,EAAE,oBAAoB;IAC9C,eAAe,EAAE,WAAW;IAC5B,eAAe,EAAE,eAAe;IAChC,WAAW,EAAE,WAAW;IACxB,SAAS,EAAE,SAAS;IACpB,uBAAuB,EAAE,uBAAuB;IAChD,YAAY,EAAE,SAAS;IACvB,gBAAgB,EAAE,iBAAiB;IACnC,kBAAkB,EAAE,eAAe;IACnC,iBAAiB,EAAE,oBAAoB;IACvC,MAAM,EAAE,MAAM;IACd,0BAA0B,EAAE,mBAAmB;IAC/C,SAAS,EAAE,QAAQ;CACpB,CAAC;AAEF,SAAS,OAAO,CAAC,IAAY;IAC3B,MAAM,UAAU,GAAG,IAAI;SACpB,SAAS,CAAC,KAAK,CAAC;SAChB,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;SAC/B,WAAW,EAAE;SACb,IAAI,EAAE,CAAC;IACV,OAAO,iBAAiB,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AACzG,CAAC;AAED,kEAAkE;AAClE,SAAS,cAAc,CAAC,KAAoB;IAC1C,IAAI,MAAkB,CAAC;IAEvB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS;YACZ,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;YAC1B,MAAM;QACR,KAAK,QAAQ;YACX,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,SAAS;YACZ,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM;QACR,KAAK,QAAQ;YACX,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/B,MAAM;QACR,KAAK,OAAO;YACV,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9B,MAAM;QACR,KAAK,MAAM;YACT,MAAM,GAAG,CAAC;iBACP,MAAM,EAAE;iBACR,QAAQ,CACP,wDAAwD,CACzD,CAAC;YACJ,MAAM;QACR,KAAK,QAAQ,CAAC;QACd;YACE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM;IACV,CAAC;IAED,gFAAgF;IAChF,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;QAC/C,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpB,MAAM,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,4DAA4D;AAC5D,wFAAwF;AACxF,kFAAkF;AAClF,0FAA0F;AAC1F,yFAAyF;AACzF,mCAAmC;AACnC,SAAS,cAAc,CACrB,MAAuB,EACvB,MAAe,EACf,eAAe,GAAG,KAAK;IAEvB,MAAM,KAAK,GAA+B,EAAE,CAAC;IAC7C,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAChC,kBAAkB,GAAG,IAAI,CAAC;YAC1B,oFAAoF;YACpF,yEAAyE;YACzE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;iBAClB,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACP,2HAA2H,CAC5H,CAAC;YACJ,SAAS;QACX,CAAC;QACD,uFAAuF;QACvF,uFAAuF;QACvF,yFAAyF;QACzF,sFAAsF;QACtF,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED,8FAA8F;IAC9F,6EAA6E;IAC7E,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC;aACpB,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,2HAA2H,CAC5H,CAAC;IACN,CAAC;IAED,kFAAkF;IAClF,IAAI,MAAM,IAAI,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,KAAK,CAAC,mBAAmB,CAAC,GAAG,CAAC;aAC3B,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,yBAAyB,CAAC,CAAC;IACzC,CAAC;IAED,+FAA+F;IAC/F,iGAAiG;IACjG,sFAAsF;IACtF,IAAI,eAAe,EAAE,CAAC;QACpB,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,iFAAiF;AACjF,SAAS,YAAY,CAAC,MAAuB;IAC3C,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;AAC/C,CAAC;AAED,4DAA4D;AAC5D,SAAS,kBAAkB,CACzB,MAAiB,EACjB,MAAc,EACd,MAAsB,EACtB,QAAqB;IAErB,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACvF,MAAM,WAAW,GAAG,kBAAkB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE9C,yFAAyF;IACzF,IAAI,WAAW,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC/E,IAAI,OAAO,EAAE,CAAC;QACZ,WAAW;YACT,+EAA+E;gBAC/E,WAAW,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,IAAI,CACT,QAAQ,CAAC,EAAE,EACX,WAAW,EACX,MAAM,CAAC,KAAK,EACZ,WAAW,EACX,KAAK,EAAE,MAA+B,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,kEAAkE;YAClE,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;gBACpE,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;oBAC5B,gFAAgF;oBAChF,wEAAwE;oBACxE,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,SAAS,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;wBACxF,OAAO;4BACL,OAAO,EAAE;gCACP;oCACE,IAAI,EAAE,MAAe;oCACrB,IAAI,EAAE,2FAA2F;iCAClG;6BACF;4BACD,OAAO,EAAE,IAAI;yBACd,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YAED,yFAAyF;YACzF,0FAA0F;YAC1F,yFAAyF;YACzF,iDAAiD;YACjD,IAAI,QAAQ,CAAC,gBAAgB,IAAI,MAAM,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChE,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EACF,IAAI,QAAQ,CAAC,EAAE,wCAAwC,QAAQ,CAAC,KAAK,KAAK;gCAC1E,4FAA4F;yBAC/F;qBACF;oBACD,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,+FAA+F;YAC/F,oFAAoF;YACpF,wDAAwD;YACxD,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;YAC1C,MAAM,kBAAkB,GACtB,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,IAAI,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC;gBACpF,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;gBACtB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;YACvB,MAAM,oBAAoB,GAA4B,EAAE,GAAG,MAAM,EAAE,CAAC;YACpE,OAAO,oBAAoB,CAAC,YAAY,CAAC,CAAC;YAE1C,uFAAuF;YACvF,gFAAgF;YAChF,8EAA8E;YAC9E,MAAM,YAAY,GAAG,MAAM,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAC;YAC1D,OAAO,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;YAEjD,wFAAwF;YACxF,wEAAwE;YACxE,OAAO,oBAAoB,CAAC,aAAa,CAAC,CAAC;YAE3C,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,cAAc,CAC5D,oBAAoB,EACpB,QAAQ,CAAC,MAAM,EACf,QAAQ,CAAC,YAAY,CACtB,CAAC;YAEF,MAAM,IAAI,GAAG,cAAc,CACzB,QAAQ,CAAC,IAAI,EACb,kBAAkB,EAClB,UAAU,CACX,CAAC;YACF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;YAC/C,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAExD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;gBAClC,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;aAClE,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,cAAc,CAAC,MAAM,EAAE;4BAC3B,IAAI,EAAE,CAAC,YAAY;4BACnB,MAAM,EAAE,QAAQ,CAAC,EAAE;yBACpB,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,gFAAgF;YAChF,uEAAuE;YACvE,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,SAAS,gBAAgB,CACvB,MAAiB,EACjB,MAAc,EACd,MAAsB;IAEtB,MAAM,CAAC,IAAI,CACT,eAAe,EACf,yFAAyF,EACzF,EAAE,EACF,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,EACpE,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;gBAClC,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,iBAAiB;aACxB,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,SAAS,0BAA0B,CACjC,MAAiB,EACjB,YAA2B;IAE3B,6EAA6E;IAC7E,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IACjD,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;QAClC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;SACtD,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,KAAK,QAAQ,CAAC;SACjD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,CAAC,IAAI,CACT,0BAA0B,EAC1B,sIAAsI,EACtI,EAAE,EACF,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,EACpE,KAAK,IAAI,EAAE;QACT,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;SACzD,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,wEAAwE;AACxE,gDAAgD;AAChD,SAAS,gCAAgC,CAAC,MAAiB;IACzD,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwflB,CAAC;IAED,MAAM,CAAC,IAAI,CACT,iCAAiC,EACjC,2PAA2P,EAC3P,EAAE,EACF,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,EACpE,KAAK,IAAI,EAAE;QACT,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;SACtD,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,aAAa,CAC3B,MAAiB,EACjB,MAAc,EACd,MAAsB;IAEtB,yFAAyF;IACzF,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACjE,IAAI,SAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,sCAAsC,aAAa,IAAI;YACvD,oDAAoD;YACpD,mBAAmB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACtE,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,GAAG,SAAS,CAAC;IAEzB,iFAAiF;IACjF,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7D,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED,sEAAsE;IACtE,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC7B,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,6DAA6D;IAC7D,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvD,CAAC;IAED,wEAAwE;IACxE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,0BAA0B,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC9C,gCAAgC,CAAC,MAAM,CAAC,CAAC;IAEzC,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,uDAAuD;AACrF,CAAC"}
package/endpoints.json CHANGED
@@ -9540,7 +9540,7 @@
9540
9540
  "method": "GET",
9541
9541
  "path": "/api/v1/accounts/{account_id}/flows",
9542
9542
  "title": "Listar Flows",
9543
- "description": "Lista todos os flows da conta com estatísticas de execução. Lista flows paginados (per_page padrão 25, máximo 100). Retorna meta (contadores) + payload (array de flow). Cada flow retorna também o campo tags (array de strings, novo 22/07) — use para organizar/agrupar flows.",
9543
+ "description": "Lista os fluxos da conta. SEM paginacao e SEM meta: devolve todos de uma vez, com os filtros opcionais inbox_id, with_webhook_trigger e with_campaign_trigger. Use a lista pra ESCOLHER e FILTRAR fluxos (id, name, description, flow_type, conversation_mode, active, inbox_ids/inboxes, tags, session_stats = totais de execucao). Pra ler o DESENHO de um fluxo (blocos, conexoes, variaveis) use flows_show — a lista nao e o lugar de inspecionar blocos. Cada flow traz o campo tags (array de strings, 22/07) — use pra organizar/agrupar.",
9544
9544
  "category": "Flows",
9545
9545
  "params": [
9546
9546
  {
@@ -9558,13 +9558,17 @@
9558
9558
  "description": "Filtra os flows vinculados a essa caixa de entrada."
9559
9559
  }
9560
9560
  ],
9561
- "returns": "Objeto { data: [flows] } — a lista fica em data, sem meta e sem payload. Sem paginacao nenhuma (o controller so filtra por inbox_id / with_webhook_trigger / with_campaign_trigger). Cada flow traz o flow_data inteiro, entao a resposta pode ser MUITO grande em conta com muitos fluxos.",
9561
+ "returns": "Objeto { data: [flows] } — a lista fica em data, sem meta e sem paginacao. Campos de cada item: id, name, description, tool_name, tool_description, flow_type, conversation_mode, active, inbox_ids, inboxes[{id,name,channel_type}], tags, session_stats{total,active,waiting,completed,exited,error}, created_at, updated_at. ATENCAO (ago/2026): hoje cada item ainda carrega flow_data inteiro e metricas por bloco, o que deixa a resposta MUITO pesada em conta com muitos fluxos/execucoes (ate 19 MB medidos) — NAO dependa disso: esta em andamento a lista leve, que tira flow_data/node_metrics/waiting_counts/variables e passa a trazer `triggers` (gatilhos do bloco Inicio). Pra flow_data use flows_show; pra conferir gatilho duplicado use flows_check_conflicts.",
9562
9562
  "common_use_cases": [
9563
9563
  "Dashboard: contar/exibir flows",
9564
9564
  "Buscar com filtros antes de outra operação",
9565
9565
  "Auditoria/relatório de flows ativas"
9566
9566
  ],
9567
- "related_tools": []
9567
+ "related_tools": [
9568
+ "lionchat_flows_show",
9569
+ "lionchat_flows_check_conflicts",
9570
+ "lionchat_flows_toggle"
9571
+ ]
9568
9572
  },
9569
9573
  {
9570
9574
  "id": "lionchat_flows_create",
@@ -25456,6 +25460,350 @@
25456
25460
  "lionchat_journey_funnel_reports"
25457
25461
  ]
25458
25462
  },
25463
+ {
25464
+ "id": "lionchat_custom_dashboards_list",
25465
+ "method": "GET",
25466
+ "path": "/api/v2/accounts/{account_id}/custom_dashboards",
25467
+ "title": "Relatorios Personalizados (listar)",
25468
+ "description": "[GET] Lista TODOS os relatorios personalizados da conta (a aba 'Personalizados' em Relatorios), cada um JA com o array `widgets` completo — id e desenho de cada bloco. E daqui que sai o `widget_id` pra calcular um bloco. So administrador (401 caso contrario). ATENCAO: NAO tem paginacao nem filtro. Com muitos relatorios cheios a resposta pode vir CORTADA no limite do conector — se vier o aviso de corte, NUNCA use essa lista pra montar um `custom_dashboards_update`: voce apagaria os blocos que nao chegaram.",
25469
+ "category": "Relatorios",
25470
+ "params": [],
25471
+ "returns": "Array de { id, name, position, widgets: [...], created_at }",
25472
+ "common_use_cases": [
25473
+ "Descobrir quais relatorios personalizados o cliente montou",
25474
+ "Pegar o widget_id de um bloco pra calcular o numero dele",
25475
+ "Ler o desenho de um bloco antes de alterar o relatorio"
25476
+ ],
25477
+ "related_tools": [
25478
+ "lionchat_custom_dashboards_widget_data",
25479
+ "lionchat_custom_dashboards_update"
25480
+ ]
25481
+ },
25482
+ {
25483
+ "id": "lionchat_custom_dashboards_widget_data",
25484
+ "method": "POST",
25485
+ "path": "/api/v2/accounts/{account_id}/custom_dashboards/{id}/widget_data",
25486
+ "title": "Calcular Bloco Salvo",
25487
+ "description": "[POST] Calcula UM bloco que JA esta salvo num relatorio personalizado e devolve os numeros. O desenho e lido de dentro do relatorio salvo — o corpo so diz QUAL bloco (widget_id). Pra calcular um desenho que ainda nao existe, use `custom_dashboards_preview_widget`. So administrador (401 caso contrario). Formato da resposta pelo campo `shape`: series {series:[{timestamp,value}]} (timestamp unix em segundos; metricas de tempo trazem tambem `count` por ponto) · categories {categories:[{label,value}]} (funnel_stages vem na ORDEM DAS ETAPAS, nunca por valor) · scalar {value,total,secondary} (use `total` pra separar 'ninguem respondeu' de 'zero legitimo'; SLA sem nenhum prazo aplicado devolve 100.0) · table {dimension,columns,rows,total} (o `total` NAO e a soma das linhas, e contagem propria por metrica). Erros: 401 = quem chamou NAO e administrador (nao mande reconectar o conector). 422 com {error} = desenho invalido ou recurso nao liberado (code:'feature_unavailable' distingue) — o motivo vem em texto, corrija e tente de novo. 422 com {message,attributes} = teto atingido (50 relatorios por conta, 12 blocos por relatorio). PERIODO SOB DEMANDA: `time_range`/`from`/`to`/`bucket` recalculam o bloco em outra janela SEM alterar o relatorio salvo — e assim que se monta um resumo DIARIO a partir de um relatorio que o cliente deixou salvo em 30 dias (peca `time_range:'yesterday'` e escreva o texto). Recorte (scope/dimension/funnel) e metrica NAO sao aceitos aqui de proposito: janela nao amplia permissao, recorte ampliaria.",
25488
+ "category": "Relatorios",
25489
+ "params": [
25490
+ {
25491
+ "name": "id",
25492
+ "type": "integer",
25493
+ "location": "path",
25494
+ "required": true,
25495
+ "description": "Id do relatorio personalizado (vem do custom_dashboards_list)"
25496
+ },
25497
+ {
25498
+ "name": "widget_id",
25499
+ "type": "string",
25500
+ "location": "body",
25501
+ "required": true,
25502
+ "description": "Id do bloco DENTRO daquele relatorio (campo `id` de um item de `widgets`)"
25503
+ },
25504
+ {
25505
+ "name": "timezone_offset",
25506
+ "type": "number",
25507
+ "location": "body",
25508
+ "required": false,
25509
+ "description": "Fuso em HORAS. NAO chute nem assuma -3: LEIA o fuso DA CONTA com `lionchat_account_show` (campo `timezone`, ex.: 'America/Sao_Paulo') e converta pra horas. O padrao da plataforma e America/Sao_Paulo (-3), mas a conta pode ter outro — Mato Grosso do Sul e -4, e cliente fora do Brasil existe. SEMPRE envie: ausente calcula em UTC e o numero nao bate com o que o cliente ve na tela. Em janela de UM DIA (time_range 'today'/'yesterday') isso e decisivo: uma hora de diferenca joga o que aconteceu de madrugada pro dia errado."
25510
+ },
25511
+ {
25512
+ "name": "time_range",
25513
+ "type": "string",
25514
+ "location": "body",
25515
+ "required": false,
25516
+ "description": "RECALCULA o bloco salvo em OUTRO periodo, sem alterar o que esta gravado (a tela do cliente nao muda). E o que permite pedir 'o relatorio de ontem' a partir de um relatorio salvo em 'ultimos 30 dias'. Valores: today, yesterday, last_7_days, last_30_days, this_month, last_month, custom. Ausente = usa o periodo salvo no bloco."
25517
+ },
25518
+ {
25519
+ "name": "from",
25520
+ "type": "string",
25521
+ "location": "body",
25522
+ "required": false,
25523
+ "description": "Inicio do periodo (AAAA-MM-DD). SO com time_range=custom; sozinho e ignorado. Janela maxima de 92 dias — acima disso volta 422 com o motivo."
25524
+ },
25525
+ {
25526
+ "name": "to",
25527
+ "type": "string",
25528
+ "location": "body",
25529
+ "required": false,
25530
+ "description": "Fim do periodo (AAAA-MM-DD). SO com time_range=custom."
25531
+ },
25532
+ {
25533
+ "name": "bucket",
25534
+ "type": "string",
25535
+ "location": "body",
25536
+ "required": false,
25537
+ "description": "Agrupamento no tempo (day, week, month) para blocos de serie. Ausente = usa o agrupamento salvo."
25538
+ }
25539
+ ],
25540
+ "returns": "Um dos 4 formatos normalizados (series | categories | scalar | table), com o campo `shape` dizendo qual",
25541
+ "common_use_cases": [
25542
+ "Responder 'o que meu relatorio de vendas mostra hoje?'",
25543
+ "Ler o numero de um grafico que o cliente montou na tela"
25544
+ ],
25545
+ "related_tools": [
25546
+ "lionchat_custom_dashboards_list",
25547
+ "lionchat_custom_dashboards_preview_widget"
25548
+ ]
25549
+ },
25550
+ {
25551
+ "id": "lionchat_custom_dashboards_preview_widget",
25552
+ "method": "POST",
25553
+ "path": "/api/v2/accounts/{account_id}/custom_dashboards/preview_widget",
25554
+ "title": "Calcular Bloco na Hora (sem salvar)",
25555
+ "description": "[POST] Monta um bloco de relatorio NA HORA e devolve os numeros, SEM salvar nada. E a ferramenta pra responder pergunta de relatorio que nao tem grafico pronto — ex.: 'quantos cards foram GANHOS em cada etapa do funil X nos ultimos 30 dias' (funnel_stages + date_basis:closed + status:won). Nao existe consulta livre: escolha um `widget_type` e preencha SO os campos daquele tipo; o servidor revalida tudo antes de calcular. So administrador (401 caso contrario). Campo que nao esta na lista abaixo e DESCARTADO EM SILENCIO. Formato da resposta pelo campo `shape`: series {series:[{timestamp,value}]} (timestamp unix em segundos; metricas de tempo trazem tambem `count` por ponto) · categories {categories:[{label,value}]} (funnel_stages vem na ORDEM DAS ETAPAS, nunca por valor) · scalar {value,total,secondary} (use `total` pra separar 'ninguem respondeu' de 'zero legitimo'; SLA sem nenhum prazo aplicado devolve 100.0) · table {dimension,columns,rows,total} (o `total` NAO e a soma das linhas, e contagem propria por metrica). Erros: 401 = quem chamou NAO e administrador (nao mande reconectar o conector). 422 com {error} = desenho invalido ou recurso nao liberado (code:'feature_unavailable' distingue) — o motivo vem em texto, corrija e tente de novo. 422 com {message,attributes} = teto atingido (50 relatorios por conta, 12 blocos por relatorio). Tabela completa de tipos, metricas e armadilhas no resource lionchat://docs/reports-guide. TIPOS NOVOS (21/08): `calls_report` = tabela de LIGACOES (feitas, atendidas, nao atendidas, nao concluidas, tempo total e medio) por atendente (`dimension:'agent'`) ou por caixa (`dimension:'inbox'`); ligacao sem atendente ganha linha propria. `stage_entries` = quantos cards ENTRARAM em cada etapa de um funil no periodo, lido do historico de movimentacao — DIFERENTE de `funnel_stages`, que conta quem ESTA na etapa agora: card que entrou e depois saiu aparece no stage_entries e SOME do funnel_stages (medido: 25% de diferenca). Use stage_entries pra 'quantos agendamentos/negocios aconteceram' e funnel_stages pra 'como esta o funil hoje'. stage_entries conta CARD DISTINTO (card que reentra na etapa conta uma vez) e o historico so existe a partir de 12/07/2026, com retencao de 365 dias — periodo anterior devolve menos do que aconteceu. PARAM NOVO `measure` em funnel_stages e stage_entries: 'count' (padrao, conta cards) ou 'value' (soma o VALOR dos cards = faturamento). Com status:'won' + date_basis:'closed' + measure:'value' sai o faturamento ganho da etapa no periodo. PERIODOS NOVOS: 'today' e 'yesterday'. CAMPO NOVO `title` em cada bloco: o nome que aparece no cabecalho. Sem ele varios blocos do mesmo tipo saem com rotulos identicos e nem a pessoa nem o agente sabem qual e qual — SEMPRE nomeie.",
25556
+ "category": "Relatorios",
25557
+ "params": [
25558
+ {
25559
+ "name": "widget_type",
25560
+ "type": "string",
25561
+ "location": "body",
25562
+ "required": false,
25563
+ "description": "OBRIGATORIO. Um de: conversations_timeseries (evolucao no tempo) | entity_ranking (comparar agente/caixa/equipe/etiqueta) | csat_summary (satisfacao) | sla_summary (cumprimento de prazo, exige o recurso 'sla') | funnel_stages (cards por etapa de UM funil, exige 'kanban_board') | lead_origin (origem do lead, exige 'liontrack') | agent_report (tabela de produtividade por operador)."
25564
+ },
25565
+ {
25566
+ "name": "chart_type",
25567
+ "type": "string",
25568
+ "location": "body",
25569
+ "required": false,
25570
+ "description": "line|bar (timeseries) · bar|table (entity_ranking, funnel_stages) · number (csat_summary, sla_summary) · bar|pie (lead_origin) · table (agent_report). Omitir aplica o padrao do tipo."
25571
+ },
25572
+ {
25573
+ "name": "metric",
25574
+ "type": "string",
25575
+ "location": "body",
25576
+ "required": false,
25577
+ "description": "timeseries: conversations | incoming_messages | outgoing_messages | resolutions | reopens | avg_first_response_time | avg_resolution_time. entity_ranking: conversations | resolutions | avg_first_response_time | avg_resolution_time. Os dois tempos vem em SEGUNDOS."
25578
+ },
25579
+ {
25580
+ "name": "dimension",
25581
+ "type": "string",
25582
+ "location": "body",
25583
+ "required": false,
25584
+ "description": "entity_ranking: agent|inbox|team|label. agent_report: agent|team|inbox (as LINHAS da tabela)."
25585
+ },
25586
+ {
25587
+ "name": "bucket",
25588
+ "type": "string",
25589
+ "location": "body",
25590
+ "required": false,
25591
+ "description": "So no timeseries: day | week | month."
25592
+ },
25593
+ {
25594
+ "name": "funnel_id",
25595
+ "type": "integer",
25596
+ "location": "body",
25597
+ "required": false,
25598
+ "description": "So no funnel_stages. Precisa ser funil DESTA conta."
25599
+ },
25600
+ {
25601
+ "name": "date_basis",
25602
+ "type": "string",
25603
+ "location": "body",
25604
+ "required": false,
25605
+ "description": "So no funnel_stages: created (entrou no funil) | moved (entrou na etapa, padrao) | closed (foi fechado) | any. Combine com status: closed+won = 'ganhos no periodo'."
25606
+ },
25607
+ {
25608
+ "name": "status",
25609
+ "type": "string",
25610
+ "location": "body",
25611
+ "required": false,
25612
+ "description": "So no funnel_stages: all | open | won | lost."
25613
+ },
25614
+ {
25615
+ "name": "scope_type",
25616
+ "type": "string",
25617
+ "location": "body",
25618
+ "required": false,
25619
+ "description": "ATENCAO — sentido DIFERENTE por tipo. No timeseries: 'o grafico e SO daquela entidade' (agent|inbox|team). No agent_report: 'mostre so as LINHAS de quem e dessa equipe/caixa'. Exige scope_id junto, senao e recusado."
25620
+ },
25621
+ {
25622
+ "name": "scope_id",
25623
+ "type": "integer",
25624
+ "location": "body",
25625
+ "required": false,
25626
+ "description": "Id da entidade do scope_type. Precisa ser DESTA conta."
25627
+ },
25628
+ {
25629
+ "name": "time_range",
25630
+ "type": "string",
25631
+ "location": "body",
25632
+ "required": false,
25633
+ "description": "last_7_days | last_30_days | this_month | last_month | custom. 'custom' SO existe em conversations_timeseries e agent_report, exige from+to, e a janela nao pode passar de 92 DIAS (o teto e da janela, vale pros dois). Pedir 'esse ano' e recusado."
25634
+ },
25635
+ {
25636
+ "name": "from",
25637
+ "type": "string",
25638
+ "location": "body",
25639
+ "required": false,
25640
+ "description": "AAAA-MM-DD. Obrigatorio quando time_range=custom."
25641
+ },
25642
+ {
25643
+ "name": "to",
25644
+ "type": "string",
25645
+ "location": "body",
25646
+ "required": false,
25647
+ "description": "AAAA-MM-DD. Obrigatorio quando time_range=custom."
25648
+ },
25649
+ {
25650
+ "name": "columns",
25651
+ "type": "array",
25652
+ "location": "body",
25653
+ "required": false,
25654
+ "description": "So no agent_report — as COLUNAS da tabela. Cada item: {metric, as?} com metric em leads | atendidos | resolvidas | abertas | pendentes | adiadas | label_count | conversao. label_count exige label_id (id de etiqueta DESTA conta). conversao exige denominator (uma das metricas do nucleo) e numerator = LISTA DE IDS DE ETIQUETA (ex: [5,9]) — NUNCA nome de metrica."
25655
+ },
25656
+ {
25657
+ "name": "timezone_offset",
25658
+ "type": "number",
25659
+ "location": "query",
25660
+ "required": false,
25661
+ "description": "Fuso em HORAS. NAO chute nem assuma -3: LEIA o fuso DA CONTA com `lionchat_account_show` (campo `timezone`, ex.: 'America/Sao_Paulo') e converta pra horas. O padrao da plataforma e America/Sao_Paulo (-3), mas a conta pode ter outro — Mato Grosso do Sul e -4, e cliente fora do Brasil existe. SEMPRE envie: ausente calcula em UTC e o numero nao bate com o que o cliente ve na tela. Em janela de UM DIA (time_range 'today'/'yesterday') isso e decisivo: uma hora de diferenca joga o que aconteceu de madrugada pro dia errado."
25662
+ },
25663
+ {
25664
+ "name": "measure",
25665
+ "type": "string",
25666
+ "location": "body",
25667
+ "required": false,
25668
+ "description": "Contar cards ('count', padrao) ou somar o VALOR deles ('value'). So em funnel_stages e stage_entries."
25669
+ },
25670
+ {
25671
+ "name": "title",
25672
+ "type": "string",
25673
+ "location": "body",
25674
+ "required": false,
25675
+ "description": "Nome do bloco, exibido no cabecalho (ate 80 caracteres). Vazio = mostra o rotulo do tipo."
25676
+ }
25677
+ ],
25678
+ "returns": "Um dos 4 formatos normalizados (series | categories | scalar | table), com o campo `shape` dizendo qual",
25679
+ "common_use_cases": [
25680
+ "Responder pergunta de relatorio que nenhuma outra ferramenta cobre",
25681
+ "Comparar atendentes/caixas/equipes num periodo",
25682
+ "Cards por etapa de um funil, com recorte por situacao e por data de fechamento",
25683
+ "Tabela de produtividade por operador com colunas escolhidas"
25684
+ ],
25685
+ "related_tools": [
25686
+ "lionchat_custom_dashboards_create",
25687
+ "lionchat_custom_dashboards_widget_data"
25688
+ ],
25689
+ "body_wrapper": "widget"
25690
+ },
25691
+ {
25692
+ "id": "lionchat_custom_dashboards_create",
25693
+ "method": "POST",
25694
+ "path": "/api/v2/accounts/{account_id}/custom_dashboards",
25695
+ "title": "Criar Relatorio Personalizado",
25696
+ "description": "[POST] Cria um relatorio personalizado que passa a APARECER NA TELA do cliente, na aba 'Personalizados' de Relatorios. ALTERA O QUE O CLIENTE VE: exige confirm:true depois de confirmar com ele. So administrador (401 caso contrario). Cada bloco de `widgets` usa os MESMOS campos do preview_widget (teste o desenho la antes de salvar) e PRECISA de um `id` proprio e unico — se faltar, o servidor gera. Um bloco invalido barra a criacao INTEIRA com 422 e o motivo em texto: nunca salva pela metade. Tetos: 50 relatorios por conta, 12 blocos por relatorio. Erros: 401 = quem chamou NAO e administrador (nao mande reconectar o conector). 422 com {error} = desenho invalido ou recurso nao liberado (code:'feature_unavailable' distingue) — o motivo vem em texto, corrija e tente de novo. 422 com {message,attributes} = teto atingido (50 relatorios por conta, 12 blocos por relatorio). TIPOS NOVOS (21/08): `calls_report` = tabela de LIGACOES (feitas, atendidas, nao atendidas, nao concluidas, tempo total e medio) por atendente (`dimension:'agent'`) ou por caixa (`dimension:'inbox'`); ligacao sem atendente ganha linha propria. `stage_entries` = quantos cards ENTRARAM em cada etapa de um funil no periodo, lido do historico de movimentacao — DIFERENTE de `funnel_stages`, que conta quem ESTA na etapa agora: card que entrou e depois saiu aparece no stage_entries e SOME do funnel_stages (medido: 25% de diferenca). Use stage_entries pra 'quantos agendamentos/negocios aconteceram' e funnel_stages pra 'como esta o funil hoje'. stage_entries conta CARD DISTINTO (card que reentra na etapa conta uma vez) e o historico so existe a partir de 12/07/2026, com retencao de 365 dias — periodo anterior devolve menos do que aconteceu. PARAM NOVO `measure` em funnel_stages e stage_entries: 'count' (padrao, conta cards) ou 'value' (soma o VALOR dos cards = faturamento). Com status:'won' + date_basis:'closed' + measure:'value' sai o faturamento ganho da etapa no periodo. PERIODOS NOVOS: 'today' e 'yesterday'. CAMPO NOVO `title` em cada bloco: o nome que aparece no cabecalho. Sem ele varios blocos do mesmo tipo saem com rotulos identicos e nem a pessoa nem o agente sabem qual e qual — SEMPRE nomeie.",
25697
+ "category": "Relatorios",
25698
+ "params": [
25699
+ {
25700
+ "name": "name",
25701
+ "type": "string",
25702
+ "location": "body",
25703
+ "required": true,
25704
+ "description": "Nome do relatorio, como aparece na aba do cliente"
25705
+ },
25706
+ {
25707
+ "name": "position",
25708
+ "type": "integer",
25709
+ "location": "body",
25710
+ "required": false,
25711
+ "description": "Ordem na lista (padrao 0)"
25712
+ },
25713
+ {
25714
+ "name": "widgets",
25715
+ "type": "array",
25716
+ "location": "body",
25717
+ "required": false,
25718
+ "description": "Lista de blocos. Cada item: {id, widget_type, chart_type, ...} com os mesmos campos do custom_dashboards_preview_widget. Maximo 12."
25719
+ }
25720
+ ],
25721
+ "returns": "{ id, name, position, widgets, created_at }",
25722
+ "common_use_cases": [
25723
+ "Montar pro cliente um relatorio que ele pediu em palavras",
25724
+ "Deixar salvo na tela um recorte que ele consulta toda semana"
25725
+ ],
25726
+ "related_tools": [
25727
+ "lionchat_custom_dashboards_preview_widget",
25728
+ "lionchat_custom_dashboards_update"
25729
+ ],
25730
+ "body_wrapper": "custom_dashboard",
25731
+ "confirm_required": true
25732
+ },
25733
+ {
25734
+ "id": "lionchat_custom_dashboards_update",
25735
+ "method": "PATCH",
25736
+ "path": "/api/v2/accounts/{account_id}/custom_dashboards/{id}",
25737
+ "title": "Alterar Relatorio Personalizado",
25738
+ "description": "[PATCH] Altera um relatorio personalizado. ALTERA O QUE O CLIENTE VE: exige confirm:true depois de confirmar com ele. So administrador (401 caso contrario). ATENCAO — `widgets` SUBSTITUI a lista inteira, nao acrescenta: mandar um bloco APAGA os outros. Leia o relatorio com custom_dashboards_list, altere o array e devolva-o COMPLETO. Se a listagem tiver vindo cortada, NAO use: voce apagaria o que nao chegou. Nao ha historico de alteracao — bloco apagado nao volta. Erros: 401 = quem chamou NAO e administrador (nao mande reconectar o conector). 422 com {error} = desenho invalido ou recurso nao liberado (code:'feature_unavailable' distingue) — o motivo vem em texto, corrija e tente de novo. 422 com {message,attributes} = teto atingido (50 relatorios por conta, 12 blocos por relatorio). TIPOS NOVOS (21/08): `calls_report` = tabela de LIGACOES (feitas, atendidas, nao atendidas, nao concluidas, tempo total e medio) por atendente (`dimension:'agent'`) ou por caixa (`dimension:'inbox'`); ligacao sem atendente ganha linha propria. `stage_entries` = quantos cards ENTRARAM em cada etapa de um funil no periodo, lido do historico de movimentacao — DIFERENTE de `funnel_stages`, que conta quem ESTA na etapa agora: card que entrou e depois saiu aparece no stage_entries e SOME do funnel_stages (medido: 25% de diferenca). Use stage_entries pra 'quantos agendamentos/negocios aconteceram' e funnel_stages pra 'como esta o funil hoje'. stage_entries conta CARD DISTINTO (card que reentra na etapa conta uma vez) e o historico so existe a partir de 12/07/2026, com retencao de 365 dias — periodo anterior devolve menos do que aconteceu. PARAM NOVO `measure` em funnel_stages e stage_entries: 'count' (padrao, conta cards) ou 'value' (soma o VALOR dos cards = faturamento). Com status:'won' + date_basis:'closed' + measure:'value' sai o faturamento ganho da etapa no periodo. PERIODOS NOVOS: 'today' e 'yesterday'. CAMPO NOVO `title` em cada bloco: o nome que aparece no cabecalho. Sem ele varios blocos do mesmo tipo saem com rotulos identicos e nem a pessoa nem o agente sabem qual e qual — SEMPRE nomeie.",
25739
+ "category": "Relatorios",
25740
+ "params": [
25741
+ {
25742
+ "name": "id",
25743
+ "type": "integer",
25744
+ "location": "path",
25745
+ "required": true,
25746
+ "description": "Id do relatorio personalizado"
25747
+ },
25748
+ {
25749
+ "name": "name",
25750
+ "type": "string",
25751
+ "location": "body",
25752
+ "required": false,
25753
+ "description": "Novo nome"
25754
+ },
25755
+ {
25756
+ "name": "position",
25757
+ "type": "integer",
25758
+ "location": "body",
25759
+ "required": false,
25760
+ "description": "Nova ordem na lista"
25761
+ },
25762
+ {
25763
+ "name": "widgets",
25764
+ "type": "array",
25765
+ "location": "body",
25766
+ "required": false,
25767
+ "description": "Lista COMPLETA de blocos — substitui a atual. Preserve o `id` de cada bloco que ja existia, senao a tela perde o vinculo. Maximo 12."
25768
+ }
25769
+ ],
25770
+ "returns": "{ id, name, position, widgets, created_at }",
25771
+ "common_use_cases": [
25772
+ "Acrescentar um bloco a um relatorio que ja existe",
25773
+ "Trocar o periodo ou o recorte de um bloco"
25774
+ ],
25775
+ "related_tools": [
25776
+ "lionchat_custom_dashboards_list",
25777
+ "lionchat_custom_dashboards_create"
25778
+ ],
25779
+ "body_wrapper": "custom_dashboard",
25780
+ "confirm_required": true
25781
+ },
25782
+ {
25783
+ "id": "lionchat_custom_dashboards_destroy",
25784
+ "method": "DELETE",
25785
+ "path": "/api/v2/accounts/{account_id}/custom_dashboards/{id}",
25786
+ "title": "Apagar Relatorio Personalizado",
25787
+ "description": "[DELETE] Apaga um relatorio personalizado da tela do cliente. SEM LIXEIRA E SEM DESFAZER. Exige confirm:true depois de confirmar com o usuario EXATAMENTE qual relatorio sera apagado (confira o nome com custom_dashboards_list antes). So administrador (401 caso contrario).",
25788
+ "category": "Relatorios",
25789
+ "params": [
25790
+ {
25791
+ "name": "id",
25792
+ "type": "integer",
25793
+ "location": "path",
25794
+ "required": true,
25795
+ "description": "Id do relatorio personalizado a apagar"
25796
+ }
25797
+ ],
25798
+ "returns": "204 sem corpo",
25799
+ "common_use_cases": [
25800
+ "Remover um relatorio que o cliente nao usa mais"
25801
+ ],
25802
+ "related_tools": [
25803
+ "lionchat_custom_dashboards_list"
25804
+ ],
25805
+ "confirm_required": true
25806
+ },
25459
25807
  {
25460
25808
  "id": "lionchat_inboxes_whatsapp_history_start",
25461
25809
  "method": "POST",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lionchat/mcp-server",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "MCP server for LionChat API — 547 tools + 10 resources + 9 prompts. Audit-log aware, completion API, progress notifications.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",