@lionchat/mcp-server 1.22.0 → 1.24.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.
@@ -170,10 +170,29 @@ Detalhes completos em `lionchat://docs/api-conventions` → "Wrapper de body".
170
170
  ### Regras de formato silenciosas
171
171
 
172
172
  - **Label.title**: kebab-case ou snake_case, **SEM espaço**. `"Lead Emive"` ❌ → `"lead-emive"` ✅
173
- - **win_reasons / loss_reasons**: array de `{id, title}`, NÃO strings
173
+ - **win_reasons / loss_reasons**: array de `{id, title}`, NÃO strings. O `id` é **TEXTO estável** (`"perda-preco"`),
174
+ nunca número. A lista SUBSTITUI a atual: leia antes e reenvie os MESMOS `id` (trocar o `id` tira o nome dos
175
+ cards já marcados). Motivo sem `id`/`title`, `id` repetido ou lista de textos → recusado com 422
176
+ - **motivo no card**: `item_details.status` `"lost"`/`"won"` + `loss_reason`/`win_reason` = o `id` (texto) de um
177
+ motivo cadastrado + `reason` = o título. Motivo livre: `loss_reason` nulo e o texto em `reason`
174
178
  - **linked_conversations**: array de `{display_id: N}`, NÃO inteiros direto
175
179
  - **phone**: E.164 (`+5511...`), sem espaços/parênteses
176
- - **funnel.stages** keys: snake_case slug
180
+ - **funnel.stages** keys: snake_case slug; cada etapa é OBJETO com `name` texto
181
+ - **Lista de objetos é lista de objetos**: onde o formato pede `[{id: 5}]`, número solto (`[5]`) ou objeto
182
+ único no lugar da lista quebra a leitura — às vezes a tela da conta inteira (incidente 18/09, funil).
183
+
184
+ ### Gravação: TROCA inteira × SOMA (conferido no código em 18/09/2026)
185
+
186
+ Muitos campos objeto/lista são **trocados inteiros** no update: o que não vier no envio é APAGADO. Nesses,
187
+ **leia antes** (show) e **reenvie tudo** com o item alterado. A descrição de cada ferramenta diz qual é qual.
188
+
189
+ | TROCA inteira (leia e reenvie tudo) | SOMA (só as chaves enviadas mudam) |
190
+ |---|---|
191
+ | `custom_attributes` da CONVERSA (`conversations_create_3` — só `captain_*` sobrevive; apaga utm/gclid/ctwa/origin) | `custom_attributes` do CONTATO (`contacts_update`) |
192
+ | card: cada chave de `item_details` (a lista `custom_attributes` do card, `offers`, `notes`), `assigned_agents`, coluna `custom_attributes` | `item_details` do card no 1º nível (chave não enviada fica) |
193
+ | `funnel.settings`, `funnel.stages` (etapa ausente = removida), `kanban_config.global_custom_attributes` | `channel.additional_attributes` da caixa WhatsApp/Instagram/Facebook/TikTok |
194
+ | `flow_data` do fluxo, `actions` de macro/automação, `query` de filtro salvo, `audience` de campanha | `roles_layout` do modelo de contrato |
195
+ | `settings` de integração (hooks), mapas de evento de gateway/Omie/Conta Azul/Meta Lead/Google Ads | |
177
196
 
178
197
  ### Native first
179
198
 
@@ -97,6 +97,31 @@ Variável desconhecida **sai crua** para o cliente ("Olá {{name}}, ...").
97
97
  horário** para a agenda — decisão do dono. Não use `attendance` para deduzir se o compromisso está
98
98
  ativo; para isso existe `status`.
99
99
 
100
+ Marcar Compareceu/Faltou também **não conclui a tarefa**: ela segue `pending` (e contando nas
101
+ pendências) até alguém concluir. É de propósito — concluir um compromisso de Booking encerra os lembretes
102
+ e as mensagens pós-atendimento dele e dispara o gatilho de fluxo "agendamento concluído". **Nunca conclua
103
+ a tarefa só porque a presença foi marcada.**
104
+
105
+ Para responder "o que aconteceu com este compromisso?" use o campo **`selo`** da tarefa (17/09), que já
106
+ junta os dois eixos e a remarcação: `cancelled` > `no_show` / `attended` > `completed` > `snoozed` >
107
+ `rescheduled` > `pending`. É o mesmo rótulo que o painel mostra ao lado da tarefa (Faltou, Compareceu,
108
+ Remarcada...). É só de leitura.
109
+
110
+ ### Responsável de compromisso de Booking é FIXO
111
+
112
+ O compromisso marcado por um tipo de evento (Booking) cai SEMPRE na agenda do profissional configurado
113
+ nele. Na criação, `assignee_ids` nunca valeu; desde 17/09 também é **ignorado na edição**
114
+ (`lionchat_tasks_update` / `_update_1`): a resposta é 200, o resto da edição vale e o responsável continua
115
+ o mesmo. Para mudar quem atende, altere o profissional do tipo de evento (`booking_event_types_update`).
116
+
117
+ ### Ver como está o dia antes de marcar
118
+
119
+ `lionchat_agent_availability_day` (17/09) devolve UM dia de até 6 pessoas: o expediente de cada uma e os
120
+ intervalos já ocupados, em "HH:MM" do fuso pedido. O intervalo ocupado de qualquer pessoa da conta vem
121
+ sempre (é o que permite marcar com um colega); o título do compromisso só vem para quem já o veria no
122
+ calendário, e compromisso privado de outra pessoa nunca vem com título. Para Booking, os horários válidos
123
+ continuam sendo os de `booking_event_types_slots` — essa ferramenta é para compromisso COMUM.
124
+
100
125
  ---
101
126
 
102
127
  ## 5. Google Calendar é UMA CONEXÃO POR AGENDA
@@ -116,12 +141,55 @@ Google dela — inclusive a **mesma pessoa** com contas Google diferentes, uma p
116
141
 
117
142
  - Agenda **sem** pessoas e **sem** equipes: todo mundo da conta enxerga.
118
143
  - Marcando pessoas e/ou equipes: só elas. Quem entrar na equipe ganha acesso sozinho.
