@lionchat/mcp-server 1.30.0 → 1.32.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.
@@ -62,7 +62,7 @@ Todo node tem essa estrutura base:
62
62
 
63
63
  **ATENÇÃO — chave `items`, não `triggers` (corrigido 19/08/2026).** Os gatilhos vão em **`data.items`**, cada item no formato **`{ "key": "...", "config": { ...filtros... } }`**. O formato antigo (`data.triggers` + `type` + filtros soltos no topo) ainda é aceito pelo motor por compatibilidade, mas **NÃO deve ser usado em fluxo novo**: o editor do FlowBuilder lê apenas `data.items`, então um fluxo gravado no formato antigo **dispara normalmente mas abre com o bloco Início EM BRANCO no painel do cliente** — e um clique em "Concluir" naquele modal apagava o gatilho em silêncio. Foram 53 fluxos de 9 contas nesse estado até o conserto de 19/08. Alguns leitores do backend (`has_webhook_trigger?` do disparo por webhook, o filtro de Campanha de Fluxo, o gatilho LionTrack e o histórico de execução) também só entendem `items`.
64
64
 
65
- **Triggers válidos:** `message_received`, `message_sent`, `conversation_created`, `conversation_resolved`, `conversation_reopened`, `team_changed`, `assignee_changed`, `label_added`, `label_removed`, `sla_missed`, `card_created`, `card_moved`, `card_won`, `card_lost`, `conversation_attribute_changed`, `card_attribute_changed`, `contact_attribute_changed`, `date_trigger`, `campaign_trigger`, `manual_trigger`, `webhook_received`, `page_track`, `group_participant_joined`, `group_participant_left`, `lead_form_completed`, `lead_form_milestone`, `lead_form_abandoned`, `booking_created`, `booking_cancelled`, `booking_rescheduled`, `booking_completed`.
65
+ **Triggers válidos:** `message_received`, `message_sent`, `conversation_created`, `conversation_resolved`, `conversation_reopened`, `team_changed`, `assignee_changed`, `label_added`, `label_removed`, `sla_missed`, `card_created`, `card_moved`, `card_won`, `card_lost`, `card_opened`, `card_snoozed`, `conversation_attribute_changed`, `card_attribute_changed`, `contact_attribute_changed`, `date_trigger`, `campaign_trigger`, `manual_trigger`, `webhook_received`, `page_track`, `group_participant_joined`, `group_participant_left`, `lead_form_completed`, `lead_form_milestone`, `lead_form_abandoned`, `booking_created`, `booking_cancelled`, `booking_rescheduled`, `booking_completed`.
66
66
 
67
67
  **NÃO existem** os gatilhos `webhook` (o correto é **`webhook_received`**) nem `cron` (para disparo por data use `date_trigger`; para disparo em lote use `campaign_trigger` + Campanha de Fluxo). Até 19/08/2026 este guia listava os dois por engano — o fluxo 180 da conta 54 ficou ATIVO com `webhook` e **nunca disparou uma vez em 26 dias**, sem erro nenhum.
68
68
 
@@ -74,6 +74,7 @@ Todo node tem essa estrutura base:
74
74
  - `label_added` / `label_removed`: `label_names` (array de slugs). NÃO use `label` (singular) — é ignorado.
75
75
  - `card_created` / `card_moved`: `funnel_ids` (array de STRINGS, ex.: `["37"]` — número puro `[37]` NÃO casa) + `funnel_stages` (array de `"funnel_id:chave_da_etapa"`, ex.: `"37:agendamento_pendente"`). A `chave_da_etapa` é a CHAVE INTERNA da etapa no funil (slug legível em funis de template; pode ser um UUID/`stage_<n>` em etapa criada à mão ou funil duplicado) — NÃO o nome exibido na tela. Sem `funnel_ids` → dispara em qualquer funil; com `funnel_ids` mas sem `funnel_stages` → qualquer etapa daquele(s) funil(is). O card precisa estar num funil listado em `funnel_ids` pra o filtro de etapa valer. ATENÇÃO: dois flows ATIVOS na mesma inbox com `card_created`/`card_moved` de funil/etapa sobrepostos são bloqueados na criação/ativação (ver `flow_trigger_conflict` no fim deste guia).
76
76
  - `team_changed`: `team_ids` (array de STRINGS; vazio = qualquer equipe). `assignee_changed`: `agent_ids` (array de STRINGS; vazio = qualquer atendente). `card_won` / `card_lost`: `funnel_ids` (array de STRINGS) — disparam SÓ quando o STATUS do card vira ganho/perdido, nunca por etapa com esse nome.
77
+ - `card_opened` ("Card aberto") / `card_snoozed` ("Card adiado") — NOVOS 11/10/2026, mesma config `funnel_ids` (array de STRINGS; vazio = qualquer funil). `card_snoozed` dispara quando o card é ADIADO. `card_opened` dispara quando um card adiado ABRE (pela data, por alguém ou por automação) e também quando um card ganho/perdido é REABERTO — card NOVO não dispara (para isso existe `card_created`). Pela data, o fluxo sai pela fila de disparos agendados, uma vez por card. Use `{{trigger.kanban.previous_status}}` (`snoozed`/`won`/`lost`) para separar "voltou do adiamento" de "reabriram um ganho/perdido".
77
78
  - `page_track` (LionTrack, só flow individual): `{ "track_type": "visited_page"|"event_fired", "urls": [...], "operator": "contains"|"exact"|"starts_with", "event_names": [...], "event_operator": "equals"|"contains", "cooldown_hours": 24 }` — `cooldown_hours` (padrão 24, mínimo 1) não re-dispara pro mesmo contato dentro da janela.
78
79
  - `conversation_attribute_changed` / `card_attribute_changed` (novo 2026-07-08): disparam na VIRADA de um atributo (da CONVERSA ou do CARD do kanban) pro valor que casa — RE-ENTRAM toda vez que o atributo muda pro valor alvo. Config: `{ "logic": "and"|"or", "rules": [ { "attr_key": "...", "operator": "...", "value": "..." } ] }` (uma rule pode usar `values: [...]` no lugar de `value` pra multi-valor). O `attrSource` é IMPLÍCITO pela chave do gatilho (conversa vs card) — NÃO informe. Operadores (contexto REATIVO — desde 09/07 `is_empty`/`is_not_empty` NÃO valem aqui, pois "está vazio AGORA" dispararia a cada evento; use-os só no nó Condição): `equal`/`not_equal`/`contains`/`not_contains`/`starts_with`/`ends_with`/`greater_than`/`less_than`/`number_range`. Só `card_attribute_changed` aceita `funnel_id`/`card_source` opcionais dentro da rule (pra achar o card). Rule sem `attr_key` é ignorada.
79
80
  - `contact_attribute_changed` (novo 2026-07-22 — "Atributo do contato muda"): dispara quando um atributo personalizado do **CONTATO** muda e a condição casa — mesmo com o contato fora de flow. Item: `{ "key": "contact_attribute_changed", "config": { "logic": "and"|"or", "rules": [ { "attrSource": "contact", "attr_key": "...", "operator": "...", "value": "..." } ] } }` (1 a 10 rules; `values: [...]` pra multi-valor). **DIFERENTE dos irmãos de 08/07: aqui o `attrSource` VAI na rule e é SEMPRE `"contact"`.** Operadores iguais aos do nó Condição. **VERSÃO SEGURA por decisão de produto:** dispara SÓ na conversa mais recente que JÁ EXISTE numa caixa do flow (reabre se resolvida); contato sem conversa NÃO dispara e NUNCA cria conversa. Dedup de 30s por contato + anti-loop por profundidade de cadeia (flow que muda atributo que dispara flow…). Só flows de conversa.
