@lionchat/mcp-server 1.15.0 → 1.16.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.
- package/dist/docs/instructions.md +12 -1
- package/dist/docs/prompts/create_flow_brainstorm.json +1 -1
- package/dist/docs/prompts/customer_health.json +1 -1
- package/dist/docs/prompts/productivity_report.json +1 -1
- package/dist/docs/prompts/quality_audit.json +1 -1
- package/dist/docs/prompts/stuck_leads.json +1 -1
- package/dist/docs/prompts/team_load_balance.json +1 -1
- package/dist/docs/prompts/weekly_recap.json +1 -1
- package/dist/docs/prompts/whatsapp_template_usage.json +1 -1
- package/dist/docs/resources/api-conventions.md +19 -0
- package/dist/docs/resources/best-practices.md +9 -3
- package/dist/docs/resources/conversation-flows.md +12 -0
- package/dist/docs/resources/data-model.md +87 -20
- package/dist/docs/resources/filtros-e-relatorios.md +7 -2
- package/dist/docs/resources/flowbuilder-design-guide.md +166 -31
- package/dist/docs/resources/flowbuilder-patterns.md +20 -5
- package/dist/docs/resources/formularios-publicos.md +110 -28
- package/dist/docs/resources/glossary.md +26 -1
- package/dist/docs/resources/kanban-deep-dive.md +19 -1
- package/dist/docs/resources/reports-guide.md +43 -7
- package/dist/docs/resources/troubleshooting.md +54 -1
- package/dist/tools.js +179 -38
- package/dist/tools.js.map +1 -1
- package/endpoints.json +2866 -36
- package/package.json +1 -1
|
@@ -73,6 +73,8 @@ Todo node tem essa estrutura base:
|
|
|
73
73
|
- `conversation_created` / `conversation_reopened`: filtro opcional de keywords via `match_mode` (`'any'`, `'contains'`, `'exact'`, `'customer_initiated'`, `'agent_initiated'`) + `keywords`. Só ESTES dois triggers usam `match_mode`.
|
|
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
|
+
- `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
|
+
- `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.
|
|
76
78
|
- `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.
|
|
77
79
|
- `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.
|
|
78
80
|
|
|
@@ -135,7 +137,7 @@ Receita completa: `flows_create`/`flows_update` com o item no start → `flows_l
|
|
|
135
137
|
4. Salvar o flow (`flows_update`) — o save sincroniza a ativação do webhook embutido (remover o item desativa a integração automaticamente).
|
|
136
138
|
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.
|
|
137
139
|
|
|
138
|
-
**Gatilhos de AGENDAMENTO — Booking NATIVO do LionChat (2026-08-20):** `booking_created` (alguém marcou pelo link público, pela IA ou pelo painel), `booking_cancelled` (paciente cancelou pelo link, IA cancelou ou a equipe cancelou na Agenda), `booking_rescheduled` (a data/hora mudou — link do paciente, IA, editar/adiar na Agenda), `booking_completed` (equipe concluiu na Agenda; exige a Agenda unificada ligada na conta). **Não é e-Clínica.** Config (tudo opcional, vazio = todos): `booking_event_type_ids` (array de ids de tipo de agendamento, **STRING**), `agent_ids` (array de ids de usuário — o RESPONSÁVEL da tarefa), `create_conversation` (boolean, padrão `false`: só dispara para quem já tem conversa numa caixa do flow; `true` = cria a conversa na 1ª caixa do flow). Exemplo: `{ "key": "booking_created", "config": { "booking_event_type_ids": ["44"], "agent_ids": [], "create_conversation": true } }`. São INERTES a evento de conversa (quem dispara é o `Booking::FlowTriggerDispatcher`, por CONTA — caixa do flow só define onde a conversa é criada/reusada). Variáveis no flow: `{{booking.date}}` (DD/MM/AAAA), `{{booking.time}}`, `{{booking.weekday}}`, `{{booking.datetime}}` (ISO), `{{booking.type}}`, `{{booking.type_format}}` (presencial/videochamada), `{{booking.duration}}`, `{{booking.agent}}`, `{{booking.status}}` (confirmado/cancelado/remarcado/concluído), `{{booking.description}}`, `{{booking.cancel_url}}`, `{{booking.reschedule_url}}`, `{{booking.color}}`, `{{booking.id}}`; só no remarcado: `{{booking.previous_date}}`, `{{booking.previous_time}}`, `{{booking.previous_datetime}}`; `{{booking.meeting_url}}` NÃO vem no criado (nasce vazio). Data/hora vêm da TAREFA da Agenda (sempre atuais). Regras: o evento mais novo SUBSTITUI a rodada ativa do mesmo flow na mesma conversa (2ª consulta do mesmo paciente, ou cancelado chegando com o criado ainda esperando resposta); excluir a tarefa, reabrir cancelada ou desfazer conclusão NÃO disparam; adiar (snooze) conta como remarcado; tipo com confirmação/lembrete próprios ligados + `booking_created` = o paciente pode receber duas mensagens (a tela avisa). Colisão entre flows é por CONTA: mesmo kind + tipos se cruzando + agentes se cruzando (vazio = todos).
|
|
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.
|
|
139
141
|
|
|
140
142
|
**TRAVA DE GATILHO DUPLICADO (2026-06-16) — leia ANTES de ativar/criar flow ativo:** o sistema BLOQUEIA ter dois flows ATIVOS com o MESMO gatilho na MESMA inbox e mesmo `conversation_mode` (evita o evento disparar dois flows). Colisão = mesmo tipo de gatilho + config cruzando (mesmas keywords/funil/labels/url/etc) + inbox compartilhada + mesmo modo. EXCEÇÕES que podem coexistir: `webhook_received`, `manual_trigger` e `campaign_trigger` (este último porque não dispara por evento — é só uma autorização para a campanha).
|
|
141
143
|
- Ao **ativar** (`flows_toggle` inativo→ativo) ou **criar já ativo**: qualquer conflito é barrado.
|
|
@@ -145,6 +147,14 @@ Webhooks embutidos NÃO aparecem na listagem de integrações standalone; exclui
|
|
|
145
147
|
|
|
146
148
|
**Como a IA deve agir:** antes de ativar um flow, confira via `flows_check_conflicts` (mesma assinatura do toggle/update, NÃO salva nada e devolve exatamente quem colide) se já não há outro flow ativo no mesmo gatilho+inbox — NÃO tente deduzir isso lendo o `flow_data` de cada item da `flows_list` (a lista é pra escolher/filtrar fluxos, não pra inspecionar blocos). Se receber 422 `flow_trigger_conflict`, NÃO fique reativando — EXPLIQUE o conflito ao usuário (nome do flow conflitante + caixa) e ofereça desativar o outro flow ou ajustar o gatilho/keywords.
|
|
147
149
|
|
|
150
|
+
**O que `flows_list` devolve (leve, desde 20/08/2026):** `id`, `name`, `description`, `flow_type`,
|
|
151
|
+
`conversation_mode`, `active`, `inbox_ids`/`inboxes`, `tags`, `tool_name`/`tool_description` e
|
|
152
|
+
**`trigger_types`** (as chaves de gatilho do bloco Início — serve pra filtrar "quais flows disparam
|
|
153
|
+
por `message_received`" sem abrir cada um). **NÃO vem** `flow_data`, `variables` nem métricas por
|
|
154
|
+
bloco — pra isso use `flows_show`. Contadores de sessão (`session_stats`: `total`, `active`,
|
|
155
|
+
`waiting`, `completed`, `exited`, `error`) só com `with_session_stats=true`. Ignore
|
|
156
|
+
`executions_count`/`errors_count` (sempre 0).
|
|
157
|
+
|
|
148
158
|
**Handles que SAEM:** `success`.
|
|
149
159
|
|
|
150
160
|
### 2.2 `send_message`
|
|
@@ -158,14 +168,16 @@ Webhooks embutidos NÃO aparecem na listagem de integrações standalone; exclui
|
|
|
158
168
|
"label": "Boas-vindas",
|
|
159
169
|
"messageItems": [
|
|
160
170
|
{ "id": "m1", "type": "text", "content": "Oi {{contact.name}}! Como posso ajudar?" },
|
|
161
|
-
{ "id": "m2", "type": "delay", "
|
|
171
|
+
{ "id": "m2", "type": "delay", "duration_seconds": 2 },
|
|
162
172
|
{ "id": "m3", "type": "whatsapp_template", "templateId": 123, "params": ["valor1"] }
|
|
163
173
|
]
|
|
164
174
|
}
|
|
165
175
|
}
|
|
166
176
|
```
|
|
167
177
|
|
|
168
|
-
**Tipos de item válidos:** `text`, `whatsapp_template` (ou `template`), `canned_response`, `user_input` (pausa esperando resposta livre), `delay`, `attachment`, `audio
|
|
178
|
+
**Tipos de item válidos:** `text`, `whatsapp_template` (ou `template`), `canned_response`, `user_input` (pausa esperando resposta livre), `delay`, `attachment`, `audio`, `url_media` (`{ "type": "url_media", "url", "caption" }` — mídia por URL; a `url` aceita `{{variável}}` e o motor baixa com proteção SSRF e detecta o tipo; `image`/`video`/`file`/`document` também são aceitos como mídia, com `attachment_url`).
|
|
179
|
+
|
|
180
|
+
**`delay` usa `duration_seconds` (0-30), NÃO `seconds`.** O motor aceita os dois, mas a tela lê/grava só `duration_seconds` — item gravado com `seconds` roda, porém abre como "undefineds" no editor e o cliente não consegue ajustar (mesma família do `percent` x `weight` do randomizador).
|
|
169
181
|
|
|
170
182
|
**ATENÇÃO:** usa `messageItems` (NÃO `items`).
|
|
171
183
|
|
|
@@ -178,7 +190,7 @@ Webhooks embutidos NÃO aparecem na listagem de integrações standalone; exclui
|
|
|
178
190
|
```
|
|
179
191
|
→ edges: `sourceHandle: "button_sim"`, `sourceHandle: "button_nao"`, e opcionalmente `"no_response"`.
|
|
180
192
|
|
|
181
|
-
**Handles que SAEM:** `success` (sem botões)
|
|
193
|
+
**Handles que SAEM:** `success` (sem botões) + `error`; com botões → `button_<value>` (um por botão) + `no_response` + `error`. E, SÓ em caixa WhatsApp OFICIAL com item sujeito à janela de 24h (`text`/`canned_response`/`attachment`/`audio`/`url_media`), **`window_closed`**: o motor segue por ele quando a janela está fechada (ligue ali um bloco com template); sem esse fio a janela fechada cai no caminho de erro. Fora de caixa oficial o handle NÃO existe (edge nele = aresta fantasma). Com botões a janela fechada também vale.
|
|
182
194
|
|
|
183
195
|
**Timeout dos botões (opcional, no MESMO item de botões):**
|
|
184
196
|
- `buttons_timeout` (número) + `buttons_timeout_unit` (`"minutes"` | `"hours"` | `"days"`) → tempo de espera sem resposta. Só com `buttons_timeout > 0` existe o handle `no_reply_timeout`.
|
|
@@ -278,7 +290,26 @@ No editor visual esses destinos só aparecem quando a validação bate (computed
|
|
|
278
290
|
|
|
279
291
|
**`timeout` vs `retries_exhausted` (DISTINTOS):** `timeout` = cliente ficou em silencio (estourou `waitTime`). `retries_exhausted` = cliente respondeu, mas errou a validacao mais que `maxRetries` vezes. Ligue cada um ao caminho desejado. Se `retries_exhausted` nao tiver edge, ha fallback p/ o edge de `timeout`; sem nenhum dos dois, o flow encerra ao esgotar as tentativas.
|
|
280
292
|
|
|
281
|
-
**Timeout
|
|
293
|
+
**Timeout dispara de verdade — e SEM fio ENCERRA (desde 20/08/2026):** `waitTime` + `waitUnit`
|
|
294
|
+
agendam o estouro. Com fio no handle `timeout`, o flow segue por ele. **Sem fio, a sessão é
|
|
295
|
+
ENCERRADA quando o tempo estoura** — a resposta que chegar depois não avança mais aquele fluxo
|
|
296
|
+
(cai na conversa como mensagem comum e pode disparar o gatilho de novo). Até 20/08 a sessão ficava
|
|
297
|
+
esperando pra sempre, fora do alcance de qualquer vigia e travando o re-disparo do próprio flow
|
|
298
|
+
naquela conversa. Decisão do dono: "se não tá ligado a nada, depois encerra". Se o cliente quer
|
|
299
|
+
"esperar até responder", ponha um `waitTime` longo e ligue o `timeout` num caminho explícito.
|
|
300
|
+
`waitTime` AUSENTE = sem timeout (espera indefinida); `waitTime` vazio (`''`) = 60 (o que a tela
|
|
301
|
+
exibe). `maxRetries` vazio/0 = 3. Vale também pro `no_reply_timeout` dos botões.
|
|
302
|
+
|
|
303
|
+
**Fallback de saída (regra de 20/08/2026, substitui a de 15/08):** fio COM rótulo só é seguido por
|
|
304
|
+
quem CASA o rótulo. O "último recurso" do avanço segue EXCLUSIVAMENTE a ligação SEM `sourceHandle`
|
|
305
|
+
(fluxo legado de bloco simples) — nunca mais "pega o primeiro fio que existir": resposta A num menu
|
|
306
|
+
com só a saída B ligada NÃO entrega no caminho do B, quem não respondeu não cai no botão ligado,
|
|
307
|
+
envio bem-sucedido não desce pelo fio `window_closed`. Sem fio elegível o fluxo TERMINA. Duas pontes
|
|
308
|
+
explícitas que continuam valendo: (a) na RESPOSTA do cliente (`wait_response`/botões),
|
|
309
|
+
`option_X`/`button_X`/`no_response` sem fio próprio cai no fio `success` SE ele existir (o bloco de
|
|
310
|
+
botões não expõe `success` na tela, então na prática isso só vale no `wait_response`); (b) a saída
|
|
311
|
+
`partial` do `update_group` sem fio cai no `success`. Conclusão prática: ligue TODA saída que pode
|
|
312
|
+
acontecer.
|
|
282
313
|
|
|
283
314
|
**REGRA:** depois de wait_response com `options` OU `varied_options`, NUNCA coloque node `condition` pra ramificar — ligue os edges direto nos handles `option_X` (em `varied_options`, `X` é o `id` do grupo).
|
|
284
315
|
|
|
@@ -516,7 +547,9 @@ headers e sem body (ou erra "URL not configured" se faltar `apiUrl`).
|
|
|
516
547
|
|
|
517
548
|
**Nomes de campo (exatos):** `apiMethod` (default `GET`), `apiUrl` (obrigatório), `apiHeaders`
|
|
518
549
|
(array de `{key,value}`), `apiBody` (string; ou `apiBodyMode:"fields"` + `apiBodyFields:[{key,value}]`),
|
|
519
|
-
`apiQueryParams`, `apiAuthType`/`apiAuthToken`, `apiTimeout`, `apiResponseVar` (nome da variável de saída
|
|
550
|
+
`apiQueryParams`, `apiAuthType`/`apiAuthToken`, `apiTimeout`, `apiResponseVar` (nome da variável de saída;
|
|
551
|
+
desde 20/08/2026 nome iniciado por `_` cai no padrão `api_response` — `_` é o prefixo reservado das
|
|
552
|
+
chaves internas da sessão).
|
|
520
553
|
|
|
521
554
|
**Variáveis de conta / segredos:** use `{{account.custom_attribute.NOME}}` — NÃO existe `{{env.X}}`
|
|
522
555
|
(resolve vazio → 401). Variáveis do tipo *secret* SÓ resolvem dentro do nó `api` (nos campos de
|
|
@@ -571,15 +604,17 @@ pra não ser oferecida numa tela e faltar na outra. Também não existem `o1-min
|
|
|
571
604
|
`gpt-5.2-pro` nem `gpt-5.5-pro` (a OpenAI recusa).
|
|
572
605
|
|
|
573
606
|
**OBRIGATÓRIO via API — `aiAssistantId`:** os modos `generate`/`intent`/`sentiment`/`extract`
|
|
574
|
-
EXIGEM um `aiAssistantId` válido (id de um assistente Captain da conta). Sem ele o nó
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
`
|
|
580
|
-
`
|
|
581
|
-
|
|
582
|
-
|
|
607
|
+
EXIGEM um `aiAssistantId` válido (id de um assistente Captain da conta). **Sem ele o nó sai pela
|
|
608
|
+
saída `error` desde 20/08/2026** (`AI Assistant not configured`), igual a assistente inexistente na
|
|
609
|
+
conta — antes ficava VERDE e seguia como se a IA tivesse respondido, com a variável de saída
|
|
610
|
+
(`aiResponseVar`/`ai_intent`/`ai_sentiment`) vazia. A tela do Flow Builder preenche esse id pelo
|
|
611
|
+
dropdown; um nó criado via API/MCP SEM `aiAssistantId` "não funciona" por isso. Só rodam sem
|
|
612
|
+
assistente: `aiMode: 'custom'` (motor cru com a chave da conta + `aiModel`) e flow `ai_tool` em
|
|
613
|
+
`generate`/`custom`. A saída fica em `aiResponseVar` (default `ai_response`); `intent` também em
|
|
614
|
+
`ai_intent`, `sentiment` em `ai_sentiment`. Nome de variável iniciado por `_` é IGNORADO e cai no
|
|
615
|
+
padrão (prefixo reservado das chaves internas da sessão) — vale pro `apiResponseVar` também.
|
|
616
|
+
|
|
617
|
+
**Contexto da conversa:** a chave é **`aiContextMessages`** — quantas mensagens da conversa a IA enxerga: `0`, `1`, `3`, `5`, `10`, `25`, `50`, `75` ou `100` (default `5`; acima de 100 é cortado; ignorado em `custom`). **`contextMessages` (sem o prefixo) é DESCARTADA em silêncio** (fica 5). Os modos `intent`/`sentiment`/`extract` rodam no motor contido (texto puro, sem persona nem ferramentas — mais barato e sem risco de vazamento); `generate` usa o assistente Captain.
|
|
583
618
|
|
|
584
619
|
**Intent — campo EXATO:** `aiIntents` é um ARRAY DE OBJETOS `{ "name": "..." }`. NÃO use `aiIntentOptions` (array de strings) — é ignorado. A intenção classificada também fica disponível na variável de sessão **`ai_intent`** (use como `{{ai_intent}}` adiante).
|
|
585
620
|
|
|
@@ -680,6 +715,10 @@ O campo raiz é **`waitMode`**: `"duration"` (default), `"date"` ou `"weekday"`.
|
|
|
680
715
|
}
|
|
681
716
|
```
|
|
682
717
|
|
|
718
|
+
**Nome da variável (desde 20/08/2026):** `name` vazio ou iniciado por `_` é IGNORADO — `_` é o prefixo
|
|
719
|
+
reservado das chaves internas da sessão. A mesma regra vale pro `saveVariable` do `wait_response`
|
|
720
|
+
(não grava) e pro `apiResponseVar`/`aiResponseVar` (caem no padrão). `value` aceita `{{ }}`.
|
|
721
|
+
|
|
683
722
|
**Handles que SAEM:** `success`.
|
|
684
723
|
|
|
685
724
|
### 2.10 `randomizer`
|
|
@@ -724,19 +763,85 @@ juntos) — a operação vai em `data.groupOperation`. São **17 operações**,
|
|
|
724
763
|
- **Admins:** `promote_admin` (promover a admin), `demote_admin` (rebaixar).
|
|
725
764
|
- **Convite:** `get_invite` (pegar link/código de convite), `revoke_invite` (revogar), `send_invite` (mandar
|
|
726
765
|
o convite por mensagem a um telefone).
|
|
766
|
+
- **Mensagem:** `send_message` (a 17ª, 08/08) — manda `messageItems` (MESMO contrato do bloco de mensagem:
|
|
767
|
+
text/delay/attachment/audio/url_media; botões/template não fazem sentido em grupo) NA CONVERSA DO GRUPO
|
|
768
|
+
(`groupTargetId` ou, vazio, o grupo da conversa atual; grupo inexistente = `error`). NÃO grava
|
|
769
|
+
`{{grupo.*}}` — é a única sem variável de resposta. Serve pra "dei ganho no lead → aviso o grupo da
|
|
770
|
+
equipe" em fluxo de qualquer canal.
|
|
727
771
|
|
|
728
772
|
**Disponível em fluxo de QUALQUER canal**, desde que a conta tenha uma caixa **WhatsApp QR Code
|
|
729
773
|
(`Channel::Waha`)** — informe a caixa em `data.groupInboxId`. Esse campo é **OBRIGATÓRIO em fluxo não-grupo**
|
|
730
|
-
(individual/qualquer canal) e **opcional em fluxo de grupo** (ali o padrão
|
|
774
|
+
(individual/qualquer canal; o save recusa caixa de outro tipo) e **opcional em fluxo de grupo** (ali o padrão
|
|
775
|
+
é o grupo da própria conversa). `groupOperation` ausente/vazio = `legacy` (comportamento antigo — NÃO usar em
|
|
776
|
+
node novo). `groupTargetId` aceita `"1203...@g.us"`, só dígitos ou `{{var}}`; é ignorado nas operações sem
|
|
777
|
+
alvo (`create`, `find_by_name`).
|
|
731
778
|
|
|
732
779
|
Cada operação guarda o resultado na variável `data.groupResponseVar` (padrão `"grupo"`) — leia depois com
|
|
733
|
-
`{{grupo.CAMPO}}` (ex.: `{{grupo.id}}`, `{{grupo.participants_count}}`, `{{grupo.invite_link}}`).
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
780
|
+
`{{grupo.CAMPO}}` (ex.: `{{grupo.id}}`, `{{grupo.participants_count}}`, `{{grupo.invite_link}}`). Toda
|
|
781
|
+
operação (menos `send_message`) também devolve `{{grupo.ok}}` (bool) e `{{grupo.error.message}}`.
|
|
782
|
+
|
|
783
|
+
**Campos que cada operação devolve em `{{grupo.*}}`:**
|
|
784
|
+
- `create` → `id`, `name`, `description`, `picture`, `participants`, `participants_count`, `conversation_id`
|
|
785
|
+
(número da conversa do grupo aberta no painel, 08/08), `added`, `added_count`, `not_added`,
|
|
786
|
+
`not_added_count`, `no_whatsapp`, `invited`, `invite_status`, `extras_failed` (só quando descrição/foto/
|
|
787
|
+
admin/conversa falharam — o grupo JÁ existe e isso NUNCA vira erro). Aceita `groupAttributes` (08/08:
|
|
788
|
+
linhas `{attr_source, attr_key, attr_value}` — grava atributos personalizados no contato-grupo
|
|
789
|
+
recém-criado; `{{grupo.id}}`/`{{grupo.conversation_id}}` já valem em `attr_value`; campos nativos
|
|
790
|
+
`name`/`email`/`card` são recusados).
|
|
791
|
+
- `find_by_id` → `id`, `name`, `description`, `picture`, `participants`, `participants_count`.
|
|
792
|
+
`find_by_name` (`groupSearchName` obrig + `groupSearchMode`: `contains` padrão | `does_not_contain` |
|
|
793
|
+
`equal_to` | `not_equal_to` | `starts_with` | `ends_with`) → `results`, `results_count`.
|
|
794
|
+
- `update_subject` (`updateSubject`) → `id`, `name`; `update_description` (`updateDescription`) → `id`,
|
|
795
|
+
`description`; `update_picture` (`updatePicture`, url) → `id`; `leave` → `id` (IRREVERSÍVEL).
|
|
796
|
+
- `list_participants` → `id`, `participants` (até 100; `participants_truncated: true` se cortou),
|
|
797
|
+
`participants_count`.
|
|
798
|
+
- `add_participants` (`groupParticipants` obrig — array de telefones/JIDs ou lista por vírgula; aceita
|
|
799
|
+
`{{var}}`) → `id`, `added` (quem está no grupo agora, inclui quem já estava), `not_added`, `added_count`,
|
|
800
|
+
`not_added_count`, `no_whatsapp`, `invited`, `invite_status`. Ninguém entrou = `error` (`nobody_changed`);
|
|
801
|
+
parte entrou = `partial`.
|
|
802
|
+
- `remove_participants` → `id`, `removed`, `not_removed`, `removed_count`, `not_removed_count` (ninguém saiu
|
|
803
|
+
= `error`; parte = `partial`). `promote_admin` / `demote_admin` → `id`, `participants`.
|
|
804
|
+
- `get_invite` → `id`, `invite_link`; `revoke_invite` → `id`, `invite_link` (IRREVERSÍVEL — invalida o link
|
|
805
|
+
antigo); `send_invite` (`groupInviteTo` obrig, `groupInviteMessage` opcional — `{{link}}` marca onde o
|
|
806
|
+
link entra, sem o marcador vai no fim) → `id`, `invite_link`, `invite_sent_to`.
|
|
807
|
+
- `settings` (pelo menos UMA de `infoAdminOnly`, `messagesAdminOnly`, `membersCanAddNewMember`) → `id`,
|
|
808
|
+
`settings_updated`, `settings_failed`, `settings_unsupported`. **Desde 20/08 cada permissão é aplicada
|
|
809
|
+
SOZINHA:** `settings_updated[]` sempre vem; `settings_failed[{setting, reason}]` = falhou de verdade;
|
|
810
|
+
`settings_unsupported[]` = o servidor WAHA não tem o recurso (ex.: quem-pode-adicionar em servidor
|
|
811
|
+
antigo). Sucesso SÓ se aplicou ao menos uma E nenhuma falhou; senão sai por `error` (código
|
|
812
|
+
`settings_failed` ou `settings_unsupported`) com o que aplicou visível na variável. ATENÇÃO:
|
|
813
|
+
`membersCanAddNewMember` tem semântica INVERTIDA (`true` = TODOS podem adicionar);
|
|
814
|
+
`infoAdminOnly`/`messagesAdminOnly` seguem o padrão (`true` = SÓ admin).
|
|
815
|
+
|
|
816
|
+
Teto de **20 participantes por execução** (`create`/`add`/`remove`/`promote`/`demote` — trava anti-banimento:
|
|
817
|
+
adicionar em lote é o que mais rápido derruba número no WhatsApp não-oficial). Detalhe das chaves de `data`
|
|
818
|
+
por operação: ver o pattern "Gestão de Grupos WhatsApp por fluxo" no `flowbuilder-patterns`.
|
|
819
|
+
|
|
820
|
+
**`create` confere quem foi COLOCADO (2026-08-21 — entra com o próximo deploy do app depois de
|
|
821
|
+
21/08/2026):** o bloco relê o grupo e compara com o pedido (por equivalência brasileira, com e sem o 9;
|
|
822
|
+
duas leituras concordantes). Quem o WhatsApp não colocou (privacidade "quem pode me adicionar" restrita,
|
|
823
|
+
número inexistente) vai pra `{{grupo.not_added}}` / `{{grupo.not_added_count}}` e a saída vira `partial`;
|
|
824
|
+
`{{grupo.no_whatsapp}}` lista os números que o WhatsApp disse não existir (seguem no pedido — é aviso, não
|
|
825
|
+
erro). Número de celular BR escrito sem o 9 é corrigido pelo WhatsApp antes de adicionar (check-exists na
|
|
826
|
+
sessão do canal, orçamento 8 s). Antes de 21/08 o passo dava SUCESSO mesmo com o lead fora do grupo. A
|
|
827
|
+
mesma correção do 9º dígito vale no `add_participants`.
|
|
828
|
+
|
|
829
|
+
**Convite no privado de quem ficou de fora (opcional, nasce desligado; só `create`/`add_participants`;
|
|
830
|
+
2026-08-21 — entra com o próximo deploy do app depois de 21/08/2026):** `groupInviteOnFailure: true` +
|
|
831
|
+
`groupInviteMessage` (texto OBRIGATÓRIO nesse modo — sem texto = `invite_status: 'sem_mensagem'` e nada
|
|
832
|
+
sai; link nu de número desconhecido é o formato que mais gera denúncia e derruba número; `{{link}}` marca
|
|
833
|
+
onde o link do grupo entra — sem o marcador, o link vai no fim). Tetos anti-bloqueio: 5 convites por
|
|
834
|
+
execução, 1 por telefone por caixa por dia, 50 por dia por caixa, 20 s entre convites; quem está em
|
|
835
|
+
`no_whatsapp` não recebe; antes de mandar o job reconfere se a pessoa já entrou. `{{grupo.invited}}` lista
|
|
836
|
+
quem recebeu e `{{grupo.invite_status}}` diz `convite_enviado` ou `sem_mensagem`. **Cada convite entregue
|
|
837
|
+
cria contato + conversa no painel** (e dispara os ouvintes de `conversation_created`: automação, outros
|
|
838
|
+
flows, webhook do cliente, Kanban) — avise o cliente antes de ligar.
|
|
839
|
+
|
|
840
|
+
**Handles que SAEM:** `success`, `error` — e **`partial`** nas operações de participante
|
|
841
|
+
(`create`, `add_participants`, `remove_participants`): a operação rodou, mas nem todo mundo
|
|
842
|
+
entrou/saiu (as listas `not_added`/`not_removed` dizem quem). Ligue `partial` num caminho próprio (ex.:
|
|
843
|
+
avisar a equipe) — sem fio, `partial` segue pelo fio `success` (única saída com esse fallback no motor) e
|
|
844
|
+
quem ficou de fora passa em silêncio.
|
|
740
845
|
|
|
741
846
|
### 2.12 `activate_flow` (LEGADO — prefira action `start_flow`)
|
|
742
847
|
|
|
@@ -889,9 +994,9 @@ Sempre presente:
|
|
|
889
994
|
|
|
890
995
|
| Variável | Conteúdo |
|
|
891
996
|
|---|---|
|
|
892
|
-
| `{{trigger.type}}` | chave do
|
|
997
|
+
| `{{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 |
|
|
893
998
|
| `{{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 |
|
|
894
|
-
| `{{trigger.activated_at}}` | quando disparou (ISO 8601) |
|
|
999
|
+
| `{{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) |
|
|
895
1000
|
| `{{trigger.kanban_item_id}}` / `{{trigger.funnel_id}}` | só quando veio do Kanban |
|
|
896
1001
|
|
|
897
1002
|
Presentes conforme o evento (ausente = resolve vazio, sem erro):
|
|
@@ -903,11 +1008,13 @@ Presentes conforme o evento (ausente = resolve vazio, sem erro):
|
|
|
903
1008
|
| `{{trigger.contact.*}}` | `id`, `name`, `phone` |
|
|
904
1009
|
| `{{trigger.ad.*}}` (anúncio/CTWA) | `id`, `name`, `creative_id`, `creative_name`, `adset_id`, `adset_name`, `campaign_id`, `campaign_name`, `source`, `headline`, `description`, `cta`, `url` |
|
|
905
1010
|
| `{{trigger.attribute.*}}` | `name`, `previous_value`, `current_value`, `changed_at` — o valor ANTES e DEPOIS do atributo que disparou (só nos gatilhos de mudança de atributo) |
|
|
906
|
-
| `{{trigger.user_name}}` | nome do usuário/atendente que disparou (gatilho manual ou
|
|
907
|
-
| `{{trigger.campaign_title}}` | título da campanha (quando disparado por Campanha de Fluxo) |
|
|
1011
|
+
| `{{trigger.user_name}}` | nome do usuário/atendente que disparou (gatilho manual ou botão do card) |
|
|
1012
|
+
| `{{trigger.campaign_id}}` / `{{trigger.campaign_title}}` | id e título da campanha (quando disparado por Campanha de Fluxo) |
|
|
908
1013
|
| `{{trigger.event_name}}` | nome do evento do site que disparou (LionTrack / visita de página) |
|
|
909
1014
|
| `{{trigger.page_url}}` | URL da página (LionTrack / visita de página) |
|
|
910
|
-
| `{{trigger.source_flow_name}}` | nome do flow de origem (quando outro flow iniciou este, ex.: action `start_flow`) |
|
|
1015
|
+
| `{{trigger.source_flow_id}}` / `{{trigger.source_flow_name}}` | id e nome do flow de origem (quando outro flow iniciou este, ex.: action `start_flow`) |
|
|
1016
|
+
| `{{trigger.lead_form_id}}` / `{{trigger.response_id}}` / `{{trigger.kind}}` | formulário público: qual formulário, qual preenchimento e o tipo (`completed`/`milestone`/`abandoned`) |
|
|
1017
|
+
| `{{trigger.kind}}` / `{{trigger.booking_id}}` / `{{trigger.event_type_id}}` | agendamento (Booking nativo): `created`/`cancelled`/`rescheduled`/`completed`, id da reserva e id do tipo |
|
|
911
1018
|
|
|
912
1019
|
Os cinco últimos são **novos 2026-08-07** e passam a aparecer no autocomplete de TODO bloco (ausente para
|
|
913
1020
|
o gatilho que não os fornece = resolve vazio, sem erro).
|
|
@@ -921,9 +1028,20 @@ sai em JSON; valor simples sai cru.
|
|
|
921
1028
|
serve pra perguntar "o que veio COM o gatilho", não pra ser um segundo nome do mesmo dado.
|
|
922
1029
|
|
|
923
1030
|
**Variáveis do agente de IA `{{ai_agent.*}}` (novo 2026-08-01):** `{{ai_agent.name}}` e
|
|
924
|
-
`{{ai_agent.id}}` trazem o AI Agente atribuído à conversa (vazio se não houver)
|
|
1031
|
+
`{{ai_agent.id}}` trazem o AI Agente atribuído à conversa (vazio se não houver) — refletem a
|
|
1032
|
+
ATRIBUIÇÃO, não "vai responder agora"; desde 20/08/2026 resolvem também no Liquid. Prefixo `ai_agent` de
|
|
925
1033
|
propósito — `agent` sozinho é o atendente HUMANO, e confundir os dois seria pior que não ter.
|
|
926
1034
|
|
|
1035
|
+
**Outras variáveis padrão que resolvem no caminho direto:** `{{contact.id}}`, `{{contact.identifier}}`,
|
|
1036
|
+
`{{contact.first_name}}`, `{{contact.last_name}}`, `{{contact.label}}` (etiquetas do contato, separadas por
|
|
1037
|
+
vírgula), `{{conversation.team.name}}` (nome da equipe; `{{conversation.team}}` é apelido).
|
|
1038
|
+
`{{last_response}}` = última resposta do cliente; `{{last_agent_response}}` = última mensagem PÚBLICA nossa
|
|
1039
|
+
(nota interna NÃO conta, desde 20/08/2026).
|
|
1040
|
+
|
|
1041
|
+
**Filtro Liquid (desde 20/08/2026):** `{{conversation.id}}`, `{{conversation.status}}` e
|
|
1042
|
+
`{{conversation.team_id}}` respondem o MESMO com e sem filtro (`|`) — antes um filtro em qualquer variável do
|
|
1043
|
+
texto trocava o protocolo pelo id interno do banco.
|
|
1044
|
+
|
|
927
1045
|
---
|
|
928
1046
|
|
|
929
1047
|
## 2-D. Exit conditions (saída automática do flow)
|
|
@@ -998,8 +1116,18 @@ tiver um gatilho do MESMO tipo que colide. Resposta: **HTTP 422** `{ "error_code
|
|
|
998
1116
|
- `option_<x>` → um por opção em `acceptedOptions`; idem.
|
|
999
1117
|
- `cond_0..N` → um por condição no array; se tirar uma condição, o `cond_N` do fim some.
|
|
1000
1118
|
- `intent_<name>` → um por intent no `ai` mode intent.
|
|
1119
|
+
- `partial` → só em `update_group` com operação `create`/`add_participants`/`remove_participants`.
|
|
1001
1120
|
Ligar edge num handle condicional inexistente = **aresta fantasma** (sai do nada no canvas, não roteia).
|
|
1002
1121
|
|
|
1122
|
+
**Fio com rótulo só é seguido por quem casa com o rótulo (desde 20/08/2026).** Não existe mais
|
|
1123
|
+
"pega o primeiro fio que existir": `option_A` não entrega quem respondeu B, `window_closed` não
|
|
1124
|
+
pega envio bem-sucedido, `timeout` não pega resposta válida. O único último recurso é a ligação
|
|
1125
|
+
SEM `sourceHandle` (fluxo legado). Sem fio elegível, o flow TERMINA ali — nunca segue o ramo errado.
|
|
1126
|
+
Consequência prática: ligue TODAS as saídas que o cliente espera que aconteçam; saída sem fio é
|
|
1127
|
+
fim de fluxo, não "cai no próximo". (Duas pontes explícitas continuam: `option_X`/`button_X`/
|
|
1128
|
+
`no_response` sem fio próprio caem no `success` do `wait_response` se ele existir; `partial` do
|
|
1129
|
+
`update_group` sem fio cai no `success`.)
|
|
1130
|
+
|
|
1003
1131
|
---
|
|
1004
1132
|
|
|
1005
1133
|
## 4. Layout e positioning
|
|
@@ -1078,7 +1206,13 @@ Nodes nunca devem ficar com a mesma coordenada `(x, y)`. Se dois nodes têm posi
|
|
|
1078
1206
|
| `method`/`url`/`headers`/`body` no node api | Runtime NÃO lê → GET vazio (ou erro "URL not configured") | `apiMethod`/`apiUrl`/`apiHeaders`/`apiBody` |
|
|
1079
1207
|
| `{{env.X}}` no node api | Não existe → resolve vazio → 401 | `{{account.custom_attribute.X}}` (secret resolve só no node api) |
|
|
1080
1208
|
| `{{api_response.payload.campo}}` | `.payload` não existe → vazio | `{{api_response.campo}}` (o corpo fica direto sob a var); status em `{{api_response_status}}` |
|
|
1081
|
-
| node `ai` via API sem `aiAssistantId` | Sai
|
|
1209
|
+
| node `ai` via API sem `aiAssistantId` | Sai pela saída `error` (desde 20/08; antes ficava verde com a variável vazia) | Informe `aiAssistantId` (obrigatório em generate/intent/sentiment/extract) |
|
|
1210
|
+
| `contextMessages` no node `ai` | Chave errada → descartada em silêncio (fica 5) | `aiContextMessages` (0, 1, 3, 5, 10, 25, 50, 75, 100) |
|
|
1211
|
+
| Nome de variável começando com `_` (`set_variable`, `saveVariable`, `apiResponseVar`, `aiResponseVar`) | Prefixo reservado → ignorado / cai no padrão | Nome sem `_` na frente |
|
|
1212
|
+
| `{ type: "delay", seconds: 2 }` no send_message | A tela não lê `seconds` → abre como "undefineds" | `duration_seconds` (0-30) |
|
|
1213
|
+
| Caixa oficial sem fio em `window_closed` | Janela de 24h fechada vira erro | Ligue `window_closed` num bloco com template |
|
|
1214
|
+
| Saída com rótulo sem fio (`button_X`, `option_X`, `cond_N`, `timeout`, `partial`) | Desde 20/08 não "cai no próximo": o flow TERMINA ali; espera com timeout sem fio ENCERRA ao estourar | Ligue TODA saída possível |
|
|
1215
|
+
| `update_group` `create`/`add_participants` sem fio em `partial` | Quem ficou de fora passa em silêncio (cai no `success`) | Ligue `partial` e leia `{{grupo.not_added}}`; convite automático exige `groupInviteMessage` |
|
|
1082
1216
|
| `funnel_stages: ["37:Nome Exibido"]` | Usa o NOME da etapa, não a chave interna → gatilho nunca dispara | `"37:chave_interna"` (slug/UUID da etapa) |
|
|
1083
1217
|
| `funnel_ids: [37]` (número) no gatilho card | String esperada → `[37].include?("37")` falso → não dispara | `funnel_ids: ["37"]` |
|
|
1084
1218
|
| `kanban_in_stage` com `value: "37:etapa"` | Formato errado → cai no default silenciosamente | `funnel_id` separado + `value: "etapa"` (slug puro) |
|
|
@@ -1097,7 +1231,8 @@ Antes de chamar `flows_create` ou `flows_update`, valide mentalmente:
|
|
|
1097
1231
|
- [ ] Nenhum par de nodes tem a mesma posição `(x, y)`
|
|
1098
1232
|
- [ ] Todo node tem `data` com campos do schema do tipo
|
|
1099
1233
|
- [ ] Todo node não-start tem pelo menos 1 edge chegando (`edge.target = node.id`)
|
|
1100
|
-
- [ ] Todo `edge.sourceHandle` é um handle que o node source EXPÕE NAQUELA config (sem handle condicional inexistente: `no_reply_timeout` só com timeout, `window_closed` só API oficial, `button_<value>`/`option_<x>`/`cond_N`/`intent_<name>` só se o botão/opção/condição/intent existir)
|
|
1234
|
+
- [ ] Todo `edge.sourceHandle` é um handle que o node source EXPÕE NAQUELA config (sem handle condicional inexistente: `no_reply_timeout` só com timeout, `window_closed` só API oficial, `button_<value>`/`option_<x>`/`cond_N`/`intent_<name>` só se o botão/opção/condição/intent existir, `partial` só em `update_group` de participantes)
|
|
1235
|
+
- [ ] TODA saída que pode acontecer tem fio (desde 20/08 saída sem fio é fim de fluxo, não "cai no próximo"): cada `button_X`/`option_X`/`cond_N`, `default`, `timeout` de toda espera com `waitTime` (sem fio, a sessão ENCERRA ao estourar), `window_closed` em caixa oficial, `partial` no `update_group`
|
|
1101
1236
|
- [ ] Todo edge tem `source`, `target` e `sourceHandle`
|
|
1102
1237
|
- [ ] Todo `sourceHandle` é um handle real exposto pelo node source (seção 2)
|
|
1103
1238
|
- [ ] `channel_type` é classe Rails (`Channel::Waha` etc)
|
|
@@ -593,15 +593,25 @@ Comuns a (quase) todas:
|
|
|
593
593
|
|
|
594
594
|
| Operação | Chaves específicas | Retorno (`{{grupo.*}}`) |
|
|
595
595
|
|---|---|---|
|
|
596
|
-
| `create` | `groupName` (obrig), `groupDescription`, `groupPicture` (url), `groupInitialAdmins`, `groupParticipants` | `id`, `name`, `participants_count` |
|
|
597
|
-
| `add_participants` | `groupParticipants` (obrig — ex.: `["5511999999999@c.us"]` ou lista) | `added`, `not_added`, `added_count` |
|
|
596
|
+
| `create` | `groupName` (obrig), `groupDescription`, `groupPicture` (url), `groupInitialAdmins`, `groupParticipants`, `groupAttributes`; opcional `groupInviteOnFailure` + `groupInviteMessage` (com `{{link}}`) | `id`, `name`, `participants_count`, `conversation_id`, `added`, `added_count`, `not_added`, `not_added_count`, `no_whatsapp`, `invited`, `invite_status` — saída `partial` quando alguém ficou de fora |
|
|
597
|
+
| `add_participants` | `groupParticipants` (obrig — ex.: `["5511999999999@c.us"]` ou lista); opcional `groupInviteOnFailure` + `groupInviteMessage` | `added`, `not_added`, `added_count`, `not_added_count`, `no_whatsapp`, `invited`, `invite_status` |
|
|
598
598
|
| `send_invite` | `groupInviteTo` (obrig — telefone de destino), `groupInviteMessage` (opc) | `invite_link` |
|
|
599
|
-
| `settings` | pelo menos uma de `infoAdminOnly` / `messagesAdminOnly` / `membersCanAddNewMember` (booleans) |
|
|
599
|
+
| `settings` | pelo menos uma de `infoAdminOnly` / `messagesAdminOnly` / `membersCanAddNewMember` (booleans) | `settings_updated` (lista do que aplicou), `settings_failed` (`[{setting, reason}]`), `settings_unsupported` |
|
|
600
|
+
| `send_message` | `messageItems` (mesmo contrato do bloco de mensagem: text/delay/attachment/audio/url_media) + `groupTargetId` opcional — manda os balões NA CONVERSA DO GRUPO (vazio = grupo da conversa atual). É a 17ª operação (08/08) | — (única sem variável de resposta) |
|
|
601
|
+
|
|
602
|
+
Os campos `added`/`not_added`/`no_whatsapp`/`invited`/`invite_status` do `create` e `groupInviteOnFailure` são
|
|
603
|
+
de 21/08/2026 (entram com o próximo deploy do app depois de 21/08/2026).
|
|
600
604
|
|
|
601
605
|
**ATENÇÃO na `settings` — semântica INVERTIDA de `membersCanAddNewMember`:** `true` = **TODOS** podem adicionar
|
|
602
606
|
membros. Já `infoAdminOnly: true` e `messagesAdminOnly: true` = **SÓ admin** edita infos / manda mensagem.
|
|
603
607
|
Confundir os dois LIBERA o grupo achando que está trancando.
|
|
604
608
|
|
|
609
|
+
**Cada permissão vale por si (2026-08-20):** o bloco aplica uma a uma; o que aplicou aparece em
|
|
610
|
+
`{{grupo.settings_updated}}` mesmo quando outra falhou, e a saída só é `success` quando algo mudou E
|
|
611
|
+
nada falhou. `membersCanAddNewMember` ("quem pode adicionar") depende do servidor de WhatsApp QR
|
|
612
|
+
Code ser 2026.7+ — no servidor atual ela volta em `settings_unsupported` (não é erro do cliente) e
|
|
613
|
+
as outras duas seguem aplicadas. Não prometa essa permissão ao cliente sem conferir o resultado.
|
|
614
|
+
|
|
605
615
|
**Gatilho novo `group_participant_joined` (entrou no grupo → manda boas-vindas):** em um flow de grupo
|
|
606
616
|
(`conversation_mode: "group"`), o start pode ter o item `{ "key": "group_participant_joined", "config": {
|
|
607
617
|
"group_match": "any", "group_name": "", "group_ids": [] } }`. Filtro OPCIONAL de quais grupos disparam:
|
|
@@ -618,8 +628,13 @@ grupo** (o grupo é a conversa do flow).
|
|
|
618
628
|
- No fluxo de card ganho acima, o `send_message` vai para a conversa do CONTATO (o lead), não para o grupo
|
|
619
629
|
criado. Para postar DENTRO do grupo, use um flow de grupo (`conversation_mode: "group"`) — normalmente com o
|
|
620
630
|
gatilho `group_participant_joined`.
|
|
621
|
-
- Em `add_participants`, quem tem privacidade de grupo ligada pode não entrar mesmo com
|
|
622
|
-
WhatsApp:
|
|
631
|
+
- Em `create` e `add_participants`, quem tem privacidade de grupo ligada pode não entrar mesmo com
|
|
632
|
+
resposta de sucesso do WhatsApp: o bloco relê o grupo e manda quem faltou em `{{grupo.not_added}}`
|
|
633
|
+
(saída `partial`). Se o cliente quiser, ligue `groupInviteOnFailure` com um texto em
|
|
634
|
+
`groupInviteMessage` — o convite vai no privado (máx. 5 por execução, 1 por telefone/dia, 50/dia
|
|
635
|
+
por caixa) e cria contato + conversa pra cada um.
|
|
636
|
+
- Handles do bloco: `success`, `error` e `partial` (em `create`/`add_participants`/`remove_participants` — a
|
|
637
|
+
operação rodou mas nem todo mundo entrou/saiu). `partial` sem fio segue pelo fio `success`.
|
|
623
638
|
|
|
624
639
|
---
|
|
625
640
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Tudo sobre os Formulários Públicos do LionChat (captação estilo Typeform/Typebot): link público,
|
|
4
4
|
caixa de WhatsApp vinculada, desenho dos blocos, publicação, respostas, funil, simulador e
|
|
5
|
-
integração com fluxos. Atualizado em
|
|
5
|
+
integração com fluxos. Atualizado em 21/08/2026 (v4.10.228 + commits de 21/08 na elvislion: título
|
|
6
|
+
público, aba Preenchimentos — entram com o próximo deploy do app depois de 21/08/2026).
|
|
6
7
|
|
|
7
8
|
## O que é
|
|
8
9
|
|
|
@@ -51,16 +52,21 @@ caixa de WhatsApp vinculada e (opcionalmente) dispara um fluxo do FlowBuilder no
|
|
|
51
52
|
| `lionchat_lead_forms_responses_show` | `GET /lead_forms/:id/responses/:rid` | Detalhe de uma resposta, com o que a pessoa respondeu |
|
|
52
53
|
| `lionchat_lead_forms_test_run` | `POST /lead_forms/:lead_form_id/test_runs` | Simulador: roda o RASCUNHO no motor real, sem deixar rastro. Qualquer agente da conta |
|
|
53
54
|
| `lionchat_lead_forms_check_embed` | `POST /lead_forms/check_embed` | Confere se um site aceita ser exibido em iframe (bloco Página externa). Admin-only |
|
|
55
|
+
| `lionchat_contacts_form_entries_list` | `GET /contacts/:contact_id/form_entries` | Tudo que UMA PESSOA preencheu, das 4 origens (nosso formulário, Meta, Webhook Universal, Fluxo). Qualquer agente |
|
|
56
|
+
| `lionchat_contacts_form_entries_show` | `GET /contacts/:contact_id/form_entries/:source/:id` | Detalhe de um preenchimento a partir do contato. Qualquer agente |
|
|
54
57
|
|
|
55
|
-
**Imagens não
|
|
56
|
-
máximo 5 MB,
|
|
57
|
-
|
|
58
|
+
**Imagens não sobem pelo conector.** A rota `POST /lead_forms/:id/media` (admin, multipart `file`,
|
|
59
|
+
só `image/*`, máximo 5 MB, devolve `{blob_id, url}`) existe, mas upload de arquivo não passa pelo MCP
|
|
60
|
+
— chamada pelo conector ela volta 422 `invalid_file_type`. Suba a imagem no editor do painel e
|
|
61
|
+
referencie o `blob_id` (o `show` traz `media_urls` com todos os ids do acervo: imagem de bloco,
|
|
62
|
+
imagem de opção e a foto do chat). **Imagem subida e não referenciada em até 1 hora é apagada** na
|
|
63
|
+
próxima varredura (todo `update`/`publish` roda a varredura).
|
|
58
64
|
|
|
59
65
|
## Campos do formulário
|
|
60
66
|
|
|
61
67
|
| Campo | Tipo | Observação |
|
|
62
68
|
|---|---|---|
|
|
63
|
-
| `name` | string | **Obrigatório
|
|
69
|
+
| `name` | string | **Obrigatório.** Nome INTERNO (lista do painel). O que o lead vê na aba do navegador e no link compartilhado é `settings.public_title`; vazio, cai no `name` |
|
|
64
70
|
| `display_mode` | string | `cards` (padrão) ou `chat` |
|
|
65
71
|
| `active` | boolean | Formulário inativo devolve 404 na página pública |
|
|
66
72
|
| `inbox_id` | integer | Caixa de WhatsApp vinculada. Obrigatória para publicar. Ver regras abaixo |
|
|
@@ -94,6 +100,14 @@ ou `null` — a caixa resolvida), e `published_inbox_id` (a caixa que está NA F
|
|
|
94
100
|
| `button_label` | string — rótulo do botão da página pública | — |
|
|
95
101
|
| `resume` | booleano — continuar de onde parou entre visitas | `true` |
|
|
96
102
|
| `chat_avatar` | id numérico de blob já subido (`media`) — foto redonda ao lado dos balões, só no formato `chat`. O público recebe `chat_avatar_url` resolvida (nunca o id) | — |
|
|
103
|
+
| `public_title` | string, até 100 caracteres — título que o LEAD vê (aba do navegador, link compartilhado). Vazio = usa o `name`. O endpoint público devolve `display_title` (nunca o `name` cru) | — (21/08 — entra com o próximo deploy do app depois de 21/08/2026) |
|
|
104
|
+
|
|
105
|
+
**No `update`, `settings` SUBSTITUI o objeto inteiro** (assim como `form_data`): não há merge. Mande
|
|
106
|
+
todas as chaves que quer manter — um `settings: {theme: "dark"}` apaga cor, avatar, título público,
|
|
107
|
+
`resume` e `abandon_minutes`. E apagar `chat_avatar` custa o arquivo: a varredura de imagens roda
|
|
108
|
+
logo após o salvamento e purga o que não está mais referenciado em `form_data`, na foto publicada ou
|
|
109
|
+
em `settings.chat_avatar` (só upload com menos de 1 hora escapa). O mesmo vale pra `image` de bloco
|
|
110
|
+
ou de opção que sair do `form_data`.
|
|
97
111
|
|
|
98
112
|
## Ciclo de vida
|
|
99
113
|
|
|
@@ -208,8 +222,16 @@ renderiza e não dá erro.
|
|
|
208
222
|
**`options`** (do `choice`): `[{ "id": "abc123", "label": "Sim", "image": "" }]`.
|
|
209
223
|
|
|
210
224
|
**`branches`** (do `condition`): lista avaliada em ordem; nenhum ramo casando cai no handle `else`.
|
|
211
|
-
|
|
212
|
-
|
|
225
|
+
Cada ramo: `{ "id": "b1", "label": "Sim", "source_node_id": "<id da pergunta/escolha/consulta/IA>",
|
|
226
|
+
"operator": "equals", "value": "...", "path": "" }`. `id` é estável (a aresta `cond_<id>` aponta
|
|
227
|
+
pra ele); `label` é o rótulo do fio no desenho (vazio = número). Operadores: `equals`, `not_equals`,
|
|
228
|
+
`contains`, `filled`, `not_filled`, `greater_than`, `less_than` (os dois últimos só numéricos;
|
|
229
|
+
`filled`/`not_filled` ignoram `value`). Fonte Escolha compara pelo **id da opção**, não pelo texto;
|
|
230
|
+
resposta múltipla: `equals` exige exatamente aquela opção, `contains` = inclui. Fonte `api_request`
|
|
231
|
+
exige `path` (caminho dentro da resposta, ex.: `localidade`); fonte `ai` compara o texto/categoria
|
|
232
|
+
direto. **Ramificar por variável** (`{{var.*}}`): o motor aceita `source_variable: "<nome>"` no
|
|
233
|
+
ramo (tem precedência sobre `source_node_id`), mas o dialog de Condição do editor **não conhece a
|
|
234
|
+
chave e a descarta ao salvar o bloco pela tela** — use só em formulário mantido pela API.
|
|
213
235
|
|
|
214
236
|
**`items`** (do `action`) — o formato novo, uma LISTA de ações `[{key, config}]`:
|
|
215
237
|
|
|
@@ -357,6 +379,7 @@ mesmo que exista — é proteção, não defeito.
|
|
|
357
379
|
| Respostas por chamada do simulador | 60 |
|
|
358
380
|
| Altura da Página externa | 240..1200 px |
|
|
359
381
|
| Conferência de embed | 5 s por salto, até 3 redirecionamentos |
|
|
382
|
+
| Lista de preenchimentos do contato | 250 por origem (lead_form / meta_lead / webhook), sem paginação; `meta.truncated` avisa |
|
|
360
383
|
|
|
361
384
|
## Simulador (`lionchat_lead_forms_test_run`)
|
|
362
385
|
|
|
@@ -366,39 +389,90 @@ exige admin. O estado do teste vive em quem chama: cada chamada reproduz o teste
|
|
|
366
389
|
da lista de respostas.
|
|
367
390
|
|
|
368
391
|
- Body: `{answers: [{node_id, value}]}` — até 60 itens; vazio = começa do início. Item cujo
|
|
369
|
-
`node_id` não é o bloco atual PARA a reprodução ali (devolve o bloco onde parou).
|
|
392
|
+
`node_id` não é o bloco atual PARA a reprodução ali (devolve o bloco onde parou). `value: null`
|
|
393
|
+
avança bloco sem resposta (`start`, `message`, `embed`).
|
|
370
394
|
- Resposta 200: `{block, log, answers, variables, form}` — `block` é o mesmo envelope da página
|
|
371
|
-
pública; `log` é o caminho percorrido (`[{node_id, type, label, answer
|
|
372
|
-
mostra variável criada, ações executadas
|
|
373
|
-
|
|
374
|
-
|
|
395
|
+
pública; `log` é o caminho percorrido (`[{node_id, type, label, answer?, detail?}]` — `detail`
|
|
396
|
+
mostra variável criada, ações executadas e o rumo das condições; `answer`/`detail` só aparecem
|
|
397
|
+
quando existem); `form.name` é o nome interno.
|
|
398
|
+
- 422: `{error: "invalid_value", reason}` (uma resposta reprovada — `reason` é o código:
|
|
399
|
+
`required`, `invalid_email`, `invalid_phone`, `invalid_cpf`, `invalid_option`, `value_too_long`…)
|
|
400
|
+
ou `{error: "test_run_failed", reason}`.
|
|
401
|
+
|
|
402
|
+
**O que o simulador NÃO faz (e como isso aparece):**
|
|
403
|
+
|
|
404
|
+
| Bloco | No simulador |
|
|
405
|
+
|---|---|
|
|
406
|
+
| Consulta externa (`api_request`) e IA (`ai`) | **Não executam** (o job de efeito é desligado no modo de teste). Ao chegar neles a resposta volta `block: {node_id, type: "wait"}` e a reprodução para ali — itens seguintes são ignorados e `detail` desses blocos fica vazio. Pra testar o que vem depois, use a página pública (link identificado/aba anônima). |
|
|
407
|
+
| Agendamento (`booking`) | `value: ""` segue por `no_booking` (só se o bloco não for obrigatório). Pra simular "agendou", mande o id de um agendamento **já existente** da conta — o simulador pela API não cria reserva. (No painel, o simulador embute a página real: uma reserva feita ali é de verdade e fica no banco.) |
|
|
408
|
+
| Envio de arquivo (`file_upload`) obrigatório | Não tem como passar (o valor precisa ser um arquivo anexado à resposta de teste, que não existe): 422 `invalid_value`. Só com `required: false` e `value: ""`. |
|
|
409
|
+
| Envio de WhatsApp, etiqueta, pixel, marco | Aparecem no `log` como reservados (`detail` lista as ações), mas nada é enviado/gravado. |
|
|
375
410
|
|
|
376
411
|
## Respostas e funil
|
|
377
412
|
|
|
378
|
-
**`lionchat_lead_forms_stats`** (`days` opcional) devolve
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
413
|
+
**`lionchat_lead_forms_stats`** (`days` opcional, default 30, máximo 90) devolve
|
|
414
|
+
`views` (contador de VIDA INTEIRA do formulário — não obedece `days`), `starts`/`completed`/
|
|
415
|
+
`abandoned` da janela, `completion_rate` (% inteiro = completed/starts), `funnel` por bloco e
|
|
416
|
+
`milestones`. O funil lista os blocos que a pessoa vê (`start`, `question`, `choice`, `message`,
|
|
417
|
+
`booking`, `embed`, `end`), na ordem do desenho **publicado**; acima de 50.000 respostas na janela
|
|
418
|
+
sai parcial. **`milestones` hoje só enxerga bloco de Ações no formato antigo (`action_type:
|
|
419
|
+
"milestone"`)** — marco configurado em `items` (formato atual do editor) não aparece aqui; aparece
|
|
420
|
+
em `responses_show.milestones`.
|
|
382
421
|
|
|
383
422
|
**`lionchat_lead_forms_responses_list`** aceita `status` (`in_progress`, `completed`,
|
|
384
|
-
`abandoned`), `days` e `page
|
|
385
|
-
current_node_label, answered_count, created_at, completed_at}
|
|
386
|
-
`{id: null, name, phone, pending: true}`** — é
|
|
387
|
-
três momentos); nome e telefone saem do que a
|
|
423
|
+
`abandoned`), `days` e `page` (até 10.000). A lista nunca traz as respostas em si — só `{id, status,
|
|
424
|
+
contact, current_node_label, answered_count, created_at, completed_at}` e `meta: {current_page,
|
|
425
|
+
per_page: 25, total_count}`. **`contact` pode vir `{id: null, name, phone, pending: true}`** — é
|
|
426
|
+
quem ainda está preenchendo (o contato só nasce nos três momentos); nome e telefone saem do que a
|
|
427
|
+
pessoa já digitou. `answered_count` conta todas as chaves de `answers`, inclusive as sintéticas.
|
|
388
428
|
|
|
389
429
|
**`lionchat_lead_forms_responses_show`** traz o conteúdo:
|
|
390
430
|
|
|
391
|
-
- `
|
|
392
|
-
|
|
431
|
+
- `status`, `contact` (mesmo shape da lista), `current_node_label`, `started_at`, `completed_at`,
|
|
432
|
+
`abandoned_at`, `created_at`.
|
|
433
|
+
- `answers`: `[{label, value_label, at, download_url?}]`, ordenado por `at`. **Atenção:**
|
|
434
|
+
(1) o bloco de Ações grava entradas SINTÉTICAS com chave `"<node_id>::<n>"` e o fim degradado
|
|
435
|
+
(beco sem saída/teto de passos) grava `"_walk_ended"` — nem toda chave é id de bloco;
|
|
436
|
+
(2) no bloco de **Agendamento `value_label` é o id da reserva, não a data** (a aba Preenchimentos
|
|
437
|
+
do contato mostra a data); (3) envio de WhatsApp pulado por falta de telefone aparece como
|
|
438
|
+
entrada vazia do bloco de mensagem.
|
|
393
439
|
- `conversation`: `{id, display_id}` ou `null` — a conversa aberta na caixa vinculada.
|
|
394
|
-
- `milestones`: os marcos atingidos.
|
|
395
|
-
- `effects`: resultado de consulta/IA — `{node_id, label, state, error, status,
|
|
396
|
-
`url_short` é esquema + host + caminho, sem a query; corpo, cabeçalhos e prompt
|
|
440
|
+
- `milestones`: os marcos atingidos (`[{node_id, name, at}]`).
|
|
441
|
+
- `effects`: resultado de consulta/IA — `{node_id, label, state: pending|done|error, error, status,
|
|
442
|
+
url_short, at}`. `url_short` é esquema + host + caminho, sem a query; corpo, cabeçalhos e prompt
|
|
443
|
+
nunca saem.
|
|
397
444
|
- `utm_params`: de onde o lead veio.
|
|
398
445
|
|
|
399
|
-
Motivos de erro em `effects`: `http_<código
|
|
400
|
-
|
|
401
|
-
`
|
|
446
|
+
Motivos de erro em `effects.error`: `invalid_method`, `invalid_url`, `http_<código>` (`http_0` =
|
|
447
|
+
falha de transporte sem código), `timeout`, `ssrf_blocked`, `result_too_large`, `stale`,
|
|
448
|
+
`effect_failed` (exceção no job), `effect_runs_exceeded` (laço: 20 execuções), `budget_exceeded`,
|
|
449
|
+
`no_ai_key`, `ai_error`. (Os motivos `busy` e `unresolved_variable`, citados antes, não existem no
|
|
450
|
+
código.)
|
|
451
|
+
|
|
452
|
+
## Preenchimentos do contato (`lionchat_contacts_form_entries_list` / `_show`) — 21/08 (entra com o próximo deploy do app depois de 21/08/2026)
|
|
453
|
+
|
|
454
|
+
A aba "Preenchimentos" da ficha junta, numa lista só ordenada por data, tudo que a pessoa preencheu
|
|
455
|
+
ou que entrou por ela, de QUATRO origens: nosso formulário público (`source: lead_form`),
|
|
456
|
+
formulário de anúncio do Meta (`meta_lead`), Webhook Universal (`custom_webhook`) e o webhook que o
|
|
457
|
+
FlowBuilder cria pro gatilho de um fluxo (`flow`, com o nome do fluxo). É a única rota que responde
|
|
458
|
+
"o que esta PESSOA preencheu" — as outras são por formulário/integração, e duas delas são só de
|
|
459
|
+
administrador; esta é liberada a qualquer agente.
|
|
460
|
+
|
|
461
|
+
- **Lista**: `{payload: [{source, id, title, status, at, count, conversation}], meta: {total,
|
|
462
|
+
truncated}}`. `id` é o id do registro NA ORIGEM — só identifica junto com `source`. `status` é
|
|
463
|
+
`in_progress|completed|abandoned` no nosso formulário e `received` nas demais. `count` = respostas
|
|
464
|
+
(nosso) ou campos vinculados e gravados (de fora). `conversation` só existe em `lead_form`. Sem
|
|
465
|
+
paginação de propósito; teto de 250 por origem — estourou, `truncated: true`. Só entram eventos
|
|
466
|
+
do Meta/webhook com status processado.
|
|
467
|
+
- **Detalhe** (`source` + `id` da mesma linha): nosso formulário devolve `answers` (`[{label,
|
|
468
|
+
value, at, download_url?, images?}]` — pergunta/resposta legíveis, na ordem; Escolha com opção
|
|
469
|
+
ilustrada traz `images`, e opção sem texto vem com `value` vazio; Agendamento mostra a DATA),
|
|
470
|
+
`written` (`[{scope: contact|conversation, label, value}]` — o que AQUELE preenchimento gravou) e
|
|
471
|
+
`utm`. Meta/webhook/fluxo devolvem SÓ `fields: [{label, value}]` — os campos que foram vinculados a
|
|
472
|
+
atributos daqui e realmente gravados, com o rótulo daqui; nunca o payload cru. `fields: []` = o
|
|
473
|
+
evento não gravou nada. Origem fora da lista ou registro de outro contato = 404.
|
|
474
|
+
- A lista **não traz o `lead_form_id`** da resposta: pra chegar em `lionchat_lead_forms_responses_show`
|
|
475
|
+
a partir dela, ache o formulário pelo `title` em `lionchat_lead_forms_list`.
|
|
402
476
|
|
|
403
477
|
## Conferência de iframe (`lionchat_lead_forms_check_embed`)
|
|
404
478
|
|
|
@@ -497,3 +571,11 @@ agendar (com `required: true` essa saída deixa de existir). O tipo de evento ve
|
|
|
497
571
|
- **Não há tool de rewind**: o "Voltar e corrigir" é da página pública (o lead corrige a própria
|
|
498
572
|
resposta; telefone corrigido reencaminha a mensagem pro número certo). Pela API de dashboard,
|
|
499
573
|
respostas são somente leitura.
|
|
574
|
+
- **`settings` no `update` também SUBSTITUI o objeto inteiro** — e sem `chat_avatar` o arquivo da
|
|
575
|
+
foto é apagado pela varredura. Leia com `show`, altere e mande completo.
|
|
576
|
+
- **Imagem que some do `form_data` é apagada de verdade** logo após o salvamento (só sobrevive se
|
|
577
|
+
ainda estiver na foto publicada ou tiver menos de 1 hora).
|
|
578
|
+
- **O simulador não roda consulta nem IA**: parou em `block.type: "wait"` = chegou num desses
|
|
579
|
+
blocos. Teste o trecho seguinte pela página pública.
|
|
580
|
+
- **`stats.views` é de vida inteira** (ignora `days`); `stats.milestones` não vê marco em `items`.
|
|
581
|
+
- **`name` é interno; `settings.public_title` é o que o lead vê** (vazio = cai no `name`).
|