@lionchat/mcp-server 1.10.0 → 1.10.2

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.
@@ -90,6 +90,25 @@ daquela caixa, mas fica FORA da distribuição automática (round-robin nunca at
90
90
  caixas das quais participa — exceção é estar atribuído a um card, que libera só aquela conversa (ver
91
91
  `lionchat://docs/kanban-deep-dive`).
92
92
 
93
+ ### Supervisor de time (TeamMember.auto_assignable) — novo 2026-07-30
94
+
95
+ O TIME passou a ter os MESMOS dois papéis da caixa. Supervisor de time é membro pra TUDO — menção ao
96
+ time, e-mail de automação, acesso ao funil do Kanban, relatório, contagem e atribuição MANUAL — mas
97
+ fica FORA da distribuição automática do time (nem conversa, nem rodízio de card do funil).
98
+
99
+ - Flag: `team_members.auto_assignable` (`false` = supervisor, `true` = agente). A coluna nasce `true`:
100
+ todo vínculo anterior a 30/07 é Agente.
101
+ - Tools: `lionchat_team_members_create` / `_update`. **O contrato é DIFERENTE do da caixa, de
102
+ propósito:** aqui `user_ids[]` é a lista COMPLETA de membros e `supervisor_ids[]` é um SUBCONJUNTO
103
+ dela. Chave `supervisor_ids` **ausente = não mexe em papel nenhum** (só entra/sai membro) — assim um
104
+ cliente antigo que só manda `user_ids` não zera em silêncio os supervisores marcados na tela.
105
+ - `lionchat_team_members_list` traz `is_supervisor` por agente.
106
+
107
+ **As duas marcas são INDEPENDENTES (decisão do dono 30/07):** a da caixa protege da distribuição da
108
+ CAIXA; a do time protege da distribuição do TIME. Supervisor de caixa que é agente de um time
109
+ CONTINUA recebendo conversa por aquele time — é intencional, não é defeito. Nunca diga ao cliente que
110
+ marcar supervisor num lugar protege no outro.
111
+
93
112
  ## 3. Convenções da API
94
113
 
95
114
  ### Datas
@@ -302,6 +321,7 @@ e pular evita que ela receba tudo em dobro. Quem está sendo atendido pela equip
302
321
  | Rate limit leitura | 1200/min por token |
303
322
  | Rate limit escrita | 600/min por token |
304
323
  | Loop detection | bloqueia chamada idêntica >10x em 10s |
324
+ | Variável de Conta — tamanho do valor | 32 KB por variável (novo 01/08/2026; passou disso = HTTP 422 na conta). Vale só pra variável DECLARADA na tela; chave interna do sistema não é barrada. É por variável, não pela soma — conta com dezenas de tokens legítimos não é punida |
305
325
 
306
326
  Exceção ao teto de 300: em ação em massa de **contatos** com `select_all: true` (agir sobre todos os
307
327
  contatos do filtro) não há teto de IDs — o servidor refaz o filtro e processa em lotes no fundo. O
@@ -352,6 +372,9 @@ Antes de escolher a ferramenta, cheque se a intenção bate com a coluna da dire
352
372
  | **Importar histórico** de caixa WhatsApp Cloud (QR) | `lionchat_inboxes_whatsapp_history_start` / `_status` / `_cancel` | Admin-only + flag `qr_history_import`; só caixa oficial. Acompanhe `state` no `_status` |
353
373
  | Ver / limpar **falhas de envio** de uma caixa | `lionchat_inboxes_failed_messages_summary` primeiro; depois `_bulk_retry` (reenviar) ou `_bulk_cancel` (descartar) | Admin-only. Reenvio em massa e cancelamento em massa são ações irreversíveis: mostre as contagens do summary e confirme (seção 7a/7b). Numa mensagem só: `lionchat_conversations_messages_create_1` (reenviar) / `lionchat_conversations_messages_cancel_retry` (cancelar) |
354
374
  | **Definir supervisor** de caixa | `lionchat_inbox_members_create` / `_update` com `supervisor_ids[]` | Supervisor vê TUDO da caixa mas fica fora do rodízio (ver abaixo) |
375
+ | **Definir supervisor** de time | `lionchat_team_members_create` / `_update` com `supervisor_ids[]` | Contrato diferente do da caixa: `user_ids[]` é a lista COMPLETA e `supervisor_ids[]` um subconjunto dela. Omitir `supervisor_ids` não mexe em papel nenhum (seção 2) |
376
+ | **Mudar a fila do time** (teto por atendente, janela, online/offline) | `lionchat_teams_update` (`assignment_mode`, `fair_distribution_limit`, `fair_distribution_window`) | Time tem PREFERÊNCIA sobre a caixa na distribuição. Leia a config atual antes de mudar (ver `lionchat://docs/conversation-flows`) |
377
+ | **Migrar conversas** de uma caixa WhatsApp pra outra (ex.: QR Code → Oficial) | `lionchat_inboxes_inbox_migration_list` (prévia) → `_execute` → acompanhar status | Admin-only e IRREVERSÍVEL: sempre mostre os números da prévia e confirme (seção 7a). Grupos não vão pro canal oficial — ficam na caixa de origem |
355
378
  | Qualquer coisa de Ligação WP (Wavoip) | **NÃO HÁ TOOL** — removidas em 2026-07-16 por decisão do dono | Ligação WP é contratada e configurada **manualmente pelo cliente no painel**. O MCP não conecta, não desconecta, não entrega token de discagem nem muda config. Oriente o usuário a fazer pelo painel (Configurações da caixa → Ligação WP). |
356
379
  | Restringir agendas que a IA oferece | `lionchat_captain_assistants_update` (`config.booking_event_type_ids: [Int]`) | binding de agenda por cenário NÃO é settable via API |
357
380
  | Configurar o Copiloto (modelo/temperatura/prompt base) | `lionchat_copilot_settings_update` | admin-only; copiloto tem motor próprio (padrão temp 0.3) |
@@ -247,6 +247,24 @@ pula o follow-up). Tipos:
247
247
  (não empurra pra frente — evita disparo em massa na abertura); a cadência segue nos passos seguintes.
248
248
  `start == end` = janela inválida (ignorada). Ideal pra não incomodar cliente de madrugada.
249
249
 
250
+ ## Follow-up fora da janela de 24h usa TEMPLATE (novo 2026-07-30)
251
+
252
+ Em caixa **WhatsApp oficial**, passadas 24h da última mensagem do cliente, a Meta recusa qualquer texto
253
+ escrito pela IA. Até 30/07 o follow-up nem perguntava: escrevia, o canal recusava e sobrava um balão
254
+ vermelho com o erro enganoso "Template not found or invalid template name" (392 balões assim num único
255
+ dia numa conta). Agora o gerador de follow-up recebe os modelos aprovados da caixa ANTES de escrever e
256
+ escolhe um deles quando a janela está fechada.
257
+
258
+ O que isso significa na prática, pra explicar ao cliente:
259
+ - **Conta em caixa oficial que quer follow-up depois de 24h PRECISA ter template aprovado na Meta.**
260
+ Sem nenhum template, não há como falar fora da janela — não é limitação da plataforma, é regra da Meta.
261
+ - **Mandar template não reabre a janela.** Só a resposta do cliente reabre. Um follow-up por template
262
+ pode ser respondido — e é a resposta dele que libera texto livre de novo.
263
+ - **Trava anti-repetição:** modelo já enviado naquele ciclo de follow-up sai da lista. Numa conta com um
264
+ único template (normalmente o mesmo da campanha), sem essa trava o cliente receberia minutos depois o
265
+ mesmo texto que acabou de ler.
266
+ - Dentro da janela nada muda: o follow-up continua sendo texto livre, escrito pela IA.
267
+
250
268
  ## Desligar a IA quando um humano assume (2026-07)
