@lionchat/mcp-server 1.29.0 → 1.31.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.
@@ -99,6 +100,21 @@ Todo node tem essa estrutura base:
99
100
  Regras: `attr_key` é **obrigatório**. `trigger_uuid` é preenchido pelo backend no save (NÃO envie; se enviar é preservado). 29/02 em ano não-bissexto colapsa p/ 28/02. Disparo vencido tem tolerância de 24h. Se `inbox_mode` for `fixed` e a caixa for desvinculada do flow depois, os envios daquele gatilho são **pulados** (visíveis em `flows_executions_list`). Caixa oficial WhatsApp exige template na 1ª mensagem se a conversa for criada nova (senão pula). Ativar o flow agenda automaticamente os contatos que já têm a data preenchida.
100
101
  - **`attr_source: 'conversation'` (novo 2026-07-18):** a data vem de um atributo de DATA da CONVERSA (não do contato). Dispara NAQUELA conversa (reabre se resolvida). `attr_key` = chave de atributo de conversa tipo Data; o horário-por-atributo lê da conversa. NÃO tem seletor de caixa (a conversa já é conhecida) — não envie `inbox_mode`/`inbox_id`. NÃO use `repeat_yearly` (conversa é evento pontual — vetado com keep_both). SEM agendamento retroativo: só agenda o que for escrito/alterado APÓS ativar (100% forward). Ausente = `'contact'` (comportamento legado).
101
102
  - **`overwrite_mode` (novo 2026-07-18):** `'replace'` (default) = trocar a data cancela o agendamento anterior (correção de data errada não dispara em dobro). `'keep_both'` = acumula (cada data seu próprio disparo) — **SÓ com `attr_source: 'conversation'`** (vetado no contato) e **incompatível com `repeat_yearly`**. Os disparos futuros pendentes ficam visíveis/canceláveis na aba "Agendados" do editor.
103
+ - **QUANDO a mensagem sai de verdade (mudou em 2026-10-02):** o horário configurado passou a ser
104
+ **respeitado exatamente**. Até 02/10 o motor somava `id do contato % 180` **MINUTOS** ao horário
105
+ escolhido (espalhamento anti-banimento): quem configurava 18:00 podia receber às 20:50, e isso
106
+ nunca esteve documentado aqui. Caso real: Villa Eyá, 18:00 configurado, 19:50 entregue.
107
+ O que espaça agora é a **fila por caixa**, no momento do envio, e só quando há fila de verdade:
108
+ - **1 pessoa na data** → sai no horário configurado, nos dois canais.
109
+ - **Caixa QR Code (WAHA)**, várias pessoas na mesma data → **20 a 40 segundos entre cada**
110
+ (sorteado — intervalo exato é padrão de robô). 50 aniversariantes ≈ 25 min; 500 ≈ 4 h.
111
+ **Ao montar aniversário em massa no QR Code, avise o cliente disso** — não é atraso, é a
112
+ proteção que impede o número de ser bloqueado. A mesma fila atende os lembretes da e-Clínica:
113
+ se as duas coisas saem pela MESMA caixa, elas dividem a vaga.
114
+ - **Caixa oficial (Cloud)** → **10 mensagens por segundo** por número da Meta, com recuo
115
+ automático se a Meta reclamar (o mesmo freio da campanha). 5.000 aniversariantes ≈ 8 min.
116
+ - **Atraso maior que 24 h não sai**: aviso parado por queda do sistema é marcado `vencida_expirada`
117
+ no histórico em vez de ser entregue atrasado. O aniversário do ano seguinte continua agendado.
102
118
  - **Ver/cancelar os agendamentos (novo 2026-07-20):** `flows_scheduled_firings_list` (flow_id) lista os disparos FUTUROS pendentes do Gatilho de Data (quando vai disparar, atributo/valor que gerou, fonte contato/conversa, contato/conversa alvo). `flows_scheduled_firings_cancel` (flow_id, id) cancela um — se já disparou responde 409 `ja_disparado` e nada muda. Só flows com Gatilho de Data; ADMIN + flowbuilder_manage.
103
119
 
104
120
  **Trigger `campaign_trigger` — Gatilho "Campanha" (novo 2026-07-28):** LIBERA o flow para ser disparado