@@ -152,7 +153,7 @@ Receita completa: `flows_create`/`flows_update` com o item no start → `flows_l
152
153
  4. Salvar o flow (`flows_update`) — o save sincroniza a ativação do webhook embutido (remover o item desativa a integração automaticamente).
153
154
  Webhooks embutidos NÃO aparecem na listagem de integrações standalone; excluir o flow destrói o webhook; duplicar o flow NÃO copia o gatilho embutido. Rate limit do endpoint público: 60/min por token.
154
155
 
155
- **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.
156
+ **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.confirm_url}}`, `{{booking.calendar_url}}` (16/10: página onde o cliente salva o compromisso na agenda DELE, sem e-mail; no remarcado já sai com a hora nova — é o jeito de avisar a mudança, pedindo para apagar o compromisso antigo), `{{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.
156
157
 
157
158
  **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).
158
159
  - **`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.
@@ -397,6 +398,7 @@ só pra destravar, ela pode sair.
397
398
  | `conversation_has_ai_agent` / `conversation_no_ai_agent` / `conversation_not_ai_agent` | agente de IA atribuído (novo 2026-08-01) — ver abaixo |
398
399
  | `contact_has_label` / `contact_no_label` / `conversation_has_label` / `conversation_no_label` | labels |
399
400
  | `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" }` |