251
269
 
252
270
  `config.feature_pause_on_human_reply` (bool, padrão false). Ligado, a IA **se desliga sozinha** na
@@ -379,6 +397,17 @@ o `exclusion` (e o `audience_mode`) vão TOP-LEVEL; ao CRIAR/EDITAR a campanha e
379
397
  `{assignee_id, priority, labels}`: `contact_labels: []` (aplica etiqueta ao CONTATO que recebeu) e
380
398
  `attribute_changes: [{scope:'contact'|'conversation', key, value}]` (grava/sobrescreve atributo de quem recebeu).
381
399
 
400
+ **Público por PLANILHA (novo 2026-07-31):** o painel passou a aceitar CSV/XLS como público do disparo,
401
+ nas três abas de campanha. **Não existe tipo novo de público** — a importação marca os contatos da
402
+ planilha com uma **etiqueta de contato** e/ou um **atributo de contato**, e a campanha usa os tipos que
403
+ já existiam (`ContactLabel` / `ContactAttribute`).
404
+
405
+ Ou seja: **dá pra reproduzir pela API.** Importe os contatos aplicando uma marca própria da lista (ex.:
406
+ etiqueta `lista-black-friday`) e monte o `audience` com `{"type": "ContactLabel", "id": <id>}`. Se o
407
+ cliente pedir "disparar pra essa planilha", esse é o caminho. A marcação em lote é feita em silêncio
408
+ (sem callback nem evento), então importar uma lista grande **não acorda automação nem fluxo** — foi
409
+ desenhada assim de propósito, pra não repetir a tempestade de eventos de 29/07.
410
+
382
411
  SEMPRE rode `campaigns_estimate_audience` com o MESMO audience + audience_mode + exclusion antes de
383
412
  criar a campanha e mostre a contagem ao usuário — estimativa e disparo usam o mesmo motor (não divergem).
384
413
 
@@ -399,6 +428,12 @@ Pra dados fixos que se repetem (slogans, endereços, horários):
399
428
  - Resolve em: mensagens, respostas prontas, campanhas e automações. NÃO resolve nas instruções do AI Agente (base/cenário) — ali só `contact.*` e `conversation.*`.
400
429
  - `secret` nunca aparece em template (sai vazio); só resolve em nós API Request do FlowBuilder.
401
430
  - Atualiza UMA vez, propaga pra todo lugar.
431
+ - **Teto de 32 KB por variável (novo 01/08/2026).** Valor maior que isso é recusado com HTTP 422. É por
432
+ VARIÁVEL, não pela soma — conta com dezenas de tokens legítimos (uma delas tem 23 tokens de
433
+ integração) não é punida. A maior variável legítima medida na plataforma tem 348 bytes, então o teto
434
+ dá ~90x de folga: cabe qualquer token, URL, endereço ou texto de configuração; **não** cabe base de
435
+ dados, JSON gigante nem script. Se o cliente quiser guardar algo desse tamanho, o lugar é um arquivo
436
+ ou uma integração, não uma Variável de Conta.
402
437
 
403
438
  Nunca hard-code esses dados em respostas geradas.
404
439
 
@@ -131,6 +131,42 @@ Regra prática:
131
131
  Quem entra no rodízio: só `inbox_members` com `auto_assignable: true` (supervisor de caixa fica de
132
132
  fora) e, se "distribuir para agentes offline" estiver desligado, só quem está online.
133
133
 
134
+ ### A fila do TIME tem preferência sobre a da caixa (novo 2026-07-31)
135
+
136
+ Existe um TERCEIRO nível de distribuição, e ele vem ANTES dos dois de cima. A regra, por conversa:
137
+
138
+ > **conversa está num time que distribui → manda o TIME (quem entra e qual o teto).
139
+ > Senão → cai pra caixa/política, exatamente como antes.**
140
+
141
+ Decisão do dono: **time tem preferência, caixa é reserva.** O time decide QUEM e QUANTO; a
142
+ caixa/política continua decidindo a ORDEM da fila, as exclusões e a capacidade por atendente.
143
+
144
+ Campos do time (tools `lionchat_teams_create` / `_update`):
145
+
146
+ | Campo | O que faz | Padrão |
147
+ |---|---|---|
148
+ | `allow_auto_assign` | liga/desliga a distribuição do time | `true` |
149
+ | `assignment_mode` | `online_only` (só quem está online) ou `include_offline` (todo mundo do time) | `online_only` |
150
+ | `fair_distribution_limit` | teto de conversas por atendente na janela | `10` |
151
+ | `fair_distribution_window` | tamanho da janela, **em segundos** | `600` |
152
+
153
+ Quem entra no bolo do time: membros com `auto_assignable: true` (supervisor de time fica de fora)
154
+ **que também sejam membros da caixa** — quem não é da caixa nunca entra, senão a conversa cairia com
155
+ alguém que nem enxerga aquela caixa.
156
+
157
+ Antes de 31/07 o time sorteava UMA vez, no instante em que a conversa caía nele; não achando ninguém
158
+ disponível, desistia PARA SEMPRE e a conversa ficava aberta sem dono. Agora a mesma varredura que já
159
+ existia para a caixa reprocessa a fila do time. Se o cliente reclamar de "conversas com time e sem
160
+ dono" de antes dessa data, a causa é essa — e já está corrigida.
161
+
162
+ **LEIA o valor do time antes de responder** — a resposta de `lionchat_teams_list` / `_show` traz os
163
+ quatro campos. Como em toda configuração, nunca recite o padrão de fábrica como se fosse o que o
164
+ cliente configurou: cite o número que a API devolveu.
165
+
166
+ > Nota de compatibilidade: `fair_distribution_limit` e `fair_distribution_window` só passaram a sair
167
+ > na resposta em **03/08/2026**. Se vierem ausentes, a instalação é anterior a essa data — nesse caso
168
+ > não afirme o valor, mande conferir em Configurações → Times → editar o time.
169
+
134
170
  **LEIA o valor da conta antes de responder — NUNCA recite um número de cabeça.** Os valores reais
135
171
  vêm na resposta da API:
136
172
  - `has_assignment_policy` (resposta da caixa) diz QUAL dos dois caminhos vale ali. `true` = política
@@ -266,6 +302,41 @@ Conversation
266
302
  2. Tem SLA policy ativando state change?
267
303
  3. Veio de external API call?
268
304
 