@@ -137,7 +153,7 @@ Receita completa: `flows_create`/`flows_update` com o item no start → `flows_l
137
153
  4. Salvar o flow (`flows_update`) — o save sincroniza a ativação do webhook embutido (remover o item desativa a integração automaticamente).
138
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.
139
155
 
140
- **Gatilhos de AGENDAMENTO — Booking NATIVO do LionChat (2026-08-20):** `booking_created` (alguém marcou pelo link público, pela IA ou pelo painel), `booking_cancelled` (paciente cancelou pelo link, IA cancelou ou a equipe cancelou na Agenda), `booking_rescheduled` (a data/hora mudou — link do paciente, IA, editar/adiar na Agenda), `booking_completed` (equipe concluiu na Agenda; exige a Agenda unificada ligada na conta). **Não é e-Clínica.** Config (tudo opcional, vazio = todos): `booking_event_type_ids` (array de ids de tipo de agendamento, **STRING**), `agent_ids` (array de ids de usuário — o RESPONSÁVEL da tarefa), `create_conversation` (boolean, padrão `false`: só dispara para quem já tem conversa numa caixa do flow; `true` = cria a conversa na 1ª caixa do flow). Exemplo: `{ "key": "booking_created", "config": { "booking_event_type_ids": ["44"], "agent_ids": [], "create_conversation": true } }`. São INERTES a evento de conversa (quem dispara é o `Booking::FlowTriggerDispatcher`, por CONTA — caixa do flow só define onde a conversa é criada/reusada). Variáveis no flow: `{{booking.date}}` (DD/MM/AAAA), `{{booking.time}}`, `{{booking.weekday}}`, `{{booking.datetime}}` (ISO), `{{booking.type}}`, `{{booking.type_format}}` (presencial/videochamada), `{{booking.duration}}`, `{{booking.agent}}`, `{{booking.status}}` (confirmado/cancelado/remarcado/concluído), `{{booking.description}}`, `{{booking.cancel_url}}`, `{{booking.reschedule_url}}`, `{{booking.color}}`, `{{booking.id}}`; só no remarcado: `{{booking.previous_date}}`, `{{booking.previous_time}}`, `{{booking.previous_datetime}}`; `{{booking.meeting_url}}` NÃO vem no criado (nasce vazio). Data/hora vêm da TAREFA da Agenda (sempre atuais). Regras: o evento mais novo SUBSTITUI a rodada ativa do mesmo flow na mesma conversa (2ª consulta do mesmo paciente, ou cancelado chegando com o criado ainda esperando resposta); excluir a tarefa, reabrir cancelada ou desfazer conclusão NÃO disparam; adiar (snooze) conta como remarcado; tipo com confirmação/lembrete próprios ligados + `booking_created` = o paciente pode receber duas mensagens (a tela avisa). Colisão entre flows é por CONTA: mesmo kind + tipos se cruzando + agentes se cruzando (vazio = todos). Há também `{{booking.event}}` (`created`|`cancelled`|`rescheduled`|`completed` — bom pra condição; não aparece no seletor da tela, mas resolve); `{{trigger.type}}` = a chave do gatilho (ex.: `booking_created`), `{{trigger.kind}}` = `created`|`cancelled`|`rescheduled`|`completed` e `{{trigger.booking_id}}`. SÓ flow de conversa **individual** (a tela nem oferece em flow de grupo); **nunca dispara flow `ai_tool`**. Em caixa WhatsApp OFICIAL, conversa criada nova (ou parada há mais de 24h) está FORA da janela — texto livre no 1º bloco cai em `window_closed`/erro; comece por template.
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.
141
157
 
142
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).
143
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.
@@ -382,6 +398,7 @@ só pra destravar, ela pode sair.
382
398
  | `conversation_has_ai_agent` / `conversation_no_ai_agent` / `conversation_not_ai_agent` | agente de IA atribuído (novo 2026-08-01) — ver abaixo |
383
399
  | `contact_has_label` / `contact_no_label` / `conversation_has_label` / `conversation_no_label` | labels |
384
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" |
385
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) |
386
403
  | `pagetrack_visited` / `pagetrack_event` | LionTrack |
387
404
  | `sla_check` | status do SLA da conversa (usa `value` = código fixo; ver abaixo) |
@@ -442,7 +459,7 @@ campanha"):
442
459
  `origin_first_kind`/`origin_first_platform`, `origin_last_kind`/`origin_last_platform`, `origin_*` em geral
443
460
  (origem unificada), `ctwa_*` (anúncio click-to-WhatsApp: `ctwa_ad_id`, `ctwa_ad_title`, `ctwa_campaign_name`,
444
461
  `ctwa_adset_name`...), `meta_lead_*` (formulário Meta Lead Ads), `lt_*` (LionTrack: UTMs, página, dispositivo).
445
- 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).
446
463
  - **Valores fechados** de `origin_kind` (e `origin_first_kind`/`origin_last_kind`): `paid_ad` (Anúncio),
447
464
  `lead_form` (Formulário), `organic` (Orgânico), `direct` (Direto), `referral` (Indicação), `manual` (Origem
448
465
  cadastrada). O painel mostra o rótulo em português, mas a comparação é pelo VALOR — `"Anúncio"` nunca casa.
@@ -543,6 +560,7 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
543
560
  | `set_kanban_item_status` | `{ status: 'won' \| 'lost' \| 'active' }` | Marca status do card |
544
561
  | `set_won` | `{}` | Atalho pra ganho |
545
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 |
546
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) |
547
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` |
548
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) |
@@ -558,7 +576,7 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
558
576
 
559
577
  **Handles que SAEM:** `success`. Não tem handle `error` — falhas viram warning silencioso e o flow continua.
560
578
 