119
- - **Administrador enxerga todas**, sempre.
144
+ - **Administrador enxerga todas**, sempre. Desde 21/09, **quem tem a caixinha `agenda_manage`** ("Gerenciar
145
+ agenda da equipe") também enxerga todas — é a função da secretária.
120
146
  - `member_user_ids` e `member_team_ids` **substituem a lista inteira** (idioma do `InboxMember`).
121
147
 
148
+ ### `agenda_manage`: a caixinha da secretária (21/09)
149
+
150
+ Permissão de função personalizada (`custom_roles.permissions`). Quem a tem, **sem ser administrador**:
151
+ - vê a agenda e os compromissos de TODA a conta (`tasks_list` deixa de recortar por pessoa);
152
+ - cria, edita, remarca, cancela e exclui compromisso de qualquer pessoa;
153
+ - marca pelo `booking_event_type` de qualquer pessoa (`tasks_create` com `booking_event_type_id` de outro);
154
+ - cria/edita/apaga `booking_event_types` de outra pessoa (inclusive mandando `user_id` de outro no create);
155
+ - lê e ajusta a disponibilidade de outra pessoa (`agent_availability_*`) e abre o painel de Agentes.
156
+
157
+ **NÃO** permite criar/editar/apagar AGENDA (unidade): `agendas_create/update/destroy` seguem só administrador —
158
+ agenda é recurso do plano, com limite. Sem a caixinha, o atendente vê só a própria agenda e a de quem ligou
159
+ `agenda_public` no perfil, e não mexe na dos outros.
160
+
122
161
  A listagem de compromissos já vem recortada pelas agendas que o usuário enxerga, **mesmo sem mandar
123
162
  `agenda_id`**. Ler ou editar compromisso de uma agenda que a pessoa não enxerga devolve 404.
124
163
 
164
+ **Quem não enxerga NENHUMA agenda não vê compromisso nenhum** (21/09). Antes, "lista de agendas
165
+ visíveis vazia" abria em vez de fechar: o recorte tratava igual dois estados opostos — *a conta não
166
+ tem agenda nenhuma* (aí não há o que recortar, devolve tudo) e *a conta tem agendas e esta pessoa não
167
+ foi posta em nenhuma* (aí ela não pode ver nada). Medido: atendente fora das duas agendas da conta
168
+ enxergava todos os compromissos.
169
+
170
+ ## 6.1. Toda conta tem a agenda "Principal" (21/09)
171
+
172
+ Antes, a agenda de uma conta era **implícita**: não havia linha no banco e todo compromisso nascia com
173
+ `agenda_id` NULO. A aba Agendas abria dizendo "Nenhuma agenda ainda" numa conta que **tinha** agenda —
174
+ e não havia onde escolher quem a enxerga.
175
+
176
+ Hoje `Agenda.garantir_padrao!` (chamada no `index`) materializa essa agenda como **Principal**, marcada
177
+ `is_default`, com o fuso da conta. Vale para **toda conta, com ou sem o recurso de múltiplas agendas**:
178
+ quem tem uma agenda só pode editá-la e escolher equipes/pessoas — só não pode **criar outra**, o que
179
+ continua barrado por `Agendas::Quota` (motivo `feature_off`, que é avaliado ANTES do número).
180
+
181
+ **NEVER CHANGE / GOTCHAS**
182
+ - A condição é "não existe agenda **PADRÃO**", nunca "não existe agenda": enquanto o defeito do
183
+ `is_default` esteve no ar, quem criou agenda ficou **sem padrão** e com os compromissos antigos
184
+ invisíveis. Perguntar só por "existe agenda?" deixaria essas contas quebradas para sempre.
185
+ - **Não promover** uma agenda existente a padrão: ela é um LUGAR que o cliente nomeou (uma filial), e
186
+ herdar os compromissos da agenda de antes jogaria o histórico inteiro dentro da filial errada.
187
+ - A Principal nasce **sem membros** — lista vazia é visível a todos, então ninguém perde acesso no
188
+ deploy.
189
+ - Criar agenda: a pergunta "é a primeira?" tem que ir ao **banco** (`exists?`) e **antes** do `.new`.
190
+ `conta.agendas.new(...)` ACRESCENTA o registro não salvo à coleção em memória, então o `.empty?`
191
+ que existia ali respondia sempre `false` e `is_default` **nunca** era marcado.
192
+
125
193
  ---
126
194
 
127
195
  ## 7. Programa de sessões (15/09)
@@ -73,7 +73,61 @@ Praticamente nunca — exceto se você está testando legacy/curl e quer ser exp
73
73
  }
