@lionchat/mcp-server 1.20.0 → 1.22.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 +2 -0
- package/dist/docs/resources/agenda-multiplas-unidades.md +185 -0
- package/dist/docs/resources/assinatura-eletronica.md +4 -2
- package/dist/docs/resources/best-practices.md +45 -1
- package/dist/docs/resources/conversation-flows.md +3 -2
- package/dist/docs/resources/data-model.md +75 -2
- package/dist/docs/resources/flowbuilder-design-guide.md +47 -12
- package/dist/docs/resources/flowbuilder-patterns.md +9 -1
- package/dist/docs/resources/formularios-publicos.md +4 -2
- package/dist/docs/resources/glossary.md +28 -1
- package/dist/docs/resources/reports-guide.md +61 -5
- package/dist/docs/resources/troubleshooting.md +14 -0
- package/dist/resources.js +6 -0
- package/dist/resources.js.map +1 -1
- package/dist/tools.js +79 -9
- package/dist/tools.js.map +1 -1
- package/endpoints.json +1143 -53
- package/package.json +1 -1
|
@@ -422,6 +422,7 @@ Antes de escolher a ferramenta, cheque se a intenção bate com a coluna da dire
|
|
|
422
422
|
| **Migrar conversas** de uma caixa WhatsApp pra outra (ex.: QR Code → Oficial) | `lionchat_inboxes_inbox_migration_list` (prévia) → `_execute` → acompanhar status | Admin-only e IRREVERSÍVEL: sempre mostre os números da prévia e confirme (seção 7a). Grupos não vão pro canal oficial — ficam na caixa de origem |
|
|
423
423
|
| Qualquer coisa de Ligação WP (Wavoip) | **INTEGRAÇÃO REMOVIDA em 12/08/2026** — não há tool nem tela | A Wavoip saiu do produto: a aba "Ligação WP" **não existe mais**. NUNCA orientar o usuário a "Configurações da caixa → Ligação WP". Para voz em caixa QR Code, o caminho hoje é o **LionCalls**. As ligações antigas seguem no histórico (`provider: "wavoip"`); as gravações delas ficavam no servidor da Wavoip e vão sumir — ao tentar ouvir, o painel mostra "Esta gravação não está mais disponível", e isso é esperado, não é defeito. |
|
|
424
424
|
| Restringir agendas que a IA oferece | `lionchat_captain_assistants_update` (`config.booking_event_type_ids: [Int]`) | binding de agenda por cenário NÃO é settable via API |
|
|
425
|
+
| **Programa de sessões** de um contato ("em que sessão ele está", ajustar total, encerrar/reabrir) | `lionchat_agenda_treatments_list` (por `contact_id`) / `_show` / `_update` | NÃO existe create: o programa nasce junto do agendamento (na TELA chama-se "programa"; na API segue `treatment`). Marcar TODAS de uma vez: `plano_sugerido` + `marcar_todas` de um tipo com `sessions_enabled` (`tasks_create` modo booking, `treatment: {...}` por pessoa; "marcar próxima" = `agenda_treatment_id` + `treatment_session_number`). Faltar CONSOME a sessão. Ver `lionchat://docs/agenda-multiplas-unidades` §7 |
|
|
425
426
|
| Configurar o Copiloto (modelo/temperatura/prompt base) | `lionchat_copilot_settings_update` | admin-only; copiloto tem motor próprio (padrão temp 0.3) |
|
|
426
427
|
| **Resetar/testar do zero a IA** numa conversa | `lionchat_conversations_captain_history_reset` (POST, sem body) | Cria pílula de marco na conversa; a partir dali TODA leitura autônoma da IA (respostas, resumo, follow-up) considera só mensagens pós-marco. Não apaga nada; o atendente segue vendo tudo. Enterprise-only |
|
|
427
428
|
|
|
@@ -441,6 +442,7 @@ Quando precisar de detalhes profundos, leia um destes documents via `resources/r
|
|
|
441
442
|
- `lionchat://docs/api-conventions` — Auth, paginação, filtros, errors, rate limits
|
|
442
443
|
- `lionchat://docs/conversation-flows` — Ciclo de vida de uma conversa (criação, auto-assignment, IA, resolução)
|
|
443
444
|
- `lionchat://docs/kanban-deep-dive` — Estrutura completa do Kanban (Funnel/Stage/Item/pipeline)
|
|
445
|
+
- `lionchat://docs/agenda-multiplas-unidades` — **Agenda por unidade**: o que `agenda_id` nulo significa, tipos de compromisso da conta, Booking ligado a unidade, página pública (cancelar/remarcar/confirmar), Google **uma conexão por agenda** e **programa de sessões** (N sessões de um tipo; faltar consome; marcar próxima e marcar todas)
|
|
444
446
|
- `lionchat://docs/best-practices` — Como economizar tokens, evitar rate limit, ordem de operações
|
|
445
447
|
- `lionchat://docs/troubleshooting` — Códigos HTTP, erros comuns, debugging
|
|
446
448
|
- `lionchat://docs/flowbuilder-design-guide` — **OBRIGATÓRIO** antes de criar/editar fluxo: schema de nodes, handles expostos por tipo, layout/positioning, erros comuns, checklist
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Agenda — unidades, tipos de compromisso e Google por agenda
|
|
2
|
+
|
|
3
|
+
Guia da Agenda depois das frentes de 13/09 e 15/09/2026. Leia antes de mexer em agendas, tipos de
|
|
4
|
+
compromisso, Booking ligado a unidade, conexão Google ou programa de sessões.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Uma agenda é um LUGAR, nunca uma pessoa
|
|
9
|
+
|
|
10
|
+
Uma **agenda** é uma unidade: a matriz, uma filial, um consultório. Ela tem nome, fuso, cor, quem
|
|
11
|
+
enxerga e **conta Google próprios**. Não confunda com o calendário de uma pessoa — quem tem
|
|
12
|
+
calendário pessoal é o atendente, e ele aparece dentro da agenda.
|
|
13
|
+
|
|
14
|
+
**Toda conta tem UMA agenda.** Ter mais de uma exige o recurso `multiple_agendas` ligado na conta
|
|
15
|
+
(Super Admin), e o número de Extras Manuais define o teto — **zero ali significa ILIMITADO**, igual
|
|
16
|
+
a Voz, Coex e Contratos. Sem o recurso, `agendas_create` é recusado.
|
|
17
|
+
|
|
18
|
+
### `agenda_id` NULO significa "a agenda principal"
|
|
19
|
+
|
|
20
|
+
Essa é a regra mais importante deste documento, e vale em **quatro** lugares: `account_tasks`,
|
|
21
|
+
`booking_event_types`, `google_calendar_connections` e nos filtros.
|
|
22
|
+
|
|
23
|
+
Nulo **não** quer dizer "sem agenda": quer dizer "a principal". É o que todo registro anterior à
|
|
24
|
+
funcionalidade tem, e é por isso que ligar múltiplas agendas não exige retrocarga nenhuma — nada
|
|
25
|
+
precisa ser migrado, nada muda de lugar.
|
|
26
|
+
|
|
27
|
+
**Ao filtrar a agenda principal, inclua os nulos.** Filtrar `agenda_id = <id da principal>` sozinho
|
|
28
|
+
esconde todo o histórico da conta.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. Tipos de compromisso
|
|
33
|
+
|
|
34
|
+
São **7 de fábrica**, no código: `task`, `follow_up`, `call`, `meeting`, `video_call`, `deadline`,
|
|
35
|
+
`custom`. Não existem `consulta`, `retorno` nem `procedimento` — foram removidos em 13/09; mandar
|
|
36
|
+
um desses em `task_type` dá 422.
|
|
37
|
+
|
|
38
|
+
Além dos 7, a empresa **cria os dela** com o nome que usa ("Consulta", "Ondas de Choque", "Visita
|
|
39
|
+
ao imóvel"), em `account_task_types`.
|
|
40
|
+
|
|
41
|
+
- `key` é derivada do nome e **nunca muda**. É ela que fica gravada em `account_tasks.task_type`.
|
|
42
|
+
Renomear o tipo troca só o que aparece na tela; o histórico fica inteiro.
|
|
43
|
+
- `icon` é uma **lista fechada de 12**. Qualquer outro valor é recusado pelo servidor — nome fora da
|
|
44
|
+
lista produz uma classe que nunca foi gerada e o ícone **some da tela, sem erro**.
|
|
45
|
+
- Excluir um tipo **em uso** não apaga: **desativa** (`deactivated: true` na resposta). Os
|
|
46
|
+
compromissos antigos continuam com nome e ícone.
|
|
47
|
+
|
|
48
|
+
⚠️ **O tipo que a conta cria conta como agendamento para a IA.** É o que impede o agente de IA de
|
|
49
|
+
concluir que ainda não agendou e marcar o cliente **duas vezes**. Não existe interruptor para
|
|
50
|
+
desligar isso — a chave saiu do `permit` de propósito.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. Booking → agenda
|
|
55
|
+
|
|
56
|
+
Cada tipo de evento do Booking tem `agenda_id`: é a unidade em que o agendamento feito pelo cliente
|
|
57
|
+
vai nascer. Nulo = principal.
|
|
58
|
+
|
|
59
|
+
Sem isso (até 13/09) **todo agendamento feito por Booking caía na agenda principal**, mesmo quando o
|
|
60
|
+
profissional atendia na filial — e quem trocasse para a agenda da filial não via o agendamento.
|
|
61
|
+
|
|
62
|
+
`agenda_id` de outra conta é recusado pelo modelo. A FK garante que a agenda existe; ela não garante
|
|
63
|
+
que é sua.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 4. Página pública (cancelar, remarcar, confirmar presença)
|
|
68
|
+
|
|
69
|
+
O tipo de evento controla o que o cliente pode fazer:
|
|
70
|
+
|
|
71
|
+
| Campo | Padrão | O que faz |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `allow_cancel` | `true` | cliente pode cancelar |
|
|
74
|
+
| `allow_reschedule` | `true` | cliente pode remarcar |
|
|
75
|
+
| `ask_attendance_confirmation` | **`false`** | mostra "Sim, vou comparecer" |
|
|
76
|
+
| `cancel_min_notice_hours` | vazio | antecedência mínima para cancelar |
|
|
77
|
+
| `reschedule_min_notice_hours` | vazio | antecedência mínima para remarcar |
|
|
78
|
+
| `reschedule_max_advance_days` | vazio | até quando pode remarcar |
|
|
79
|
+
| `max_reschedules` | vazio | quantas vezes pode remarcar |
|
|
80
|
+
|
|
81
|
+
**Vazio ou zero = sem limite** (falha aberta, de propósito: uma regra em branco nunca deve trancar o
|
|
82
|
+
cliente para fora).
|
|
83
|
+
|
|
84
|
+
### Variáveis da mensagem de abertura
|
|
85
|
+
|
|
86
|
+
`{{nome}} {{titulo}} {{tipo}} {{agente}} {{data}} {{horario}} {{dia_semana}} {{duracao}}
|
|
87
|
+
{{telefone}} {{email}} {{link_cancelar}} {{link_remarcar}} {{link_confirmar}}`
|
|
88
|
+
|
|
89
|
+
As chaves são **em português nos dois idiomas** — elas são o identificador, não a tradução.
|
|
90
|
+
Variável desconhecida **sai crua** para o cliente ("Olá {{name}}, ...").
|
|
91
|
+
|
|
92
|
+
`{{link_confirmar}}` só é útil com `ask_attendance_confirmation` ligado.
|
|
93
|
+
|
|
94
|
+
### Presença é CARIMBO, não status
|
|
95
|
+
|
|
96
|
+
`attendance` (`attended` / `no_show`) é um campo separado de `status`. Marcar falta **não devolve o
|
|
97
|
+
horário** para a agenda — decisão do dono. Não use `attendance` para deduzir se o compromisso está
|
|
98
|
+
ativo; para isso existe `status`.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 5. Google Calendar é UMA CONEXÃO POR AGENDA
|
|
103
|
+
|
|
104
|
+
A regra já mudou duas vezes; hoje é: **uma conta Google por agenda**. Cada unidade liga a conta
|
|
105
|
+
Google dela — inclusive a **mesma pessoa** com contas Google diferentes, uma por unidade.
|
|
106
|
+
|
|
107
|
+
- `google_calendar_agendas` devolve uma linha por agenda com o estado de cada uma.
|
|
108
|
+
- `agendas_list` / `agendas_show` trazem `google: { connected, email, state }` em cada agenda — dá para saber qual conta Google está ligada a qual unidade sem chamar o Google (o estado sai do banco). [13/09]
|
|
109
|
+
- Agenda **sem** Google conectado simplesmente **não sincroniza** — ela não cai no Google da matriz.
|
|
110
|
+
O contrário mandaria o compromisso de uma unidade para o calendário de outra.
|
|
111
|
+
- Os sub-calendários compartilhados com agentes pertencem à conexão daquela unidade.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 6. Quem enxerga uma agenda
|
|
116
|
+
|
|
117
|
+
- Agenda **sem** pessoas e **sem** equipes: todo mundo da conta enxerga.
|
|
118
|
+
- Marcando pessoas e/ou equipes: só elas. Quem entrar na equipe ganha acesso sozinho.
|
|
119
|
+
- **Administrador enxerga todas**, sempre.
|
|
120
|
+
- `member_user_ids` e `member_team_ids` **substituem a lista inteira** (idioma do `InboxMember`).
|
|
121
|
+
|
|
122
|
+
A listagem de compromissos já vem recortada pelas agendas que o usuário enxerga, **mesmo sem mandar
|
|
123
|
+
`agenda_id`**. Ler ou editar compromisso de uma agenda que a pessoa não enxerga devolve 404.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 7. Programa de sessões (15/09)
|
|
128
|
+
|
|
129
|
+
Um **programa** é um pacote de N sessões do mesmo tipo de evento para um contato ("10 sessões de ondas
|
|
130
|
+
de choque a cada 30 dias", "8 encontros de mentoria", "4 sessões de onboarding").
|
|
131
|
+
|
|
132
|
+
⚠️ **Na TELA o nome é "programa"** (bloco "Programas", botão "Encerrar programa") — decisão do dono em
|
|
133
|
+
15/09, porque nem sempre é saúde. No CÓDIGO e na API tudo continua `treatment`/`agenda_treatments`: as
|
|
134
|
+
ferramentas, os campos e a tabela não mudaram. Ao falar com o cliente, diga **programa**; ao chamar a
|
|
135
|
+
API, use os nomes de sempre. Cada encontro continua se chamando **sessão** nos dois lados. Só a primeira sessão nasce marcada; as outras ficam "a marcar" e a
|
|
136
|
+
ficha do contato mostra em que pé está: quantas foram feitas, quantos por cento, quando é a próxima
|
|
137
|
+
e se está atrasada.
|
|
138
|
+
|
|
139
|
+
- **Liga-se no tipo de evento**: `sessions_enabled` + `sessions_default_count` (2 a 60) +
|
|
140
|
+
`sessions_interval_value`/`sessions_interval_unit` (`days`/`weeks`/`months`). Tipo sem isso não
|
|
141
|
+
cria programa nunca.
|
|
142
|
+
- **O programa NASCE junto do agendamento** — não existe `create`. Todo agendamento de um tipo com
|
|
143
|
+
sessões (painel, link público ou IA) cria o programa do contato ou **engrossa** o que já está em
|
|
144
|
+
andamento como a próxima sessão livre. Um programa ativo por contato + tipo.
|
|
145
|
+
- **Plano por pessoa**: no `tasks_create` em modo booking, `treatment: {sessions, interval_value,
|
|
146
|
+
interval_unit}` vence o padrão do tipo. É validado ANTES da reserva (422 sem agendar nada).
|
|
147
|
+
- **"Marcar próxima"**: `tasks_create` com `agenda_treatment_id` + `treatment_session_number`. Sessão já
|
|
148
|
+
marcada dá 422; programa de outra ficha dá 422.
|
|
149
|
+
- **Sessão k = compromisso VIVO (não cancelado) de número k.** Cancelar devolve a sessão; remarcar
|
|
150
|
+
mantém o número; **faltar consome** (decisão do dono — a falta gasta a sessão). "Usadas" = compareceu
|
|
151
|
+
+ faltou. Nada disso é cache: o resumo é derivado das tarefas na leitura.
|
|
152
|
+
- **Cheio** = todas as N marcadas e nenhuma usada ainda. Agendamento novo pelo painel/IA num
|
|
153
|
+
programa cheio vira **sessão extra** (N+1, até 60); pelo link público não vira nada (o
|
|
154
|
+
agendamento nasce solto, sem programa).
|
|
155
|
+
- **Concluído** = usadas ≥ N. O `status` do programa só vira `completed` na próxima reserva daquele
|
|
156
|
+
tipo (aí abre um ciclo novo); `closed` é encerramento pela equipe (`close_reason: manual`) ou pela
|
|
157
|
+
junção de contatos (`mesclagem` — o programa do perdedor que colide com um do vencedor).
|
|
158
|
+
- **Atrasado** = em andamento, próxima sessão com data prevista já passada e nenhuma sessão viva com
|
|
159
|
+
data futura. A previsão é a última data conhecida + intervalo × distância.
|
|
160
|
+
- **Marcar TODAS as sessões que faltam** (15/09): `lionchat_agenda_treatments_plano_sugerido` devolve o dia e a
|
|
161
|
+
hora sugeridos por sessão (data prevista; sem vaga, anda para a frente e marca `deslocada`), e
|
|
162
|
+
`lionchat_agenda_treatments_marcar_todas` marca o que for confirmado. **Não é transação única**: sucesso parcial é
|
|
163
|
+
200 com `falhas` nomeadas, e o que entrou na agenda fica. Com confirmação ligada no tipo, o cliente recebe UMA
|
|
164
|
+
mensagem por sessão — avise isso antes de marcar em lote.
|
|
165
|
+
- Ferramentas: `lionchat_agenda_treatments_list` (por `contact_id`, resumo pronto por programa),
|
|
166
|
+
`_show`, `_update` (`planned_sessions` nunca abaixo da maior sessão já marcada; `status`
|
|
167
|
+
`closed`/`active`). O compromisso devolve `tratamento: {id, sessao, total, titulo}` quando pertence a
|
|
168
|
+
um. Compromisso de agenda que o usuário não enxerga aparece na sessão como `oculta: true`.
|
|
169
|
+
- O relatório de Agendamentos tem o bloco `treatments` (vale o período escolhido: programas, sessões do período e quem parou no meio — ver `reports-guide`).
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 8. Erros comuns
|
|
174
|
+
|
|
175
|
+
| Sintoma | Causa |
|
|
176
|
+
|---|---|
|
|
177
|
+
| 422 'Ha sessoes marcadas alem dessa quantidade' ao reduzir `planned_sessions` | já existe sessão marcada acima do novo total; cancele-a antes |
|
|
178
|
+
| 422 'Ja existe um tratamento em andamento deste tipo para este contato' (texto do servidor) ao reabrir | um programa ativo por contato + tipo; encerre o outro primeiro |
|
|
179
|
+
| Agendamento novo não entrou no programa | tipo sem `sessions_enabled`, ou programa CHEIO agendado pelo link público (sessão extra só pelo painel/IA) |
|
|
180
|
+
| 422 ao criar compromisso com `task_type: "consulta"` | os tipos clínicos saíram; use a `key` que `account_task_types_list` devolve |
|
|
181
|
+
| Ícone do tipo não aparece | valor fora da lista fechada de 12 |
|
|
182
|
+
| Agendamento do Booking caiu na unidade errada | `agenda_id` do tipo de evento não foi definido (nulo = principal) |
|
|
183
|
+
| Compromisso da filial no Google da matriz | não acontece mais; se aparecer, a conexão daquela agenda está com `agenda_id` errado |
|
|
184
|
+
| `agendas_create` recusado | recurso `multiple_agendas` desligado ou teto de Extras Manuais atingido |
|
|
185
|
+
| Histórico sumiu ao filtrar a principal | faltou incluir os compromissos com `agenda_id` **nulo** |
|
|
@@ -50,8 +50,10 @@ Participante: `pending` → `viewed` → `verified` (confirmou o código) → `s
|
|
|
50
50
|
leitura de um modelo continuam abertas a qualquer membro. Cancelar contrato e apagar modelo: só administrador.
|
|
51
51
|
- **Trava sagrada**: campo do modelo sem valor na ficha bloqueia o envio. Preencher a ficha primeiro.
|
|
52
52
|
- **Validade** (`roles_layout.validity_days`, padrão 7) conta do ENVIO; `{{document.deadline}}` imprime a data no
|
|
53
|
-
texto; passou = `expired` (cron de hora em hora). **Lembrete
|
|
54
|
-
|
|
53
|
+
texto; passou = `expired` (cron de hora em hora). **Lembrete NÃO é mais função do sistema** (14/09): o campo
|
|
54
|
+
`reminder_days` e a tarefa diária das 10h foram REMOVIDOS. Quem lembra é um fluxo do cliente, com o texto e a
|
|
55
|
+
caixa dele — gatilho "Link de assinatura enviado para uma pessoa" (`signature_delivered`, um disparo por
|
|
56
|
+
pessoa), bloco Esperar em dias e a condição "Situação do contrato". Nunca sugerir `reminder_days`.
|
|
55
57
|
- **Conversor de Word** é um serviço separado (docx-converter). Fora do ar = `422 conversor_indisponivel` na prévia e no envio de modelo Word — é o serviço, não o arquivo; repetir em instantes. Modelos `text` e `file` não passam por ele.
|
|
56
58
|
- **Fila única de conversão do Word**: uma conversão por vez em toda a instalação (todas as contas); pela tela espera-se até 10 s, automação/macro/fluxo/formulário até 2 s.
|
|
57
59
|
Passou disso o envio/prévia volta `422 fila_cheia` — é só repetir em instantes (o modelo está bom).
|
|
@@ -390,6 +390,42 @@ integração conectada na conta (senão é pulado em silêncio — confira as li
|
|
|
390
390
|
(o que iniciou o fluxo ou o mais recente da conversa); sem card, o registro NÃO aparece nas telas de
|
|
391
391
|
eventos do Funil (só no histórico do fluxo). Formato completo no `flowbuilder-design-guide`.
|
|
392
392
|
|
|
393
|
+
### Meta CAPI — lead que chegou por ANÚNCIO DE WHATSAPP (2026-09-10)
|
|
394
|
+
|
|
395
|
+
Quem chegou pelo botão de WhatsApp de um anúncio (a conversa carrega `ctwa_clid` e a caixa é a
|
|
396
|
+
oficial, com WABA) sai pra Meta como evento de MENSAGEM (`action_source: business_messaging`) — e a
|
|
397
|
+
Meta só aceita isso num **dataset vinculado à WABA**, nunca no pixel do site (recusa 2804132). Até
|
|
398
|
+
10/09 100% desses eventos caíam no plano B (saíam como site) sem ninguém ver. Regras pro MCP:
|
|
399
|
+
|
|
400
|
+
- **Vincular a caixa primeiro:** normalmente NÃO é preciso — o vínculo é automático (na criação da caixa
|
|
401
|
+
e por varredura a cada 10 min). Se `capi_dataset_id` estiver vazio, `lionchat_inboxes_capi_dataset_link`
|
|
402
|
+
(POST `/inboxes/{id}/capi_dataset`) força na hora.
|
|
403
|
+
Idempotente, vale para todos os números da mesma WABA, usa a chave da PRÓPRIA caixa (a do pixel não
|
|
404
|
+
serve). O id fica em `provider_config.capi_dataset_id` (visível em `lionchat_inboxes_show` para admin).
|
|
405
|
+
Sem o vínculo os eventos de anúncio saem como site com `fallback_reason: lionchat:dataset_nao_configurado`.
|
|
406
|
+
- **Nome do evento em mensagem = SÓ 14:** `LeadSubmitted`, `QualifiedLead`, `ViewContent`, `AddToCart`, `InitiateCheckout`, `Purchase`, `OrderCreated`, `OrderShipped`, `OrderDelivered`, `OrderCanceled`, `OrderReturned`, `CartAbandoned`, `RatingProvided`, `ReviewProvided`.
|
|
407
|
+
O evento de SITE continua com o nome que o cliente quiser. A escolha de WhatsApp mora em
|
|
408
|
+
`messaging_name` (etapa e `won` do `meta_events_config`, tool `lionchat_funnels_meta_events_config`),
|
|
409
|
+
em `messaging_event_names.meta` (bloco `send_conversion` do fluxo) e em `messaging_event_name`
|
|
410
|
+
(disparo manual `lionchat_kanban_items_meta_capi_fire`). Tri-estado: chave AUSENTE = automático
|
|
411
|
+
(Lead/Contact → LeadSubmitted, Purchase → Purchase, InitiateCheckout → InitiateCheckout; sem sugestão
|
|
412
|
+
o lead de anúncio sai como site com motivo), `''` = não enviar como WhatsApp, um dos 14 = escolha.
|
|
413
|
+
Nome fora dos 14 é descartado no funil e dá 422 no disparo manual. `lost` (Perdido) é sempre site —
|
|
414
|
+
não existe evento de "perdido" em mensagens. O nome interno do cliente vai em `custom_data.internal_event`.
|
|
415
|
+
- **Diagnóstico "por que a venda não contou como conversão de WhatsApp?":**
|
|
416
|
+
`lionchat_funnels_meta_capi_events_list` com `degraded=true` e ler `fallback_reason` + `action_source`.
|
|
417
|
+
Motivos nossos: `lionchat:dataset_nao_configurado` (falta o vínculo), `lionchat:caixa_token_recusado`
|
|
418
|
+
(a Meta recusou a chave da caixa — reconectar a caixa), `lionchat:caixa_nao_encontrada`,
|
|
419
|
+
`lionchat:caixa_sem_token`, `lionchat:dataset_id_invalido`, `lionchat:desligado_pelo_cliente`,
|
|
420
|
+
`lionchat:nome_sem_equivalente_whatsapp`; da Meta: `"<subcode>: <mensagem>"` (ex.: 2804087 = clique
|
|
421
|
+
do anúncio inválido/expirado).
|
|
422
|
+
- Recusa ao vincular (422 com `subcode`, ex.: 2804116 / "whatsapp_business_manage_events"): a chave da
|
|
423
|
+
caixa não tem a permissão — reconectar a caixa pelo Cadastro Incorporado da Meta; nunca pedir o token
|
|
424
|
+
do pixel para isso. Um 401/403 da caixa NUNCA desliga o pixel da conta (site, formulário e agenda
|
|
425
|
+
continuam) — o evento de anúncio apenas sai como site com o motivo.
|
|
426
|
+
- NUNCA prometer que eventos antigos voltam: o plano B marca como enviado (não há retry) e a Meta só
|
|
427
|
+
aceita evento com até 7 dias.
|
|
428
|
+
|
|
393
429
|
### Meta Lead — token de anúncios de OUTRO app (2026-09-01)
|
|
394
430
|
|
|
395
431
|
`lionchat_meta_lead_validate_token` pode voltar `app_verified: false` com `expires_at` nulo: é token
|
|
@@ -693,7 +729,7 @@ automática. Ver a seção de variáveis de conta acima.
|
|
|
693
729
|
|
|
694
730
|
- **Tipo de atributo `time` (Hora 24h):** ao criar custom attribute com `attribute_display_type: "time"` (ou o numérico `9`), o valor canônico é `"HH:MM"` 24h e o fuso mora na definição (`attribute_timezone`, IANA, default `America/Sao_Paulo`). Uso principal: alimentar o campo Horário do node `wait` (modo date, `waitTimeMode: "variable"`).
|
|
695
731
|
- **Tipo de atributo `datetime` (Data e Hora, novo 2026-07-18):** `attribute_display_type: "datetime"` (ou numérico `10`), com `attribute_timezone` (default `America/Sao_Paulo`). Guarda data+hora juntas em ISO com offset (`"2026-07-18T14:55:00-03:00"`); exibe `"DD/MM/AAAA - HH:MM"`. Um porteiro no backend aceita e converte vários formatos de entrada (ISO/UTC/BR/AM-PM/unix). **ATENÇÃO ao montar flows:** o tipo `datetime` NÃO é selecionável no Gatilho de Data nem nos campos Data/Horário do node `wait` — esses continuam exigindo `date`(5) pra data e `time`(9) pra hora, SEPARADOS. O `datetime` é, por ora, tipo de armazenamento/exibição (sidebar + mensagens humanas), não fonte de entrada de fluxo. Em condição, atributo `datetime` é tratado como string (compara o ISO cru).
|
|
696
|
-
- **Timeout de flow `ai_tool` com node `api`:** sobe sozinho de 20s pro teto de 45s quando ainda está no padrão
|
|
732
|
+
- **Timeout de flow `ai_tool` com node `api`:** sobe sozinho de 20s pro teto de 45s quando ainda está no padrão e, desde 10/09/2026, DESCE de volta pro teto quando o último node `api` é apagado (antes o valor gravado ficava acima do teto e o flow não salvava mais — o 422 vinha como "configuração incompleta" sem nenhum bloco vermelho). Não precisa configurar nada; só evite setar `execution_timeout_ms` custom se quiser manter o auto-ajuste. Valor acima do teto enviado por API/MCP continua recusado.
|
|
697
733
|
- **Teste do node `api`:** o resultado do último teste fica salvo no servidor (pin, TTL 30 dias) — sobrevive a trocar de navegador/máquina. Isso é recurso do editor humano; não há tool MCP pra isso.
|
|
698
734
|
|
|
699
735
|
## Status codes a respeitar
|
|
@@ -888,3 +924,11 @@ administrador."* — a recusa é `before_action`, então NADA do resto do update
|
|
|
888
924
|
Ao atualizar o próprio usuário: mande times e caixas à vontade; **não mande `role` nem
|
|
889
925
|
`custom_role_id`** — se mandar, a chamada inteira falha e as outras alterações se perdem também.
|
|
890
926
|
Para trocar o cargo de alguém, use a conta de OUTRO administrador.
|
|
927
|
+
|
|
928
|
+
## Cargo personalizado: descrição CURTA (16/09/2026)
|
|
929
|
+
|
|
930
|
+
Em `custom_roles_create` / `custom_roles_update`, a `description` é **uma frase de até 60 caracteres**
|
|
931
|
+
(ex.: *"Tudo, menos conversas das caixas em que não foi adicionado."*). **Não liste as permissões
|
|
932
|
+
na descrição** — a tela já as mostra na coluna "Permissões". Uma descrição de 180 caracteres
|
|
933
|
+
escondeu o cargo recém-criado na lista de Funções personalizadas da conta 1 (a tela foi corrigida
|
|
934
|
+
para cortar o texto com reticências, mas a descrição longa continua ilegível ali).
|
|
@@ -439,7 +439,7 @@ POST .../conversations/{id}/toggle_status
|
|
|
439
439
|
|
|
440
440
|
| Ação | Tool / endpoint | Efeito |
|
|
441
441
|
|---|---|---|
|
|
442
|
-
| Marcar como **lida** | `update_last_seen` (POST `.../update_last_seen`) | Atualiza `agent_last_seen_at
|
|
442
|
+
| Marcar como **lida** | `update_last_seen` (POST `.../update_last_seen`) | Atualiza `agent_last_seen_at` e, em canal WhatsApp (QR Code ou oficial), dispara o check azul — **só quando há mensagem do cliente ainda não lida** (12/09); sem entrante nova a chamada cai na trava de 1 h e devolve o carimbo já gravado |
|
|
443
443
|
| Marcar como **não-lida** | `conversations_unread` (POST `.../unread`) | Recua o `last_seen` pra antes da última mensagem do cliente — conversa volta a aparecer como não-lida |
|
|
444
444
|
|
|
445
445
|
Ambas são **ações de escrita** (POST). `unread` é o oposto de `update_last_seen`, não uma consulta.
|
|
@@ -704,6 +704,7 @@ Todos os formatos abaixo foram conferidos linha a linha em `app/services/action_
|
|
|
704
704
|
| `assign_agent` | `[70]` — o agente precisa ser membro da caixa. `["nil"]` desatribui |
|
|
705
705
|
| `assign_team` | `[3]`. `["nil"]` desatribui |
|
|
706
706
|
| `assign_captain_assistant` | `[17]` ou `[{"assistant_id": 17, "proactive": true}]` — `proactive` false = a IA assume mas nao fala na hora |
|
|
707
|
+
| `deactivate_captain` | `[]` — desliga o AI Agente da conversa (novo 10/09/2026; a macro também tem) |
|
|
707
708
|
| `send_email_to_team` | `[{"team_ids": [1], "message": "texto"}]` |
|
|
708
709
|
| `send_webhook_event` | `["https://..."]` |
|
|
709
710
|
| `send_attachment` | `[blob_ids]` — so funciona se a regra ja tiver arquivo anexado; nao da pra montar so por API |
|
|
@@ -713,7 +714,7 @@ Todos os formatos abaixo foram conferidos linha a linha em `app/services/action_
|
|
|
713
714
|
| `mute_conversation` | `[]` |
|
|
714
715
|
| `snooze_conversation` | `[]` |
|
|
715
716
|
| `resolve_conversation` / `open_conversation` / `pending_conversation` | `[]` |
|
|
716
|
-
| `update_contact_attribute` / `update_conversation_attribute` | `[{"attribute_key": "chave", "value": "texto ou {{contact.phone_number}}"}]` |
|
|
717
|
+
| `update_contact_attribute` / `update_conversation_attribute` | `[{"attribute_key": "chave", "value": "texto ou {{contact.phone_number}}"}]` — chave PROTEGIDA é recusada na execução e fica só no log (10/09/2026): prefixos `waha_whatsapp_`, `whatsapp_`, `origin_`, `ctwa_`, `meta_lead_`, `lt_` e atributos de sistema como `utm_*`/`gclid`. Vale pra macro e automação |
|
|
717
718
|
| `create_kanban_item` | `[{"funnel_id": 31, "funnel_stage": "prospeccao", "allow_duplicates": false}]` |
|
|
718
719
|
| `move_kanban_item_to_stage` | `[{"funnel_id": 31, "funnel_stage": "qualificacao"}]` |
|
|
719
720
|
| `assign_agent_to_kanban_item` | `[{"funnel_id": 31, "agent_id": 70, "mode": "add"}]` — `mode` aceita `add` (padrao) ou `remove_all` |
|
|
@@ -150,8 +150,28 @@ Company
|
|
|
150
150
|
├── account_id (FK)
|
|
151
151
|
├── name
|
|
152
152
|
└── domain
|
|
153
|
+
|
|
154
|
+
AttributeChange (histórico de alterações de atributo — 2026-09-11)
|
|
155
|
+
├── id (PK)
|
|
156
|
+
├── account_id (FK)
|
|
157
|
+
├── contact_id (FK, opcional) / conversation_id (FK, opcional)
|
|
158
|
+
├── target_type + target_id ('Contact' | 'Conversation')
|
|
159
|
+
├── field (name | email | phone_number | date_of_birth | gender | cadastral.* | address.* | custom.<chave>)
|
|
160
|
+
├── old_value / new_value (jsonb)
|
|
161
|
+
├── actor_type + actor_id + actor_label (User | Captain::Assistant | Flow | AutomationRule | LeadForm | Integration | System)
|
|
162
|
+
├── source (panel | api | mcp:npm | mcp:remote | flow | automation | ai | form | integration:<nome> | merge | system)
|
|
163
|
+
├── merged_from_contact_id (linha que veio de ficha mesclada)
|
|
164
|
+
└── created_at
|
|
153
165
|
```
|
|
154
166
|
|
|
167
|
+
**Histórico de alterações (`attribute_changes`, 2026-09-11):** uma linha por campo que mudou (contato ou
|
|
168
|
+
conversa), com valor antes/depois, quem mudou e por onde. Mudança feita pelo MCP sai com `source`
|
|
169
|
+
`mcp:npm`/`mcp:remote` e o usuário do token como ator. Criação não gera linha; importação de planilha
|
|
170
|
+
não gera; chaves protegidas (rastreio `utm_*`/`gclid`, `waha_*`, `eclinica_*`, e toda definição
|
|
171
|
+
`system: true`) ficam fora. Leitura: `GET contacts/{id}/attribute_changes` e
|
|
172
|
+
`GET conversations/{id}/attribute_changes` (50/página; filtros `field`, `source`); telefone, e-mail,
|
|
173
|
+
nascimento e documentos vêm mascarados (`masked: true`) para quem não é administrador. Retenção 180 dias.
|
|
174
|
+
|
|
155
175
|
**`additional_attributes` vs `custom_attributes` no Contato:**
|
|
156
176
|
- `additional_attributes` (jsonb): campos do sistema porém **EDITÁVEIS via API** (`permitted_params` permite `additional_attributes: {}`). Guarda chaves padrão como `city`, `company`, `country_code` — que são as chaves filtráveis em `lib/filters/filter_keys.yml` (tipo `additional_attributes`). NÃO é não-editável.
|
|
157
177
|
- `custom_attributes` (jsonb): dado de negócio livre, definido pelo cliente via Atributos Customizados.
|
|
@@ -196,12 +216,23 @@ Funnel
|
|
|
196
216
|
├── stages (jsonb: hash com slug_etapa => { name, color, position, description, checklist_templates })
|
|
197
217
|
├── settings (jsonb: { agents: [], goals: [], automations: [{trigger_type, action, action_config, enabled}] })
|
|
198
218
|
├── global_custom_attributes (jsonb array)
|
|
199
|
-
├── meta_events_config (jsonb: won/lost/stages → Meta Pixel/CAPI events
|
|
219
|
+
├── meta_events_config (jsonb: won/lost/stages → Meta Pixel/CAPI events; cada bloco: enabled, name,
|
|
220
|
+
│ is_standard, value_strategy, value_fixed, currency, messaging_name — este último é o evento de
|
|
221
|
+
│ ANÚNCIO DE WHATSAPP, tri-estado: ausente = automático, '' = não enviar, um dos 14 nomes da Meta;
|
|
222
|
+
│ só em stages e won, nunca em lost)
|
|
200
223
|
├── archived (bool)
|
|
201
224
|
├── active (bool)
|
|
202
225
|
├── position (int, ordem entre funis)
|
|
203
226
|
└── created_at
|
|
204
227
|
|
|
228
|
+
MetaCapiEvent (meta_capi_events — cada envio de conversão pro Meta)
|
|
229
|
+
├── event_name (nome INTERNO, o que o cliente escolheu), event_id (idempotência), status (pending/sent/failed/duplicate)
|
|
230
|
+
├── trigger_type, kanban_item_id, contact_id, conversation_id, funnel_id, meta_pixel_integration_id
|
|
231
|
+
├── inbox_id (2026-09-10: a caixa oficial de onde saiu a WABA — o envio de anúncio de WhatsApp usa o dataset e a chave DESSA caixa)
|
|
232
|
+
├── fallback_reason (2026-09-10: preenchido quando um evento de anúncio de WhatsApp saiu como SITE — plano B; NULL = não degradou)
|
|
233
|
+
├── payload_sent (jsonb: data[0].action_source = website | business_messaging; data[0].event_name = o nome que FOI pra Meta)
|
|
234
|
+
└── response_body (jsonb: resposta da Meta; `fallback_from` = a recusa original quando houve plano B)
|
|
235
|
+
|
|
205
236
|
KanbanItem
|
|
206
237
|
├── id (PK)
|
|
207
238
|
├── account_id (FK)
|
|
@@ -341,6 +372,17 @@ Macro
|
|
|
341
372
|
└── visibility (personal/global)
|
|
342
373
|
```
|
|
343
374
|
|
|
375
|
+
**Macro — ações e freios (10/09/2026).** `actions` aceita 40 nomes: todos os das automações (mesmos
|
|
376
|
+
`action_params`, ver a tabela em conversation-flows.md) mais os que só a macro tem — `mark_unread` (`[]`),
|
|
377
|
+
`add_contact_label`/`remove_contact_label` (`[titulos]`, etiqueta do CONTATO), `distribute_agents`
|
|
378
|
+
(`[agent_ids]`, rodízio de verdade), `update_card_attribute` (`[{funnel_id, attribute_key, value}]`),
|
|
379
|
+
`add_card_checklist` (`[{funnel_id, checklist_template_ids}]`), `add_card_offer` (`[{funnel_id, offer_ids}]`)
|
|
380
|
+
e `send_conversion` (`[{destinations, event_names, value}]` — só administrador ou cargo com
|
|
381
|
+
`marketing_integrations_manage` consegue SALVAR a macro; executar segue livre; teto de 50 conversões por
|
|
382
|
+
execução). Atributo protegido em `update_*_attribute` é recusado na execução (só no log). Executar
|
|
383
|
+
(`lionchat_macros_execute`): até 100 conversas por chamada, a mesma conversa em 5 s é pulada em silêncio,
|
|
384
|
+
30 execuções por minuto por usuário (429). A mensagem de `send_canned_response` sai assinada por quem executou.
|
|
385
|
+
|
|
344
386
|
## Agenda / Tarefas / Booking
|
|
345
387
|
|
|
346
388
|
```
|
|
@@ -362,8 +404,25 @@ AccountTask (agenda interna)
|
|
|
362
404
|
├── meeting_url (string, sala de reunião call/meeting)
|
|
363
405
|
├── guest_emails (jsonb array, convidados extras — máx 20)
|
|
364
406
|
├── conversation_id / contact_id / linked_kanban_item_id (FK opcionais)
|
|
407
|
+
├── booked_by (polimórfico User/Contact/Captain::Assistant) / booked_by_name / booking_source
|
|
408
|
+
│ (painel|link|ia|sistema|fluxo) — QUEM agendou e por onde (14/09)
|
|
409
|
+
├── attendance (enum attended=0 / no_show=1, NULO = nada registrado — CARIMBO, não muda status)
|
|
410
|
+
│ / attendance_by (polimórfico) / attendance_source — quem marcou Compareceu/Faltou (14/09)
|
|
411
|
+
├── agenda_treatment_id (FK → AgendaTreatment, nullify) / treatment_session_number (1..N) — a sessão
|
|
412
|
+
│ que este compromisso é; índice único parcial por tratamento+número entre os NÃO cancelados (15/09)
|
|
365
413
|
└── assignees (has_many → User, via account_task_assignments)
|
|
366
414
|
|
|
415
|
+
AgendaTreatment (tratamento em sessões — tools `agenda_treatments_*`; tabela agenda_treatments; 15/09)
|
|
416
|
+
├── id (PK) / account_id (FK) / contact_id (FK) / booking_event_type_id (FK, nullify)
|
|
417
|
+
├── event_type_title (cópia do nome do tipo — sobrevive à exclusão do tipo)
|
|
418
|
+
├── planned_sessions (2..60) / interval_value (1..365) / interval_unit (days|weeks|months)
|
|
419
|
+
├── status (active|completed|closed) / close_reason (manual|mesclagem) / closed_at
|
|
420
|
+
├── source (painel|link|ia|sistema|fluxo) / created_by_id (FK → User, só quando foi gente)
|
|
421
|
+
├── índice único parcial (account_id, contact_id, booking_event_type_id) WHERE status='active'
|
|
422
|
+
│ — UM tratamento ativo por contato + tipo
|
|
423
|
+
└── account_tasks (has_many, nullify) — sessão k = tarefa VIVA de número k; usadas/percentual/atrasado/
|
|
424
|
+
concluído são DERIVADOS na leitura (nenhuma coluna de cache)
|
|
425
|
+
|
|
367
426
|
BookingEventType (template de agendamento — tools `booking_event_types_*`; colunas de
|
|
368
427
|
db/schema.rb, tabela booking_event_types)
|
|
369
428
|
├── id (PK) / account_id (FK) / user_id (FK → agente padrão)
|
|
@@ -375,6 +434,8 @@ db/schema.rb, tabela booking_event_types)
|
|
|
375
434
|
├── color (hex `#RRGGBB`, opcional — cor dos compromissos desse tipo no calendário; vazio = cor do
|
|
376
435
|
│ agente; 2026-08-19)
|
|
377
436
|
├── active / ask_email / ask_description
|
|
437
|
+
├── sessions_enabled (bool) / sessions_default_count (2..60) / sessions_interval_value (1..365) /
|
|
438
|
+
│ sessions_interval_unit (days|weeks|months) — tratamento em sessões: padrão do tipo (15/09)
|
|
378
439
|
├── confirmation_* (mensagem de confirmação própria: enabled, inbox_id, channel_type,
|
|
379
440
|
│ template_name, blocks). Variáveis nos textos de confirmação E de lembrete (interpolador único):
|
|
380
441
|
│ {{nome}} {{email}} {{telefone}} {{data}} {{horario}} {{dia_semana}} (NOVO 24/08 — dia por
|
|
@@ -436,6 +497,11 @@ daquele tipo no calendário; vazio = cor do agente. A tarefa devolve `booking_co
|
|
|
436
497
|
`cliente_alteracao_pagamento`, `odontograma_finalizado`, `inclusao_procedimento`
|
|
437
498
|
(capturados ao vivo, não documentados pelo e-Clínica).
|
|
438
499
|
|
|
500
|
+
O recurso `eclinica_integration` da conta passou a ser ligado e desligado pelo PRÓPRIO cadastro da
|
|
501
|
+
integração (10/09/2026, `auto_managed`): sumiu do Super Admin e não há passo manual — os blocos de
|
|
502
|
+
relatório e as tools da e-Clínica valem assim que a integração existe. Contas que já tinham integração
|
|
503
|
+
receberam o recurso no deploy (migração).
|
|
504
|
+
|
|
439
505
|
Os 3 últimos são do perfil ODONTOLÓGICO: `agendamento_aguardando` = a recepção marcou a
|
|
440
506
|
chegada do paciente na clínica (grava a hora da chegada e `eclinica_status_agendamento = aguardando`);
|
|
441
507
|
`odontograma_aprovado` = plano de tratamento aprovado (grava o cabeçalho do odontograma);
|
|
@@ -468,6 +534,8 @@ Atributos de sistema no CONTATO (prefixo `eclinica_`, protegidos, usáveis como
|
|
|
468
534
|
| `eclinica_data_consulta` | date | Data da consulta, ISO |
|
|
469
535
|
| `eclinica_hora_consulta` | **time** | Hora da consulta 24h `"HH:MM"` (novo 2026-07-06) |
|
|
470
536
|
| `eclinica_hora_final` | **time** | Hora final da consulta (novo 2026-07-27) |
|
|
537
|
+
| `eclinica_link_confirmacao` | **link** | Link ecli.co para o paciente confirmar/cancelar a ÚLTIMA consulta (novo 2026-09-08; só existe quando a unidade aprendeu o padrão na aba Unidades). No LEMBRETE use a variável `{{link_confirmacao}}` do fluxo (ou `{{agendamento.link_confirmacao}}` na automação): é da consulta certa — a ficha aponta para a última, e quem tem duas marcadas receberia o link errado |
|
|
538
|
+
| `eclinica_paciente_nome` | text | Nome do PACIENTE como está no cadastro da e-Clínica (novo 2026-09-09). O nome do contato pode ser o do WhatsApp (mãe que agenda o filho pelo celular dela) — por isso NUNCA use `{{contact.name}}` nas mensagens da clínica. Nos fluxos por evento: `{{ contact.custom_attribute.eclinica_paciente_nome \| default: contact.name \| primeiro_nome }}`; no LEMBRETE use a variável `{{ paciente_nome \| default: contact.name \| primeiro_nome }}` (por consulta) ou `{{agendamento.paciente_nome}}` na automação. `primeiro_nome` = primeira palavra com inicial maiúscula. |
|
|
471
539
|
| `eclinica_status_agendamento` | text | `agendado` / `aguardando` (paciente chegou) / `no_show` / `atendido` / `desmarcado`. Vem do TIPO do evento, nunca da letra da situação. **Estado terminal do MESMO agendamento não volta pra `agendado`** (2026-08-20): a e-Clínica manda `agendamento_alterado` a cada edição da consulta, inclusive depois da chegada/atendimento, e isso NÃO reabre o status. Consulta com `eclinica_idagenda` DIFERENTE nasce `agendado` |
|
|
472
540
|
| `eclinica_situacao` | text | Situação do agendamento no painel, **POR EXTENSO** (2026-08-21, legenda oficial da e-Clínica): AGUARDANDO, NA CADEIRA, PASSAR FINANCEIRO, AGENDAR RETORNO, ATENDIDO, CONFIRMADO, CONFIRMADO PELO LINK, CONFIRMADO PELA API, FALTA, DESMARCADO, CANCELADO PELO LINK, CANCELADO PELA API. Antes guardava a letra crua (`A`, `C`…). Código nunca visto aparece como veio |
|
|
473
541
|
| `eclinica_compromisso` | text | Tipo da consulta — TEXTO LIVRE da recepção (ex: Consulta, Retorno) |
|
|
@@ -590,6 +658,11 @@ MetaLeadIntegration (Facebook Lead Ads)
|
|
|
590
658
|
├── facebook_page_id (FK polimórfico)
|
|
591
659
|
├── status (active/token_expired/paused)
|
|
592
660
|
└── meta_lead_forms (has_many)
|
|
661
|
+
→ cada lead vira CONTATO com os atributos meta_lead_* (form_id/form_name, campaign_id/name,
|
|
662
|
+
adset_id/name, ad_id/name, creative_id/name, platform, account_id/name; definidos por conta como
|
|
663
|
+
atributo de contato). Filtráveis em contacts_filter como atributo personalizado; desde 10/09/2026
|
|
664
|
+
também no filtro da tela de contatos e no painel "Atributos de Campanha" da conversa
|
|
665
|
+
(meta_lead_form_name aparece como "Origem - Formulário")
|
|
593
666
|
|
|
594
667
|
LeadForm (Formulário público de captação — feature flag lead_forms, por conta)
|
|
595
668
|
├── account_id (FK)
|
|
@@ -733,7 +806,7 @@ SignatureDocument (modelo) 1 ──── N SignatureEnvelope (contrato de UMA
|
|
|
733
806
|
body ({{variáveis}}), tags, contact_id, conversation_id, status, phone/email, delivery_channel,
|
|
734
807
|
roles_layout {signers, witnesses, original_sha256, sealed_sha256, conversation_id (a conversa DELE),
|
|
735
808
|
sender_signs, require_id_photo, created_by (quem mandou) public_url, signed_at, viewed_at
|
|
736
|
-
validity_days
|
|
809
|
+
validity_days} │
|
|
737
810
|
progress (contagem por balde) └── N SignatureEvent (append-only: kind, occurred_at, ip, user_agent, detail)
|
|
738
811
|
```
|
|
739
812
|
- Contrato aponta pro contato (`contact_id`, nulifica se a ficha for apagada — a prova sobrevive) e pra conversa
|
|
@@ -529,8 +529,9 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
|
|
|
529
529
|
| `add_card_offer` | `{ offer_id, use_custom_value?, custom_value?, funnel_id?, card_source? }` | Adiciona uma OFERTA (produto/serviço) ao card. `offer_id` de `offers_list`. `use_custom_value: true` + `custom_value` grava um valor personalizado na oferta; senão usa o valor cadastrado. O total do card recalcula sozinho (soma das ofertas). Respeita `card_source` (funnel só localiza o card) |
|
|
530
530
|
| `send_webhook` | `{ url, headers?, body? }` | Dispara webhook externo |
|
|
531
531
|
| `start_flow` | `{ flow_id }` | Inicia outro fluxo. **NÃO encerra o fluxo de origem** (desde 31/08): se houver bloco ligado depois dele, o fluxo SEGUE normalmente. Sendo o último do desenho, o fluxo termina ali como sempre. |
|
|
532
|
-
| `send_conversion` (novo 2026-09-01; `event_names` 2026-09-02) | `{ destinations: ['meta'\|'ga4'\|'google_ads'], event_names: { meta?, google_ads?, ga4? }, event_name (reserva), value? }` | Manda o evento de conversão pro Meta (CAPI), Google Ads e/ou GA4 — o mesmo caminho do Funil, de dentro do fluxo. Aba Sistema (só flow `conversation`). Ver bloco próprio abaixo |
|
|
532
|
+
| `send_conversion` (novo 2026-09-01; `event_names` 2026-09-02; `messaging_event_names` 2026-09-10) | `{ destinations: ['meta'\|'ga4'\|'google_ads'], event_names: { meta?, google_ads?, ga4? }, messaging_event_names: { meta? }, event_name (reserva), value? }` | Manda o evento de conversão pro Meta (CAPI), Google Ads e/ou GA4 — o mesmo caminho do Funil, de dentro do fluxo. Aba Sistema (só flow `conversation`). Ver bloco próprio abaixo |
|
|
533
533
|
| `deactivate_flow` ou `disable_flow` | `{}` | Encerra fluxo atual |
|
|
534
|
+
| `set_booking_situation` (novo 2026-09-14) | `{ situation: 'cancelled' \| 'completed' \| 'attended' \| 'no_show' \| 'confirmed' }` | Muda a situação do agendamento **que disparou o fluxo** (gatilho de agendamento do bloco Início, ou fluxo ligado na configuração do tipo de agendamento). Aba **Agenda** (só flow `conversation`, só com a Agenda ligada na conta). Disparado por outra coisa: **não faz efeito e o passo fica com erro visível** (não há como adivinhar o agendamento). Já no estado pedido = passo PULADO, sem escrita (sem evento, sem laço). `cancelled` cancela o **agendamento** (desarma lembretes, invalida o link de gerenciar). Origem gravada = `fluxo`, ator = o próprio flow. Sem `rescheduled`/`snoozed`: o bloco não mexe em data |
|
|
534
535
|
| `update_attribute` | `{ attr_source: 'contact'\|'conversation'\|'card', attr_key, attr_value }` | Seta custom_attribute (ver abaixo) |
|
|
535
536
|
| `assign_captain` (ou `assign_captain_assistant`) | `{ assistant_id }` | Atribui IA Captain |
|
|
536
537
|
| `deactivate_captain` | `{}` | Tira a IA da conversa |
|
|
@@ -542,7 +543,7 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
|
|
|
542
543
|
**`send_conversion` — Enviar conversão (2026-09-01):**
|
|
543
544
|
|
|
544
545
|
```json
|
|
545
|
-
{ "key": "send_conversion", "config": { "destinations": ["meta", "google_ads"], "event_names": { "meta": "Lead", "google_ads": "Contact" }, "event_name": "Lead", "value": "1500,50" } }
|
|
546
|
+
{ "key": "send_conversion", "config": { "destinations": ["meta", "google_ads"], "event_names": { "meta": "Lead", "google_ads": "Contact" }, "messaging_event_names": { "meta": "QualifiedLead" }, "event_name": "Lead", "value": "1500,50" } }
|
|
546
547
|
```
|
|
547
548
|
|
|
548
549
|
- `destinations`: array com `meta`, `ga4` e/ou `google_ads` — **OBRIGATÓRIO e não vazio** (vazio = a ação
|
|
@@ -555,6 +556,15 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
|
|
|
555
556
|
Chave presente e em branco = aquele destino é PULADO ("sem nome de evento"); chave ausente cai em `event_name`.
|
|
556
557
|
Google Ads só envia evento que tenha ação mapeada na integração; a tela lista Meta (padrão + personalizados
|
|
557
558
|
da conta) e Google Ads (mapeados), GA4 é livre. Grave sempre `event_names` E `event_name` (= primeiro nome).
|
|
559
|
+
- `messaging_event_names` (**desde 2026-09-10**, só a chave `meta`): o evento de **ANÚNCIO DE WHATSAPP**. Quem chegou
|
|
560
|
+
por anúncio de WhatsApp (clique para o WhatsApp, caixa oficial vinculada à Meta) sai como `business_messaging`, e
|
|
561
|
+
nesse caso a Meta só aceita 14 nomes: `LeadSubmitted`, `QualifiedLead`, `ViewContent`, `AddToCart`,
|
|
562
|
+
`InitiateCheckout`, `Purchase`, `OrderCreated`, `OrderShipped`, `OrderDelivered`, `OrderCanceled`, `OrderReturned`,
|
|
563
|
+
`CartAbandoned`, `RatingProvided`, `ReviewProvided`. Tri-estado: chave **ausente** = automático (sugestão pelo
|
|
564
|
+
evento de site: Lead/Contact → LeadSubmitted, Purchase → Purchase, InitiateCheckout → InitiateCheckout; sem sugestão
|
|
565
|
+
o lead de anúncio sai como site com `fallback_reason`); `""` = não enviar como WhatsApp (sai como site); um dos
|
|
566
|
+
14 = escolha. Nome fora da lista é ignorado (vira automático). Não afeta GA4/Google Ads. O nome interno do
|
|
567
|
+
cliente (`event_names.meta`) vai em `custom_data.internal_event`.
|
|
558
568
|
- `event_name`: reserva/compatibilidade (fluxos de antes de 02/09), só letras/números/sublinhado (até 40; ex.: `Lead`, `Schedule`,
|
|
559
569
|
`Purchase`); aceita variável `{{ }}`. No **Google Ads** o nome precisa estar mapeado no
|
|
560
570
|
`conversion_action_map` da integração, senão é pulado.
|
|
@@ -834,7 +844,7 @@ tela do cliente (a tela lê só `percent`).
|
|
|
834
844
|
### 2.11 `update_group` — Gestão de Grupos (WAHA apenas)
|
|
835
845
|
|
|
836
846
|
Bloco "Gestão de Grupos". Faz **UMA operação por bloco** (não mais um formulário-só com nome/foto/descrição
|
|
837
|
-
juntos) — a operação vai em `data.groupOperation`. São **
|
|
847
|
+
juntos) — a operação vai em `data.groupOperation`. São **18 operações**, agrupadas por tema:
|
|
838
848
|
|
|
839
849
|
- **Grupo:** `create` (criar), `find_by_id` (buscar por id), `find_by_name` (buscar por nome), `update_subject`
|
|
840
850
|
(mudar nome), `update_description` (mudar descrição), `update_picture` (mudar foto), `settings` (3
|
|
@@ -843,6 +853,10 @@ juntos) — a operação vai em `data.groupOperation`. São **17 operações**,
|
|
|
843
853
|
- **Admins:** `promote_admin` (promover a admin), `demote_admin` (rebaixar).
|
|
844
854
|
- **Convite:** `get_invite` (pegar link/código de convite), `revoke_invite` (revogar), `send_invite` (mandar
|
|
845
855
|
o convite por mensagem a um telefone).
|
|
856
|
+
- **Mensagem no privado:** `send_private_message` (a 18ª, 16/09) — `groupPrivateTo` (obrig, telefone com código do
|
|
857
|
+
país, aceita `{{trigger.participant.phone}}`) + `messageItems`; manda no PRIVADO desse telefone, criando contato
|
|
858
|
+
e conversa para quem nunca conversou. Sem variável de resposta, sem `groupTargetId`, sem teto diário. É o jeito
|
|
859
|
+
de falar com quem acabou de entrar no grupo (o flow individual só roda para quem já tem conversa).
|
|
846
860
|
- **Mensagem:** `send_message` (a 17ª, 08/08) — manda `messageItems` (MESMO contrato do bloco de mensagem:
|
|
847
861
|
text/delay/attachment/audio/url_media; botões/template não fazem sentido em grupo) NA CONVERSA DO GRUPO
|
|
848
862
|
(`groupTargetId` ou, vazio, o grupo da conversa atual; grupo inexistente = `error`). NÃO grava
|
|
@@ -1022,6 +1036,7 @@ O campo `flow_type` (definido na criação, IMUTÁVEL depois) decide a natureza
|
|
|
1022
1036
|
| Campos extra | — | `tool_name` (snake_case, `[a-z][a-z0-9_]`, max 50) + `tool_description` (max 500) OBRIGATÓRIOS |
|
|
1023
1037
|
| Retorno | manda mensagens | retorna dado estruturado ao LLM via node `end` |
|
|
1024
1038
|
| Nodes permitidos | todos | `start`, `end`, `api`, `condition`, `set_variable`, `ai`, `note`, `randomizer`, `action`, `send_message` |
|
|
1039
|
+
| Aviso de espera (10/09/2026) | — | enquanto a ferramenta roda, o cliente recebe "Só um momento, estou verificando isso pra você...". Cada ferramenta escolhe o seu no node `start`: `data.toolWaitMessageEnabled` (`false` desliga; AUSENTE = ligado — só um `false` de verdade desliga, `""` ou lixo mantém ligado) e `data.toolWaitMessageText` (texto próprio, até 1000 caracteres; vazio = frase padrão). Vive no `flow_data` e viaja no snapshot de versão; ferramenta já existente não muda de comportamento |
|
|
1025
1040
|
|
|
1026
1041
|
Se o cliente pediu "uma ferramenta que a IA usa pra consultar X / calcular Y", é `ai_tool`. Se pediu "quando chega mensagem, faça Z", é `conversation`. Na dúvida, `conversation`.
|
|
1027
1042
|
|
|
@@ -1130,15 +1145,35 @@ Dois atalhos: `{{trigger}}` devolve o contexto INTEIRO em JSON e `{{trigger.data
|
|
|
1130
1145
|
sem os metadados — úteis pra jogar tudo dentro de um node `ai` ou `api`. Bloco (Hash/Array) inteiro
|
|
1131
1146
|
sai em JSON; valor simples sai cru.
|
|
1132
1147
|
|
|
1133
|
-
**
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1148
|
+
**Os fatos do que disparou VIRARAM variáveis (08/09/2026).** Desde 22/08 o passo Início do histórico
|
|
1149
|
+
mostra, em todo gatilho, o contato, a conversa e os fatos do que disparou. Desde 08/09 esses fatos
|
|
1150
|
+
também RESOLVEM como `{{trigger.<bloco>.<campo>}}` (contexto do gatilho vence o fato na mesma chave).
|
|
1151
|
+
Eles continuam FORA de `{{trigger.data}}` e de `{{trigger}}` — fluxo já em produção não muda de conteúdo.
|
|
1152
|
+
|
|
1153
|
+
Oferecidos no autocompletar (só quando o bloco Início tem o gatilho que os preenche):
|
|
1154
|
+
|
|
1155
|
+
| Gatilhos do Início | Variáveis |
|
|
1156
|
+
|---|---|
|
|
1157
|
+
| `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) |
|
|
1158
|
+
| `card_moved` | `{{trigger.kanban.previous_stage_name}}`, `{{trigger.kanban.previous_stage}}` |
|
|
1159
|
+
| `card_won`, `card_lost` | `{{trigger.kanban.status}}` (`won`/`lost`) |
|
|
1160
|
+
| `label_added`, `label_removed` | `{{trigger.label.name}}` |
|
|
1161
|
+
| `assignee_changed` | `{{trigger.assignee.name}}` (vazio = responsável removido) |
|
|
1162
|
+
| `team_changed` | `{{trigger.team.name}}` (vazio = equipe removida) |
|
|
1163
|
+
| `sla_missed` | `{{trigger.sla.policy_name}}`, `{{trigger.sla.type}}` (`frt`/`nrt`/`rt`) |
|
|
1164
|
+
| `group_participant_joined`, `group_participant_left` | `{{trigger.group.name}}`, `{{trigger.group.id}}`, `{{trigger.participant.phone}}`, `{{trigger.participant.name}}` (16/09 — quem entrou/saiu; nome só se já é contato) |
|
|
1165
|
+
|
|
1166
|
+
Resolvem mas NÃO aparecem no seletor (produtor único — só o gatilho daquela integração preenche):
|
|
1167
|
+
`{{trigger.attribute.name}}`/`current_value` (atributo do contato entrou no valor), `{{trigger.form.name}}`/
|
|
1168
|
+
`milestone`, `{{trigger.payment.gateway|event|product|offer|method|status|amount}}`,
|
|
1169
|
+
`{{trigger.eclinica.event|unit|date|time|compromisso|idagenda}}`, `{{trigger.lead.form|page|ad|adset|campaign|platform}}`
|
|
1170
|
+
(nome do anúncio, não dado de pessoa), `{{trigger.dtmf.key|campaign}}`, `{{trigger.reminder.date|time|compromisso|unit|days_before}}`,
|
|
1171
|
+
além de `{{trigger.kanban.id}}`/`funnel_id` (use `{{trigger.kanban_item_id}}`/`{{trigger.funnel_id}}`),
|
|
1172
|
+
`{{trigger.assignee.id}}`, `{{trigger.team.id}}`, `{{trigger.kanban.previous_status}}` e
|
|
1173
|
+
`{{trigger.group.participants_count}}`. As RESPOSTAS do lead do Meta não viram variável (só aparecem no
|
|
1174
|
+
histórico). Sessões anteriores a 22/08 não têm fatos gravados: essas variáveis saem vazias nelas.
|
|
1175
|
+
O bloco do card chama-se `kanban` — nunca `card`. Se o cliente precisar de um dado que não está aqui,
|
|
1176
|
+
use a variável de origem quando existir (`{{booking.*}}`, `{{form_*}}`, `{{contact.*}}`).
|
|
1142
1177
|
|
|
1143
1178
|
**NÃO duplique o que já tem variável própria.** `{{contact.name}}`, `{{conversation.status}}`,
|
|
1144
1179
|
`{{inbox.name}}` e os atributos `ctwa_*` continuam existindo e são a forma canônica. O `{{trigger.*}}`
|
|
@@ -597,7 +597,8 @@ Comuns a (quase) todas:
|
|
|
597
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
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) | — (
|
|
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) | — (sem variável de resposta) |
|
|
601
|
+
| `send_private_message` | `groupPrivateTo` (obrig — telefone com código do país; aceita `{{trigger.participant.phone}}`) + `messageItems` — manda os balões no PRIVADO desse telefone. Quem nunca conversou ganha contato + conversa (dispara os ouvintes de conversa criada). 18ª operação (16/09), sem teto diário | — (sem variável de resposta) |
|
|
601
602
|
|
|
602
603
|
Os campos `added`/`not_added`/`no_whatsapp`/`invited`/`invite_status` do `create` e `groupInviteOnFailure` são
|
|
603
604
|
de 21/08/2026 (entram com o próximo deploy do app depois de 21/08/2026).
|
|
@@ -620,6 +621,13 @@ as outras duas seguem aplicadas. Não prometa essa permissão ao cliente sem con
|
|
|
620
621
|
dígitos, aceita `1203...@g.us` ou só o número). Quando alguém entra, o `send_message` seguinte cai **no próprio
|
|
621
622
|
grupo** (o grupo é a conversa do flow).
|
|
622
623
|
|
|
624
|
+
**Quem entrou (16/09):** o gatilho entrega `{{trigger.participant.phone}}` (telefone com `+`) e
|
|
625
|
+
`{{trigger.participant.name}}` (só quando a pessoa já é contato). Para falar **no privado** de quem entrou, use no
|
|
626
|
+
flow de grupo um `update_group` com `groupOperation: "send_private_message"` e `groupPrivateTo:
|
|
627
|
+
"{{trigger.participant.phone}}"`. **Não use flow individual para isso:** ele só roda para quem JÁ tem conversa
|
|
628
|
+
naquela caixa — quem entra pela primeira vez (link de convite) é pulado. Limite conhecido: o flow de grupo atende
|
|
629
|
+
uma entrada por vez; quem entrar enquanto os balões da pessoa anterior saem não recebe (prefira 1 balão).
|
|
630
|
+
|
|
623
631
|
**Lembretes:**
|
|
624
632
|
- **Teto de 20 participantes por execução** (`add_participants`/`create`) — trava ANTI-BANIMENTO, não performance.
|
|
625
633
|
Adicionar em lote é o que mais rápido derruba número no WhatsApp não-oficial.
|