561
- **`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).
579
+ **`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`, `update_attribute` (com `attr_source: 'card'`) e às condições `card_attr_equals`/`card_attr_contains` (gravando `card_source` na própria regra).
562
580
 
563
581
  **`send_conversion` — Enviar conversão (2026-09-01):**
564
582
 
@@ -608,6 +626,12 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
608
626
 
609
627
  **Empresa e Cargo (2026-09-25):** `contact.company_name` e `contact.job_title` valem em CONDIÇÃO (`attr_source: 'contact'`, `attr_key` igual ao path), em filtro de regras e como variável (`{{contact.company_name}}`, `{{contact.job_title}}`). São SÓ LEITURA: a ação "Alterar atributo do contato" NÃO grava esses dois (o texto Empresa anda junto com a empresa vinculada — para ligar a empresa use `lionchat_contacts_update` com `company_id`).
610
628
 
629
+ **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.
630
+ - 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.
631
+ - 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.
632
+ - `attrSource` fora de `contact`/`conversation`/`card`/`company` NÃO casa (antes comparava a última mensagem do cliente) e marca o bloco com aviso.
633
+ - 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).
634
+
611
635
  **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).
612
636
 
613
637
  **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.
@@ -944,6 +968,24 @@ erro). Número de celular BR escrito sem o 9 é corrigido pelo WhatsApp antes de
944
968
  sessão do canal, orçamento 8 s). Antes de 21/08 o passo dava SUCESSO mesmo com o lead fora do grupo. A
945
969
  mesma correção do 9º dígito vale no `add_participants`.
946
970
 
971
+ **Quem volta só com o código interno (2026-10-09):** o WhatsApp devolve a pessoa RECÉM-COLOCADA só com o
972
+ código interno dela (`<código>@lid`, sem telefone) — e horas depois continua assim. Até 09/10 o bloco comparava
973
+ só telefone: `add_participants` acusava `nobody_changed`/`not_added` com a pessoa DENTRO do grupo (o fluxo
974
+ parava), `create` dava `partial` falso (e mandava convite para quem já estava) e `remove_participants` dava
975
+ SUCESSO falso. Desde 09/10 o bloco confere pelo telefone e, só para quem não casou, pergunta ao WhatsApp o
976
+ código daquele telefone. Sem resposta do WhatsApp = conta como NÃO colocado (nunca inventa que entrou). Limite
977
+ conhecido: número que não passou pela conferência de existência (fora do Brasil) e volte só com o código
978
+ continua contando como fora.
979
+
980
+ **De onde veio cada número (2026-10-09):** nas operações com `groupParticipants`, o passo do bloco no
981
+ HISTÓRICO da execução (não a variável `{{grupo.*}}`) traz `participant_sources` — um item por número com
982
+ `number`, `source` (a LINHA do campo de participantes de onde ele saiu), `typed` (`true` = digitado, sem
983
+ variável), `informed` (o número como foi escrito, quando o WhatsApp usa outra forma) e, quando a linha é UM
984
+ atributo personalizado sozinho (`{{conversation.custom_attribute.x}}` / `{{contact.custom_attribute.x}}`),
985
+ `filled_by`/`filled_at` (quem preencheu aquele atributo e quando). É só anotação: nunca muda quem vai no
986
+ pedido; origem e autor podem faltar, mas nunca saem errados. Use para responder "de onde saiu esse número?"
987
+ em vez de concluir que o número "não está configurado".
988
+
947
989
  **Convite no privado de quem ficou de fora (opcional, nasce desligado; só `create`/`add_participants`;
948
990
  2026-08-21 — entra com o próximo deploy do app depois de 21/08/2026):** `groupInviteOnFailure: true` +
949
991
  `groupInviteMessage` (texto OBRIGATÓRIO nesse modo — sem texto = `invite_status: 'sem_mensagem'` e nada
@@ -1049,6 +1091,31 @@ de conexão e viram bola de neve visual. Ao gerar `position`:
1049
1091
 
1050
1092
  ---
1051
1093
 
1094
+ ### 2.15 `ai_call` — "Ligar com IA" (17/10/2026)
1095
+
1096
+ So em conta com `ai_calling` + `lioncalls_calling`. A IA liga para o **telefone do contato** da conversa do flow
1097
+ pela linha de ligacao do WhatsApp (LionCalls).
1098
+
1099
+ ```json
1100
+ { "id": "ligar", "type": "ai_call", "position": {"x": 600, "y": 0},
1101
+ "data": { "aiCallAgentId": 4, "aiCallInboxId": 82 } }
1102
+ ```
1103
+
1104
+ - `aiCallAgentId`: id de `lionchat_captain_call_agents_list`. O que a IA fala, a voz e o **tempo maximo** da ligacao
1105
+ vem do agente (`max_duration_seconds`, 30-600 s; no tempo o motor desliga sozinho).
1106
+ - `aiCallInboxId`: id de `meta.dial_inboxes` da mesma lista (caixa com a ligacao ligada).
1107
+ - Saidas: `answered` (atendeu), `no_answer` (nao atendeu/recusou), `error` (nao deu pra ligar; motivo no historico).
1108
+ `answered`/`no_answer` sem edge = o flow termina; `error` sem edge = execucao "Falhou".
1109
+ - Liga **uma vez**. Para insistir, outro `ai_call` depois de `no_answer` (com um `wait` no meio). **Nunca** ligue
1110
+ `error` direto em outro `ai_call`.
1111
+ - Nao confere horario (use `condition` antes). Linha ocupada: espera a vez por ate 2 h (a IA sempre deixa uma vaga
1112
+ para a equipe).
1113
+ - **Proibido** em flow `action`, em flow de grupo e em `ai_tool` (o save recusa). Id de outra conta tambem e recusado.
1114
+
1115
+ **Receita — a IA de atendimento pede a ligacao:** etiqueta `ligar`; o cenario manda por a etiqueta quando o cliente
1116
+ pede para falar por telefone; flow `conversation` com gatilho `label_added` (`label_names: ["ligar"]`) -> action
1117
+ `remove_conversation_label` (`labels: ["ligar"]`) logo depois do start -> `ai_call`.
1118
+
1052
1119
  ## 2-B. Três tipos de flow: `conversation`, `ai_tool` e `action`
1053
1120
 
1054
1121
  O campo `flow_type` (definido na criação, IMUTÁVEL depois) decide a natureza do flow:
@@ -1165,7 +1232,7 @@ Sempre presente:
1165
1232
 
1166
1233
  | Variável | Conteúdo |
1167
1234
  |---|---|
1168
- | `{{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 |
1235
+ | `{{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 |
1169
1236
  | `{{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 |
1170
1237
  | `{{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) |
1171
1238
  | `{{trigger.kanban_item_id}}` / `{{trigger.funnel_id}}` | só quando veio do Kanban |
@@ -1205,9 +1272,10 @@ Oferecidos no autocompletar (só quando o bloco Início tem o gatilho que os pre
1205
1272
 
1206
1273
  | Gatilhos do Início | Variáveis |
1207
1274
  |---|---|
1208
- | `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) |
1275
+ | `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) |
1209
1276
  | `card_moved` | `{{trigger.kanban.previous_stage_name}}`, `{{trigger.kanban.previous_stage}}` |
1210
- | `card_won`, `card_lost` | `{{trigger.kanban.status}}` (`won`/`lost`) |
1277
+ | `card_won`, `card_lost`, `card_opened`, `card_snoozed` | `{{trigger.kanban.status}}` (`won`/`lost`/`open`/`snoozed`) |
1278
+ | `card_opened` | `{{trigger.kanban.previous_status}}` (`snoozed` = voltou do adiamento; `won`/`lost` = reabriram um ganho/perdido) |
1211
1279
  | `label_added`, `label_removed` | `{{trigger.label.name}}` |
1212
1280
  | `assignee_changed` | `{{trigger.assignee.name}}` (vazio = responsável removido) |
1213
1281
  | `team_changed` | `{{trigger.team.name}}` (vazio = equipe removida) |
@@ -1220,7 +1288,7 @@ Resolvem mas NÃO aparecem no seletor (produtor único — só o gatilho daquela
1220
1288
  `{{trigger.eclinica.event|unit|date|time|compromisso|idagenda}}`, `{{trigger.lead.form|page|ad|adset|campaign|platform}}`
1221
1289
  (nome do anúncio, não dado de pessoa), `{{trigger.dtmf.key|campaign}}`, `{{trigger.reminder.date|time|compromisso|unit|days_before}}`,
1222
1290
  além de `{{trigger.kanban.id}}`/`funnel_id` (use `{{trigger.kanban_item_id}}`/`{{trigger.funnel_id}}`),
1223
- `{{trigger.assignee.id}}`, `{{trigger.team.id}}`, `{{trigger.kanban.previous_status}}` e
1291
+ `{{trigger.assignee.id}}`, `{{trigger.team.id}}` (`{{trigger.kanban.previous_status}}` passou a ser oferecida no `card_opened`, 11/10/2026) e
1224
1292
  `{{trigger.group.participants_count}}`. As RESPOSTAS do lead do Meta não viram variável (só aparecem no
1225
1293
  histórico). Sessões anteriores a 22/08 não têm fatos gravados: essas variáveis saem vazias nelas.
1226
1294
  O bloco do card chama-se `kanban` — nunca `card`. Se o cliente precisar de um dado que não está aqui,
@@ -1265,7 +1333,7 @@ texto trocava o protocolo pelo id interno do banco.
1265
1333
  { "attr_key": "status", "attrSource": "conversation", "operator": "equal", "value": "cancelado" }
1266
1334
  ] }
1267
1335
  ```
1268
- - `attrSource` (`'contact'`|`'conversation'`|`'card'`) VAI na rule (ao contrário do gatilho de entrada por atributo, onde é implícito).
1336
+ - `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.
1269
1337
  - `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`.
1270
1338
  - 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.
1271
1339
  - `card` aceita `funnel_id`/`card_source` opcionais na rule.
@@ -549,6 +549,23 @@
549
549
 
550
550
  **Lembretes:** `attr_key` é obrigatório; `_date_of_birth` é o Aniversário nativo. `trigger_uuid` é preenchido pelo backend (não envie). Ativar o flow já agenda os contatos que têm a data. Pulos (contato sem telefone, caixa desvinculada, etc.) aparecem em `flows_executions_list`.
551
551
 
552
+ **Quanto tempo leva, com muita gente na mesma data (mudou em 2026-10-02):** o horário configurado é
553
+ respeitado — a primeira mensagem sai às 09:00 em ponto. As demais saem no ritmo da caixa, que existe
554
+ para o número não ser bloqueado:
555
+
556
+ | Caixa | Ritmo | 50 pessoas | 500 pessoas |
557
+ |---|---|---|---|
558
+ | QR Code (WAHA) | 20-40 s entre cada | ~25 min | ~4 h |
559
+ | Oficial (Cloud) | 10 por segundo | 5 s | ~50 s |
560
+
561
+ **Ao montar aniversário para uma base grande em QR Code, diga isso ao cliente antes** — ele vai ver as
562
+ mensagens pingando e achar que travou. Não é atraso: é a trava anti-banimento. Se precisar sair rápido
563
+ em volume, o caminho é caixa oficial ou Campanha de Fluxo (que tem agendamento por dias).
564
+
565
+ Até 02/10/2026 o motor somava `id do contato % 180` MINUTOS ao horário escolhido — 18:00 podia virar
566
+ 20:50, sem nada na tela explicando. Isso acabou; se encontrar documentação antiga dizendo que o
567
+ Gatilho de Data "espalha em até 3 horas", está desatualizada.
568
+
552
569
  ---
553
570
 
554
571
  ## 17. Gestão de Grupos WhatsApp por fluxo (`update_group`)
@@ -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
 
@@ -608,3 +609,141 @@ agendar (com `required: true` essa saída deixa de existir). O tipo de evento ve
608
609
  blocos. Teste o trecho seguinte pela página pública.
609
610
  - **`stats.views` é de vida inteira** (ignora `days`); `stats.milestones` não vê marco em `items`.
610
611
  - **`name` é interno; `settings.public_title` é o que o lead vê** (vazio = cai no `name`).
612
+
613
+ ## Formulário do WhatsApp (WhatsApp Flows da Meta) — 17/10/2026
614
+
615
+ Segundo tipo da área Formulários. É o formulário **nativo do WhatsApp**: o cliente toca num botão de um modelo de
616
+ mensagem e preenche sem sair do WhatsApp; as respostas voltam para a conversa ("Formulário respondido", pergunta por
617
+ pergunta). Só caixa do **WhatsApp Oficial** (Cloud API). Montar, publicar, aposentar e excluir = só administrador;
618
+ listar e ver = qualquer pessoa da conta. Não confundir com o **Formulário público** (o resto deste documento, página
619
+ com link próprio) nem com o formulário de anúncio da Meta (MetaLeadForm).
620
+
621
+ ### Ferramentas
622
+
623
+ | Ferramenta | Para quê |
624
+ |---|---|
625
+ | `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) |
626
+ | `lionchat_whatsapp_forms_show` | Rascunho completo, `save_targets`, `image_urls`, `limits` (os limites da Meta, por tipo), `published_version`, `version_outdated` |
627
+ | `lionchat_whatsapp_forms_create` | Cria rascunho `{name, inbox_id, editor_data?}` — nada vai para a Meta ainda |
628
+ | `lionchat_whatsapp_forms_update` | Salva `name`, `editor_data` (SUBSTITUI o rascunho inteiro) e/ou `save_targets` |
629
+ | `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}` |
630
+ | `lionchat_whatsapp_forms_publish` | Gera o arquivo, cria/edita na Meta e publica |
631
+ | `lionchat_whatsapp_forms_preview` | Endereço da prévia oficial da Meta (abre sem login, 30 dias) |
632
+ | `lionchat_whatsapp_forms_deprecate` | Aposenta na Meta — IRREVERSÍVEL |
633
+ | `lionchat_whatsapp_forms_destroy` | Exclui rascunho ou aposentado |
634
+
635
+ ### Passo a passo
636
+ 1. `_create` com `name` (único na conta do WhatsApp — a Meta recusa nome repetido ao publicar) e `inbox_id` de uma
637
+ caixa oficial da conta (outra caixa = 422 `inbox_must_be_official`).
638
+ 2. Imagem (se houver): `_images` com o link → use o `blob_id` em `{ "type": "image", "image": "<blob_id>" }`.
639
+ 3. `_update` com o `editor_data` completo (leia com `_show` antes de editar: o rascunho é substituído inteiro).
640
+ 4. `_publish`. Fica PARA SEMPRE na conta do WhatsApp do cliente (só dá para aposentar). Republicar depois de editar
641
+ mantém os modelos funcionando, sem nova aprovação.
642
+ 5. Botão no modelo: `lionchat_inboxes_whatsapp_templates_create` com
643
+ `{type: 'BUTTONS', buttons: [{type: 'FLOW', text: 'Preencher', flow_id: '<meta_flow_id>', flow_action: 'navigate'}]}`
644
+ (`meta_flow_id` vem do `_list`/`_show`; o formulário tem de estar publicado; no máximo 1 botão FLOW por modelo; texto
645
+ do botão até 25). A Meta costuma aprovar em minutos e pode trocar a categoria para Marketing. Trocar o formulário do
646
+ botão de um modelo aprovado manda o modelo de volta para análise.
647
+ 6. Envie o modelo como qualquer outro (conversa, campanha, fluxo, automação). O componente que a Meta exige para o
648
+ botão de formulário é montado pelo servidor — não mande nada a mais.
649
+ 7. (Opcional) Fluxo com gatilho `whatsapp_form_answered` — config `{whatsapp_form_id: '<id nosso, STRING>'}` (vazio =
650
+ qualquer formulário; lista também é aceita). Variáveis `{{trigger.form.name}}`, `{{trigger.form.id}}`,
651
+ `{{trigger.form.f_<n>}}` (a resposta de cada pergunta já em texto).
652
+
653
+ ### O `editor_data`
654
+ ```json
655
+ { "category": "SURVEY", "next_label": "Continuar", "submit_label": "Enviar",
656
+ "screens": [
657
+ { "title": "Seus dados",
658
+ "fields": [
659
+ { "type": "heading", "text": "Conte um pouco sobre você" },
660
+ { "type": "image", "image": "123", "alt_text": "Logo da clínica" },
661
+ { "type": "short_text", "label": "Seu nome", "required": true, "max_chars": 60 },
662
+ { "type": "single_choice", "label": "Como nos achou?",
663
+ "options": [{ "label": "Instagram" }, { "label": "Indicação", "description": "Amigo ou parente" }] },
664
+ { "type": "calendar", "label": "Entrada", "mode": "range", "end_label": "Saída", "days": ["Fri", "Sat"] },
665
+ { "type": "photo", "label": "Foto do documento", "max_files": 2 },
666
+ { "type": "link", "text": "Nossos termos", "url": "https://exemplo.com/termos" }
667
+ ] } ] }
668
+ ```
669
+ - `category`: SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION, CONTACT_US, CUSTOMER_SUPPORT, SURVEY, OTHER.
670
+ - `next_label` (botão das telas do meio) e `submit_label` (botão da última tela, que envia): até 35 caracteres.
671
+ Só a ÚLTIMA tela envia; as anteriores levam para a seguinte.
672
+ - Campo NOVO vai sem `code`: o servidor dá `f_<n>` (opções `o_<n>`). Código já publicado NUNCA é reaproveitado.
673
+ - Perguntas (limite do texto da pergunta entre parênteses — use `helper`, até 80, para explicar mais):
674
+ `short_text`, `email`, `phone`, `number`, `long_text` (20); `date` e `calendar` (40); `single_choice` (30; até 200
675
+ 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
676
+ até 300; `opt_in` (aceite, 120; `read_more_url` mostra um "Ler mais" que abre o link); `photo` e `document` (80;
677
+ `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
678
+ recebimento do WhatsApp oficial; foto: `source` camera_gallery|camera|gallery; arquivo: `file_kinds`
679
+ pdf|word|spreadsheet|presentation|image|text|compressed|video). `required: true` = obrigatória (na foto/arquivo
680
+ vira "pelo menos 1").
681
+ - Ajustes: `max_chars` (NÚMERO 1-4000), `min_date`/`max_date` "AAAA-MM-DD" (data fixa — não existe "a partir de
682
+ hoje"), calendário `mode` single|range + `end_label`/`end_helper` (rótulo e ajuda do fim; a ajuda vai nas DUAS
683
+ pontas ou em nenhuma) + `title` (80) + `description` (300) + `days` (Mon..Sun, os dias que podem ser escolhidos);
684
+ escolha múltipla `min_selected`/`max_selected` (nunca acima do número de opções, mínimo nunca acima do máximo).
685
+ - Conteúdo (`text`): `heading` e `subheading` (80), `paragraph` e `caption` (4096), `link` (`text` até 25 + `url`
686
+ http/https), `image` (`image` = `blob_id` do `_images`, `alt_text` até 100).
687
+ - 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;
688
+ até 10 arquivos no formulário inteiro (soma do `max_files`).
689
+
690
+ ### Salvar x publicar (onde cada erro aparece)
691
+ - **Salvar** (`_create`/`_update`) recusa com 422 `{errors: [...]}` só o FORMATO: tipo desconhecido, número fora da
692
+ faixa, data fora do formato, lista com valor desconhecido, `category` fora da lista, telas fora de 1-10, mais de 40
693
+ campos ou 5 aceites numa tela, opções em formato errado. Limite de caracteres da Meta NÃO barra o salvar
694
+ (dá para salvar no meio do caminho).
695
+ - **Publicar** recusa com 422 `{problems: [{code, screen, field, limit}], error, form}` (`screen`/`field` começam em
696
+ 0). Códigos de `problems`:
697
+
698
+ | code | O que corrigir |
699
+ |---|---|
700
+ | `no_questions` | o formulário não tem nenhuma pergunta |
701
+ | `empty_label` / `empty_text` | pergunta sem texto / conteúdo sem texto |
702
+ | `too_long` (`limit`) | texto acima do limite daquele tipo (pergunta, ajuda, fim do intervalo, link, texto alternativo) |
703
+ | `title_too_long` / `button_too_long` | título de tela acima de 30 / botão acima de 35 |
704
+ | `description_too_long` | descrição de foto/arquivo/calendário acima de 300 |
705
+ | `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 |
706
+ | `selection_invalid` | mínimo/máximo de marcações impossível |
707
+ | `date_range_invalid` | data mínima depois da máxima |
708
+ | `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 |
709
+ | `invalid_url` / `invalid_read_more_url` | link ou "Ler mais" sem http/https |
710
+ | `no_image` / `image_missing` | imagem sem arquivo / arquivo que não é deste formulário (suba de novo pelo `_images`) |
711
+ | `too_many_links` / `too_many_images` / `too_many_pickers` | mais de 2 links / 3 imagens / 1 foto-arquivo na mesma tela |
712
+ | `too_many_files` | soma de arquivos acima de 10 no formulário |
713
+
714
+ `error.kind`: `no_channel` (nenhuma caixa oficial dessa conta do WhatsApp), `busy` (publicação em andamento),
715
+ `duplicate_name` (nome já usado nessa conta do WhatsApp), `validation` (a Meta recusou o arquivo; a mensagem diz
716
+ onde), `meta`, `network`, `too_large`, `unexpected`.
717
+ - `_update` durante uma publicação devolve 409 `busy`.
718
+
719
+ ### "Salvar em" (`save_targets`)
720
+ `{"f_1": "contact.name", "f_2": "custom.cidade"}` no `_update`. Destinos: `contact.name`, `contact.email`,
721
+ `contact.cadastral.<cpf|rg|cnpj|passport|profession|marital_status|gender|date_of_birth>`,
722
+ `contact.address.<cep|street|number|complement|neighborhood|city|state|country>`, `custom.<chave do contato>`,
723
+ `conversation_custom.<chave da conversa>`. O atributo precisa existir na conta (`lionchat_custom_attributes_list`)
724
+ e não ser protegido; destino inválido = 422 (inclusive código de pergunta de foto/arquivo, ou que não existe no
725
+ rascunho nem na última publicação). Telefone nunca. Vale na hora (não precisa publicar). Grava quando o cliente
726
+ envia: dado novo vence, resposta em branco não apaga, valor que não cabe no tipo do atributo não grava. Pergunta
727
+ 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
728
+ destino: chegam como anexo da própria mensagem de resposta.
729
+
730
+ ### Como a resposta chega (para ler depois)
731
+ - Mensagem ENTRANTE na conversa do cliente, texto fixo "Formulário respondido", `content_type: form`, com
732
+ `content_attributes.items` (`[{name: 'f_1', label: 'Seu nome'}]`) e `content_attributes.submitted_values`
733
+ (`[{name: 'f_1', value: 'Maria'}]`) — leia por `lionchat_conversations_messages_list`.
734
+ - 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,
735
+ foto/arquivo "N arquivos: nomes" (os arquivos vêm como anexos da mesma mensagem; arquivo acima do limite de
736
+ recebimento fica de fora).
737
+ - `content_attributes.whatsapp_form`: `form_id`, `publication_number`, `flow_token` e `raw` (as respostas cruas).
738
+ Sem nenhuma resposta: "Formulário respondido (nenhuma resposta preenchida)"; ilegível: "Formulário recebido (não
739
+ foi possível ler as respostas)" com `whatsapp_form.unreadable = true`.
740
+ - A IA de atendimento lê as respostas; a resposta reabre a conversa como qualquer mensagem do cliente. Fluxo parado
741
+ em "Aguardar resposta" só segue depois do "Salvar em" gravar.
742
+
743
+ ### Armadilhas
744
+ - **Aposentar (`_deprecate`) é irreversível** e derruba todo modelo que abre o formulário. Confirme com o usuário.
745
+ - `_destroy` só apaga rascunho ou aposentado (publicado = `deprecate_first`).
746
+ - `version_outdated: true` no `_show` = publicado numa versão que a Meta não aceita mais: publicar de novo.
747
+ - `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.
748
+ - Imagem: só por LINK de imagem pública (página da web, endereço interno ou arquivo que não é imagem = 422
749
+ `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
@@ -141,6 +141,8 @@ Um card individual dentro de uma etapa.
141
141
  | `activities` | jsonb array — log de atividades |
142
142
  | `checklist` | **O que fica guardado:** jsonb array de tarefas do card (item: `text`/`completed`/`position` + `group_id`/`group_name` opcionais para agrupar). **O que a API devolve é OUTRA coisa** — ver o aviso logo abaixo |
143
143
  | `timer_started_at`, `timer_duration` | Timer interno do card |
144
+ | `effective_status` | **Só na resposta, não é coluna (11/10/2026).** A situação EFETIVA do card: `open`, `snoozed` (adiado), `won` ou `lost`. `item_details.status` continua `open` num card adiado — quem quer saber se ele está adiado lê ESTE campo. Vem em `kanban_items_list`/`_show`/`_filter`/`_search`, na busca geral, na ficha do contato e no card embutido em `conversations_show`. Ver "Card adiado" no fim |
145
+ | `snoozed_until` | **Só na resposta (11/10/2026).** Até quando o card está adiado (ISO 8601); `null` quando não está adiado |
144
146
 
145
147
  > **ATENÇÃO — na maioria das respostas o `checklist` NÃO é a lista de tarefas, e sim um resumo com
146
148
  > os números:** `{ total_count, completed_count, groups: [{ group_id, group_name, total_count,
@@ -198,9 +200,9 @@ Como gravar pelo `lionchat_kanban_items_update` (`kanban_item.item_details`):
198
200
  - Mandar objeto (`{"origem_lead": "Facebook Ads"}`) no lugar da lista **APAGA todos os campos do card**: o
199
201
  servidor só aceita itens `{name, type, value}` e o objeto vira vazio.
200
202
  - Lista fechada de chaves do `item_details` (outras são descartadas em silêncio): title, description, status,
201
- reason, win_reason, loss_reason, attributed_to, duplicated_from_id, priority, value, currency,
202
- custom_attributes, offers, closed_offers, closed_offer, deadline_at, scheduling_type, scheduled_at,
203
- conversation_id, automations, notes.
203
+ reason, win_reason, loss_reason, attributed_to, co_attributed_to (13/10/2026, ver "Co-participante da venda"),
204
+ duplicated_from_id, priority, value, currency, custom_attributes, offers, closed_offers, closed_offer, deadline_at,
205
+ scheduling_type, scheduled_at, conversation_id, automations, notes.
204
206
  - Para mudar UM campo sem reenviar a lista, as ações de automação/macro `update_card_attribute`
205
207
  (`[{funnel_id, attribute_key: "<nome do campo>", value}]`) mexem só naquela entrada.
206
208
 
@@ -284,6 +286,10 @@ Log automático de eventos do card:
284
286
 
285
287
  Tipos comuns: `created`, `stage_changed`, `agent_assigned`, `agent_removed`, `note_added`, `value_changed`, `priority_changed`, `archived`.
286
288
 
289
+ Desde 11/10/2026 também `item_snoozed` (o card foi adiado — leva `snoozed_until`) e `item_unsnoozed` (o card adiado
290
+ abriu — leva `reason`: `person` = alguém abriu, `automation` = automação, macro ou fluxo, `due` = chegou a data; e a
291
+ data que estava marcada).
292
+
287
293
  Quem fez: `by_user_id`/`user` traz a pessoa; ação de automação ou flow vem com o nome da regra/fluxo;
288
294
  ação da IA (desde 21/08/2026 — entra com o próximo deploy do app depois de 21/08/2026) vem como
289
295
  `"IA: <nome do assistente>"` com `id` nulo — antes saía sem autor. Ao explicar um histórico de card,
@@ -503,7 +509,7 @@ PUT /api/v1/accounts/43/funnels/43
503
509
  | trigger_type | trigger_value |
504
510
  |---|---|
505
511
  | `card_created` | `"card_created"` (literal — NUNCA vazio) |
506
- | `status_change` | `"won"` ou `"lost"` |
512
+ | `status_change` | `"won"`, `"lost"` ou `"snoozed"` (desde 11/10/2026: roda quando o card é ADIADO; trocar a data de um card já adiado NÃO roda de novo). NÃO existe `"open"` aqui |
507
513
  | `stage_moved` | chave da etapa de DESTINO no hash `funnel.stages` (ex. `"agendar_instalacao"` — a chave, NÃO o campo `id` interno) |
508
514
 
509
515
  **Ações (`action` + `action_config`):**
@@ -647,6 +653,9 @@ Cada card pode receber atributos custom de 3 origens diferentes. **A escolha do
647
653
  `funnel_stage` com o slug que aparece em `kanban_items_show` e compare com `funnels_show`.
648
654
  3. `position` está faltando? (cards sem position vão pro fim)
649
655
  4. Está em outro funil? (mover entre funis muda `funnel_id`)
656
+ 5. Está **adiado**? (desde 11/10/2026) Card adiado sai da coluna e só aparece com o botão **Adiados** ligado no topo
657
+ do quadro. Confira `effective_status`/`snoozed_until` em `lionchat_kanban_items_show`: ele volta sozinho na data, ou
658
+ abra agora com `lionchat_kanban_items_unsnooze`. Ver "Card adiado" abaixo.
650
659
 
651
660
  ## Diagnóstico: "Valor do pipeline errado"
652
661
 
@@ -747,3 +756,77 @@ com o `retroativo_em` certo.
747
756
 
748
757
  A hora é montada em **UTC**, com o horário atual no dia escolhido. É o mesmo motivo do diagnóstico
749
758
  acima: o relatório compara o carimbo como texto, então a data escolhida tem que ser a data contada.
759
+
760
+ ## Card adiado — "Adiar até" (2026-10-11)
761
+
762
+ O card ganha uma situação a mais: **Adiado** — "este negócio só volta a importar no dia X". Ele sai da coluna e, na
763
+ data marcada, **abre sozinho** e volta para a coluna (disparando o fluxo "Card aberto", se houver).
764
+
765
+ | Regra | Detalhe |
766
+ |---|---|
767
+ | O que é | Situação **do card**, não da conversa. Mensagem do cliente NÃO abre o card; adiar o card não mexe na conversa. |
768
+ | Como fica gravado | `item_details.status` **continua `open`** — o adiamento mora numa tabela à parte (`kanban_item_snoozes`). Por isso o aplicativo de celular, os webhooks do cliente e a IA de atendimento continuam vendo `open`/`won`/`lost`. Para saber se está adiado, leia `effective_status` (`snoozed`) e `snoozed_until`. |
769
+ | Quem pode | Quem pode editar o card. Só card **aberto** pode ser adiado (ganho/perdido = 422). |
770
+ | Data | Pelo menos **1 hora** à frente (menos que isso = 422). Sem prazo máximo. Adiar um card já adiado **troca a data**. |
771
+ | O que NÃO encerra | Mover de etapa ou de funil, editar, nota, tarefa, responsável. |
772
+ | O que encerra | Marcar **ganho** ou **perdido** (por qualquer caminho); **abrir** de propósito (`lionchat_kanban_items_unsnooze`, `lionchat_kanban_items_create_2` com `status: "open"`, ação "Aberto" de automação/macro/fluxo); e a chegada da data. |
773
+ | `status: "open"` no `lionchat_kanban_items_update` | **NÃO abre** card adiado (é o mesmo salvar que o aplicativo faz o tempo todo). Para abrir, use `lionchat_kanban_items_unsnooze` ou o `change_status`. |
774
+ | Na data | O sistema abre o card numa rodada de até ~5 minutos depois da hora e dispara "Card aberto" **uma vez por card**. Esse fluxo entra na fila de disparos agendados: depois dos lembretes de consulta e, em caixa QR Code, no ritmo da caixa (um lote grande que vence junto sai aos poucos). |
775
+ | "Criar card" acha um adiado | Trata como card que **já existe**: não cria outro, move se a etapa for diferente e **não muda a situação** (continua adiado). Vale para automação, macro, fluxo e IA. |
776
+
777
+ **Ferramentas:**
778
+ - `lionchat_kanban_items_snooze` — `POST .../kanban_items/{id}/snooze` com `snoozed_until`.
779
+ - `lionchat_kanban_items_unsnooze` — `DELETE .../kanban_items/{id}/snooze` (abre agora e dispara "Card aberto").
780
+ - Automações de dentro do funil (`settings.automations`): gatilho `status_change` com `trigger_value: "snoozed"`
781
+ ("Adiado" na tela do funil) roda quando o card passa de aberto para adiado.
782
+ - Automação e macro: ação `snooze_kanban_item` — `[{"funnel_id": 31, "amount": 3, "unit": "days"}]` (`unit` `hours`
783
+ ou `days`; `amount` inteiro, 1 ou mais). Em regra de evento de CARD basta `[{"amount": 3, "unit": "days"}]` (o card
784
+ é o do evento). Diferente das outras ações de card, ela **falha visível** quando não dá para adiar (card fechado,
785
+ sem card no funil, tempo inválido) — nunca passa verde sem adiar.
786
+ - Fluxo: ação `snooze_kanban_item`, gatilhos `card_opened` ("Card aberto") e `card_snoozed` ("Card adiado") e
787
+ condição `kanban_snoozed`. Ver o resource do FlowBuilder.
788
+
789
+ **Onde o adiado aparece e como filtrar:**
790
+
791
+ | Lugar | Comportamento |
792
+ |---|---|
793
+ | Quadro (`lionchat_kanban_items_list`) | Sem nenhum `show_*`, a lista traz TODAS as situações (o adiado vem com `status: "open"` e `effective_status: "snoozed"`). Com qualquer `show_*`: `show_open` vale `true` salvo se vier `"false"` (= aberto e **não** adiado) e `show_snoozed=true` inclui os adiados. **Só os adiados:** `show_snoozed=true` **e** `show_open=false`. Esta lista **ignora** `statuses`. |
794
+ | Filtro rico (`lionchat_kanban_items_filter`), exportação e relatório da etapa | `statuses: ["open"]` = aberto e **não** adiado; `statuses: ["snoozed"]` = adiados. |
795
+ | Ficha do contato (`lionchat_contacts_kanban_items_list`) | `status=snoozed` = adiados; `status=open` deixa os adiados de fora. |
796
+ | Busca geral (`lionchat_search_kanban_items`) | `kanban_filters` com `statuses: ["snoozed"]`. |
797
+ | Números | `lionchat_funnels_open_counts` conta aberto e **não** adiado. `lionchat_funnels_stage_report` traz a linha `snoozed` — mas esse relatório SEMPRE aplica os botões de situação com o padrão da tela: sem `show_*` conta só os abertos não adiados e as linhas `won`/`lost`/`snoozed` voltam zeradas (mande `show_won`/`show_lost`/`show_snoozed=true`). No relatório agregado (`lionchat_kanban_items_list_3`), `statusDistribution.open` **continua incluindo** os adiados (é a soma que o aplicativo lê) e `statusDistribution.snoozed` vem ao lado: abertos de verdade = `open − snoozed`. Parados, maiores negócios, ranking e previsão já deixam o adiado de fora. |
798
+ | Histórico do card | `item_snoozed` (com a data) e `item_unsnoozed` (com o motivo). |
799
+
800
+ **Laço de cadência é permitido:** um fluxo "Card aberto" que manda mensagem e adia de novo roda até alguém parar (no
801
+ máximo uma volta por hora por card). Avise o cliente antes de montar. **Abrir à mão também dispara "Card aberto"**: se o
802
+ fluxo manda mensagem, o cliente recebe na hora.
803
+
804
+ ## Co-participante da venda (vendedor + SDR) — 2026-10-13
805
+
806
+ No ganho, além do vendedor (`attributed_to`, "Atribuir ganho a"), dá para marcar **UM co-participante** — ex.: o SDR
807
+ que trouxe a venda. A venda conta INTEIRA para o vendedor; o co-participante tem números próprios no relatório.
808
+ Marcar alguém como co-participante **não dá acesso ao card** nem o põe como responsável.
809
+
810
+ **Como gravar:**
811
+ - Junto com o ganho: `lionchat_kanban_items_create_2` (`change_status`) com `status: "won"` e `co_attributed_to: <id do
812
+ agente>`. Fora de `won` o parâmetro é ignorado.
813
+ - Depois, num card JÁ ganho: `lionchat_kanban_items_update` com `kanban_item.item_details.co_attributed_to`. Para tirar,
814
+ mande `null`.
815
+ - Mande SÓ o id (número). O nome (`co_attributed_name`) o próprio card preenche — nunca envie.
816
+ - Objeto ou lista no lugar do id = 422 antes de gravar.
817
+
818
+ **Recusas (422, com `error` legível):** id que não é número; pessoa que não é membro da conta; o próprio vendedor do
819
+ card; pessoa sem acesso ao funil (vale: funil sem lista de agentes, agente da lista do funil ou dos times dele,
820
+ administrador, ou quem já é responsável do card). A conferência só roda quando o valor MUDA — quem perdeu o acesso
821
+ depois não trava outro salvamento.
822
+
823
+ **O que mais saber:**
824
+ - Reabrir ou marcar como perdido **apaga** o co-participante (o campo só existe em card ganho).
825
+ - A ação "marcar como ganho" de fluxo, automação e macro NÃO marca co-participante (decisão do dono): ganho por ação
826
+ nasce sem ele, que entra depois pela tela, por `lionchat_kanban_items_update` ou pela planilha de "Importar vendas"
827
+ (coluna de co-participante, por nome ou e-mail).
828
+ - Duplicar o card não copia o co-participante.
829
+ - Histórico: marcado junto com o ganho vai dentro do registro da mudança de status; marcado depois gera
830
+ `co_participant_changed` (antes e depois, com nome e quem marcou).
831
+ - Números: `coParticipantRanking`, `sharedSalesSummary`/`sharedSalesBySeller` (com `include_shared_sales=true`) no
832
+ relatório agregado e a lista `lionchat_kanban_items_shared_sales` — ver `lionchat://docs/filtros-e-relatorios` §4.