74
74
  ```
75
75
 
76
- Strings simples (`"win_reasons": ["motivo"]`) causam erro de serialização.
76
+ Regras (conferidas no servidor desde 21/09):
77
+
78
+ - O `id` é **texto estável** (`"wr-1"`, `"perda-preco"`) — **nunca número**. Número é convertido para texto
79
+ (`7` vira `"7"`), mas o certo é já mandar texto. Motivo cadastrado com `id` numérico derrubava o relatório do
80
+ funil (conta 137, 21/09).
81
+ - Como a lista **substitui** a atual, **reenvie os mesmos `id`** dos motivos que já existem. Trocar o `id` de um
82
+ motivo faz os cards já marcados com ele perderem o nome no relatório.
83
+ - Recusado com **422** (mensagem diz o formato): lista de textos soltos (`["motivo"]` — antes APAGAVA os motivos da
84
+ conta em silêncio), motivo sem `id`, motivo sem `title`, `id` repetido. O servidor **não inventa** `id`.
85
+ - Lista vazia (`[]`) limpa os motivos.
86
+
87
+ **Marcar o card com o motivo** (`PATCH /kanban_items/{id}`, `item_details` mescla no 1º nível):
88
+
89
+ ```json
90
+ { "item_details": { "status": "lost", "loss_reason": "perda-preco", "reason": "Preço alto" } }
91
+ ```
92
+
93
+ `loss_reason`/`win_reason` = o `id` (texto) de um motivo cadastrado; `reason` = o título. Motivo livre (fora da
94
+ lista): `loss_reason` nulo e o texto em `reason`.
95
+
96
+ ### Outras listas de OBJETOS — formato conferido no servidor (desde 21/09)
97
+
98
+ Estes campos são **listas de objetos**. Mandar lista de textos/números soltos, lista misturada (alguns objetos e
99
+ alguns textos) ou um objeto solto no lugar da lista é recusado com **422**, e a mensagem mostra o formato certo.
100
+ Antes o servidor aceitava, gravava **lista vazia** e respondia 200 — ou seja, apagava a configuração do cliente.
101
+
102
+ | Ferramenta | Campo | Formato |
103
+ |---|---|---|
104
+ | `custom_dashboards_create/update` | `widgets` | `[{"widget_type": "conversations_timeseries", "metric": "conversations", ...}]`; dentro de cada bloco, `columns` também é lista de objetos |
105
+ | `kanban_config_update` | `global_custom_attributes` | `[{"name": "Origem", "type": "text"}]` (`list_values` é lista de textos) |
106
+ | `kanban_config_update` | `checklist_templates` | `[{"name": "Onboarding", "items": [{"text": "Ligar"}]}]` — `items` é lista de objetos |
107
+ | `funnels_create/update` | `global_custom_attributes` | igual ao do Kanban |
108
+ | `booking_event_types_create/update` | `confirmation_blocks` e `follow_up_schedules_attributes[].blocks` | `[{"type": "text", "content": "Olá"}]` |
109
+ | `inboxes_update` | `csat_config.survey_rules` | **objeto** `{"operator": "contains", "values": ["vip"]}` — `values` é lista de textos |
110
+
111
+ Continua valendo: a lista **substitui** a anterior (leia com GET e reenvie inteira) e `[]` limpa.
112
+ Recebeu 422 de formato? **Não repita a mesma chamada**: leia a mensagem, corrija o formato e mande de novo.
113
+
114
+ ### Recusa por PERMISSÃO (401) — o que mudou em 21/09
115
+
116
+ O servidor responde **401** (não 403) quando o usuário do token não tem a permissão. Não retente; explique ao usuário.
117
+
118
+ - **Fluxos** (`flows_create/update/destroy/toggle`, duplicar, fixar teste, testar nó): só **administrador** ou cargo com
119
+ **Gerenciar FlowBuilder**. `flows_list` e `flows_show` seguem abertos. "Gerenciar Regras de Automação" **não** libera fluxo.
120
+ - **Respostas de formulário** (`lead_forms_responses_list/show`, `lead_forms_stats`, `lead_forms_test_run`): administrador ou
121
+ **Gerenciar Formulários**. As respostas de UM contato continuam em `contacts_form_entries_list`.
122
+ - **Mesclar contatos** (`contacts_create_4` — "Mesclar Contatos", `POST actions/contact_merge`): administrador ou **Gerenciar contatos**
123
+ (mesclar apaga uma das fichas).
124
+ - **Configuração do Kanban** (`kanban_config_update`): motivos de ganho/perda = qualquer usuário; campos do card, checklists e
125
+ `config` = administrador ou **Gerenciar Funis**; webhook e liga/desliga = só administrador. A recusa lista as chaves barradas.
126
+ - **Card do Kanban**: `kanban_items_show/update` e os arquivos do card devolvem **404** para card que o usuário não enxerga no
127
+ quadro. Cargo sem opção de Kanban só edita o card dele ou o sem responsável.
128
+ - **Tipo de agendamento** (`booking_event_types_update/destroy`): só o dono do link ou administrador.
129
+ - **Relatório do Kanban** (`kanban_items` reports): traz `metrics.visibilityScope` — `partial` significa "só os cards que ESTE
130
+ usuário vê". Diga isso ao usuário antes de apresentar o número como total da conta.
77
131
 
78
132
  ## Restrições de formato em campos comuns
79
133
 
@@ -282,7 +336,7 @@ Vale pra automações, filtro avançado de conversas/contatos e busca "+ Filtro"
282
336
  | Código | Significado | O que fazer |
283
337
  |---|---|---|
284
338
  | `400` Bad Request | Parâmetros mal formados | Confira sintaxe |
285
- | `401` Unauthorized | Token inválido ou expirado | Reporte ao usuário (não retente) |
339
+ | `401` Unauthorized | Token inválido/expirado **ou usuário sem a permissão** (o LionChat recusa permissão com 401, não 403) | Reporte ao usuário (não retente) |
286
340
  | `403` Forbidden | Sem permissão (papel insuficiente) | Reporte (não retente) |
287
341
  | `404` Not Found | Recurso não existe (ou de outra conta) | Confira ID |
288
342
  | `422` Unprocessable Entity | Validação falhou | Leia mensagem, corrija |
@@ -172,10 +172,13 @@ valores desta lista:
172
172
  | Econômicos / rápidos | `gpt-4.1-nano`, `gpt-4o-mini`, `gpt-4.1-mini`, `gpt-5.4-nano`, `gpt-5.4-mini` |
173
173
  | Intermediários | `gpt-4o`, `gpt-4.1`, `gpt-5-mini`, `gpt-5.4` |
174
174
  | Raciocínio | `o3-mini`, `o4-mini` |
175
- | Premium | `gpt-5`, `gpt-5.2`, `gpt-5.5`, `gpt-5.6-luna`, `gpt-5.6-terra`, `gpt-5.6-sol`, `o1`, `o3` |
175
+ | Premium | `gpt-5`, `gpt-5.2`, `gpt-5.5`, `gpt-5.6-luna`, `gpt-5.6-terra`, `gpt-5.6-sol`, `gpt-6-sol`, `o1`, `o3` |
176
+ | Custo-benefício (nova geração) | `gpt-6-luna` — US$ 0,10/0,50 por 1M tokens, raciocínio, sem temperatura; liberado em 23/09/2026 |
176
177
 
177
178
  **`GPT-5.2 Pro` não existe.** O valor válido é `gpt-5.2`, sem "Pro".
178
179
 
180
+ > **Família `gpt-6` (`gpt-6-luna`, `gpt-6-sol`) — LIBERADA em 23/09/2026**, pelo mesmo trilho do `gpt-5.6`: sem temperatura e fora do bloco de IA do Formulário de Lead. `gpt-6-astra` NÃO está disponível.
181
+ >
179
182
  > **Família `gpt-5.6` (`gpt-5.6-luna`, `gpt-5.6-terra`, `gpt-5.6-sol`) — LIBERADA em 09/2026.**
180
183
  > Foi removida em 29/07/2026 porque recusa function tools no canal antigo da OpenAI e deixava a IA MUDA.
181
184
  > Desde 09/2026 o servidor fala com esses três pela porta `/v1/responses` (só para eles — nenhum outro
@@ -190,6 +193,47 @@ valores desta lista:
190
193
  > Desde 29/07 o erro vira uma **nota privada em português** na conversa (`llm_request_rejected`) — é o
191
194
  > primeiro lugar a olhar quando "a IA parou de responder logo depois de mexerem nas configurações".
192
195
 
196
+ ## Ligação por IA — Agentes de ligação (2026-09-24)
197
+
198
+ A ligação por voz **saiu do agente de texto**. Desde 24/09 ela mora em **Agentes de ligação**, uma área
199
+ própria: cada agente de ligação tem um objetivo e **não ocupa vaga de IA do plano**. As chaves antigas
200
+ `config.call_*` do `captain_assistants_update` **não existem mais** — mandar elas não tem efeito.
201
+
202
+ | Ferramenta | Para quê |
203
+ |---|---|
204
+ | `captain_call_agents_list` | lista os agentes de ligação e `meta.dial_inboxes` (caixas que podem ligar) |
205
+ | `captain_call_agents_create` / `_update` / `_duplicate` / `_destroy` | cadastro |
206
+ | `captain_call_test_phones_list` / `_create` / `_destroy` | números autorizados a receber ligação de teste |
207
+ | `captain_call_agents_test_call` | **liga de verdade** para um número de teste cadastrado |
208
+ | `captain_call_agents_test_call_status` | tocando / atendeu / não atendeu, e em qual conversa ficou |
209
+
210
+ Campos do agente de ligação: `name`, `objective` (roteiro, até 4000), `first_message` (até 400), `style`
211
+ (jeito de falar, até 1200), `voice_provider` (`openai` padrão ou `elevenlabs`), `engine` (`gptlive` padrão
212
+ ou `realtime`, só na OpenAI), `voice_id`, `elevenlabs_agent_id` (obrigatório na ElevenLabs),
213
+ `max_duration_seconds` (30 a 600, padrão 180) e **`captain_assistant_id`**.
214
+
215
+ **Como a ligação trabalha (GPT-Live).** A voz conversa e **só consulta** agenda (horários livres, agenda do
216
+ atendente, quem está trabalhando). Quando o cliente pede algo que precisa ser FEITO — mandar link,
217
+ endereço ou proposta pelo WhatsApp, mover o card, anotar — a voz **repassa** ao agente de texto de
218
+ `captain_assistant_id`, diz "já pedi, vai chegar no seu WhatsApp" e **segue a conversa** (nunca diz "já
219
+ mandei"). O agente de texto executa na conversa daquele contato, com as ferramentas e a configuração
220
+ DELE, e decide se escreve ao cliente. Regras fixas desse pedido: roda mesmo com atendente humano na
221
+ conversa ou com a IA pausada; não liga a IA na conversa nem mexe na IA que já estava lá; não passa para
222
+ humano, não encerra, não transfere e não agenda retorno (fluxos rodam completos); no máximo 3 pedidos
223
+ por ligação; caixa oficial fora da janela de 24h faz as ações mas não escreve.
224
+ Sem `captain_assistant_id` (ou no motor `realtime`/ElevenLabs), a voz **só conversa**.
225
+
226
+ **Voz.** É **própria** do agente de ligação — não mexe na voz do áudio da IA no WhatsApp
227
+ (`config.voice_provider`/`config.voice_id` do agente de texto continuam sendo só do WhatsApp). No GPT-Live
228
+ use `captain_voices_list` com `engine=gptlive`: aceita alloy, ash, ballad, coral, echo, sage, shimmer, verse,
229
+ **marin** e **cedar** (as duas mais naturais de telefone); voz fora disso cai na marin.
230
+
231
+ **Ligação de teste.** Só para número **cadastrado** em `captain_call_test_phones` (o telefone nunca vai no
232
+ corpo, só o id). Cria contato e conversa de verdade, pode disparar automações de "conversa criada" e fica
233
+ gravada. Limite de 10 por hora na conta. Precisa da Ligação com IA liberada na conta e da linha da caixa
234
+ conectada; recusa volta 422 com o motivo em `message`. Depois que a ligação termina, a transcrição do
235
+ próprio fornecedor aparece no card da ligação.
236
+
193
237
  ## Follow-up automático multi-etapa (2026-06)
194
238
 
195
239
  O follow-up do AI Agente agora suporta **cadência de até 3 etapas** via `config.follow_up_steps`:
@@ -463,7 +507,13 @@ puxados. Se a ativação reclamar de sample, NÃO é bloqueio — a integração
463
507
  - `ContactLabel` `{id}` — etiqueta de CONTATO (novo)
464
508
  - `ContactAttribute` `{key, value}` — atributo do contato (novo)
465
509
  - `CardAttribute` `{key, value}` — atributo do card Kanban (novo)
466
- - `AgentTeam` `{assignee_ids:[], team_ids:[]}` — responsável atual OU time da conversa (novo)
510
+ - `AgentTeam` `{assignee_ids:[], team_ids:[], assignee_mode?, team_mode?}` — responsável atual OU time da
511
+ conversa. **Modos (24/09/2026):** `assignee_mode:'none'` = conversa SEM responsável ("Sem atendente"),
512
+ `'any'` = com qualquer responsável; `team_mode:'none'|'any'` igual para time. Ausente/`'specific'` = usa os
513
+ ids (comportamento de sempre). Vale QUALQUER conversa do contato, de qualquer status. Atendente e time
514
+ SEMPRE somam (um OU outro) — inclusive em blocos `AgentTeam` separados, que viram UMA seção só; o
515
+ `audience_mode:'all'` cruza o AgentTeam com as OUTRAS seções (etiqueta, funil, atributo), não atendente com
516
+ time. "Sem atendente E com time" não é montável hoje. Mesmos modos na `exclusion`.
467
517
 
468
518
  O campo `audience_mode` define a combinação ENTRE seções:
469
519
  - `"sum"` (default): união — contato em QUALQUER seção entra
@@ -932,3 +982,22 @@ Em `custom_roles_create` / `custom_roles_update`, a `description` é **uma frase
932
982
  na descrição** — a tela já as mostra na coluna "Permissões". Uma descrição de 180 caracteres
933
983
  escondeu o cargo recém-criado na lista de Funções personalizadas da conta 1 (a tela foi corrigida
934
984
  para cortar o texto com reticências, mas a descrição longa continua ilegível ali).
985
+
986
+ ## Entrar num grupo por convite (17/09/2026)
987
+
988
+ Só em caixa de WhatsApp QR Code (`Channel::Waha`). São DUAS tools e a ordem importa.
989
+
990
+ 1. `lionchat_inboxes_waha_groups_list_3` (join_info) — SÓ LEITURA: diz qual é o grupo (`Name`,
991
+ `ParticipantCount`, `IsJoinApprovalRequired`). **Chame sempre primeiro** e confirme com a pessoa
992
+ em qual grupo ela vai entrar. Entrar é ação que muda o WhatsApp do cliente.
993
+ 2. `lionchat_inboxes_waha_groups_create_6` (join) — entra, como participante comum.
994
+
995
+ As duas aceitam o código cru, o link inteiro (`https://chat.whatsapp.com/CODIGO`) ou um texto com o
996
+ link no meio — não precisa extrair o código antes.
997
+
998
+ **Recusa esperada:** 422 com `reason: groups_disabled` quando a caixa está com "Aceitar grupos"
999
+ desligado. Não insista nem tente contornar: sem essa chave o WhatsApp nem entrega as mensagens do
1000
+ grupo, então o número entraria num grupo que nunca apareceria no painel. O caminho é o cliente ligar
1001
+ a opção nas configurações da caixa e tentar de novo.
1002
+
1003
+ A resposta do join_info vem no formato cru do motor, em PascalCase (`Name`, não `name`).
@@ -215,7 +215,8 @@ Funnel
215
215
  ├── name