305
+ ## Pesquisa de satisfação (CSAT) — vale em TODO canal (corrigido 2026-07-31)
306
+
307
+ A pesquisa de satisfação é enviada **dentro da própria conversa** (modo `in_chat`) em **todos os
308
+ canais** — WhatsApp, Telegram, Instagram, SMS, site. O backend sempre funcionou assim; era a TELA que
309
+ só mostrava a configuração quando a caixa era WhatsApp, então de 01/04 a 31/07 quem usava outro canal
310
+ via a aba quase vazia e herdava um texto padrão que não conseguia editar. Hoje a configuração aparece
311
+ igual pra todos.
312
+
313
+ Duas coisas pra não errar ao explicar:
314
+ - **A regra de etiqueta (`survey_rules`) agora é respeitada de verdade.** Ela aparecia na tela e era
315
+ IGNORADA no envio — a pesquisa saía pra todo mundo. Se o cliente configurou "só enviar quando a
316
+ conversa tem a etiqueta X" antes de 31/07 e reclamou que ia pra todos, a causa era essa e já está
317
+ corrigida.
318
+ - **Caixa que nunca teve a aba salva** pode continuar sem `csat_mode` gravado e cair no caminho antigo.
319
+ Se duas caixas iguais se comportarem diferente, mande abrir a aba de Pesquisa de Satisfação e salvar
320
+ uma vez.
321
+
322
+ ## Migrar conversas entre caixas de WhatsApp (2026-08-01)
323
+
324
+ Serve pra quem está saindo do WhatsApp por QR Code e indo pro Oficial (ou trocando de número): leva as
325
+ conversas e os contatos da caixa antiga pra nova. Tools: `lionchat_inboxes_inbox_migration_list`
326
+ (prévia), `_execute` (executa) e o acompanhamento de status. **Admin-only e irreversível** — mostre os
327
+ números da prévia e confirme antes.
328
+
329
+ O que a prévia devolve (e o que ela conta): a contagem é de **CONVERSAS**, dividida em migráveis,
330
+ conflitantes e não-migráveis; contato sem conversa é ignorado, e os baldes fecham com o total.
331
+
332
+ **Regras que mudam o resultado — diga ao cliente ANTES de executar:**
333
+ - **Grupos não vão pro canal Oficial.** A Meta não tem grupos: conversa de grupo (`@g.us`), lista de
334
+ transmissão e canal ficam na caixa de ORIGEM e aparecem contados como pulados. Não é falha.
335
+ - QR Code → Oficial passou a funcionar de verdade em 01/08. Antes a migração falhava — em uma conta
336
+ gravou "0 migradas" por causa de um grupo, e na prática teria falhado em 100% dos contatos, porque o
337
+ formato de identificador do QR Code não estava sendo convertido. Se o cliente tentou migrar antes
338
+ dessa data e não deu certo, mande tentar de novo.
339
+
269
340
  ## Receitas de ação (qual tool usar pra cada coisa)
270
341
 
271
342
  ### ENVIAR MENSAGEM (`conversations_messages_create`)
@@ -420,6 +491,154 @@ POST /api/v1/accounts/{account_id}/automation_rules
420
491
  "event_name": "conversation_updated",
421
492
  "action_types": ["label_added"],
422
493
  "conditions": [{"attribute_key": "labels", "filter_operator": "contains", "values": ["vip"]}],
423
- "actions": [{"action_name": "send_email_to_team", "action_params": {"team_ids": [1], "message": "VIP!"}}]
494
+ "actions": [{"action_name": "send_email_to_team", "action_params": [{"team_ids": [1], "message": "VIP!"}]}]
495
+ }
496
+ ```
497
+
498
+ ---
499
+
500
+ ## Condicoes de automacao — e a REGRA DO CONECTOR
501
+
502
+ ```json
503
+ "conditions": [
504
+ {"attribute_key": "status", "filter_operator": "equal_to", "values": ["resolved"], "query_operator": "AND"},
505
+ {"attribute_key": "labels", "filter_operator": "equal_to", "values": ["vip"], "query_operator": null}
506
+ ]
507
+ ```
508
+
509
+ ### O conector (`query_operator`) — leia isto antes de gravar
510
+
511
+ O conector **liga a condicao com a SEGUINTE**. Logo:
512
+
513
+ - A **ULTIMA** condicao **TEM** que ter `query_operator: null`.
514
+ - Condicao **UNICA** = `query_operator: null` (nao ha proxima pra ligar).
515
+ - Valores aceitos nas demais: `"AND"` ou `"OR"`.
516
+
517
+ > **A API valida o VALOR do conector, mas NAO a POSICAO.** Conector na ultima condicao **salva com
518
+ > sucesso**, aparece certo na tela — e a regra nunca dispara.
519
+ >
520
+ > Incidente real: **8 regras criadas pelo conector de IA entre 26 e 30/07/2026, em 3 contas
521
+ > diferentes, todas com conector na ultima condicao. Todas nasceram mortas.** A pergunta ao banco
522
+ > saia pela metade (`... IN ($1) and`), o erro era engolido e a regra respondia "nada casou". A conta
523
+ > 62 ficou com as **6** regras de etiquetagem automatica paradas. Medido em producao em 03/08:
524
+ > **732 erros por hora**.
525
+ >
526
+ > O motor foi corrigido em 03/08 (a limpeza acontece ao montar a consulta), entao regra torta nao
527
+ > quebra mais. **Mas continue gravando certo:** a tela de edicao le o dado cru e formato errado
528
+ > aparece estranho ali — mesmo efeito do campo de template que sumia da tela.
529
+
530
+ ### Campos da condicao
531
+
532
+ | Campo | Obrigatorio | O que e |
533
+ |---|---|---|
534
+ | `attribute_key` | SIM | Campo padrao (ver abaixo) ou a chave de um atributo personalizado que **exista na conta** |
535
+ | `filter_operator` | SIM | Como comparar |
536
+ | `values` | SIM (menos em `is_present`/`is_not_present`) | Array, sempre |
537
+ | `query_operator` | SIM em todas | `"AND"`/`"OR"`; **`null` na ultima** |
538
+ | `custom_attribute_type` | so p/ atributo personalizado | `conversation_attribute` ou `contact_attribute` |
539
+
540
+ ### Operadores de filtro
541
+
542
+ `equal_to`, `not_equal_to`, `contains`, `does_not_contain`, `is_present`, `is_not_present`,
543
+ `is_greater_than`, `is_less_than`, `starts_with`, `days_before`, `days_ago`, `last_days`, `today`,
544
+ `yesterday`.
545
+
546
+ **Cada campo aceita so um subconjunto.** Operador que o campo nao aceita faz a regra inteira falhar
547
+ na validacao. Na duvida, leia uma regra que ja funciona com `lionchat_automation_rules_show` e copie.
548
+
549
+ ### Chave que a conta nao conhece
550
+
551
+ Se `attribute_key` nao for um campo padrao **nem** um atributo personalizado existente na conta, a
552
+ regra **inteira** falha na validacao e nunca dispara — erro
553
+ `Automation conditions <chave> not supported`. Aconteceu de verdade na conta 62 (`tipo_interesse`).
554
+ Antes de usar atributo personalizado numa condicao, confirme que ele existe com
555
+ `lionchat_custom_attributes_list`.
556
+
557
+ ---
558
+
559
+ ## Acoes de automacao — formato do `action_params`
560
+
561
+ > **REGRA DE OURO:** `action_params` e SEMPRE um **array**, mesmo quando so tem um item.
562
+ > O backend le `params[0]`. Passar um objeto solto (`{...}` em vez de `[{...}]`) quebra a acao.
563
+
564
+ > **PERIGO — falha silenciosa:** o `action_params` e um campo livre (JSONB). A API **aceita e grava
565
+ > qualquer formato**, sem validar. Se as chaves estiverem erradas, a regra e salva com sucesso, o
566
+ > painel mostra a acao na tela, e na hora de disparar ela **nao faz nada e nao registra erro nenhum**
567
+ > — sem log, sem mensagem falhada, sem aviso. Por isso: **nunca invente nome de chave**. Se a acao
568
+ > nao estiver documentada abaixo, leia uma regra existente com `lionchat_automation_rules_show` e
569
+ > copie o formato exato dela.
570
+ >
571
+ > Incidente real (conta 39, 02-03/08/2026): uma IA gravou `template_name`/`template_id` em vez de
572
+ > `name`/`id`. A regra ficou 14 horas sem enviar nada, 9 leads pagos entraram e ninguem foi avisado.
573
+
574
+ ### `send_whatsapp_template` — ATENCAO ESPECIAL
575
+
576
+ Envia um modelo aprovado da Meta. **So funciona em caixa WhatsApp API Oficial (Cloud).**
577
+
578
+ ```json
579
+ {
580
+ "action_name": "send_whatsapp_template",
581
+ "action_params": [{
582
+ "name": "confirmacao_pedido",
583
+ "id": "1071396275361941",
584
+ "language": "pt_BR",
585
+ "category": "UTILITY",
586
+ "processed_params": {"body": {"1": "{{contact.first_name}}"}}
587
+ }]
424
588
  }