401
+ | `kanban_snoozed` (11/10/2026) | card ADIADO no funil — `field: '_kanban_status'`, `funnel_id` separado, igual às irmãs `kanban_won`/`kanban_lost` (mesma regra do card que iniciou o fluxo). Não existe condição "card aberto" |
400
402
  | `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) |
401
403
  | `pagetrack_visited` / `pagetrack_event` | LionTrack |
402
404
  | `sla_check` | status do SLA da conversa (usa `value` = código fixo; ver abaixo) |
@@ -457,7 +459,7 @@ campanha"):
457
459
  `origin_first_kind`/`origin_first_platform`, `origin_last_kind`/`origin_last_platform`, `origin_*` em geral
458
460
  (origem unificada), `ctwa_*` (anúncio click-to-WhatsApp: `ctwa_ad_id`, `ctwa_ad_title`, `ctwa_campaign_name`,
459
461
  `ctwa_adset_name`...), `meta_lead_*` (formulário Meta Lead Ads), `lt_*` (LionTrack: UTMs, página, dispositivo).
460
- Veja a lista real com `lionchat_custom_attributes_list` (`attribute_model: conversation`, `include_system: true` — as chaves de campanha são atributos de sistema).
462
+ Veja a lista real com `lionchat_custom_attributes_list` (`attribute_model: conversation_attribute` — com o sufixo; só `conversation` devolve a lista VAZIA —, `include_system: true` — as chaves de campanha são atributos de sistema).
461
463
  - **Valores fechados** de `origin_kind` (e `origin_first_kind`/`origin_last_kind`): `paid_ad` (Anúncio),
462
464
  `lead_form` (Formulário), `organic` (Orgânico), `direct` (Direto), `referral` (Indicação), `manual` (Origem
463
465
  cadastrada). O painel mostra o rótulo em português, mas a comparação é pelo VALOR — `"Anúncio"` nunca casa.
@@ -558,6 +560,7 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
558
560
  | `set_kanban_item_status` | `{ status: 'won' \| 'lost' \| 'active' }` | Marca status do card |
559
561
  | `set_won` | `{}` | Atalho pra ganho |
560
562
  | `set_lost` | `{ reason? }` | Atalho pra perdido |
563
+ | `snooze_kanban_item` (11/10/2026) | `{ funnel_id, amount, unit: 'hours' \| 'days', card_source? }` | "Adiar card": ADIA o card por N horas ou dias a partir da execução (`amount` inteiro, 1 ou mais; mínimo 1 hora). Só card aberto; adiar de novo troca a data. `card_source: 'trigger'` mira o card que iniciou o fluxo (aí `funnel_id` não é exigido). Diferente das irmãs de card: card ganho/perdido, sem card ou fluxo que NÃO veio de card com `card_source: 'trigger'` = ERRO visível no passo, nunca pulado. `set_open` (e `set_kanban_item_status` com `open`) num card ADIADO abre o card na hora e dispara "Card aberto". Ver "Card adiado" no kanban-deep-dive |
561
564
  | `assign_agent_card` | `{ agent_id, mode? }` | Responsável do card. `mode`: `'add'` (default — SOMA na lista), `'replace'` (só o escolhido fica; atribui antes de remover os demais) ou `'remove_all'` (tira TODOS os responsáveis; `agent_id` dispensado) |
562
565
  | `add_card_note` | `{ text }` | Nota no card. **A chave é `text`**, igual à irmã `add_private_note`. Aqui é mais grave: o motor lê SÓ `text`, sem rede — nota criada com `content` sairia REALMENTE vazia, não só invisível no formulário. Corrigido em 04/09 (a linha dizia `content`); a gravação normaliza `content` para `text` |
563
566
  | `add_card_checklist` | `{ template_id, funnel_id?, card_source? }` | Aplica um MODELO de checklist ao card (vira um grupo). `template_id` de `kanban_config.checklist_templates`. Um modelo por bloco (repita o bloco pra mais de um) |
@@ -567,13 +570,50 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
567
570
  | `send_conversion` (novo 2026-09-01; `event_names` 2026-09-02; `messaging_event_names` 2026-09-10) | `{ destinations: ['meta'\|'ga4'\|'google_ads'], event_names: { meta?, google_ads?, ga4? }, messaging_event_names: { meta? }, event_name (reserva), value? }` | Manda o evento de conversão pro Meta (CAPI), Google Ads e/ou GA4 — o mesmo caminho do Funil, de dentro do fluxo. Aba Sistema (só flow `conversation`). Ver bloco próprio abaixo |
568
571
  | `deactivate_flow` ou `disable_flow` | `{}` | Encerra fluxo atual |
569
572
  | `set_booking_situation` (novo 2026-09-14) | `{ situation: 'cancelled' \| 'completed' \| 'attended' \| 'no_show' \| 'confirmed' }` | Muda a situação do agendamento **que disparou o fluxo** (gatilho de agendamento do bloco Início, ou fluxo ligado na configuração do tipo de agendamento). Aba **Agenda** (só flow `conversation`, só com a Agenda ligada na conta). Disparado por outra coisa: **não faz efeito e o passo fica com erro visível** (não há como adivinhar o agendamento). Já no estado pedido = passo PULADO, sem escrita (sem evento, sem laço). `cancelled` cancela o **agendamento** (desarma lembretes, invalida o link de gerenciar). Origem gravada = `fluxo`, ator = o próprio flow. Sem `rescheduled`/`snoozed`: o bloco não mexe em data |
573
+ | `create_account_task` (novo 2026-10-20) | `{ title, description?, task_type, agenda_id?, priority?, occupies_agenda?, due: {amount, unit: 'hours' \| 'days', at?, business_days?}, responsible: {mode: 'card_owner' \| 'fixed' \| 'funnel_rotation' \| 'team_rotation', user_id?, team_id?, fallback_user_id?}, card_source: 'funnel' \| 'trigger', funnel_id? }` | "Criar tarefa": cria uma tarefa COMUM da Agenda, presa ao card do fluxo (ou só à conversa, se não houver card). Os campos de `title` a `responsible` são os MESMOS da ação `create_task` da automação do funil (ver `kanban-deep-dive`). Aba **Agenda**; exige a Agenda unificada ligada na conta. **NÃO roda em fluxo `ai_tool` nem em fluxo de grupo** (o motor recusa com erro visível no passo; o save aceita, a tela esconde o item). Detalhes logo abaixo |
570
574
  | `update_attribute` | `{ attr_source: 'contact'\|'conversation'\|'card', attr_key, attr_value }` | Seta custom_attribute (ver abaixo) |
571
575
  | `assign_captain` (ou `assign_captain_assistant`) | `{ assistant_id }` | Atribui IA Captain |
572
576
  | `deactivate_captain` | `{}` | Tira a IA da conversa |
573
577
 
574
578
  **Handles que SAEM:** `success`. Não tem handle `error` — falhas viram warning silencioso e o flow continua.
575
579
 
576
- **`card_source` (blocos de card) — opcional:** as ações de card aceitam `card_source`: `'funnel'` (default — procura o card pelo `funnel_id`) ou `'trigger'` (usa o card que DISPAROU o flow, em flows iniciados por `card_created`/`card_moved`/`card_won`/`card_lost`). Com `'trigger'`, `funnel_id` deixa de ser obrigatório — EXCETO em `move_kanban_stage`/`create_kanban_item`, cujo funil/etapa são o DESTINO. Sem card-gatilho disponível, a ação é pulada (não cai no fallback de funil). Aplica-se a `move_kanban_stage`, `set_won`/`set_lost`/`set_open`, `assign_agent_card`, `add_card_note`, `add_card_checklist`, `add_card_offer`, `update_attribute` (com `attr_source: 'card'`) e às condições `card_attr_equals`/`card_attr_contains` (gravando `card_source` na própria regra).
580
+ **`card_source` (blocos de card) — opcional:** as ações de card aceitam `card_source`: `'funnel'` (default — procura o card pelo `funnel_id`) ou `'trigger'` (usa o card que DISPAROU o flow, em flows iniciados por `card_created`/`card_moved`/`card_won`/`card_lost`/`card_opened`/`card_snoozed`). Com `'trigger'`, `funnel_id` deixa de ser obrigatório — EXCETO em `move_kanban_stage`/`create_kanban_item`, cujo funil/etapa são o DESTINO. Sem card-gatilho disponível, a ação é pulada (não cai no fallback de funil). Aplica-se a `move_kanban_stage`, `set_won`/`set_lost`/`set_open`, `snooze_kanban_item` (esta, sem card-gatilho, dá ERRO em vez de pular), `assign_agent_card`, `add_card_note`, `add_card_checklist`, `add_card_offer`, `create_account_task` (aqui sem card-gatilho = ERRO; ver abaixo), `update_attribute` (com `attr_source: 'card'`) e às condições `card_attr_equals`/`card_attr_contains` (gravando `card_source` na própria regra).
581
+
582
+ **`create_account_task` — Criar tarefa (2026-10-20):**
583
+
584
+ ```json
585
+ { "key": "create_account_task", "config": {
586
+ "title": "Ligar para {{contact.name}}", "description": "", "task_type": "call", "agenda_id": null,
587
+ "priority": "none", "occupies_agenda": false,
588
+ "due": { "amount": 3, "unit": "days", "at": "09:00", "business_days": true },
589
+ "responsible": { "mode": "card_owner", "fallback_user_id": 12 },
590
+ "card_source": "trigger", "funnel_id": ""
591
+ } }
592
+ ```
593
+
594
+ - **Os campos da regra** (`title`, `description`, `task_type`, `agenda_id`, `priority`, `due`, `responsible`,
595
+ `occupies_agenda`) têm o mesmo formato e os mesmos limites da ação `create_task` do funil: `title` obrigatório; `due`
596
+ em horas (1 a 720) ou dias (0 a 365, com `at` "HH:MM" e `business_days` só em dias); `responsible.mode` com a chave
597
+ obrigatória do modo (`fallback_user_id`, `user_id` ou `team_id`; `funnel_rotation` não pede nenhuma no save,
598
+ mas no fluxo SEM card o rodízio do funil não tem funil e cai na pessoa reserva `fallback_user_id` — sem reserva a
599
+ tarefa não nasce, com o motivo no histórico: mande `fallback_user_id` se o fluxo puder rodar sem card). Regra torta =
600
+ passo VERMELHO com a frase do campo a consertar, sem tarefa.
601
+ - **De onde vem o card (`card_source`)**: `'trigger'` = o card que INICIOU o fluxo (gatilhos `card_created`,
602
+ `card_moved`, `card_won`, `card_lost`, `card_opened`, `card_snoozed`); fluxo que não nasceu de card = ERRO
603
+ visível, nunca pulado. `'funnel'` (o padrão da tela) = o card DESSA conversa no funil `funnel_id` (**obrigatório**
604
+ neste modo; funil sem `funnel_id` ou que não existe mais = erro). Conversa sem card naquele funil não é erro: a
605
+ tarefa nasce SÓ na conversa e o passo diz "sem card". Variáveis `{{ }}` em `title` e `description` resolvem pelo
606
+ resolvedor do FLUXO.
607
+ - **Uma tarefa por passagem do bloco**: repetir o MESMO passo (nova tentativa do sistema) não duplica; uma nova
608
+ passagem pelo bloco (o fluxo rodando de novo) cria outra tarefa.
609
+ - **Ritmo**: há um freio de 1 tarefa por segundo por conta. Passo com vaga futura já fica verde ("nasce às
610
+ HH:MM:SS"); se depois a tarefa for reprovada (responsável que saiu da conta, dado inválido), o aviso vai para a
611
+ linha do card ("Fluxo X não criou a tarefa", só com card) e para o log — o passo não muda.
612
+ - **Histórico do card**: "Fluxo X criou uma tarefa (tipo) para Fulana" / "Fluxo X não criou a tarefa: motivo".
613
+ A tarefa grava o Fluxo como quem agendou (`booking_source` `fluxo`).
614
+ - O interruptor de emergência da automação do funil NÃO vale para o fluxo; a Agenda unificada desligada vale (erro
615
+ visível no passo).
616
+ - **Fluxo `ai_tool` e fluxo de grupo**: não use este item (recusado na execução, com erro visível no passo).
577
617
 
578
618
  **`send_conversion` — Enviar conversão (2026-09-01):**
579
619
 
@@ -623,6 +663,12 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
623
663
 
624
664
  **Empresa e Cargo (2026-09-25):** `contact.company_name` e `contact.job_title` valem em CONDIÇÃO (`attr_source: 'contact'`, `attr_key` igual ao path), em filtro de regras e como variável (`{{contact.company_name}}`, `{{contact.job_title}}`). São SÓ LEITURA: a ação "Alterar atributo do contato" NÃO grava esses dois (o texto Empresa anda junto com a empresa vinculada — para ligar a empresa use `lionchat_contacts_update` com `company_id`).
625
665
 
666
+ **Empresa do contato (2026-10-16):** a empresa ligada ao contato da conversa (no máximo UMA por contato) vale como VARIÁVEL e como CONDIÇÃO, só com o módulo Empresas ligado.
667
+ - Variável em qualquer bloco de mensagem: `{{contact.company.name}}`, `{{contact.company.legal_name}}`, `{{contact.company.cnpj}}`, `{{contact.company.phone}}`, `{{contact.company.email}}`, `{{contact.company.address.city}}`, `{{contact.company.address.state}}` e `{{contact.company.custom_attribute.<chave>}}` (campo personalizado da empresa). Contato sem empresa = vazio. Data do campo personalizado: DD/MM/AAAA no bloco de mensagem; crua (AAAA-MM-DD) em API, Definir variável e Condição — igual ao atributo do contato.
668
+ - Condição (bloco Condição, aba Atributos, card "Atributo da empresa"): `{ "valueType": "attr_config", "attrSource": "company", "attr_key": "segmento", "operator": "equal", "value": "", "values": ["Varejo"] }`. `attr_key` = chave do campo personalizado OU cadastro com prefixo `company.name`, `company.legal_name`, `company.cnpj` (comparado só pelos dígitos), `company.phone`, `company.email`, `company.address.city`, `company.address.state`. Contato sem empresa = valor vazio (`not_equal`/`is_empty` casam). Módulo Empresas DESLIGADO = a regra NUNCA casa, nem a negativa.
669
+ - `attrSource` fora de `contact`/`conversation`/`card`/`company` NÃO casa (antes comparava a última mensagem do cliente) e marca o bloco com aviso.
670
+ - NÃO vale em condição de SAÍDA (`exit_conditions`), gatilho de entrada, parâmetro de ferramenta de IA nem em `update_attribute` (a ação não grava na empresa).
671
+
626
672
  **Campos NATIVOS do contato (2026-07-29):** com `attr_source: 'contact'`, `attr_key` também aceita os paths canônicos `contact.name`, `contact.email`, `contact.cpf`, `contact.cnpj`, `contact.rg`, `contact.passport`, `contact.date_of_birth`, `contact.gender`, `contact.marital_status`, `contact.profession` e `contact.address.{cep,street,number,complement,neighborhood,city,state,country}` — gravam na ficha nativa do contato (não em custom_attributes). Regras: telefone/identifier/etiquetas NÃO são aceitos (mapa fechado — chave fora dele vira atributo personalizado comum); documentos, nascimento, gênero e **e-mail** só preenchem campo VAZIO; nome/profissão/estado civil/endereço sobrescrevem; valor em branco nunca grava; gênero aceita `m|f|o|na` e estado civil `solteiro|casado|uniao_estavel|divorciado|separado|viuvo`; data de nascimento em `dd/mm/aaaa` ou `aaaa-mm-dd`. Recusas aparecem no histórico de execução (passo 'skipped'/'error' com mensagem).
627
673
 
628
674
  **Campos NATIVOS do contato em CONDIÇÃO (2026-08-13):** o bloco de Condições e o filtro do gatilho de data passaram a LER os mesmos paths nativos (`contact.name`, `contact.cpf`, `contact.marital_status`, `contact.address.city`, ...) na fonte `contact` — antes só liam atributo personalizado e o campo da ficha voltava vazio, jogando tudo na saída padrão em silêncio. Use com `is_empty`/`is_not_empty` para montar "o CPF está preenchido?". Aceita a forma curta (`contact.cpf`) e a longa (`contact.cadastral.cpf`). **`contact.label` NÃO é aceito** em condição (consulta cara em caminho reativo). **Gênero e estado civil são listas fechadas**: compare com o valor canônico (`m|f|o|na`, `solteiro|casado|uniao_estavel|divorciado|separado|viuvo`) — comparar com "Masculino" nunca casa.
@@ -959,6 +1005,24 @@ erro). Número de celular BR escrito sem o 9 é corrigido pelo WhatsApp antes de
959
1005
  sessão do canal, orçamento 8 s). Antes de 21/08 o passo dava SUCESSO mesmo com o lead fora do grupo. A
960
1006
  mesma correção do 9º dígito vale no `add_participants`.
961
1007
 
1008
+ **Quem volta só com o código interno (2026-10-09):** o WhatsApp devolve a pessoa RECÉM-COLOCADA só com o
1009
+ código interno dela (`<código>@lid`, sem telefone) — e horas depois continua assim. Até 09/10 o bloco comparava
1010
+ só telefone: `add_participants` acusava `nobody_changed`/`not_added` com a pessoa DENTRO do grupo (o fluxo
1011
+ parava), `create` dava `partial` falso (e mandava convite para quem já estava) e `remove_participants` dava
1012
+ SUCESSO falso. Desde 09/10 o bloco confere pelo telefone e, só para quem não casou, pergunta ao WhatsApp o
1013
+ código daquele telefone. Sem resposta do WhatsApp = conta como NÃO colocado (nunca inventa que entrou). Limite
1014
+ conhecido: número que não passou pela conferência de existência (fora do Brasil) e volte só com o código
1015
+ continua contando como fora.
1016
+
1017
+ **De onde veio cada número (2026-10-09):** nas operações com `groupParticipants`, o passo do bloco no
1018
+ HISTÓRICO da execução (não a variável `{{grupo.*}}`) traz `participant_sources` — um item por número com
1019
+ `number`, `source` (a LINHA do campo de participantes de onde ele saiu), `typed` (`true` = digitado, sem
1020
+ variável), `informed` (o número como foi escrito, quando o WhatsApp usa outra forma) e, quando a linha é UM
1021
+ atributo personalizado sozinho (`{{conversation.custom_attribute.x}}` / `{{contact.custom_attribute.x}}`),
1022
+ `filled_by`/`filled_at` (quem preencheu aquele atributo e quando). É só anotação: nunca muda quem vai no
1023
+ pedido; origem e autor podem faltar, mas nunca saem errados. Use para responder "de onde saiu esse número?"
1024
+ em vez de concluir que o número "não está configurado".
1025
+
962
1026
  **Convite no privado de quem ficou de fora (opcional, nasce desligado; só `create`/`add_participants`;
963
1027
  2026-08-21 — entra com o próximo deploy do app depois de 21/08/2026):** `groupInviteOnFailure: true` +
964
1028
  `groupInviteMessage` (texto OBRIGATÓRIO nesse modo — sem texto = `invite_status: 'sem_mensagem'` e nada
@@ -1064,6 +1128,31 @@ de conexão e viram bola de neve visual. Ao gerar `position`:
1064
1128
 
1065
1129
  ---
1066
1130
 
1131
+ ### 2.15 `ai_call` — "Ligar com IA" (17/10/2026)
1132
+
1133
+ So em conta com `ai_calling` + `lioncalls_calling`. A IA liga para o **telefone do contato** da conversa do flow
1134
+ pela linha de ligacao do WhatsApp (LionCalls).
1135
+
1136
+ ```json
1137
+ { "id": "ligar", "type": "ai_call", "position": {"x": 600, "y": 0},
1138
+ "data": { "aiCallAgentId": 4, "aiCallInboxId": 82 } }
1139
+ ```
1140
+
1141
+ - `aiCallAgentId`: id de `lionchat_captain_call_agents_list`. O que a IA fala, a voz e o **tempo maximo** da ligacao
1142
+ vem do agente (`max_duration_seconds`, 30-600 s; no tempo o motor desliga sozinho).
1143
+ - `aiCallInboxId`: id de `meta.dial_inboxes` da mesma lista (caixa com a ligacao ligada).
1144
+ - Saidas: `answered` (atendeu), `no_answer` (nao atendeu/recusou), `error` (nao deu pra ligar; motivo no historico).
1145
+ `answered`/`no_answer` sem edge = o flow termina; `error` sem edge = execucao "Falhou".
1146
+ - Liga **uma vez**. Para insistir, outro `ai_call` depois de `no_answer` (com um `wait` no meio). **Nunca** ligue
1147
+ `error` direto em outro `ai_call`.
1148
+ - Nao confere horario (use `condition` antes). Linha ocupada: espera a vez por ate 2 h (a IA sempre deixa uma vaga
1149
+ para a equipe).
1150
+ - **Proibido** em flow `action`, em flow de grupo e em `ai_tool` (o save recusa). Id de outra conta tambem e recusado.
1151
+
1152
+ **Receita — a IA de atendimento pede a ligacao:** etiqueta `ligar`; o cenario manda por a etiqueta quando o cliente
1153
+ pede para falar por telefone; flow `conversation` com gatilho `label_added` (`label_names: ["ligar"]`) -> action
1154
+ `remove_conversation_label` (`labels: ["ligar"]`) logo depois do start -> `ai_call`.
1155
+
1067
1156
  ## 2-B. Três tipos de flow: `conversation`, `ai_tool` e `action`
1068
1157
 
1069
1158
  O campo `flow_type` (definido na criação, IMUTÁVEL depois) decide a natureza do flow:
@@ -1180,7 +1269,7 @@ Sempre presente:
1180
1269
 
1181
1270
  | Variável | Conteúdo |
1182
1271
  |---|---|
1183
- | `{{trigger.type}}` | código do **EVENTO** que disparou — **NÃO é a chave do item do Início**. Valores reais: `message_created` (gatilho `message_received`), `message_sent`, `conversation_created`, `conversation_opened` (gatilho `conversation_reopened`), `conversation_resolved`, `label_added`, `label_removed`, `team_changed`, `assignee_changed`, `sla_missed`, `kanban_item_created`/`card_created`, `kanban_item_stage_changed`/`card_moved`, `card_status_changed` (`card_won` e `card_lost` — olhe `trigger.kind` ou o status do card), `conversation_attributes_changed`, `card_attributes_changed`, `contact_attribute_changed`, `group_participant_joined`/`_left`, `webhook_received`, `date_trigger`, `manual` (sidebar), `kanban_manual` (botão do card), `campaign` (Campanha de Fluxo), `page_track`, `activated_by_flow` (fluxo chamou fluxo), `lead_form` (os 3 gatilhos de formulário — `trigger.kind` diz `completed`/`milestone`/`abandoned`), `booking_created`/`booking_cancelled`/`booking_rescheduled`/`booking_completed`. Compare SEMPRE com esses códigos numa condição |
1272
+ | `{{trigger.type}}` | código do **EVENTO** que disparou — **NÃO é a chave do item do Início**. Valores reais: `message_created` (gatilho `message_received`), `message_sent`, `conversation_created`, `conversation_opened` (gatilho `conversation_reopened`), `conversation_resolved`, `label_added`, `label_removed`, `team_changed`, `assignee_changed`, `sla_missed`, `kanban_item_created`/`card_created`, `kanban_item_stage_changed`/`card_moved`, `card_status_changed` (`card_won`, `card_lost`, `card_opened` e `card_snoozed` — olhe `{{trigger.kanban.status}}`: `won`/`lost`/`open`/`snoozed`; rótulo "Situação do card mudou" desde 11/10/2026), `conversation_attributes_changed`, `card_attributes_changed`, `contact_attribute_changed`, `group_participant_joined`/`_left`, `webhook_received`, `date_trigger`, `manual` (sidebar), `kanban_manual` (botão do card), `campaign` (Campanha de Fluxo), `page_track`, `activated_by_flow` (fluxo chamou fluxo), `lead_form` (os 3 gatilhos de formulário — `trigger.kind` diz `completed`/`milestone`/`abandoned`), `booking_created`/`booking_cancelled`/`booking_rescheduled`/`booking_completed`. Compare SEMPRE com esses códigos numa condição |
1184
1273
  | `{{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 |
1185
1274
  | `{{trigger.activated_at}}` | quando disparou (ISO 8601). Desde 20/08/2026 `name` e `activated_at` são preenchidos em TODOS os caminhos de disparo (antes só no de evento de conversa) |
1186
1275
  | `{{trigger.kanban_item_id}}` / `{{trigger.funnel_id}}` | só quando veio do Kanban |
@@ -1220,9 +1309,10 @@ Oferecidos no autocompletar (só quando o bloco Início tem o gatilho que os pre
1220
1309
 
1221
1310
  | Gatilhos do Início | Variáveis |
1222
1311
  |---|---|
1223
- | `card_created`, `card_moved`, `card_won`, `card_lost`, `card_attribute_changed` | `{{trigger.kanban.title}}`, `{{trigger.kanban.funnel_name}}`, `{{trigger.kanban.stage_name}}`, `{{trigger.kanban.stage}}` (código da etapa) |
1312
+ | `card_created`, `card_moved`, `card_won`, `card_lost`, `card_opened`, `card_snoozed`, `card_attribute_changed` | `{{trigger.kanban.title}}`, `{{trigger.kanban.funnel_name}}`, `{{trigger.kanban.stage_name}}`, `{{trigger.kanban.stage}}` (código da etapa) |
1224
1313
  | `card_moved` | `{{trigger.kanban.previous_stage_name}}`, `{{trigger.kanban.previous_stage}}` |
1225
- | `card_won`, `card_lost` | `{{trigger.kanban.status}}` (`won`/`lost`) |
1314
+ | `card_won`, `card_lost`, `card_opened`, `card_snoozed` | `{{trigger.kanban.status}}` (`won`/`lost`/`open`/`snoozed`) |
1315
+ | `card_opened` | `{{trigger.kanban.previous_status}}` (`snoozed` = voltou do adiamento; `won`/`lost` = reabriram um ganho/perdido) |
1226
1316
  | `label_added`, `label_removed` | `{{trigger.label.name}}` |
1227
1317
  | `assignee_changed` | `{{trigger.assignee.name}}` (vazio = responsável removido) |
1228
1318
  | `team_changed` | `{{trigger.team.name}}` (vazio = equipe removida) |
@@ -1235,7 +1325,7 @@ Resolvem mas NÃO aparecem no seletor (produtor único — só o gatilho daquela
1235
1325
  `{{trigger.eclinica.event|unit|date|time|compromisso|idagenda}}`, `{{trigger.lead.form|page|ad|adset|campaign|platform}}`
1236
1326
  (nome do anúncio, não dado de pessoa), `{{trigger.dtmf.key|campaign}}`, `{{trigger.reminder.date|time|compromisso|unit|days_before}}`,
1237
1327
  além de `{{trigger.kanban.id}}`/`funnel_id` (use `{{trigger.kanban_item_id}}`/`{{trigger.funnel_id}}`),
1238
- `{{trigger.assignee.id}}`, `{{trigger.team.id}}`, `{{trigger.kanban.previous_status}}` e
1328
+ `{{trigger.assignee.id}}`, `{{trigger.team.id}}` (`{{trigger.kanban.previous_status}}` passou a ser oferecida no `card_opened`, 11/10/2026) e
1239
1329
  `{{trigger.group.participants_count}}`. As RESPOSTAS do lead do Meta não viram variável (só aparecem no
1240
1330
  histórico). Sessões anteriores a 22/08 não têm fatos gravados: essas variáveis saem vazias nelas.
1241
1331
  O bloco do card chama-se `kanban` — nunca `card`. Se o cliente precisar de um dado que não está aqui,
@@ -1280,7 +1370,7 @@ texto trocava o protocolo pelo id interno do banco.
1280
1370
  { "attr_key": "status", "attrSource": "conversation", "operator": "equal", "value": "cancelado" }
1281
1371
  ] }
1282
1372
  ```
1283
- - `attrSource` (`'contact'`|`'conversation'`|`'card'`) VAI na rule (ao contrário do gatilho de entrada por atributo, onde é implícito).
1373
+ - `attrSource` (`'contact'`|`'conversation'`|`'card'`) VAI na rule (ao contrário do gatilho de entrada por atributo, onde é implícito). `'company'` NÃO vale aqui (só no bloco Condição): a regra de saída não lê a empresa.
1284
1374
  - `operator` (contexto REATIVO — desde 09/07 SEM `is_empty`/`is_not_empty`, que dispararia a cada evento; use-os só no nó Condição): `equal`/`not_equal`/`contains`/`not_contains`/`starts_with`/`ends_with`/`greater_than`/`less_than`/`number_range`. Multi-valor via `values: [...]` no lugar de `value`.
1285
1375
  - Reativa (09/07): só é reavaliada quando o atributo VIGIADO daquela rule realmente muda — não a cada mensagem. Cada rule exige `attr_key` + (`value` ou `values`); rule sem isso é ignorada.
1286
1376
  - `card` aceita `funnel_id`/`card_source` opcionais na rule.
@@ -663,6 +663,39 @@ uma entrada por vez; quem entrar enquanto os balões da pessoa anterior saem nã
663
663
 
664
664
  ---
665
665
 
666
+ ## 18. Tarefa de acompanhamento ao ganhar um card (`create_account_task`)
667
+
668
+ **Caso:** quando um card é marcado como GANHO, criar para o responsável do card uma tarefa "Ligar para <cliente>" daqui a
669
+ 3 dias úteis, às 09:00 (pula sábado, domingo e feriado de dia inteiro da agenda). Usa o item "Criar tarefa" do bloco
670
+ Ações (aba Agenda). Pré-requisito: Agenda unificada ligada na conta. Não funciona em fluxo `ai_tool` nem de grupo.
671
+
672
+ ```json
673
+ {
674
+ "nodes": [
675
+ { "id": "n1", "type": "start", "position": { "x": 50, "y": 300 }, "data": { "label": "Início", "items": [{ "key": "card_won", "config": { "funnel_ids": ["1"] } }] } },
676
+ { "id": "n2", "type": "action", "position": { "x": 370, "y": 300 }, "data": { "label": "Tarefa de pós-venda", "items": [
677
+ { "key": "create_account_task", "config": {
678
+ "title": "Ligar para {{contact.name}} (pós-venda)", "description": "", "task_type": "call", "agenda_id": null,
679
+ "priority": "medium", "occupies_agenda": false,
680
+ "due": { "amount": 3, "unit": "days", "at": "09:00", "business_days": true },
681
+ "responsible": { "mode": "card_owner", "fallback_user_id": 12 },
682
+ "card_source": "trigger", "funnel_id": ""
683
+ } }
684
+ ] } }
685
+ ],
686
+ "edges": [
687
+ { "id": "e1", "source": "n1", "target": "n2", "sourceHandle": "success", "type": "deletable", "animated": true }
688
+ ]
689
+ }
690
+ ```
691
+
692
+ Notas:
693
+ - `card_source: "trigger"` usa o card que ganhou; sem `funnel_id`. Para agir sobre o card da conversa em outro funil
694
+ (fluxo disparado por mensagem, por exemplo), use `card_source: "funnel"` com `funnel_id` preenchido.
695
+ - `fallback_user_id` (12 aqui) é a pessoa que recebe quando o card não tem responsável: troque por um id de
696
+ `agents_list`. `task_type` é um dos 7 de fábrica ou uma `key` de `account_task_types_list`.
697
+ - Para a tarefa aparecer SÓ no card (sem bloquear o horário da Agenda) deixe `occupies_agenda` em `false`.
698
+
666
699
  ## Como usar este catálogo
667
700
 
668
701
  1. **Identifique o pattern mais próximo** do que o cliente pediu
@@ -35,6 +35,7 @@ caixa de WhatsApp vinculada e (opcionalmente) dispara um fluxo do FlowBuilder no
35
35
  | **Formulário público** (este documento) | Página de captação hospedada pelo LionChat, com blocos e link próprio |
36
36
  | **MetaLeadForm** | Formulário de anúncio da Meta (Facebook/Instagram Lead Ads) — outro módulo |
37
37
  | **Flow / FlowBuilder** | Motor de automação de conversa. O formulário *dispara* fluxos, mas não é um fluxo |
38
+ | **Formulário do WhatsApp** (17/10/2026) | Formulário nativo da Meta, preenchido DENTRO do WhatsApp, aberto por botão de modelo de mensagem. Ferramentas `lionchat_whatsapp_forms_*` — seção própria no fim deste documento |
38
39
 
39
40
  ## Ferramentas disponíveis
40
41
 
@@ -450,7 +451,7 @@ em `responses_show.milestones`.
450
451
 
451
452
  **`lionchat_lead_forms_responses_list`** aceita `status` (`in_progress`, `completed`,
452
453
  `abandoned`), `days` e `page` (até 10.000). A lista nunca traz as respostas em si — só `{id, status,
453
- contact, current_node_label, answered_count, created_at, completed_at}` e `meta: {current_page,
454
+ contact, current_node_label, answered_count, created_at, completed_at, filled_by}` e `meta: {current_page,
454
455
  per_page: 25, total_count}`. **`contact` pode vir `{id: null, name, phone, pending: true}`** — é
455
456
  quem ainda está preenchendo (o contato só nasce nos três momentos); nome e telefone saem do que a
456
457
  pessoa já digitou. `answered_count` conta todas as chaves de `answers`, inclusive as sintéticas.
@@ -478,6 +479,24 @@ falha de transporte sem código), `timeout`, `ssrf_blocked`, `result_too_large`,
478
479
  `no_ai_key`, `ai_error`. (Os motivos `busy` e `unresolved_variable`, citados antes, não existem no
479
480
  código.)
480
481
 
482
+ ### Preenchimento pela equipe, dentro da conversa — 20/10/2026 (entra com o próximo deploy do app)
483
+
484
+ No painel, a barra lateral da conversa ganhou o quadro **Formulários**: o atendente (SDR) abre qualquer formulário
485
+ publicado e ligado numa janela e preenche durante a ligação. Essa resposta é **da equipe**:
486
+
487
+ - `filled_by` (lista, show e Preenchimentos do contato): `null` = o próprio cliente pelo link; `{id, name}` = o
488
+ atendente que preencheu; `{id, removed: true}` = o atendente foi apagado (o selo continua).
489
+ - Contato e conversa ficam **fixos**: os da conversa. Telefone digitado nunca troca de contato nem cria ficha; se já
490
+ for de outra ficha (com ou sem o nono dígito) não é gravado.
491
+ - Todos os blocos rodam como no link (atributos, etiquetas, marco, API, IA, WhatsApp, contrato, agenda). A única
492
+ diferença: os fluxos **"Formulário concluído"** e **"Formulário abandonado"** NÃO disparam — o pulo fica registrado
493
+ com o motivo `filled_by_team`. "Marco do formulário" dispara.
494
+ - Fica **fora** de `lionchat_lead_forms_stats` (funil, conclusão) e dos contadores da lista (`responses_count`,
495
+ `completed_count`), e do relatório de formulários. Aparece em `responses_list`/`responses_show` com o selo.
496
+ - Não há ferramenta do MCP para começar esse preenchimento — ele só existe na janela do painel.
497
+ - Atributo gravado por QUALQUER formulário (link ou equipe) acorda o mesmo que a edição manual: automação "atributo
498
+ alterado", fluxos de atributo e webhook (desde 20/10/2026).
499
+
481
500
  ## Preenchimentos do contato (`lionchat_contacts_form_entries_list` / `_show`) — 21/08 (entra com o próximo deploy do app depois de 21/08/2026)
482
501
 
483
502
  A aba "Preenchimentos" da ficha junta, numa lista só ordenada por data, tudo que a pessoa preencheu
@@ -487,7 +506,7 @@ FlowBuilder cria pro gatilho de um fluxo (`flow`, com o nome do fluxo). É a ún
487
506
  "o que esta PESSOA preencheu" — as outras são por formulário/integração, e duas delas são só de
488
507
  administrador; esta é liberada a qualquer agente.
489
508
 
490
- - **Lista**: `{payload: [{source, id, title, status, at, count, conversation}], meta: {total,
509
+ - **Lista**: `{payload: [{source, id, title, status, at, count, conversation, filled_by}], meta: {total,
491
510
  truncated}}`. `id` é o id do registro NA ORIGEM — só identifica junto com `source`. `status` é
492
511
  `in_progress|completed|abandoned` no nosso formulário e `received` nas demais. `count` = respostas
493
512
  (nosso) ou campos vinculados e gravados (de fora). `conversation` só existe em `lead_form`. Sem
@@ -608,3 +627,141 @@ agendar (com `required: true` essa saída deixa de existir). O tipo de evento ve
608
627
  blocos. Teste o trecho seguinte pela página pública.
609
628
  - **`stats.views` é de vida inteira** (ignora `days`); `stats.milestones` não vê marco em `items`.
610
629
  - **`name` é interno; `settings.public_title` é o que o lead vê** (vazio = cai no `name`).
630
+
631
+ ## Formulário do WhatsApp (WhatsApp Flows da Meta) — 17/10/2026
632
+
633
+ Segundo tipo da área Formulários. É o formulário **nativo do WhatsApp**: o cliente toca num botão de um modelo de
634
+ mensagem e preenche sem sair do WhatsApp; as respostas voltam para a conversa ("Formulário respondido", pergunta por
635
+ pergunta). Só caixa do **WhatsApp Oficial** (Cloud API). Montar, publicar, aposentar e excluir = só administrador;
636
+ listar e ver = qualquer pessoa da conta. Não confundir com o **Formulário público** (o resto deste documento, página
637
+ com link próprio) nem com o formulário de anúncio da Meta (MetaLeadForm).
638
+
639
+ ### Ferramentas
640
+
641
+ | Ferramenta | Para quê |
642
+ |---|---|
643
+ | `lionchat_whatsapp_forms_list` | Lista (`inbox_id` filtra pela conta do WhatsApp; `with_questions=true` traz `questions: [{code, label}]`; `refresh=true` relê o estado na Meta) |
644
+ | `lionchat_whatsapp_forms_show` | Rascunho completo, `save_targets`, `image_urls`, `limits` (os limites da Meta, por tipo), `published_version`, `version_outdated` |
645
+ | `lionchat_whatsapp_forms_create` | Cria rascunho `{name, inbox_id, editor_data?}` — nada vai para a Meta ainda |
646
+ | `lionchat_whatsapp_forms_update` | Salva `name`, `editor_data` (SUBSTITUI o rascunho inteiro) e/ou `save_targets` |
647
+ | `lionchat_whatsapp_forms_images` | Sobe imagem POR LINK (`url` http/https de imagem pública): o servidor baixa, reduz para o limite da Meta e devolve `{blob_id, url, width, height}` |
648
+ | `lionchat_whatsapp_forms_publish` | Gera o arquivo, cria/edita na Meta e publica |
649
+ | `lionchat_whatsapp_forms_preview` | Endereço da prévia oficial da Meta (abre sem login, 30 dias) |
650
+ | `lionchat_whatsapp_forms_deprecate` | Aposenta na Meta — IRREVERSÍVEL |
651
+ | `lionchat_whatsapp_forms_destroy` | Exclui rascunho ou aposentado |
652
+
653
+ ### Passo a passo
654
+ 1. `_create` com `name` (único na conta do WhatsApp — a Meta recusa nome repetido ao publicar) e `inbox_id` de uma
655
+ caixa oficial da conta (outra caixa = 422 `inbox_must_be_official`).
656
+ 2. Imagem (se houver): `_images` com o link → use o `blob_id` em `{ "type": "image", "image": "<blob_id>" }`.
657
+ 3. `_update` com o `editor_data` completo (leia com `_show` antes de editar: o rascunho é substituído inteiro).
658
+ 4. `_publish`. Fica PARA SEMPRE na conta do WhatsApp do cliente (só dá para aposentar). Republicar depois de editar
659
+ mantém os modelos funcionando, sem nova aprovação.
660
+ 5. Botão no modelo: `lionchat_inboxes_whatsapp_templates_create` com
661
+ `{type: 'BUTTONS', buttons: [{type: 'FLOW', text: 'Preencher', flow_id: '<meta_flow_id>', flow_action: 'navigate'}]}`
662
+ (`meta_flow_id` vem do `_list`/`_show`; o formulário tem de estar publicado; no máximo 1 botão FLOW por modelo; texto
663
+ do botão até 25). A Meta costuma aprovar em minutos e pode trocar a categoria para Marketing. Trocar o formulário do
664
+ botão de um modelo aprovado manda o modelo de volta para análise.
665
+ 6. Envie o modelo como qualquer outro (conversa, campanha, fluxo, automação). O componente que a Meta exige para o
666
+ botão de formulário é montado pelo servidor — não mande nada a mais.
667
+ 7. (Opcional) Fluxo com gatilho `whatsapp_form_answered` — config `{whatsapp_form_id: '<id nosso, STRING>'}` (vazio =
668
+ qualquer formulário; lista também é aceita). Variáveis `{{trigger.form.name}}`, `{{trigger.form.id}}`,
669
+ `{{trigger.form.f_<n>}}` (a resposta de cada pergunta já em texto).
670
+
671
+ ### O `editor_data`
672
+ ```json
673
+ { "category": "SURVEY", "next_label": "Continuar", "submit_label": "Enviar",
674
+ "screens": [
675
+ { "title": "Seus dados",
676
+ "fields": [
677
+ { "type": "heading", "text": "Conte um pouco sobre você" },
678
+ { "type": "image", "image": "123", "alt_text": "Logo da clínica" },
679
+ { "type": "short_text", "label": "Seu nome", "required": true, "max_chars": 60 },
680
+ { "type": "single_choice", "label": "Como nos achou?",
681
+ "options": [{ "label": "Instagram" }, { "label": "Indicação", "description": "Amigo ou parente" }] },
682
+ { "type": "calendar", "label": "Entrada", "mode": "range", "end_label": "Saída", "days": ["Fri", "Sat"] },
683
+ { "type": "photo", "label": "Foto do documento", "max_files": 2 },
684
+ { "type": "link", "text": "Nossos termos", "url": "https://exemplo.com/termos" }
685
+ ] } ] }
686
+ ```
687
+ - `category`: SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY, OTHER.
688
+ - `next_label` (botão das telas do meio) e `submit_label` (botão da última tela, que envia): até 35 caracteres.
689
+ Só a ÚLTIMA tela envia; as anteriores levam para a seguinte.
690
+ - Campo NOVO vai sem `code`: o servidor dá `f_<n>` (opções `o_<n>`). Código já publicado NUNCA é reaproveitado.
691
+ - Perguntas (limite do texto da pergunta entre parênteses — use `helper`, até 80, para explicar mais):
692
+ `short_text`, `email`, `phone`, `number`, `long_text` (20); `date` e `calendar` (40); `single_choice` (30; até 200
693
+ opções, passou de 20 vira lista suspensa) e `multiple_choice` (30; até 20 opções); opção até 30, descrição da opção
694
+ até 300; `opt_in` (aceite, 120; `read_more_url` mostra um "Ler mais" que abre o link); `photo` e `document` (80;
695
+ `description` até 300, `max_files` 1-10, `max_size_mb` 1-25 — a foto sai SEMPRE com no máximo 5 MB, o limite de
696
+ recebimento do WhatsApp oficial; foto: `source` camera_gallery|camera|gallery; arquivo: `file_kinds`
697
+ pdf|word|spreadsheet|presentation|image|text|compressed|video). `required: true` = obrigatória (na foto/arquivo
698
+ vira "pelo menos 1").
699
+ - Ajustes: `max_chars` (NÚMERO 1-4000), `min_date`/`max_date` "AAAA-MM-DD" (data fixa — não existe "a partir de
700
+ hoje"), calendário `mode` single|range + `end_label`/`end_helper` (rótulo e ajuda do fim; a ajuda vai nas DUAS
701
+ pontas ou em nenhuma) + `title` (80) + `description` (300) + `days` (Mon..Sun, os dias que podem ser escolhidos);
702
+ escolha múltipla `min_selected`/`max_selected` (nunca acima do número de opções, mínimo nunca acima do máximo).
703
+ - Conteúdo (`text`): `heading` e `subheading` (80), `paragraph` e `caption` (4096), `link` (`text` até 25 + `url`
704
+ http/https), `image` (`image` = `blob_id` do `_images`, `alt_text` até 100).
705
+ - Tetos: 10 telas; 40 campos e 5 aceites por tela; título de tela 30; por tela 1 foto/arquivo, 3 imagens e 2 links;
706
+ até 10 arquivos no formulário inteiro (soma do `max_files`).
707
+
708
+ ### Salvar x publicar (onde cada erro aparece)
709
+ - **Salvar** (`_create`/`_update`) recusa com 422 `{errors: [...]}` só o FORMATO: tipo desconhecido, número fora da
710
+ faixa, data fora do formato, lista com valor desconhecido, `category` fora da lista, telas fora de 1-10, mais de 40
711
+ campos ou 5 aceites numa tela, opções em formato errado. Limite de caracteres da Meta NÃO barra o salvar
712
+ (dá para salvar no meio do caminho).
713
+ - **Publicar** recusa com 422 `{problems: [{code, screen, field, limit}], error, form}` (`screen`/`field` começam em
714
+ 0). Códigos de `problems`:
715
+
716
+ | code | O que corrigir |
717
+ |---|---|
718
+ | `no_questions` | o formulário não tem nenhuma pergunta |
719
+ | `empty_label` / `empty_text` | pergunta sem texto / conteúdo sem texto |
720
+ | `too_long` (`limit`) | texto acima do limite daquele tipo (pergunta, ajuda, fim do intervalo, link, texto alternativo) |
721
+ | `title_too_long` / `button_too_long` | título de tela acima de 30 / botão acima de 35 |
722
+ | `description_too_long` | descrição de foto/arquivo/calendário acima de 300 |
723
+ | `no_options` / `empty_option` / `option_too_long` / `option_description_too_long` | escolha sem opções / opção vazia / opção acima de 30 / descrição de opção acima de 300 |
724
+ | `selection_invalid` | mínimo/máximo de marcações impossível |
725
+ | `date_range_invalid` | data mínima depois da máxima |
726
+ | `empty_end_label` / `range_helper_incomplete` / `calendar_title_too_long` | intervalo sem rótulo do fim / ajuda numa ponta só / título do calendário acima de 80 |
727
+ | `invalid_url` / `invalid_read_more_url` | link ou "Ler mais" sem http/https |
728
+ | `no_image` / `image_missing` | imagem sem arquivo / arquivo que não é deste formulário (suba de novo pelo `_images`) |
729
+ | `too_many_links` / `too_many_images` / `too_many_pickers` | mais de 2 links / 3 imagens / 1 foto-arquivo na mesma tela |
730
+ | `too_many_files` | soma de arquivos acima de 10 no formulário |
731
+
732
+ `error.kind`: `no_channel` (nenhuma caixa oficial dessa conta do WhatsApp), `busy` (publicação em andamento),
733
+ `duplicate_name` (nome já usado nessa conta do WhatsApp), `validation` (a Meta recusou o arquivo; a mensagem diz
734
+ onde), `meta`, `network`, `too_large`, `unexpected`.
735
+ - `_update` durante uma publicação devolve 409 `busy`.
736
+
737
+ ### "Salvar em" (`save_targets`)
738
+ `{"f_1": "contact.name", "f_2": "custom.cidade"}` no `_update`. Destinos: `contact.name`, `contact.email`,
739
+ `contact.cadastral.<cpf|rg|cnpj|passport|profession|marital_status|gender|date_of_birth>`,
740
+ `contact.address.<cep|street|number|complement|neighborhood|city|state|country>`, `custom.<chave do contato>`,
741
+ `conversation_custom.<chave da conversa>`. O atributo precisa existir na conta (`lionchat_custom_attributes_list`)
742
+ e não ser protegido; destino inválido = 422 (inclusive código de pergunta de foto/arquivo, ou que não existe no
743
+ rascunho nem na última publicação). Telefone nunca. Vale na hora (não precisa publicar). Grava quando o cliente
744
+ envia: dado novo vence, resposta em branco não apaga, valor que não cabe no tipo do atributo não grava. Pergunta
745
+ tirada do rascunho mantém o destino até publicar de novo (a versão no ar ainda pergunta). Foto e arquivo não têm
746
+ destino: chegam como anexo da própria mensagem de resposta.
747
+
748
+ ### Como a resposta chega (para ler depois)
749
+ - Mensagem ENTRANTE na conversa do cliente, texto fixo "Formulário respondido", `content_type: form`, com
750
+ `content_attributes.items` (`[{name: 'f_1', label: 'Seu nome'}]`) e `content_attributes.submitted_values`
751
+ (`[{name: 'f_1', value: 'Maria'}]`) — leia por `lionchat_conversations_messages_list`.
752
+ - Os valores já vêm legíveis: opção pelo nome, data DD/MM/AAAA, intervalo "DD/MM/AAAA a DD/MM/AAAA", aceite Sim/Não,
753
+ foto/arquivo "N arquivos: nomes" (os arquivos vêm como anexos da mesma mensagem; arquivo acima do limite de
754
+ recebimento fica de fora).
755
+ - `content_attributes.whatsapp_form`: `form_id`, `publication_number`, `flow_token` e `raw` (as respostas cruas).
756
+ Sem nenhuma resposta: "Formulário respondido (nenhuma resposta preenchida)"; ilegível: "Formulário recebido (não
757
+ foi possível ler as respostas)" com `whatsapp_form.unreadable = true`.
758
+ - A IA de atendimento lê as respostas; a resposta reabre a conversa como qualquer mensagem do cliente. Fluxo parado
759
+ em "Aguardar resposta" só segue depois do "Salvar em" gravar.
760
+
761
+ ### Armadilhas
762
+ - **Aposentar (`_deprecate`) é irreversível** e derruba todo modelo que abre o formulário. Confirme com o usuário.
763
+ - `_destroy` só apaga rascunho ou aposentado (publicado = `deprecate_first`).
764
+ - `version_outdated: true` no `_show` = publicado numa versão que a Meta não aceita mais: publicar de novo.
765
+ - `flow_id` do botão é o `meta_flow_id` (número da Meta), não o `id` nosso; o gatilho do fluxo usa o `id` nosso.
766
+ - Imagem: só por LINK de imagem pública (página da web, endereço interno ou arquivo que não é imagem = 422
767
+ `fetch_failed`/`invalid_image`; acima de 10 MB = `file_too_large`).
@@ -214,6 +214,13 @@ comportamento intencional.
214
214
  - `funnel_stage`: nome da etapa atual (string)
215
215
  - `position`: ordem dentro da etapa (inteiro)
216
216
  - `stage_entered_at`: timestamp de quando entrou na etapa atual
217
+ - Situação (11/10/2026): `item_details.status` guarda `open`/`won`/`lost`; a resposta traz também `effective_status`
218
+ (`open`, **`snoozed`** = adiado, `won`, `lost`) e `snoozed_until`. **Card adiado** = card aberto que sai da coluna até
219
+ uma data e volta sozinho nela ("Adiar até" na tela). Ferramentas `lionchat_kanban_items_snooze`/`_unsnooze`; regras
220
+ em kanban-deep-dive, seção "Card adiado".
221
+ - Venda (13/10/2026): `item_details.attributed_to` = **vendedor** do ganho ("Atribuir ganho a");
222
+ `item_details.co_attributed_to` = **co-participante da venda** (ex.: o SDR), só em card ganho. Regras em
223
+ kanban-deep-dive, seção "Co-participante da venda".
217
224
 
218
225
  ### `item_details` (jsonb)
219
226
  ```json
@@ -529,6 +536,22 @@ A plataforma opera com 6 idiomas: `en`, `es`, `fr`, `it`, `pt`, `pt_BR` (default
529
536
  brasileiras: pt_BR). Conta com idioma antigo/descontinuado cai em inglês. Notas internas geradas
530
537
  pela IA saem no idioma da conta.
531
538
 
539
+ ## Formulário do WhatsApp (WhatsappForm) — 17/10/2026
540
+
541
+ Formulário NATIVO da Meta (WhatsApp Flows), preenchido DENTRO do WhatsApp e aberto por um botão FLOW num modelo
542
+ de mensagem. Só caixa do WhatsApp Oficial. Montado no LionChat (2º tipo da área Formulários) e publicado na conta
543
+ do WhatsApp do cliente. Ferramentas `lionchat_whatsapp_forms_*`; regras completas no resource
544
+ `lionchat://docs/formularios-publicos`, seção "Formulário do WhatsApp". Três formulários que NÃO são a mesma coisa:
545
+ **Formulário público** (LeadForm, página com link próprio), **Formulário do WhatsApp** (este) e **formulário de
546
+ anúncio da Meta** (MetaLeadForm, Lead Ads).
547
+
548
+ | Status | Significado |
549
+ |---|---|
550
+ | `draft` | rascunho, nunca publicado (pode excluir) |
551
+ | `publishing` | publicação em andamento |
552
+ | `published` | no ar; `unpublished_changes: true` = o rascunho mudou depois da última publicação |
553
+ | `deprecated` | aposentado na Meta (irreversível); pode excluir |
554
+
532
555
  ## LeadForm (Formulário público de captação)
533
556
 
534
557
  Formulário de captura próprio da conta, com página pública (link `/forms/<conta>/<slug>`), montado