216
216
  ├── stages (jsonb: hash com slug_etapa => { name, color, position, description, checklist_templates })
217
217
  ├── settings (jsonb: { agents: [], goals: [], automations: [{trigger_type, action, action_config, enabled}] })
218
- ├── global_custom_attributes (jsonb array)
218
+ ├── global_custom_attributes (jsonb array — NENHUMA tela lê este campo do funil; os campos da aba do card
219
+ │ são os do KanbanConfig acima)
219
220
  ├── meta_events_config (jsonb: won/lost/stages → Meta Pixel/CAPI events; cada bloco: enabled, name,
220
221
  │ is_standard, value_strategy, value_fixed, currency, messaging_name — este último é o evento de
221
222
  │ ANÚNCIO DE WHATSAPP, tri-estado: ausente = automático, '' = não enviar, um dos 14 nomes da Meta;
@@ -241,9 +242,10 @@ KanbanItem
241
242
  ├── stage_entered_at (datetime)
242
243
  ├── position (int, ordem dentro da etapa)
243
244
  ├── conversation_display_id (FK opcional → Conversation.display_id)
244
- ├── item_details (jsonb)
245
- ├── custom_attributes (jsonb)
246
- ├── assigned_agents (jsonb array)
245
+ ├── item_details (jsonb — os campos da aba Campos ficam em item_details.custom_attributes, LISTA de
246
+ │ {name, type, value}; nunca objeto {chave: valor})
247
+ ├── custom_attributes (jsonb — coluna que NENHUMA tela mostra; nao confundir com a lista acima)
248
+ ├── assigned_agents (jsonb array — para gravar: lista de NUMEROS [5, 12])
247
249
  ├── linked_conversations (jsonb array de { display_id })
248
250
  ├── checklist (jsonb array de itens — NAO e tabela separada; ver abaixo)
249
251
  ├── activities (jsonb)
@@ -408,9 +410,16 @@ AccountTask (agenda interna)
408
410
  │ (painel|link|ia|sistema|fluxo) — QUEM agendou e por onde (14/09)
409
411
  ├── attendance (enum attended=0 / no_show=1, NULO = nada registrado — CARIMBO, não muda status)
410
412
  │ / attendance_by (polimórfico) / attendance_source — quem marcou Compareceu/Faltou (14/09)
413
+ ├── selo (SÓ LEITURA, calculado — 17/09): o rótulo ÚNICO que a tela mostra ao lado da tarefa, juntando
414
+ │ `status` + `attendance` + remarcação: cancelled > no_show / attended > completed > snoozed >
415
+ │ rescheduled > pending. `rescheduled` = data trocada pelo painel/fluxo/IA (`reschedule_source`) OU pelo
416
+ │ cliente no link do agendamento. Para dizer "o que aconteceu com este compromisso", LEIA `selo` —
417
+ │ `status: pending` sozinho NÃO significa "nada aconteceu" (a pessoa pode ter faltado)
411
418
  ├── agenda_treatment_id (FK → AgendaTreatment, nullify) / treatment_session_number (1..N) — a sessão
412
419
  │ que este compromisso é; índice único parcial por tratamento+número entre os NÃO cancelados (15/09)
413
- └── assignees (has_many → User, via account_task_assignments)
420
+ └── assignees (has_many → User, via account_task_assignments). Compromisso NASCIDO DE BOOKING
421
+ (`booking_id` presente) tem responsável FIXO = o agente configurado no tipo de evento:
422
+ `assignee_ids` enviado no update é IGNORADO (200, o resto da edição vale — 17/09)
414
423
 
415
424
  AgendaTreatment (tratamento em sessões — tools `agenda_treatments_*`; tabela agenda_treatments; 15/09)
416
425
  ├── id (PK) / account_id (FK) / contact_id (FK) / booking_event_type_id (FK, nullify)
@@ -485,7 +494,7 @@ daquele tipo no calendário; vazio = cor do agente. A tarefa devolve `booking_co
485
494
 
486
495
  ## e-Clínicas (Efficient) — integração de clínicas
487
496
 
488
- 17 eventos de webhook, cada um mapeável a automação OU flow na tela Integrações > e-Clínica
497
+ 19 eventos de webhook, cada um mapeável a automação OU flow na tela Integrações > e-Clínica
489
498
  (o mapeamento e os lembretes passaram a ser editáveis por MCP em 31/08/2026, com
490
499
  `lionchat_eclinica_integrations_update` — ele **SUBSTITUI** o objeto inteiro, então leia com
491
500
  `_show` e devolva completo, senão apaga o resto):
@@ -495,7 +504,10 @@ daquele tipo no calendário; vazio = cor do agente. A tarefa devolve `booking_co
495
504
  `cliente_inclusao_pagamento`, `controle_laboratorio_novo`, `controle_laboratorio_alterado`,
496
505
  `agendamento_aguardando`, `odontograma_aprovado`, `procedimento_finalizado`,
497
506
  `cliente_alteracao_pagamento`, `odontograma_finalizado`, `inclusao_procedimento`
498
- (capturados ao vivo, não documentados pelo e-Clínica).
507
+ (capturados ao vivo, não documentados pelo e-Clínica) +
508
+ `agendamento_confirmado_link` e `agendamento_cancelado_link` (18/09/2026 — o PACIENTE confirmando/
509
+ cancelando pelo link do portal, ecli.co; confirmado processa como `agendamento_alterado`, mantendo o
510
+ lembrete; cancelado processa EXATAMENTE como `agendamento_desmarcado`, cancelando o lembrete).
499
511
 
500
512
  O recurso `eclinica_integration` da conta passou a ser ligado e desligado pelo PRÓPRIO cadastro da
501
513
  integração (10/09/2026, `auto_managed`): sumiu do Super Admin e não há passo manual — os blocos de
@@ -606,6 +618,21 @@ da consulta) — em vez de herdar sempre o horário da consulta. Há ainda um fi
606
618
  ("Só quando") por atributo `eclinica_*` do contato. Nada disso é editável pelo MCP: só pelo painel.