425
589
  ```
590
+
591
+ | Chave | Obrigatoria | O que e |
592
+ |---|---|---|
593
+ | `name` | **SIM** | Nome EXATO do modelo aprovado na Meta. E por ele que o envio acontece |
594
+ | `id` | nao | ID do modelo na Meta. Serve so de reserva se `name` faltar |
595
+ | `language` | nao (padrao `pt_BR`) | Precisa bater com o idioma do modelo aprovado |
596
+ | `category` | nao | `UTILITY`, `MARKETING` ou `AUTHENTICATION` |
597
+ | `processed_params` | nao | Valores das variaveis: `{"body": {"1": "...", "2": "..."}}`. Aceita Liquid (`{{contact.first_name}}`) |
598
+
599
+ **NUNCA use `template_name`, `template_id`, `template_category` nem `template_language` aqui.**
600
+ Esse e o formato dos **nos do FlowBuilder**, que e outra coisa. Na automacao, essas chaves sao
601
+ ignoradas e a acao vira um nada silencioso.
602
+
603
+ Antes de gravar, confirme que o modelo existe e esta aprovado com
604
+ `lionchat_inboxes_whatsapp_templates_list` — o envio so acha o modelo se `name` + `language`
605
+ casarem e o status for `approved`.
606
+
607
+ ### Demais acoes
608
+
609
+ Todos os formatos abaixo foram conferidos linha a linha em `app/services/action_service.rb` e
610
+ `app/services/automation_rules/action_service.rb`.
611
+
612
+ | `action_name` | `action_params` |
613
+ |---|---|
614
+ | `send_message` | `["texto da mensagem"]` |
615
+ | `add_private_note` | `["nota interna"]` |
616
+ | `send_canned_response` | `[12]` — id da resposta pronta |
617
+ | `add_label` / `remove_label` | `["etiqueta1", "etiqueta2"]` |
618
+ | `assign_agent` | `[70]` — o agente precisa ser membro da caixa. `["nil"]` desatribui |
619
+ | `assign_team` | `[3]`. `["nil"]` desatribui |
620
+ | `assign_captain_assistant` | `[17]` ou `[{"assistant_id": 17, "proactive": true}]` — `proactive` false = a IA assume mas nao fala na hora |
621
+ | `send_email_to_team` | `[{"team_ids": [1], "message": "texto"}]` |
622
+ | `send_webhook_event` | `["https://..."]` |
623
+ | `send_attachment` | `[blob_ids]` — so funciona se a regra ja tiver arquivo anexado; nao da pra montar so por API |
624
+ | `change_status` | `["resolved"]`, `["open"]`, `["pending"]` ou `["snoozed"]` |
625
+ | `change_priority` | `["urgent"]`, `["high"]`, `["medium"]`, `["low"]`. `["nil"]` limpa |
626
+ | `send_email_transcript` | `["a@b.com,c@d.com"]` — UMA string, varios e-mails separados por virgula |
627
+ | `mute_conversation` | `[]` |
628
+ | `snooze_conversation` | `[]` |
629
+ | `resolve_conversation` / `open_conversation` / `pending_conversation` | `[]` |
630
+ | `update_contact_attribute` / `update_conversation_attribute` | `[{"attribute_key": "chave", "value": "texto ou {{contact.phone_number}}"}]` |
631
+ | `create_kanban_item` | `[{"funnel_id": 31, "funnel_stage": "prospeccao", "allow_duplicates": false}]` |
632
+ | `move_kanban_item_to_stage` | `[{"funnel_id": 31, "funnel_stage": "qualificacao"}]` |
633
+ | `assign_agent_to_kanban_item` | `[{"funnel_id": 31, "agent_id": 70, "mode": "add"}]` — `mode` aceita `add` (padrao) ou `remove_all` |
634
+ | `add_note_to_kanban_item` | `[{"funnel_id": 31, "text": "texto da nota"}]` — a chave e **`text`**, nao `note` |
635
+ | `set_kanban_item_status` | `[{"funnel_id": 31, "status": "won"}]` — `won`, `lost` ou `open`. Status fora disso e ignorado |
636
+ | `start_kanban_item_timer` / `stop_kanban_item_timer` | `[{"funnel_id": 31}]` |
637
+
638
+ **Acoes de Kanban:** todas precisam que a conversa ja tenha um card no funil informado. Sem card,
639
+ a acao e pulada (fica so no log do servidor). Use `create_kanban_item` antes, na mesma regra.
640
+
641
+ Lista autoritativa de nomes validos: `AutomationRule#actions_attributes` no backend. Nome de acao
642
+ fora dessa lista e recusado com 422 ao salvar — isso a API valida. O que ela **nao** valida e o
643
+ conteudo do `action_params`. `apply_kanban_checklist_template` existe no motor mas **nao** e uma
644
+ acao de automacao valida (e so do FlowBuilder) — usar aqui devolve 422.
@@ -396,11 +396,12 @@ Booking (agendamento confirmado)
396
396
 
397
397
  ## e-Clínicas (Efficient) — integração de clínicas
398
398
 
399
- 9 eventos de webhook, cada um mapeável a automação OU flow na tela Integrações > e-Clínica:
399
+ 13 eventos de webhook, cada um mapeável a automação OU flow na tela Integrações > e-Clínica:
400
400
  `cliente_novo`, `agendamento_novo`, `falta`, `pagamento` (os 4 da doc oficial) +
401
401
  `agendamento_atendido`, `agendamento_alterado`, `agendamento_desmarcado`,
402
- `agendamento_transferido`, `cliente_baixa_pagamento` (capturados ao vivo, não documentados
403
- pelo e-Clínica).
402
+ `agendamento_transferido`, `cliente_baixa_pagamento`, `cliente_alteracao`,
403
+ `cliente_inclusao_pagamento`, `controle_laboratorio_novo`, `controle_laboratorio_alterado`
404
+ (capturados ao vivo, não documentados pelo e-Clínica).
404
405
 
405
406
  Atributos de sistema no CONTATO (prefixo `eclinica_`, protegidos, usáveis como variável):
406
407
 
@@ -29,13 +29,44 @@ E/OU é encadeado condição a condição (sem parênteses).
29
29
  | `display_id` | equal_to, not_equal_to, contains, does_not_contain (o `contains`, busca por trecho do número, passou a funcionar de verdade em 2026-07-25 — antes dava erro de banco) |
30
30
  | `referer`, `mail_subject` | equal_to, not_equal_to, contains, does_not_contain |
31
31
  | `browser_language`, `conversation_language` | equal_to, not_equal_to |
32
- | `created_at`, `last_activity_at` (valor = data) | is_greater_than, is_less_than, days_before |
32
+ | `created_at`, `last_activity_at` (valor = data) | is_greater_than, is_less_than, days_before + relativos `today`, `yesterday`, `last_days`, `days_ago` (novo 01/08 — ver abaixo) |
33
33
  | `card_funnel_stage` (conversa POR CARD do Kanban — valor `funnel:<id>` ou `stage:<id>:<chave_etapa>`) | equal_to, not_equal_to |