607
619
  Pra auditar o que foi/será disparado, use `lionchat_eclinica_reminder_history_list`.
608
620
 
621
+ **Um aviso por CONSULTA (desde 2026-09-17).** A regra antiga "um lembrete por dia" saiu: paciente com duas
622
+ consultas no mesmo dia — ou duas pessoas no mesmo celular (mãe e filho) — recebe um lembrete para CADA
623
+ consulta, cada um com o seu nome, horário, profissional e link. Eles não saem juntos: sai o da consulta
624
+ mais cedo e, ~10 minutos depois (na rodada seguinte), o da próxima. No histórico
625
+ (`lionchat_eclinica_reminder_history_list`) isso aparece assim: o que espera a vez fica `pending` com
626
+ `fire_at` no passado por alguns minutos (normal); `skip_reason = outra_consulta_no_mesmo_dia` só existe em
627
+ linhas ANTIGAS (até 17/09); e `fail_reason = fluxo_ainda_em_andamento_com_este_contato` significa que o
628
+ fluxo do aviso anterior ainda estava em andamento com aquele contato (típico de fluxo que aguarda o
629
+ paciente clicar num botão) — pode ser reenviado com `lionchat_eclinica_reminder_history_reprocess`.
630
+ O fluxo do lembrete recebe também `{{cliente_id}}` (código do paciente DAQUELA consulta na e-Clínica) e
631
+ `{{agendatipo}}`; na automação, `{{agendamento.cliente_id}}` e `{{agendamento.agendatipo}}`. Ao consultar a
632
+ agenda do paciente dentro do fluxo, use `{{cliente_id}}` do lembrete — a ficha do contato é uma por
633
+ TELEFONE e guarda só o último paciente. Quem quiser "só um lembrete por dia" monta no próprio fluxo
634
+ (condição num atributo da conversa com `{{data_consulta}}` + ação que grava o dia depois de enviar).
635
+
609
636
  ## Relatórios e Métricas