34
34
  | **qualquer outra chave** = atributo personalizado | conforme o tipo do atributo (ver tabela em api-conventions) |
35
35
 
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
+ ### Data relativa — "Hoje", "Ontem", "últimos X dias" (novo 2026-08-01)
40
+
41
+ Não calcule data no seu lado pra perguntar "quantas conversas entraram hoje". Existem operadores
42
+ próprios, e eles resolvem tudo em **dias INTEIROS no fuso da CONTA** (`Account#timezone`, padrão
43
+ `America/Sao_Paulo`). Valem em `created_at` e `last_activity_at`, tanto em
44
+ `lionchat_conversations_filter` quanto em `lionchat_contacts_filter`.
45
+
46
+ | Operador | Valor | Janela |
47
+ |---|---|---|
48
+ | `today` | **nenhum** | hoje, 00:00 → 23:59 |
49
+ | `yesterday` | **nenhum** | ontem, 00:00 → 23:59 |
50
+ | `last_days` | `values: [N]` | últimos N dias **incluindo hoje** (N=7 → de 6 dias atrás 00:00 até hoje 23:59) |
51
+ | `days_ago` | `values: [N]` | o dia EXATO de N dias atrás (um dia só, não um intervalo) |
52
+ | `days_before` | `values: [N]` | tudo **mais antigo** que N dias atrás (o comportamento que já existia) |
53
+
54
+ ```json
55
+ { "attribute_key": "created_at", "filter_operator": "today", "values": [], "query_operator": null }
56
+ { "attribute_key": "created_at", "filter_operator": "last_days", "values": [7], "query_operator": null }
57
+ ```
58
+
59
+ **Regras que derrubam a chamada se ignoradas:**
60
+ - `today` e `yesterday` NÃO levam valor — o servidor nem espera o campo.
61
+ - Nos outros três, N tem que ser inteiro entre **1 e 998**. Zero, negativo, texto ou vazio = HTTP 422
62
+ (`InvalidValue`). Não existe `last_days: 0` pra dizer "hoje" — pra hoje use `today`.
63
+ - `is_greater_than` / `is_less_than` continuam sendo o caminho da data FIXA (`values: ["2026-08-01"]`).
64
+ Use-os quando o cliente citou um dia específico; use os relativos quando ele disse "hoje", "ontem"
65
+ ou "últimos X dias".
66
+
67
+ Por que mudou: até 01/08 a borda do dia era montada em UTC, o que errava **3 horas** no Brasil —
68
+ conversa criada às 22h aparecia no dia seguinte. Agora a borda é a do fuso da conta.
69
+
39
70
  ## 2. Contatos — `lionchat_contacts_filter`
40
71
 
41
72
  Mesmo shape de condição. 15/página.
@@ -186,6 +186,13 @@ Webhooks embutidos NÃO aparecem na listagem de integrações standalone; exclui
186
186
  - `title` = texto **EXATO** do botão quick-reply aprovado na Meta (o WhatsApp devolve o TÍTULO no clique; o match é por ele). `value` = slug do título (deduplicado se dois títulos colidem).
187
187
  - Handles: `button_<value>` por botão + `no_response` (sempre — texto livre em vez de clique) + `no_reply_timeout` (só com `buttons_timeout > 0`; `buttons_timeout_unit` `minutes|hours|days`; `0` = sem timeout).
188
188
  - Modo síncrono (`ai_tool`) e `dry_run` NÃO pausam/roteiam — o template só é enviado (botões decorativos). Só faz sentido em caixa WhatsApp com template de botões aprovado.
189
+ - **O item guarda uma FOTO do template** (corpo + botões) tirada quando foi configurado. Se o modelo for
190
+ alterado na Meta depois disso, a foto envelhece: até 2026-07-30 a atualização automática ao reabrir o
191
+ bloco comparava só o CORPO, então mudança APENAS de botão passava batido — a tela mostrava os botões
192
+ novos (ela lê o modelo vivo) e o flow continuava roteando pelos ANTIGOS. Caso real: um botão renomeado
193
+ fazia todo mundo cair em "outra resposta". Hoje corpo E botões são comparados. **Se você mudou os
194
+ botões de um template na Meta, avise que é preciso reabrir o bloco e RELIGAR a saída renomeada** — a
195
+ aresta do botão antigo é removida junto.
189
196
 
190
197
  > ⚠️ **SAÍDAS CONDICIONAIS — NUNCA ligue edge nelas sem ativar a condição.** Estas saídas SÓ existem