610
637
 
611
638
  ```
@@ -36,6 +36,14 @@ E/OU é encadeado condição a condição (sem parênteses).
36
36
  Atributo personalizado: informe `custom_attribute_type` na condição — `'conversation_attribute'`
37
37
  (atributo da conversa) ou `'contact_attribute'`. Sem ele o escopo pode sair errado.
38
38
 
39
+ **`contact_attribute` lê a FICHA DO CONTATO da conversa (funciona de verdade desde 24/09/2026).** Antes o
40
+ servidor ignorava o valor e lia o atributo da própria conversa — por isso lead de formulário da Meta
41
+ (formulário, anúncio, campanha e 1ª/última origem ficam SÓ no contato) não era achado. Agora a conversa entra
42
+ quando o contato dela casa. Chave precisa ter cadastro de atributo DE CONTATO na conta (senão 422). Exemplo:
43
+ `{attribute_key:'meta_lead_form_name', custom_attribute_type:'contact_attribute', filter_operator:'contains',
44
+ values:['vendas']}`. Negativos (`not_equal_to`, `does_not_contain`, `is_not_present`) incluem a conversa cujo
45
+ contato não tem o dado. Texto compara sem maiúscula/acento.
46
+
39
47
  - `chat_type` em `conversations_filter` funciona desde 19/08/2026 (antes respondia 500). `group` =
40
48
  conversa de grupo de WhatsApp; `individual` cobre também canal, transmissão e status. Com
41
49
  `not_equal_to`, conversa sem contato vinculado NÃO some do resultado.
@@ -162,6 +170,13 @@ Params: `funnel_id` (obrigatório), `from`, `to`, `user_ids[]` (agentes), `team_
162
170
  | `moved` | só quando entrou na etapa atual (mesma expressão do `stage_date_*` da seção 3) | "o que se mexeu no período" |
163
171
  | `closed` | só ganhos/perdidos no período | **a pergunta de receita** — é a régua certa pra "quanto vendemos em julho" |
164
172
 
173
+ **Ganho/perda pela DATA DO FECHAMENTO (23/09/2026).** Com a régua `any` o relatório traz também cards que
174
+ nasceram no período mas fecharam DEPOIS dele. Por isso, para "quanto ganhamos/perdemos em julho", leia
175
+ `wonInPeriod`/`wonValueInPeriod` e `lostInPeriod`/`lostValueInPeriod` (este último é campo NOVO) — **nunca**
176
+ `statusDistribution`, que é a foto do status de HOJE (serve só para "negócios abertos agora"). Desde 23/09 os
177
+ motivos de ganho/perda, os perdidos recentes, a receita por semana e o ranking de vendedores também contam só
178
+ o que FECHOU no período — antes somavam por status de hoje e divergiam do quadro de Ganhos na mesma tela.
179
+
165
180
  O relatório de UMA etapa — tool `lionchat_funnels_stage_report` (`GET /funnels/:id/stage_report`, o
166
181
  painel que abre dentro do quadro) — tem as MESMAS 4 opções de régua, mesmo parâmetro `date_basis` e as
167
182
  MESMAS expressões da seção 3, mas o **padrão dele é `moved`**, não `any`. Não é inconsistência: ele
@@ -140,6 +140,7 @@ Webhooks embutidos NÃO aparecem na listagem de integrações standalone; exclui
140
140
  **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). Há também `{{booking.event}}` (`created`|`cancelled`|`rescheduled`|`completed` — bom pra condição; não aparece no seletor da tela, mas resolve); `{{trigger.type}}` = a chave do gatilho (ex.: `booking_created`), `{{trigger.kind}}` = `created`|`cancelled`|`rescheduled`|`completed` e `{{trigger.booking_id}}`. SÓ flow de conversa **individual** (a tela nem oferece em flow de grupo); **nunca dispara flow `ai_tool`**. Em caixa WhatsApp OFICIAL, conversa criada nova (ou parada há mais de 24h) está FORA da janela — texto livre no 1º bloco cai em `window_closed`/erro; comece por template.
141
141
 
142
142
  **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).
143
+ - **`date_trigger` separa por HORÁRIO (24/09/2026):** dois flows no mesmo campo de data, mesma direção e mesmos dias NÃO colidem se os dois tiverem horário FIXO diferente (ex.: "1 dia depois às 08:00" e "às 14:00"). Horário vindo de atributo/fórmula continua colidindo (só existe na hora do disparo); `send_time` vazio conta como 09:00 e "8:00" = "08:00". A entrega é de 10 em 10 minutos — horários a menos de 10 min podem sair juntos.
143
144
  - Ao **ativar** (`flows_toggle` inativo→ativo) ou **criar já ativo**: qualquer conflito é barrado.
144
145
  - Ao **editar** um flow já ativo: só conflito NOVO é barrado (duplicados que já existiam são preservados).
145
146
  - A API responde **422** com `{ "error_code": "flow_trigger_conflict", "conflicts": [{ flow_id, flow_name, trigger_type, inbox_id, inbox_name }] }`.