191
198
  > quando a config abaixo está presente. Ligar edge nelas sem isso cria uma **aresta fantasma** (linha
@@ -269,6 +276,13 @@ No editor visual esses destinos só aparecem quando a validação bate (computed
269
276
 
270
277
  **REGRA:** depois de wait_response com `options` OU `varied_options`, NUNCA coloque node `condition` pra ramificar — ligue os edges direto nos handles `option_X` (em `varied_options`, `X` é o `id` do grupo).
271
278
 
279
+ **Node que NÃO salva também avança (corrigido 2026-08-01):** até essa data, o avanço do `wait_response`
280
+ dependia de ter uma variável configurada — com `saveTo: ""` o node RECEBIA a resposta e reentrava em si
281
+ mesmo, e quem respondia no prazo acabava saindo pelo caminho de `timeout`. Sintoma que o cliente
282
+ relatava: "mando a mensagem e o fluxo trava em aguardando resposta". Hoje o node avança sempre; salvar
283
+ o valor é opcional de novo, como o schema sempre prometeu. Se um flow antigo tiver ganhado uma variável
284
+ só pra destravar, ela pode sair.
285
+
272
286
  ### 2.4 `condition`
273
287
 
274
288
  ```json
@@ -325,7 +339,8 @@ No editor visual esses destinos só aparecem quando a validação bate (computed
325
339
  | `equal_any` / `not_equal_any` / `contains_any` / `not_contains_any` | multi-valor (usa `values` array). `contains_any` = contém ALGUMA; `not_contains_any` = não contém NENHUMA (útil pra "seguir só quando a msg não tem nenhuma das palavras-chave de outros flows") |
326
340
  | `business_hours` / `outside_business_hours` | horário comercial (par: dentro/fora). `business_hours` aceita `start_hour`/`end_hour` (0-23), `days` (array 0=Dom..6=Sab, ausente=todos) e **`timezone`** (IANA, ex.: `America/Sao_Paulo` — default se ausente). **REGRA:** a saída `outside_business_hours` HERDA `start_hour`/`end_hour`/`days`/`timezone` da `business_hours` ANTERIOR no array — pode deixá-los ausentes na "fora" (o backend preenche). O horário é avaliado no `timezone` (não em UTC) |
327
341
  | `can_reply` / `can_reply_closed` | janela 24h aberta/fechada |
328
- | `conversation_has_agent` / `conversation_no_agent` / `conversation_not_agent` | agente atribuído |
342
+ | `conversation_has_agent` / `conversation_no_agent` / `conversation_not_agent` | agente atribuído (HUMANO) |
343
+ | `conversation_has_ai_agent` / `conversation_no_ai_agent` / `conversation_not_ai_agent` | agente de IA atribuído (novo 2026-08-01) — ver abaixo |
329
344
  | `contact_has_label` / `contact_no_label` / `conversation_has_label` / `conversation_no_label` | labels |
330
345
  | `kanban_exists` / `kanban_in_stage` / `kanban_won` / `kanban_lost` | card no funil. `funnel_id` é CHAVE SEPARADA da condição (número, ex.: `"funnel_id": 37`). A etapa vai em `value` (slug puro da etapa, ex.: `"avaliacao_aceita"`) ou em `stage` — NÃO no formato `"37:etapa"`. Ex.: `{ "operator": "kanban_in_stage", "funnel_id": 37, "value": "avaliacao_aceita" }` |
331
346
  | `card_attr_equals` / `card_attr_contains` | atributo do card (`attrSource: 'card'` + `attr_key`; aceita `card_source: 'trigger'` p/ ler o card que iniciou o flow) |
@@ -342,6 +357,22 @@ No editor visual esses destinos só aparecem quando a validação bate (computed
342
357
 
343
358
  `greater_than`/`less_than` valem em atributo número (valor numérico) E temporais `date`/`time`/`datetime` (o backend detecta o formato do valor: instante > dia > minutos > número). `number_range` SÓ em número. Nunca use maior/menor em texto/lista.
344
359
 
360
+ **Agente de IA — `conversation_has_ai_agent` e irmãos (novo 2026-08-01):** o flow sempre soube LIGAR e
361
+ DESLIGAR a IA (ações `assign_captain` / `deactivate_captain`), mas não sabia PERGUNTAR se ela estava
362
+ ligada. Agora sabe. É o espelho exato do trio do agente humano:
363
+
364
+ | Forma | Verdadeiro quando |
365
+ |---|---|
366
+ | `{ "operator": "conversation_has_ai_agent" }` (sem valor) | tem QUALQUER IA atribuída |
367
+ | `{ "operator": "conversation_has_ai_agent", "values": [12, 15] }` | a IA atribuída é uma dessas |
368
+ | `{ "operator": "conversation_has_ai_agent", "value": 12 }` | a IA atribuída é exatamente essa |
369
+ | `{ "operator": "conversation_no_ai_agent" }` | não tem IA nenhuma |
370
+ | `{ "operator": "conversation_not_ai_agent", "values": [12] }` | tem IA, mas não é nenhuma dessas |
371
+
372
+ **Semântica (decisão do dono 01/08): reflete ATRIBUIÇÃO, não "vai responder agora".** Assistente
373
+ pausado no botão de pânico global continua contando como atribuído. Se o cliente perguntar "a IA está
374
+ respondendo?", essa condição NÃO responde isso — ela responde "a IA está ligada nesta conversa?".
375
+
345
376
  **SLA — operador `sla_check` (2026-06):** verifica se a conversa está dentro/fora do prazo de SLA. A regra NÃO usa `field`, só `value` (código fixo):
346
377
 
347
378
  | `value` | Verdadeiro quando |
@@ -813,6 +844,41 @@ continua servindo pra GRAVAR no contato). Teto: payload acima de 256KB não vira
813
844
  disparado por OUTRO gatilho (mensagem, card, data): `{{webhook.*}}` resolve vazio. O autocomplete
814
845
  do editor mostra o grupo WEBHOOK com os campos do último payload recebido.
815
846
 
847
+ **Variáveis do gatilho `{{trigger.*}}` (novo 2026-08-01):** todo flow — não importa o gatilho — passa
848
+ a saber O QUE o disparou. Antes só existia o tipo, e um flow com VÁRIOS gatilhos não tinha como se
849
+ ramificar por qual deles disparou. Agora dá: `{{trigger.type}}` num node `condition`.
850
+
851
+ Sempre presente:
852
+
853
+ | Variável | Conteúdo |
854
+ |---|---|
855
+ | `{{trigger.type}}` | chave do evento (`message_created`, `conversation_created`, `label_added`, `kanban_item_stage_changed`, `webhook_received`, `date_trigger`, `flow_campaign`, `manual`, `pagetrack_visited`…) |
856
+ | `{{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 |
857
+ | `{{trigger.activated_at}}` | quando disparou (ISO 8601) |
858
+ | `{{trigger.kanban_item_id}}` / `{{trigger.funnel_id}}` | só quando veio do Kanban |
859
+
860
+ Presentes conforme o evento (ausente = resolve vazio, sem erro):
861
+
862
+ | Bloco | Campos |
863
+ |---|---|
864
+ | `{{trigger.message.*}}` | `id`, `content` (texto da mensagem, cortado em 2.000 caracteres), `type`, `created_at` |
865
+ | `{{trigger.conversation.*}}` | `id` (o display_id), `status`, `channel` (nome amigável: `Waha`, `Whatsapp`, `Instagram`), `inbox_id`, `inbox_name`, `created_at` |
866
+ | `{{trigger.contact.*}}` | `id`, `name`, `phone` |
867
+ | `{{trigger.ad.*}}` (anúncio/CTWA) | `id`, `name`, `creative_id`, `creative_name`, `adset_id`, `adset_name`, `campaign_id`, `campaign_name`, `source`, `headline`, `description`, `cta`, `url` |
868
+ | `{{trigger.attribute.*}}` | `name`, `previous_value`, `current_value`, `changed_at` — o valor ANTES e DEPOIS do atributo que disparou (só nos gatilhos de mudança de atributo) |
869
+
870
+ Dois atalhos: `{{trigger}}` devolve o contexto INTEIRO em JSON e `{{trigger.data}}` devolve o mesmo
871
+ sem os metadados — úteis pra jogar tudo dentro de um node `ai` ou `api`. Bloco (Hash/Array) inteiro
872
+ sai em JSON; valor simples sai cru.
873
+
874
+ **NÃO duplique o que já tem variável própria.** `{{contact.name}}`, `{{conversation.status}}`,
875
+ `{{inbox.name}}` e os atributos `ctwa_*` continuam existindo e são a forma canônica. O `{{trigger.*}}`
876
+ serve pra perguntar "o que veio COM o gatilho", não pra ser um segundo nome do mesmo dado.
877
+
878
+ **Variáveis do agente de IA `{{ai_agent.*}}` (novo 2026-08-01):** `{{ai_agent.name}}` e
879
+ `{{ai_agent.id}}` trazem o AI Agente atribuído à conversa (vazio se não houver). Prefixo `ai_agent` de
880
+ propósito — `agent` sozinho é o atendente HUMANO, e confundir os dois seria pior que não ter.
881
+
816
882
  ---
817
883
 
818
884
  ## 2-D. Exit conditions (saída automática do flow)
@@ -168,6 +168,23 @@ do rodízio).
168
168
  aceitam `supervisor_ids[]` além de `user_ids[]` — os IDs em `supervisor_ids` viram supervisores;
169
169
  os de `user_ids` viram membros comuns. A resposta traz `is_supervisor` (boolean) por agente.
170
170
 
171
+ ### Supervisor de TIME (novo 2026-07-30) — é outra marca, independente da de caixa
172
+
173
+ O time ganhou os mesmos dois papéis. Supervisor de time continua membro pra tudo (menção, e-mail de
174
+ automação, acesso a funil do Kanban, relatório, contagem, atribuição MANUAL) e fica fora da
175
+ distribuição automática do time — tanto de conversa quanto do rodízio de card do funil.
176
+
177
+ **Onde fica armazenado:** flag `team_members.auto_assignable` (espelho da de caixa).
178
+
179
+ **Como definir/ler via tools:** `lionchat_team_members_create` / `_update` com `supervisor_ids[]`.
180
+ **Diferença importante do contrato da caixa:** aqui `user_ids[]` é sempre a lista COMPLETA de membros
181
+ e `supervisor_ids[]` é um SUBCONJUNTO dela; omitir `supervisor_ids` não mexe em papel nenhum.
182
+ `lionchat_team_members_list` traz `is_supervisor`.
183
+
184
+ **As duas marcas NÃO se comunicam:** a da caixa protege da distribuição da caixa, a do time da
185
+ distribuição do time. Supervisor de caixa que é agente num time continua recebendo por aquele time —
186
+ comportamento intencional.
187
+
171
188
  ## KanbanItem (Card do CRM)
172
189
 
173
190
  ### Estrutura
@@ -489,6 +489,25 @@ viraram conversa). É um relatório de LEITURA puro — não cria nem altera nad
489
489
  **Perguntas que esse relatório responde:** "qual plataforma trouxe mais leads?", "qual campanha/
490
490
  conjunto converteu melhor?", "quantos leads únicos vieram esse mês e quantos fecharam?".
491
491
 
492
+ ### Atributos de anúncio (`ctwa_*`) — dois novos e nomes alinhados (2026-08-01)
493
+
494
+ Conversa que veio de anúncio "Clique para WhatsApp" carrega atributos `ctwa_*` (`ctwa_campaign_name`,
495
+ `ctwa_adset_name`, `ctwa_ad_name`, `ctwa_creative_name`, `ctwa_source_url`…). Eles são atributos de
496
+ conversa normais: dá pra filtrar (`lionchat_conversations_filter`), usar em público de campanha e ler
497
+ em flow (`{{trigger.ad.*}}`).
498
+
499
+ Dois deles apareciam no painel **sem cadastro nenhum** e por isso não dava pra filtrar por eles em
500
+ lugar algum. Foram criados em 01/08 e agora funcionam como os demais:
501
+
502
+ | Chave | Rótulo | Tipo |
503
+ |---|---|---|
504
+ | `ctwa_conversion_source` | Plataforma de Origem do Anúncio | texto |
505
+ | `ctwa_conversion_delay_seconds` | Tempo até a Conversão (s) | número |
506
+
507
+ Na mesma data os rótulos de exibição dos `ctwa_*` foram alinhados com os do painel. **As CHAVES não
508
+ mudaram** — flow, automação, campanha e filtro guardam a chave, nunca o rótulo, então nada que já
509
+ existia quebrou. Se o cliente disser que "o nome do campo mudou", é só o rótulo na tela.
510
+
492
511
  ## Quando o usuário pede algo MUITO específico que não existe num endpoint
493
512
 
494
513
  Se a pergunta requer cálculos custom (ex: "conversas por agente que duraram mais de X minutos"):
@@ -283,6 +283,42 @@ Cancelar vale pra TODOS os grupos (inclusive `permanent`, `window_expired`, `cam
283
283
  que nunca seriam reenviados) — é assim que se limpa o painel. Ações irreversíveis: confirme com o
284
284
  usuário e mostre as contagens do summary antes.
285
285
 
286
+ ### "Erro 131026 / Message undeliverable" — NUNCA chute a causa (30/07)
287
+ A Meta devolve o MESMO código 131026 pra duas coisas diferentes: **o número não tem WhatsApp** e **a
288
+ pessoa não está recebendo** (bloqueou, aparelho fora, não aceita mensagem de empresa). A Meta não diz
289
+ qual das duas — então a plataforma carimba um fato NOSSO em
290
+ `content_attributes.undeliverable_history`:
291
+
292
+ | Carimbo | O que significa | O que dizer ao cliente |
293
+ |---|---|---|
294
+ | `never_delivered` | nunca entregamos nada pra esse contato | provavelmente o número não tem WhatsApp — vale conferir o número |
295
+ | `delivered_before` | já entregamos antes, hoje não entrega | o número existe; a pessoa é que não está recebendo agora |
296
+ | ausente (mensagem antiga) | sem histórico carimbado | enuncie as DUAS possibilidades, sem escolher |
297
+
298
+ **Nunca afirme "esse número não tem WhatsApp" sem o carimbo `never_delivered`.** Foi exatamente esse
299
+ palpite que fez atendente encerrar conversa de paciente que existia (conta 56, conv 3629).
300
+
301
+ ### "Mídia recusada pela Meta (131053) e a faixa vermelha sumiu sozinha" (30/07)
302
+ Comportamento novo, não é bug. O 131053 é um balde: uma das variantes (a que menciona `weblink`) é
303
+ TRANSITÓRIA — o buscador da Meta falhou ao baixar o arquivo pela URL. Nesse caso a plataforma reenvia
304
+ sozinha, agora por upload direto, e a mensagem fica verde sem precisar de F5. As demais variantes do
305
+ 131053 (formato/tamanho recusado) NÃO são resgatadas — regra fail-closed de propósito, pra não ficar
306
+ reenviando eternamente algo que a Meta nunca vai aceitar.
307
+
308
+ ### "A mensagem falhou dizendo 'Template not found or invalid template name'" (30/07)
309
+ Se a caixa é **WhatsApp oficial** e faz mais de 24h que o cliente não fala, a causa quase sempre é a
310
+ **janela de 24h fechada** — não um template faltando. Até 30/07 o erro gravado era esse texto
311
+ enganoso; hoje a mensagem diz que a janela fechou, e o classificador põe a falha no grupo
312
+ `window_expired` (fora do reenvio em lote — reenviar não adiantaria).
313
+
314
+ Regras de produto pra explicar ao cliente:
315
+ - **Mandar template NÃO abre a janela.** Só a mensagem do CLIENTE abre.
316
+ - Fora da janela, só template aprovado sai. Texto livre é recusado pela Meta.
317
+ - O follow-up automático da IA passou a entender isso: fora das 24h ele escolhe um modelo aprovado em
318
+ vez de escrever texto livre e tomar recusa (ver `lionchat://docs/best-practices`).
319
+ - Erro sem tradução deixou de existir: todo `external_error` agora vira texto em português na bolha e
320
+ no painel de falhas. Se aparecer erro em inglês cru pro usuário, isso é reportável.
321
+
286
322
  ### "O Messenger parou de enviar / 'Invalid parameter'" (24/07)
287
323
  Causa raiz do apagão de 22 a 24/07: a Meta **aposentou a message tag** que o sistema mandava em todo
288
324
  envio de Messenger. Corrigido em 24/07. Agora o Messenger respeita a **janela de 24h** de verdade:
package/endpoints.json CHANGED
@@ -10091,14 +10091,14 @@
10091
10091
  "type": "array",
10092
10092
  "location": "body",
10093
10093
  "required": true,
10094
- "description": "[{attribute_key, filter_operator, query_operator, values[]}]"
10094
+ "description": "[{attribute_key, filter_operator, query_operator, values[]}] REGRA DO CONECTOR (query_operator) — LEIA ANTES DE GRAVAR: o conector liga a condicao com a SEGUINTE, logo a ULTIMA condicao TEM que ter query_operator: null (condicao UNICA tambem = null). Nas demais: 'AND' ou 'OR'. A API valida o VALOR do conector mas NAO a POSICAO — conector na ultima condicao SALVA COM SUCESSO, aparece certo na tela e a regra NUNCA dispara. Incidente real: 8 regras criadas pelo conector de IA entre 26 e 30/07/2026 em 3 contas, todas com conector na ultima condicao, todas nasceram mortas (732 erros/hora medidos em producao 03/08; a conta 62 ficou com as 6 regras de etiquetagem automatica paradas). attribute_key precisa ser campo padrao OU atributo personalizado que EXISTA na conta (senao a regra inteira falha na validacao: 'Automation conditions <chave> not supported') — conferir antes com lionchat_custom_attributes_list; atributo personalizado exige tambem custom_attribute_type ('conversation_attribute' ou 'contact_attribute'). filter_operator: equal_to, not_equal_to, contains, does_not_contain, is_present, is_not_present, is_greater_than, is_less_than, starts_with, days_before, days_ago, last_days, today, yesterday — cada campo aceita so um subconjunto. Na duvida, leia uma regra que ja funciona com lionchat_automation_rules_show e copie o formato. Detalhe no resource 'conversation-flows'."
10095
10095
  },
10096
10096
  {
10097
10097
  "name": "actions",
10098
10098
  "type": "array",
10099
10099
  "location": "body",
10100
10100
  "required": true,
10101
- "description": "CADA item de actions DEVE ser um OBJETO {action_name (string), action_params (array)} — NUNCA um texto/string solto. Ex.: [{\"action_name\":\"send_message\",\"action_params\":[\"Oi\"]}]. [{action_name, action_params[]}] Actions novas (2026-07-21): 'update_contact_attribute' e 'update_conversation_attribute' — action_params: [{attribute_key, value}], value aceita texto fixo OU variável Liquid (ex. {{contact.phone_number}}); grava atributo personalizado com MERGE (não apaga os demais) e passa pelo porteiro de data. 'pending_conversation' (2026-07-20, sem params) — deixa a conversa PENDENTE (handoff; mensagem nova do cliente NÃO reabre)."
10101
+ "description": "CADA item de actions DEVE ser um OBJETO {action_name (string), action_params (array)} — NUNCA um texto/string solto. Ex.: [{\"action_name\":\"send_message\",\"action_params\":[\"Oi\"]}]. [{action_name, action_params[]}] Actions novas (2026-07-21): 'update_contact_attribute' e 'update_conversation_attribute' — action_params: [{attribute_key, value}], value aceita texto fixo OU variável Liquid (ex. {{contact.phone_number}}); grava atributo personalizado com MERGE (não apaga os demais) e passa pelo porteiro de data. 'pending_conversation' (2026-07-20, sem params) — deixa a conversa PENDENTE (handoff; mensagem nova do cliente NÃO reabre). ATENCAO — 'send_whatsapp_template': action_params: [{name, id, language, category, processed_params}]. 'name' e OBRIGATORIO e e o nome EXATO do modelo aprovado na Meta (o envio acontece por ele); 'language' precisa bater com o idioma aprovado (padrao pt_BR); processed_params: {\"body\": {\"1\": \"{{contact.first_name}}\"}}. NUNCA use template_name/template_id/template_category/template_language — esse e o formato dos nos do FlowBuilder e aqui e IGNORADO: a regra salva com sucesso, aparece na tela e NAO ENVIA NADA, sem erro nem log (incidente real conta 39 em 02/08/2026: 14h sem enviar, 9 leads perdidos). Confira o modelo antes com lionchat_inboxes_whatsapp_templates_list. So funciona em caixa WhatsApp API Oficial (Cloud). REGRA GERAL: action_params e SEMPRE um array (o backend le params[0]) e seu CONTEUDO nao e validado pela API. Se nao souber o formato de uma acao, leia uma regra existente com lionchat_automation_rules_show e copie — nunca invente nome de chave. Formato de TODAS as acoes no resource 'conversation-flows'."
10102
10102
  },
10103
10103
  {
10104
10104
  "name": "active",
@@ -10189,14 +10189,14 @@
10189
10189
  "type": "array",
10190
10190
  "location": "body",
10191
10191
  "required": false,
10192
- "description": "Condicoes"
10192
+ "description": "Condicoes REGRA DO CONECTOR (query_operator) — LEIA ANTES DE GRAVAR: o conector liga a condicao com a SEGUINTE, logo a ULTIMA condicao TEM que ter query_operator: null (condicao UNICA tambem = null). Nas demais: 'AND' ou 'OR'. A API valida o VALOR do conector mas NAO a POSICAO — conector na ultima condicao SALVA COM SUCESSO, aparece certo na tela e a regra NUNCA dispara. Incidente real: 8 regras criadas pelo conector de IA entre 26 e 30/07/2026 em 3 contas, todas com conector na ultima condicao, todas nasceram mortas (732 erros/hora medidos em producao 03/08; a conta 62 ficou com as 6 regras de etiquetagem automatica paradas). attribute_key precisa ser campo padrao OU atributo personalizado que EXISTA na conta (senao a regra inteira falha na validacao: 'Automation conditions <chave> not supported') — conferir antes com lionchat_custom_attributes_list; atributo personalizado exige tambem custom_attribute_type ('conversation_attribute' ou 'contact_attribute'). filter_operator: equal_to, not_equal_to, contains, does_not_contain, is_present, is_not_present, is_greater_than, is_less_than, starts_with, days_before, days_ago, last_days, today, yesterday — cada campo aceita so um subconjunto. Na duvida, leia uma regra que ja funciona com lionchat_automation_rules_show e copie o formato. Detalhe no resource 'conversation-flows'."
10193
10193
  },
10194
10194
  {
10195
10195
  "name": "actions",
10196
10196
  "type": "array",
10197
10197
  "location": "body",
10198
10198
  "required": false,
10199
- "description": "CADA item de actions DEVE ser um OBJETO {action_name (string), action_params (array)} — NUNCA um texto/string solto. Ex.: [{\"action_name\":\"send_message\",\"action_params\":[\"Oi\"]}]. Acoes Actions novas (2026-07-21): 'update_contact_attribute' e 'update_conversation_attribute' — action_params: [{attribute_key, value}], value aceita texto fixo OU variável Liquid (ex. {{contact.phone_number}}); grava atributo personalizado com MERGE (não apaga os demais) e passa pelo porteiro de data. 'pending_conversation' (2026-07-20, sem params) — deixa a conversa PENDENTE (handoff; mensagem nova do cliente NÃO reabre)."
10199
+ "description": "CADA item de actions DEVE ser um OBJETO {action_name (string), action_params (array)} — NUNCA um texto/string solto. Ex.: [{\"action_name\":\"send_message\",\"action_params\":[\"Oi\"]}]. Acoes Actions novas (2026-07-21): 'update_contact_attribute' e 'update_conversation_attribute' — action_params: [{attribute_key, value}], value aceita texto fixo OU variável Liquid (ex. {{contact.phone_number}}); grava atributo personalizado com MERGE (não apaga os demais) e passa pelo porteiro de data. 'pending_conversation' (2026-07-20, sem params) — deixa a conversa PENDENTE (handoff; mensagem nova do cliente NÃO reabre). ATENCAO — 'send_whatsapp_template': action_params: [{name, id, language, category, processed_params}]. 'name' e OBRIGATORIO e e o nome EXATO do modelo aprovado na Meta (o envio acontece por ele); 'language' precisa bater com o idioma aprovado (padrao pt_BR); processed_params: {\"body\": {\"1\": \"{{contact.first_name}}\"}}. NUNCA use template_name/template_id/template_category/template_language — esse e o formato dos nos do FlowBuilder e aqui e IGNORADO: a regra salva com sucesso, aparece na tela e NAO ENVIA NADA, sem erro nem log (incidente real conta 39 em 02/08/2026: 14h sem enviar, 9 leads perdidos). Confira o modelo antes com lionchat_inboxes_whatsapp_templates_list. So funciona em caixa WhatsApp API Oficial (Cloud). REGRA GERAL: action_params e SEMPRE um array (o backend le params[0]) e seu CONTEUDO nao e validado pela API. Se nao souber o formato de uma acao, leia uma regra existente com lionchat_automation_rules_show e copie — nunca invente nome de chave. Formato de TODAS as acoes no resource 'conversation-flows'."
10200
10200
  }
10201
10201
  ],
10202
10202
  "returns": "regra de automação atualizado",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lionchat/mcp-server",
3
- "version": "1.10.0",
3
+ "version": "1.10.2",
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",