@@ -288,7 +289,7 @@ No editor visual esses destinos só aparecem quando a validação bate (computed
288
289
  - `validation: 'options'` → `option_<valor>` para cada valor em `acceptedOptions` (ex: `option_1`, `option_2`, `option_sim`) + `timeout` + `retries_exhausted`
289
290
  - `validation: 'varied_options'` → `option_<group_id>` para cada grupo em `optionGroups` (ex: `option_sim`, `option_nao`) + `timeout` + `retries_exhausted`
290
291
 
291
- **`timeout` vs `retries_exhausted` (DISTINTOS):** `timeout` = cliente ficou em silencio (estourou `waitTime`). `retries_exhausted` = cliente respondeu, mas errou a validacao mais que `maxRetries` vezes. Ligue cada um ao caminho desejado. Se `retries_exhausted` nao tiver edge, ha fallback p/ o edge de `timeout`; sem nenhum dos dois, o flow encerra ao esgotar as tentativas.
292
+ **`timeout` vs `retries_exhausted` (DISTINTOS):** `timeout` = cliente ficou em silencio (estourou `waitTime`). `retries_exhausted` = cliente respondeu, mas errou a validacao mais que `maxRetries` vezes (`maxRetries` = quantas vezes a `invalidMessage` e enviada; a resposta errada seguinte esgota — com `maxRetries: 1` o cliente recebe o erro uma vez e sai no 2o erro). Ligue cada um ao caminho desejado. Se `retries_exhausted` nao tiver edge, ha fallback p/ o edge de `timeout`; sem nenhum dos dois, o flow encerra ao esgotar as tentativas.
292
293
 
293
294
  **Timeout dispara de verdade — e SEM fio ENCERRA (desde 20/08/2026):** `waitTime` + `waitUnit`
294
295
  agendam o estouro. Com fio no handle `timeout`, o flow segue por ele. **Sem fio, a sessão é
@@ -385,6 +386,25 @@ só pra destravar, ela pode sair.
385
386
  | `pagetrack_visited` / `pagetrack_event` | LionTrack |
386
387
  | `sla_check` | status do SLA da conversa (usa `value` = código fixo; ver abaixo) |
387
388
 
389
+ **CAIXA DA CONVERSA e TIPO DE CAIXA (novo 17/09/2026):** duas condições da aba Conversas que perguntam
390
+ ONDE a conversa está. Nasceram para o **Fluxo de Ações** (que roda em qualquer caixa), mas valem em
391
+ qualquer tipo de flow. Não têm operador novo: só `equal` e `not_equal`, e o valor vai numa **LISTA**
392
+ (`values`) — com lista, `equal` significa "é ALGUMA destas" e `not_equal` "não é NENHUMA destas".
393
+
394
+ - **Caixa da conversa:** `{ "id": 1, "field": "{{inbox.id}}", "operator": "equal", "value": "", "values": ["342", "204"], "valueType": "inbox" }`
395
+ — cada item de `values` é o **id da caixa como STRING**.
396
+ - **Tipo de caixa:** `{ "id": 1, "field": "{{inbox.channel_type}}", "operator": "equal", "value": "", "values": ["Channel::Waha"], "valueType": "channel_type" }`
397
+ — cada item é o **nome técnico do canal**: `Channel::Waha` (WhatsApp QR Code), `Channel::Whatsapp`
398
+ (WhatsApp API Oficial), `Channel::WebWidget`, `Channel::Api`, `Channel::Email`,
399
+ `Channel::FacebookPage` (Facebook/Instagram), `Channel::TwilioSms`, `Channel::Sms`,
400
+ `Channel::Telegram`, `Channel::Line`.
401
+
402
+ O `valueType` é OBRIGATÓRIO nas duas: é ele que faz a tela abrir o seletor certo (lista de caixas /
403
+ lista de tipos). Sem ele a regra roda no motor e a tela não desenha campo nenhum para ela. Gravar o
404
+ valor em `value` em vez de `values` é a mesma família do defeito de 02/09 descrito abaixo: o motor
405
+ compara certo e a tela abre o seletor VAZIO. `{{inbox.name}}` existe como variável mas **não** é
406
+ condição: comparar por nome quebra quando o cliente renomeia a caixa.
407
+
388
408
  > **ATENÇÃO — onde o VALOR vai (defeito real de 02/09/2026, conta 137):** no nó **Condição**, uma regra de atributo
389
409
  > (`valueType: "attr_config"`) com operador `equal`, `not_equal`, `contains` ou `not_contains` guarda o valor em
390
410
  > **`values: ["..."]` (lista)** e deixa `value: ""`. A TELA lê SÓ a lista: regra gravada com `value: "paid_ad"` e
@@ -686,7 +706,9 @@ Exemplo: `{"aiMode":"sentiment","aiAssistantId":"12","aiModel":"gpt-4.1-mini","a
686
706
  curinga** — não existe "a família gpt-5 inteira":
687
707
  `gpt-4o-mini`, `gpt-4o`, `gpt-4.1-nano`, `gpt-4.1-mini`, `gpt-4.1`, `gpt-5-mini`, `gpt-5`,
688
708
  `gpt-5.2`, `gpt-5.4-nano`, `gpt-5.4-mini`, `gpt-5.4`, `gpt-5.5`, `gpt-5.6-luna`, `gpt-5.6-terra`,
689
- `gpt-5.6-sol`, `o1`, `o3`, `o3-mini`, `o4-mini`.
709
+ `gpt-5.6-sol`, `gpt-6-luna`, `gpt-6-sol`, `o1`, `o3`, `o3-mini`, `o4-mini`.
710
+ A família **`gpt-6` (luna/sol) entrou em 23/09/2026** pelo mesmo trilho do `gpt-5.6` (porta `/v1/responses`,
711
+ sem temperatura, fora do Formulário de Lead).
690
712
  A família **`gpt-5.6` (luna/terra/sol) voltou em 09/2026** (tinha sido removida em 2026-07-29 porque
691
713
  recusa function tools no canal antigo): o servidor fala com esses três pela porta `/v1/responses`.
692
714
  Eles ignoram a temperatura do bloco (só o padrão) e **NÃO valem no bloco de IA do Formulário de Lead**
@@ -1025,17 +1047,43 @@ de conexão e viram bola de neve visual. Ao gerar `position`:
1025
1047
 
1026
1048
  ---
1027
1049
 
1028
- ## 2-B. Dois tipos de flow: `conversation` vs `ai_tool`
1050
+ ## 2-B. Três tipos de flow: `conversation`, `ai_tool` e `action`
1029
1051
 
1030
1052
  O campo `flow_type` (definido na criação, IMUTÁVEL depois) decide a natureza do flow:
1031
1053
 
1032
- | | `conversation` (default) | `ai_tool` |
1033
- |---|---|---|
1034
- | Como dispara | Por evento de inbox (trigger no node `start`) | Invocado pelo AI Agente (Captain) como ferramenta |
1035
- | Inboxes | usa `inbox_ids` | **PROIBIDO** ter inboxes (validação barra) |
1036
- | Campos extra | — | `tool_name` (snake_case, `[a-z][a-z0-9_]`, max 50) + `tool_description` (max 500) OBRIGATÓRIOS |
1037
- | Retorno | manda mensagens | retorna dado estruturado ao LLM via node `end` |
1038
- | Nodes permitidos | todos | `start`, `end`, `api`, `condition`, `set_variable`, `ai`, `note`, `randomizer`, `action`, `send_message` |
1054
+ | | `conversation` (default) | `ai_tool` | `action` (17/09/2026) |
1055
+ |---|---|---|---|
1056
+ | Como dispara | Por evento de inbox (trigger no node `start`) | Invocado pelo AI Agente (Captain) como ferramenta | Pelo MESMO trigger do `conversation`, mas em QUALQUER inbox |
1057
+ | Inboxes | usa `inbox_ids` | **PROIBIDO** ter inboxes (validação barra) | **PROIBIDO** ter inboxes (roda em todas) |
1058
+ | Campos extra | — | `tool_name` (snake_case, `[a-z][a-z0-9_]`, max 50) + `tool_description` (max 500) OBRIGATÓRIOS | — |
1059
+ | Retorno | manda mensagens | retorna dado estruturado ao LLM via node `end` | **não manda mensagem nenhuma** |
1060
+ | Nodes permitidos | todos | `start`, `end`, `api`, `condition`, `set_variable`, `ai`, `note`, `randomizer`, `action`, `send_message` | todos MENOS `send_message`, `wait_response`, `update_group` e `end` |
1061
+
1062
+ **`action` (na tela: "Fluxo de Ações")** é para regra que vale na empresa inteira e não depende da
1063
+ caixa: "conversa que receber a etiqueta urgente vai pra equipe de suporte", "card que entrar na
1064
+ etapa Fechado grava a data no contato". Antes exigia repetir o mesmo flow em cada inbox.
1065
+
1066
+ Criar: `flows_create` com `flow_type: 'action'`, SEM `inbox_ids` e SEM `conversation_mode`
1067
+ (o backend fixa `individual`; mandar `group` é ignorado). Aceita os MESMOS gatilhos do
1068
+ `conversation` no node `start` e usa o mesmo `flow_data`. Roda LADO A LADO com os flows de mensagem
1069
+ (não entra no aviso de conflito de gatilho) e vale também em conversa de grupo.
1070
+
1071
+ **NUNCA** pôr `send_message` / `wait_response` / `update_group` num flow `action` — a validação
1072
+ recusa o save inteiro com `contains node types not allowed in action flow`. Se o cliente precisa
1073
+ falar com a pessoa, o flow é `conversation`.
1074
+
1075
+ Três regras que andam com o tipo `action` (17/09/2026):
1076
+ - **`start_flow` (bloco Ações > Sistema)**: o flow de ações pode ser alvo a partir de QUALQUER flow,
1077
+ sem casar caixa. Já um flow `conversation` alvo que NÃO atenda a caixa da conversa é RECUSADO em
1078
+ runtime (passo com erro `inbox_mismatch` no histórico) — antes rodava no lugar errado em silêncio.
1079
+ - **Macro**: a ação `start_flow` da macro (`macros_*`) aceita **só** `flow_type: 'action'`.
1080
+ - **Condições**: `{{inbox.id}}` e `{{inbox.channel_type}}` (novo) dão "Caixa da conversa" e "Tipo de
1081
+ caixa" na aba Conversas — como o flow de ações roda em todas as caixas, é assim que ele pergunta
1082
+ onde está antes de agir. `channel_type` grava o valor técnico (`Channel::Waha` etc.).
1083
+ - **AINDA NÃO FUNCIONA**: disparar flow `action` por webhook de integração (gateways de pagamento,
1084
+ Webhook Universal, Meta Lead, e-Clínica) ou por gatilho de formulário. Esses caminhos criam a
1085
+ conversa usando a CAIXA do flow, e o flow de ações não tem caixa — ele é selecionado e descartado
1086
+ (`no_inbox_linked` no log). Não prometa isso ao cliente até a decisão de produto.
1039
1087
  | Aviso de espera (10/09/2026) | — | enquanto a ferramenta roda, o cliente recebe "Só um momento, estou verificando isso pra você...". Cada ferramenta escolhe o seu no node `start`: `data.toolWaitMessageEnabled` (`false` desliga; AUSENTE = ligado — só um `false` de verdade desliga, `""` ou lixo mantém ligado) e `data.toolWaitMessageText` (texto próprio, até 1000 caracteres; vazio = frase padrão). Vive no `flow_data` e viaja no snapshot de versão; ferramenta já existente não muda de comportamento |
1040
1088
 
1041
1089
  Se o cliente pediu "uma ferramenta que a IA usa pra consultar X / calcular Y", é `ai_tool`. Se pediu "quando chega mensagem, faça Z", é `conversation`. Na dúvida, `conversation`.
@@ -1055,10 +1103,11 @@ no fim de uma cadeia automação→flow→automação, suspeite desse limite —
1055
1103
 
1056
1104
  Todo flow `conversation` tem um `conversation_mode`: `individual` (default — conversa 1-a-1) ou `group`
1057
1105
  (grupo de WhatsApp). Definido na CRIAÇÃO e **IMUTÁVEL depois** (não dá pra converter um no outro; pra trocar,
1058
- crie outro flow). Não confundir com `flow_type` — um flow `ai_tool` não tem `conversation_mode`.
1106
+ crie outro flow). Não confundir com `flow_type` — um flow `ai_tool` não tem `conversation_mode`, e no
1107
+ `action` ele é fixado em `individual` (o campo não se aplica: o flow de ações roda nos dois).
1059
1108
 
1060
- Na tela o usuário vê isso como TRÊS opções ao criar: "Mensagem" (= `conversation` + `individual`),
1061
- "Grupo" (= `conversation` + `group`) e "IA Agente" (= `ai_tool`).
1109
+ Na tela o usuário escolhe o TIPO (Conversa / IA Agente / Ações) e, só no tipo Conversa, o modo
1110
+ (Mensagens individuais / Grupos WhatsApp).
1062
1111
 
1063
1112
  **A diferença entre individual e grupo é quais nodes/abas ficam disponíveis:**
1064
1113
 
@@ -388,13 +388,15 @@ mandar). Detalhes em `lionchat://docs/best-practices`.
388
388
 
389
389
  **Onde fica armazenado:** `kanban_config.win_reasons` e `kanban_config.loss_reasons` (jsonb arrays na tabela `kanban_configs`).
390
390
 
391
- **Formato:** array de objetos `{id, title}`:
391
+ **Formato:** array de objetos `{id, title}`, com `id` em **texto estável** (nunca número) — detalhes e erros 422 em
392
+ `lionchat://docs/api-conventions` → "Estrutura interna das listas":
392
393
  ```json
393
394
  "win_reasons": [
394
395
  {"id": "wr-1", "title": "Preço competitivo"},
395
396
  {"id": "wr-2", "title": "Indicação forte"}
396
397
  ]
397
398
  ```
399
+ **No card:** `item_details.loss_reason` / `win_reason` guardam o `id` (texto) do motivo escolhido; `reason`, o título.
398
400
 
399
401
  **Endpoint:** `PUT /api/v1/accounts/{id}/kanban_config` com body wrapped:
400
402
  ```json
@@ -414,7 +416,7 @@ mandar). Detalhes em `lionchat://docs/best-practices`.
414
416
  |---|---|---|
415
417
  | Motivo de Ganho/Perda do card | `kanban_config.win_reasons` / `loss_reasons` | ❌ NÃO |
416
418
  | Atributo em TODO card | `kanban_config.global_custom_attributes` | só se não couber acima |
417
- | Atributo de UM card específico | `kanban_item.custom_attributes` (jsonb direto) | já é nativo, não precisa definition |
419
+ | Valor de um campo em UM card | `item_details.custom_attributes` — LISTA `[{name, type, value}]` casando com o campo de `kanban_config.global_custom_attributes` | a coluna `kanban_item.custom_attributes` NÃO aparece em tela nenhuma — não use pra dado que o cliente precisa ver |
418
420
  | CPF, RG, CNPJ, endereço, data nasc., gênero do cliente | mecanismo NATIVO cadastral: `PATCH /contacts/{id}/cadastral` (`update_cadastral`) | ❌ NÃO (tem nativo) |
419
421
  | Tag pro contato (residencial, empresarial) | `labels` | ✅ SIM via Label |
420
422
  | Etapa do funil | `funnel.stages` | ❌ NÃO |