@lionchat/mcp-server 1.21.0 → 1.23.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 +210 -0
- package/dist/docs/resources/assinatura-eletronica.md +4 -2
- package/dist/docs/resources/best-practices.md +30 -1
- package/dist/docs/resources/conversation-flows.md +1 -1
- package/dist/docs/resources/data-model.md +63 -2
- package/dist/docs/resources/flowbuilder-design-guide.md +65 -14
- package/dist/docs/resources/flowbuilder-patterns.md +9 -1
- package/dist/docs/resources/formularios-publicos.md +4 -2
- package/dist/docs/resources/glossary.md +14 -2
- package/dist/docs/resources/reports-guide.md +33 -5
- package/dist/docs/resources/troubleshooting.md +92 -0
- package/dist/resources.js +6 -0
- package/dist/resources.js.map +1 -1
- package/dist/tools.js +84 -12
- package/dist/tools.js.map +1 -1
- package/endpoints.json +1227 -63
- 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,210 @@
|
|
|
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
|
+
Marcar Compareceu/Faltou também **não conclui a tarefa**: ela segue `pending` (e contando nas
|
|
101
|
+
pendências) até alguém concluir. É de propósito — concluir um compromisso de Booking encerra os lembretes
|
|
102
|
+
e as mensagens pós-atendimento dele e dispara o gatilho de fluxo "agendamento concluído". **Nunca conclua
|
|
103
|
+
a tarefa só porque a presença foi marcada.**
|
|
104
|
+
|
|
105
|
+
Para responder "o que aconteceu com este compromisso?" use o campo **`selo`** da tarefa (17/09), que já
|
|
106
|
+
junta os dois eixos e a remarcação: `cancelled` > `no_show` / `attended` > `completed` > `snoozed` >
|
|
107
|
+
`rescheduled` > `pending`. É o mesmo rótulo que o painel mostra ao lado da tarefa (Faltou, Compareceu,
|
|
108
|
+
Remarcada...). É só de leitura.
|
|
109
|
+
|
|
110
|
+
### Responsável de compromisso de Booking é FIXO
|
|
111
|
+
|
|
112
|
+
O compromisso marcado por um tipo de evento (Booking) cai SEMPRE na agenda do profissional configurado
|
|
113
|
+
nele. Na criação, `assignee_ids` nunca valeu; desde 17/09 também é **ignorado na edição**
|
|
114
|
+
(`lionchat_tasks_update` / `_update_1`): a resposta é 200, o resto da edição vale e o responsável continua
|
|
115
|
+
o mesmo. Para mudar quem atende, altere o profissional do tipo de evento (`booking_event_types_update`).
|
|
116
|
+
|
|
117
|
+
### Ver como está o dia antes de marcar
|
|
118
|
+
|
|
119
|
+
`lionchat_agent_availability_day` (17/09) devolve UM dia de até 6 pessoas: o expediente de cada uma e os
|
|
120
|
+
intervalos já ocupados, em "HH:MM" do fuso pedido. O intervalo ocupado de qualquer pessoa da conta vem
|
|
121
|
+
sempre (é o que permite marcar com um colega); o título do compromisso só vem para quem já o veria no
|
|
122
|
+
calendário, e compromisso privado de outra pessoa nunca vem com título. Para Booking, os horários válidos
|
|
123
|
+
continuam sendo os de `booking_event_types_slots` — essa ferramenta é para compromisso COMUM.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 5. Google Calendar é UMA CONEXÃO POR AGENDA
|
|
128
|
+
|
|
129
|
+
A regra já mudou duas vezes; hoje é: **uma conta Google por agenda**. Cada unidade liga a conta
|
|
130
|
+
Google dela — inclusive a **mesma pessoa** com contas Google diferentes, uma por unidade.
|
|
131
|
+
|
|
132
|
+
- `google_calendar_agendas` devolve uma linha por agenda com o estado de cada uma.
|
|
133
|
+
- `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]
|
|
134
|
+
- Agenda **sem** Google conectado simplesmente **não sincroniza** — ela não cai no Google da matriz.
|
|
135
|
+
O contrário mandaria o compromisso de uma unidade para o calendário de outra.
|
|
136
|
+
- Os sub-calendários compartilhados com agentes pertencem à conexão daquela unidade.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 6. Quem enxerga uma agenda
|
|
141
|
+
|
|
142
|
+
- Agenda **sem** pessoas e **sem** equipes: todo mundo da conta enxerga.
|
|
143
|
+
- Marcando pessoas e/ou equipes: só elas. Quem entrar na equipe ganha acesso sozinho.
|
|
144
|
+
- **Administrador enxerga todas**, sempre.
|
|
145
|
+
- `member_user_ids` e `member_team_ids` **substituem a lista inteira** (idioma do `InboxMember`).
|
|
146
|
+
|
|
147
|
+
A listagem de compromissos já vem recortada pelas agendas que o usuário enxerga, **mesmo sem mandar
|
|
148
|
+
`agenda_id`**. Ler ou editar compromisso de uma agenda que a pessoa não enxerga devolve 404.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 7. Programa de sessões (15/09)
|
|
153
|
+
|
|
154
|
+
Um **programa** é um pacote de N sessões do mesmo tipo de evento para um contato ("10 sessões de ondas
|
|
155
|
+
de choque a cada 30 dias", "8 encontros de mentoria", "4 sessões de onboarding").
|
|
156
|
+
|
|
157
|
+
⚠️ **Na TELA o nome é "programa"** (bloco "Programas", botão "Encerrar programa") — decisão do dono em
|
|
158
|
+
15/09, porque nem sempre é saúde. No CÓDIGO e na API tudo continua `treatment`/`agenda_treatments`: as
|
|
159
|
+
ferramentas, os campos e a tabela não mudaram. Ao falar com o cliente, diga **programa**; ao chamar a
|
|
160
|
+
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
|
|
161
|
+
ficha do contato mostra em que pé está: quantas foram feitas, quantos por cento, quando é a próxima
|
|
162
|
+
e se está atrasada.
|
|
163
|
+
|
|
164
|
+
- **Liga-se no tipo de evento**: `sessions_enabled` + `sessions_default_count` (2 a 60) +
|
|
165
|
+
`sessions_interval_value`/`sessions_interval_unit` (`days`/`weeks`/`months`). Tipo sem isso não
|
|
166
|
+
cria programa nunca.
|
|
167
|
+
- **O programa NASCE junto do agendamento** — não existe `create`. Todo agendamento de um tipo com
|
|
168
|
+
sessões (painel, link público ou IA) cria o programa do contato ou **engrossa** o que já está em
|
|
169
|
+
andamento como a próxima sessão livre. Um programa ativo por contato + tipo.
|
|
170
|
+
- **Plano por pessoa**: no `tasks_create` em modo booking, `treatment: {sessions, interval_value,
|
|
171
|
+
interval_unit}` vence o padrão do tipo. É validado ANTES da reserva (422 sem agendar nada).
|
|
172
|
+
- **"Marcar próxima"**: `tasks_create` com `agenda_treatment_id` + `treatment_session_number`. Sessão já
|
|
173
|
+
marcada dá 422; programa de outra ficha dá 422.
|
|
174
|
+
- **Sessão k = compromisso VIVO (não cancelado) de número k.** Cancelar devolve a sessão; remarcar
|
|
175
|
+
mantém o número; **faltar consome** (decisão do dono — a falta gasta a sessão). "Usadas" = compareceu
|
|
176
|
+
+ faltou. Nada disso é cache: o resumo é derivado das tarefas na leitura.
|
|
177
|
+
- **Cheio** = todas as N marcadas e nenhuma usada ainda. Agendamento novo pelo painel/IA num
|
|
178
|
+
programa cheio vira **sessão extra** (N+1, até 60); pelo link público não vira nada (o
|
|
179
|
+
agendamento nasce solto, sem programa).
|
|
180
|
+
- **Concluído** = usadas ≥ N. O `status` do programa só vira `completed` na próxima reserva daquele
|
|
181
|
+
tipo (aí abre um ciclo novo); `closed` é encerramento pela equipe (`close_reason: manual`) ou pela
|
|
182
|
+
junção de contatos (`mesclagem` — o programa do perdedor que colide com um do vencedor).
|
|
183
|
+
- **Atrasado** = em andamento, próxima sessão com data prevista já passada e nenhuma sessão viva com
|
|
184
|
+
data futura. A previsão é a última data conhecida + intervalo × distância.
|
|
185
|
+
- **Marcar TODAS as sessões que faltam** (15/09): `lionchat_agenda_treatments_plano_sugerido` devolve o dia e a
|
|
186
|
+
hora sugeridos por sessão (data prevista; sem vaga, anda para a frente e marca `deslocada`), e
|
|
187
|
+
`lionchat_agenda_treatments_marcar_todas` marca o que for confirmado. **Não é transação única**: sucesso parcial é
|
|
188
|
+
200 com `falhas` nomeadas, e o que entrou na agenda fica. Com confirmação ligada no tipo, o cliente recebe UMA
|
|
189
|
+
mensagem por sessão — avise isso antes de marcar em lote.
|
|
190
|
+
- Ferramentas: `lionchat_agenda_treatments_list` (por `contact_id`, resumo pronto por programa),
|
|
191
|
+
`_show`, `_update` (`planned_sessions` nunca abaixo da maior sessão já marcada; `status`
|
|
192
|
+
`closed`/`active`). O compromisso devolve `tratamento: {id, sessao, total, titulo}` quando pertence a
|
|
193
|
+
um. Compromisso de agenda que o usuário não enxerga aparece na sessão como `oculta: true`.
|
|
194
|
+
- 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`).
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 8. Erros comuns
|
|
199
|
+
|
|
200
|
+
| Sintoma | Causa |
|
|
201
|
+
|---|---|
|
|
202
|
+
| 422 'Ha sessoes marcadas alem dessa quantidade' ao reduzir `planned_sessions` | já existe sessão marcada acima do novo total; cancele-a antes |
|
|
203
|
+
| 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 |
|
|
204
|
+
| 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) |
|
|
205
|
+
| 422 ao criar compromisso com `task_type: "consulta"` | os tipos clínicos saíram; use a `key` que `account_task_types_list` devolve |
|
|
206
|
+
| Ícone do tipo não aparece | valor fora da lista fechada de 12 |
|
|
207
|
+
| Agendamento do Booking caiu na unidade errada | `agenda_id` do tipo de evento não foi definido (nulo = principal) |
|
|
208
|
+
| Compromisso da filial no Google da matriz | não acontece mais; se aparecer, a conexão daquela agenda está com `agenda_id` errado |
|
|
209
|
+
| `agendas_create` recusado | recurso `multiple_agendas` desligado ou teto de Extras Manuais atingido |
|
|
210
|
+
| 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).
|
|
@@ -397,7 +397,9 @@ oficial, com WABA) sai pra Meta como evento de MENSAGEM (`action_source: busines
|
|
|
397
397
|
Meta só aceita isso num **dataset vinculado à WABA**, nunca no pixel do site (recusa 2804132). Até
|
|
398
398
|
10/09 100% desses eventos caíam no plano B (saíam como site) sem ninguém ver. Regras pro MCP:
|
|
399
399
|
|
|
400
|
-
- **Vincular a caixa primeiro:**
|
|
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.
|
|
401
403
|
Idempotente, vale para todos os números da mesma WABA, usa a chave da PRÓPRIA caixa (a do pixel não
|
|
402
404
|
serve). O id fica em `provider_config.capi_dataset_id` (visível em `lionchat_inboxes_show` para admin).
|
|
403
405
|
Sem o vínculo os eventos de anúncio saem como site com `fallback_reason: lionchat:dataset_nao_configurado`.
|
|
@@ -922,3 +924,30 @@ administrador."* — a recusa é `before_action`, então NADA do resto do update
|
|
|
922
924
|
Ao atualizar o próprio usuário: mande times e caixas à vontade; **não mande `role` nem
|
|
923
925
|
`custom_role_id`** — se mandar, a chamada inteira falha e as outras alterações se perdem também.
|
|
924
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).
|
|
935
|
+
|
|
936
|
+
## Entrar num grupo por convite (17/09/2026)
|
|
937
|
+
|
|
938
|
+
Só em caixa de WhatsApp QR Code (`Channel::Waha`). São DUAS tools e a ordem importa.
|
|
939
|
+
|
|
940
|
+
1. `lionchat_inboxes_waha_groups_list_3` (join_info) — SÓ LEITURA: diz qual é o grupo (`Name`,
|
|
941
|
+
`ParticipantCount`, `IsJoinApprovalRequired`). **Chame sempre primeiro** e confirme com a pessoa
|
|
942
|
+
em qual grupo ela vai entrar. Entrar é ação que muda o WhatsApp do cliente.
|
|
943
|
+
2. `lionchat_inboxes_waha_groups_create_6` (join) — entra, como participante comum.
|
|
944
|
+
|
|
945
|
+
As duas aceitam o código cru, o link inteiro (`https://chat.whatsapp.com/CODIGO`) ou um texto com o
|
|
946
|
+
link no meio — não precisa extrair o código antes.
|
|
947
|
+
|
|
948
|
+
**Recusa esperada:** 422 com `reason: groups_disabled` quando a caixa está com "Aceitar grupos"
|
|
949
|
+
desligado. Não insista nem tente contornar: sem essa chave o WhatsApp nem entrega as mensagens do
|
|
950
|
+
grupo, então o número entraria num grupo que nunca apareceria no painel. O caminho é o cliente ligar
|
|
951
|
+
a opção nas configurações da caixa e tentar de novo.
|
|
952
|
+
|
|
953
|
+
A resposta do join_info vem no formato cru do motor, em PascalCase (`Name`, não `name`).
|
|
@@ -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.
|
|
@@ -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.
|
|
@@ -384,7 +404,31 @@ AccountTask (agenda interna)
|
|
|
384
404
|
├── meeting_url (string, sala de reunião call/meeting)
|
|
385
405
|
├── guest_emails (jsonb array, convidados extras — máx 20)
|
|
386
406
|
├── conversation_id / contact_id / linked_kanban_item_id (FK opcionais)
|
|
387
|
-
|
|
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
|
+
├── selo (SÓ LEITURA, calculado — 17/09): o rótulo ÚNICO que a tela mostra ao lado da tarefa, juntando
|
|
412
|
+
│ `status` + `attendance` + remarcação: cancelled > no_show / attended > completed > snoozed >
|
|
413
|
+
│ rescheduled > pending. `rescheduled` = data trocada pelo painel/fluxo/IA (`reschedule_source`) OU pelo
|
|
414
|
+
│ cliente no link do agendamento. Para dizer "o que aconteceu com este compromisso", LEIA `selo` —
|
|
415
|
+
│ `status: pending` sozinho NÃO significa "nada aconteceu" (a pessoa pode ter faltado)
|
|
416
|
+
├── agenda_treatment_id (FK → AgendaTreatment, nullify) / treatment_session_number (1..N) — a sessão
|
|
417
|
+
│ que este compromisso é; índice único parcial por tratamento+número entre os NÃO cancelados (15/09)
|
|
418
|
+
└── assignees (has_many → User, via account_task_assignments). Compromisso NASCIDO DE BOOKING
|
|
419
|
+
(`booking_id` presente) tem responsável FIXO = o agente configurado no tipo de evento:
|
|
420
|
+
`assignee_ids` enviado no update é IGNORADO (200, o resto da edição vale — 17/09)
|
|
421
|
+
|
|
422
|
+
AgendaTreatment (tratamento em sessões — tools `agenda_treatments_*`; tabela agenda_treatments; 15/09)
|
|
423
|
+
├── id (PK) / account_id (FK) / contact_id (FK) / booking_event_type_id (FK, nullify)
|
|
424
|
+
├── event_type_title (cópia do nome do tipo — sobrevive à exclusão do tipo)
|
|
425
|
+
├── planned_sessions (2..60) / interval_value (1..365) / interval_unit (days|weeks|months)
|
|
426
|
+
├── status (active|completed|closed) / close_reason (manual|mesclagem) / closed_at
|
|
427
|
+
├── source (painel|link|ia|sistema|fluxo) / created_by_id (FK → User, só quando foi gente)
|
|
428
|
+
├── índice único parcial (account_id, contact_id, booking_event_type_id) WHERE status='active'
|
|
429
|
+
│ — UM tratamento ativo por contato + tipo
|
|
430
|
+
└── account_tasks (has_many, nullify) — sessão k = tarefa VIVA de número k; usadas/percentual/atrasado/
|
|
431
|
+
concluído são DERIVADOS na leitura (nenhuma coluna de cache)
|
|
388
432
|
|
|
389
433
|
BookingEventType (template de agendamento — tools `booking_event_types_*`; colunas de
|
|
390
434
|
db/schema.rb, tabela booking_event_types)
|
|
@@ -397,6 +441,8 @@ db/schema.rb, tabela booking_event_types)
|
|
|
397
441
|
├── color (hex `#RRGGBB`, opcional — cor dos compromissos desse tipo no calendário; vazio = cor do
|
|
398
442
|
│ agente; 2026-08-19)
|
|
399
443
|
├── active / ask_email / ask_description
|
|
444
|
+
├── sessions_enabled (bool) / sessions_default_count (2..60) / sessions_interval_value (1..365) /
|
|
445
|
+
│ sessions_interval_unit (days|weeks|months) — tratamento em sessões: padrão do tipo (15/09)
|
|
400
446
|
├── confirmation_* (mensagem de confirmação própria: enabled, inbox_id, channel_type,
|
|
401
447
|
│ template_name, blocks). Variáveis nos textos de confirmação E de lembrete (interpolador único):
|
|
402
448
|
│ {{nome}} {{email}} {{telefone}} {{data}} {{horario}} {{dia_semana}} (NOVO 24/08 — dia por
|
|
@@ -567,6 +613,21 @@ da consulta) — em vez de herdar sempre o horário da consulta. Há ainda um fi
|
|
|
567
613
|
("Só quando") por atributo `eclinica_*` do contato. Nada disso é editável pelo MCP: só pelo painel.
|
|
568
614
|
Pra auditar o que foi/será disparado, use `lionchat_eclinica_reminder_history_list`.
|
|
569
615
|
|
|
616
|
+
**Um aviso por CONSULTA (desde 2026-09-17).** A regra antiga "um lembrete por dia" saiu: paciente com duas
|
|
617
|
+
consultas no mesmo dia — ou duas pessoas no mesmo celular (mãe e filho) — recebe um lembrete para CADA
|
|
618
|
+
consulta, cada um com o seu nome, horário, profissional e link. Eles não saem juntos: sai o da consulta
|
|
619
|
+
mais cedo e, ~10 minutos depois (na rodada seguinte), o da próxima. No histórico
|
|
620
|
+
(`lionchat_eclinica_reminder_history_list`) isso aparece assim: o que espera a vez fica `pending` com
|
|
621
|
+
`fire_at` no passado por alguns minutos (normal); `skip_reason = outra_consulta_no_mesmo_dia` só existe em
|
|
622
|
+
linhas ANTIGAS (até 17/09); e `fail_reason = fluxo_ainda_em_andamento_com_este_contato` significa que o
|
|
623
|
+
fluxo do aviso anterior ainda estava em andamento com aquele contato (típico de fluxo que aguarda o
|
|
624
|
+
paciente clicar num botão) — pode ser reenviado com `lionchat_eclinica_reminder_history_reprocess`.
|
|
625
|
+
O fluxo do lembrete recebe também `{{cliente_id}}` (código do paciente DAQUELA consulta na e-Clínica) e
|
|
626
|
+
`{{agendatipo}}`; na automação, `{{agendamento.cliente_id}}` e `{{agendamento.agendatipo}}`. Ao consultar a
|
|
627
|
+
agenda do paciente dentro do fluxo, use `{{cliente_id}}` do lembrete — a ficha do contato é uma por
|
|
628
|
+
TELEFONE e guarda só o último paciente. Quem quiser "só um lembrete por dia" monta no próprio fluxo
|
|
629
|
+
(condição num atributo da conversa com `{{data_consulta}}` + ação que grava o dia depois de enviar).
|
|
630
|
+
|
|
570
631
|
## Relatórios e Métricas
|
|
571
632
|
|
|
572
633
|
```
|
|
@@ -767,7 +828,7 @@ SignatureDocument (modelo) 1 ──── N SignatureEnvelope (contrato de UMA
|
|
|
767
828
|
body ({{variáveis}}), tags, contact_id, conversation_id, status, phone/email, delivery_channel,
|
|
768
829
|
roles_layout {signers, witnesses, original_sha256, sealed_sha256, conversation_id (a conversa DELE),
|
|
769
830
|
sender_signs, require_id_photo, created_by (quem mandou) public_url, signed_at, viewed_at
|
|
770
|
-
validity_days
|
|
831
|
+
validity_days} │
|
|
771
832
|
progress (contagem por balde) └── N SignatureEvent (append-only: kind, occurred_at, ip, user_agent, detail)
|
|
772
833
|
```
|
|
773
834
|
- Contrato aponta pro contato (`contact_id`, nulifica se a ficha for apagada — a prova sobrevive) e pra conversa
|
|
@@ -288,7 +288,7 @@ No editor visual esses destinos só aparecem quando a validação bate (computed
|
|
|
288
288
|
- `validation: 'options'` → `option_<valor>` para cada valor em `acceptedOptions` (ex: `option_1`, `option_2`, `option_sim`) + `timeout` + `retries_exhausted`
|
|
289
289
|
- `validation: 'varied_options'` → `option_<group_id>` para cada grupo em `optionGroups` (ex: `option_sim`, `option_nao`) + `timeout` + `retries_exhausted`
|
|
290
290
|
|
|
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.
|
|
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 (`maxRetries` = quantas vezes a `invalidMessage` e enviada; a resposta errada seguinte esgota — com `maxRetries: 1` o cliente recebe o erro uma vez e sai no 2o erro). 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.
|
|
292
292
|
|
|
293
293
|
**Timeout dispara de verdade — e SEM fio ENCERRA (desde 20/08/2026):** `waitTime` + `waitUnit`
|
|
294
294
|
agendam o estouro. Com fio no handle `timeout`, o flow segue por ele. **Sem fio, a sessão é
|
|
@@ -385,6 +385,25 @@ só pra destravar, ela pode sair.
|
|
|
385
385
|
| `pagetrack_visited` / `pagetrack_event` | LionTrack |
|
|
386
386
|
| `sla_check` | status do SLA da conversa (usa `value` = código fixo; ver abaixo) |
|
|
387
387
|
|
|
388
|
+
**CAIXA DA CONVERSA e TIPO DE CAIXA (novo 17/09/2026):** duas condições da aba Conversas que perguntam
|
|
389
|
+
ONDE a conversa está. Nasceram para o **Fluxo de Ações** (que roda em qualquer caixa), mas valem em
|
|
390
|
+
qualquer tipo de flow. Não têm operador novo: só `equal` e `not_equal`, e o valor vai numa **LISTA**
|
|
391
|
+
(`values`) — com lista, `equal` significa "é ALGUMA destas" e `not_equal` "não é NENHUMA destas".
|
|
392
|
+
|
|
393
|
+
- **Caixa da conversa:** `{ "id": 1, "field": "{{inbox.id}}", "operator": "equal", "value": "", "values": ["342", "204"], "valueType": "inbox" }`
|
|
394
|
+
— cada item de `values` é o **id da caixa como STRING**.
|
|
395
|
+
- **Tipo de caixa:** `{ "id": 1, "field": "{{inbox.channel_type}}", "operator": "equal", "value": "", "values": ["Channel::Waha"], "valueType": "channel_type" }`
|
|
396
|
+
— cada item é o **nome técnico do canal**: `Channel::Waha` (WhatsApp QR Code), `Channel::Whatsapp`
|
|
397
|
+
(WhatsApp API Oficial), `Channel::WebWidget`, `Channel::Api`, `Channel::Email`,
|
|
398
|
+
`Channel::FacebookPage` (Facebook/Instagram), `Channel::TwilioSms`, `Channel::Sms`,
|
|
399
|
+
`Channel::Telegram`, `Channel::Line`.
|
|
400
|
+
|
|
401
|
+
O `valueType` é OBRIGATÓRIO nas duas: é ele que faz a tela abrir o seletor certo (lista de caixas /
|
|
402
|
+
lista de tipos). Sem ele a regra roda no motor e a tela não desenha campo nenhum para ela. Gravar o
|
|
403
|
+
valor em `value` em vez de `values` é a mesma família do defeito de 02/09 descrito abaixo: o motor
|
|
404
|
+
compara certo e a tela abre o seletor VAZIO. `{{inbox.name}}` existe como variável mas **não** é
|
|
405
|
+
condição: comparar por nome quebra quando o cliente renomeia a caixa.
|
|
406
|
+
|
|
388
407
|
> **ATENÇÃO — onde o VALOR vai (defeito real de 02/09/2026, conta 137):** no nó **Condição**, uma regra de atributo
|
|
389
408
|
> (`valueType: "attr_config"`) com operador `equal`, `not_equal`, `contains` ou `not_contains` guarda o valor em
|
|
390
409
|
> **`values: ["..."]` (lista)** e deixa `value: ""`. A TELA lê SÓ a lista: regra gravada com `value: "paid_ad"` e
|
|
@@ -531,6 +550,7 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
|
|
|
531
550
|
| `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
551
|
| `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
552
|
| `deactivate_flow` ou `disable_flow` | `{}` | Encerra fluxo atual |
|
|
553
|
+
| `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
554
|
| `update_attribute` | `{ attr_source: 'contact'\|'conversation'\|'card', attr_key, attr_value }` | Seta custom_attribute (ver abaixo) |
|
|
535
555
|
| `assign_captain` (ou `assign_captain_assistant`) | `{ assistant_id }` | Atribui IA Captain |
|
|
536
556
|
| `deactivate_captain` | `{}` | Tira a IA da conversa |
|
|
@@ -843,7 +863,7 @@ tela do cliente (a tela lê só `percent`).
|
|
|
843
863
|
### 2.11 `update_group` — Gestão de Grupos (WAHA apenas)
|
|
844
864
|
|
|
845
865
|
Bloco "Gestão de Grupos". Faz **UMA operação por bloco** (não mais um formulário-só com nome/foto/descrição
|
|
846
|
-
juntos) — a operação vai em `data.groupOperation`. São **
|
|
866
|
+
juntos) — a operação vai em `data.groupOperation`. São **18 operações**, agrupadas por tema:
|
|
847
867
|
|
|
848
868
|
- **Grupo:** `create` (criar), `find_by_id` (buscar por id), `find_by_name` (buscar por nome), `update_subject`
|
|
849
869
|
(mudar nome), `update_description` (mudar descrição), `update_picture` (mudar foto), `settings` (3
|
|
@@ -852,6 +872,10 @@ juntos) — a operação vai em `data.groupOperation`. São **17 operações**,
|
|
|
852
872
|
- **Admins:** `promote_admin` (promover a admin), `demote_admin` (rebaixar).
|
|
853
873
|
- **Convite:** `get_invite` (pegar link/código de convite), `revoke_invite` (revogar), `send_invite` (mandar
|
|
854
874
|
o convite por mensagem a um telefone).
|
|
875
|
+
- **Mensagem no privado:** `send_private_message` (a 18ª, 16/09) — `groupPrivateTo` (obrig, telefone com código do
|
|
876
|
+
país, aceita `{{trigger.participant.phone}}`) + `messageItems`; manda no PRIVADO desse telefone, criando contato
|
|
877
|
+
e conversa para quem nunca conversou. Sem variável de resposta, sem `groupTargetId`, sem teto diário. É o jeito
|
|
878
|
+
de falar com quem acabou de entrar no grupo (o flow individual só roda para quem já tem conversa).
|
|
855
879
|
- **Mensagem:** `send_message` (a 17ª, 08/08) — manda `messageItems` (MESMO contrato do bloco de mensagem:
|
|
856
880
|
text/delay/attachment/audio/url_media; botões/template não fazem sentido em grupo) NA CONVERSA DO GRUPO
|
|
857
881
|
(`groupTargetId` ou, vazio, o grupo da conversa atual; grupo inexistente = `error`). NÃO grava
|
|
@@ -1020,17 +1044,43 @@ de conexão e viram bola de neve visual. Ao gerar `position`:
|
|
|
1020
1044
|
|
|
1021
1045
|
---
|
|
1022
1046
|
|
|
1023
|
-
## 2-B.
|
|
1047
|
+
## 2-B. Três tipos de flow: `conversation`, `ai_tool` e `action`
|
|
1024
1048
|
|
|
1025
1049
|
O campo `flow_type` (definido na criação, IMUTÁVEL depois) decide a natureza do flow:
|
|
1026
1050
|
|
|
1027
|
-
| | `conversation` (default) | `ai_tool` |
|
|
1028
|
-
|
|
1029
|
-
| Como dispara | Por evento de inbox (trigger no node `start`) | Invocado pelo AI Agente (Captain) como ferramenta |
|
|
1030
|
-
| Inboxes | usa `inbox_ids` | **PROIBIDO** ter inboxes (validação barra) |
|
|
1031
|
-
| Campos extra | — | `tool_name` (snake_case, `[a-z][a-z0-9_]`, max 50) + `tool_description` (max 500) OBRIGATÓRIOS |
|
|
1032
|
-
| Retorno | manda mensagens | retorna dado estruturado ao LLM via node `end` |
|
|
1033
|
-
| Nodes permitidos | todos | `start`, `end`, `api`, `condition`, `set_variable`, `ai`, `note`, `randomizer`, `action`, `send_message` |
|
|
1051
|
+
| | `conversation` (default) | `ai_tool` | `action` (17/09/2026) |
|
|
1052
|
+
|---|---|---|---|
|
|
1053
|
+
| Como dispara | Por evento de inbox (trigger no node `start`) | Invocado pelo AI Agente (Captain) como ferramenta | Pelo MESMO trigger do `conversation`, mas em QUALQUER inbox |
|
|
1054
|
+
| Inboxes | usa `inbox_ids` | **PROIBIDO** ter inboxes (validação barra) | **PROIBIDO** ter inboxes (roda em todas) |
|
|
1055
|
+
| Campos extra | — | `tool_name` (snake_case, `[a-z][a-z0-9_]`, max 50) + `tool_description` (max 500) OBRIGATÓRIOS | — |
|
|
1056
|
+
| Retorno | manda mensagens | retorna dado estruturado ao LLM via node `end` | **não manda mensagem nenhuma** |
|
|
1057
|
+
| Nodes permitidos | todos | `start`, `end`, `api`, `condition`, `set_variable`, `ai`, `note`, `randomizer`, `action`, `send_message` | todos MENOS `send_message`, `wait_response`, `update_group` e `end` |
|
|
1058
|
+
|
|
1059
|
+
**`action` (na tela: "Fluxo de Ações")** é para regra que vale na empresa inteira e não depende da
|
|
1060
|
+
caixa: "conversa que receber a etiqueta urgente vai pra equipe de suporte", "card que entrar na
|
|
1061
|
+
etapa Fechado grava a data no contato". Antes exigia repetir o mesmo flow em cada inbox.
|
|
1062
|
+
|
|
1063
|
+
Criar: `flows_create` com `flow_type: 'action'`, SEM `inbox_ids` e SEM `conversation_mode`
|
|
1064
|
+
(o backend fixa `individual`; mandar `group` é ignorado). Aceita os MESMOS gatilhos do
|
|
1065
|
+
`conversation` no node `start` e usa o mesmo `flow_data`. Roda LADO A LADO com os flows de mensagem
|
|
1066
|
+
(não entra no aviso de conflito de gatilho) e vale também em conversa de grupo.
|
|
1067
|
+
|
|
1068
|
+
**NUNCA** pôr `send_message` / `wait_response` / `update_group` num flow `action` — a validação
|
|
1069
|
+
recusa o save inteiro com `contains node types not allowed in action flow`. Se o cliente precisa
|
|
1070
|
+
falar com a pessoa, o flow é `conversation`.
|
|
1071
|
+
|
|
1072
|
+
Três regras que andam com o tipo `action` (17/09/2026):
|
|
1073
|
+
- **`start_flow` (bloco Ações > Sistema)**: o flow de ações pode ser alvo a partir de QUALQUER flow,
|
|
1074
|
+
sem casar caixa. Já um flow `conversation` alvo que NÃO atenda a caixa da conversa é RECUSADO em
|
|
1075
|
+
runtime (passo com erro `inbox_mismatch` no histórico) — antes rodava no lugar errado em silêncio.
|
|
1076
|
+
- **Macro**: a ação `start_flow` da macro (`macros_*`) aceita **só** `flow_type: 'action'`.
|
|
1077
|
+
- **Condições**: `{{inbox.id}}` e `{{inbox.channel_type}}` (novo) dão "Caixa da conversa" e "Tipo de
|
|
1078
|
+
caixa" na aba Conversas — como o flow de ações roda em todas as caixas, é assim que ele pergunta
|
|
1079
|
+
onde está antes de agir. `channel_type` grava o valor técnico (`Channel::Waha` etc.).
|
|
1080
|
+
- **AINDA NÃO FUNCIONA**: disparar flow `action` por webhook de integração (gateways de pagamento,
|
|
1081
|
+
Webhook Universal, Meta Lead, e-Clínica) ou por gatilho de formulário. Esses caminhos criam a
|
|
1082
|
+
conversa usando a CAIXA do flow, e o flow de ações não tem caixa — ele é selecionado e descartado
|
|
1083
|
+
(`no_inbox_linked` no log). Não prometa isso ao cliente até a decisão de produto.
|
|
1034
1084
|
| 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 |
|
|
1035
1085
|
|
|
1036
1086
|
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`.
|
|
@@ -1050,10 +1100,11 @@ no fim de uma cadeia automação→flow→automação, suspeite desse limite —
|
|
|
1050
1100
|
|
|
1051
1101
|
Todo flow `conversation` tem um `conversation_mode`: `individual` (default — conversa 1-a-1) ou `group`
|
|
1052
1102
|
(grupo de WhatsApp). Definido na CRIAÇÃO e **IMUTÁVEL depois** (não dá pra converter um no outro; pra trocar,
|
|
1053
|
-
crie outro flow). Não confundir com `flow_type` — um flow `ai_tool` não tem `conversation_mode
|
|
1103
|
+
crie outro flow). Não confundir com `flow_type` — um flow `ai_tool` não tem `conversation_mode`, e no
|
|
1104
|
+
`action` ele é fixado em `individual` (o campo não se aplica: o flow de ações roda nos dois).
|
|
1054
1105
|
|
|
1055
|
-
Na tela o usuário
|
|
1056
|
-
|
|
1106
|
+
Na tela o usuário escolhe o TIPO (Conversa / IA Agente / Ações) e, só no tipo Conversa, o modo
|
|
1107
|
+
(Mensagens individuais / Grupos WhatsApp).
|
|
1057
1108
|
|
|
1058
1109
|
**A diferença entre individual e grupo é quais nodes/abas ficam disponíveis:**
|
|
1059
1110
|
|
|
@@ -1156,7 +1207,7 @@ Oferecidos no autocompletar (só quando o bloco Início tem o gatilho que os pre
|
|
|
1156
1207
|
| `assignee_changed` | `{{trigger.assignee.name}}` (vazio = responsável removido) |
|
|
1157
1208
|
| `team_changed` | `{{trigger.team.name}}` (vazio = equipe removida) |
|
|
1158
1209
|
| `sla_missed` | `{{trigger.sla.policy_name}}`, `{{trigger.sla.type}}` (`frt`/`nrt`/`rt`) |
|
|
1159
|
-
| `group_participant_joined`, `group_participant_left` | `{{trigger.group.name}}`, `{{trigger.group.id}}` |
|
|
1210
|
+
| `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) |
|
|
1160
1211
|
|
|
1161
1212
|
Resolvem mas NÃO aparecem no seletor (produtor único — só o gatilho daquela integração preenche):
|
|
1162
1213
|
`{{trigger.attribute.name}}`/`current_value` (atributo do contato entrou no valor), `{{trigger.form.name}}`/
|
|
@@ -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.
|
|
@@ -232,8 +232,10 @@ renderiza e não dá erro.
|
|
|
232
232
|
- **Lista de recusa no servidor** (o valor é pulado de forma visível, sem erro): `name`, `email`,
|
|
233
233
|
`phone_number`, `identifier`, `card` e chaves começando com `captain_`, `eclinica_`, `origin_`,
|
|
234
234
|
`lt_`, `booking_`, `waha_`, `whatsapp_`, `ctwa_`, `meta_lead_`.
|
|
235
|
-
- Contato pré-existente
|
|
236
|
-
|
|
235
|
+
- Contato pré-existente: **dado novo vence** (regra de 11/09) — nome, e-mail, documentos e atributos
|
|
236
|
+
são sobrescritos por resposta nova (mesmo valor não regrava). Só o **telefone** fica protegido
|
|
237
|
+
(endereço da conversa): preenche vazio ou corrige o que a própria resposta gravou. O que cada
|
|
238
|
+
resposta gravou fica em `written_attributes` (detalhe da resposta).
|
|
237
239
|
|
|
238
240
|
**`options`** (do `choice`): `[{ "id": "abc123", "label": "Sim", "image": "" }]`.
|
|
239
241
|
|
|
@@ -449,7 +449,7 @@ Campos MUTÁVEIS (atualizáveis livremente por IA/integração): `marital_status
|
|
|
449
449
|
| **LionCalls** | Voz pelo WhatsApp em caixa **QR Code**, mas pelo **motor próprio do LionChat** (credencial global, 1 sessão por caixa; flag `lioncalls_calling`, nasce desligada e é ligada conta a conta) | **NENHUMA — sem tool no MCP** | WhatsApp QR Code |
|
|
450
450
|
| **WhatsApp Calling (Cloud)** | Voz pelo WhatsApp na **API oficial** (enterprise) | `whatsapp_calls_*` + enable/disable_whatsapp_calling | WhatsApp oficial |
|
|
451
451
|
| **VoIP (Zenvia)** | Telefonia COMUM com softphone no navegador (ramais, ligações) | `voip_*` | Telefone |
|
|
452
|
-
| **VTCall** | Telefonia por **PABX click-to-call**, config por conta + ramal por atendente; **sem softphone no navegador** — o atendente fala pelo app/ramal do VTCall | `vtcall_settings_*` (show/update/test_connection), `vtcall_ramals_*` (list/create/destroy) | Telefone (PABX do cliente) |
|
|
452
|
+
| **VTCall** | Telefonia por **PABX click-to-call**, config por conta + ramal por atendente; **sem softphone no navegador** — o atendente fala pelo app/ramal do VTCall; desde 11/09/2026 a ligação discada direto no aparelho (ramal cadastrado) também entra no histórico | `vtcall_settings_*` (show/update/test_connection), `vtcall_ramals_*` (list/create/destroy) | Telefone (PABX do cliente) |
|
|
453
453
|
|
|
454
454
|
Quando o usuário falar "ligação", descubra o canal ANTES de responder: caixa QR Code → **LionCalls**;
|
|
455
455
|
caixa oficial → `whatsapp_calls`; telefone/ramal → Zenvia (softphone) ou VTCall (PABX).
|
|
@@ -503,7 +503,8 @@ cards daquele funil).
|
|
|
503
503
|
pelo botão de WhatsApp de um anúncio (conversa com `ctwa_clid` numa caixa oficial). A Meta só
|
|
504
504
|
aceita 14 nomes nele (`LeadSubmitted`, `QualifiedLead`, `ViewContent`, `AddToCart`, `InitiateCheckout`, `Purchase`, `OrderCreated`, `OrderShipped`, `OrderDelivered`, `OrderCanceled`, `OrderReturned`, `CartAbandoned`, `RatingProvided`, `ReviewProvided`) e só num dataset vinculado à WABA.
|
|
505
505
|
- **Dataset da WABA** (`provider_config.capi_dataset_id` da caixa oficial): destino desses eventos,
|
|
506
|
-
criado/recuperado
|
|
506
|
+
criado/recuperado AUTOMATICAMENTE (na criação da caixa e por varredura a cada 10 min) e, como plano B,
|
|
507
|
+
pelo botão "Vincular à Meta" (`lionchat_inboxes_capi_dataset_link`) com a chave da
|
|
507
508
|
própria caixa. Um por conta do WhatsApp Business; vale para todos os números dela.
|
|
508
509
|
- **`messaging_name` / `messaging_event_names.meta` / `messaging_event_name`**: a escolha do evento de
|
|
509
510
|
WhatsApp (funil / fluxo / disparo manual). Tri-estado: ausente = automático, `''` = não enviar como
|
|
@@ -600,6 +601,17 @@ conversa). Quem atendeu voltou a aparecer nas recebidas. Se o cliente diz que o
|
|
|
600
601
|
funciona numa ligação de hoje, o mais provável é que o arquivo permanente ainda não exista — a
|
|
601
602
|
reconferência resolve sozinha dentro de 36h.
|
|
602
603
|
|
|
604
|
+
## Ligação feita direto no aparelho do VTCall (11/09/2026)
|
|
605
|
+
|
|
606
|
+
Antes, só a ligação iniciada pelo botão Ligar do LionChat entrava no histórico e na coluna "Feitas" do bloco de
|
|
607
|
+
ligações. Agora a ligação que o atendente disca direto no aparelho ou no app do VTCall também entra, desde que o ramal
|
|
608
|
+
esteja cadastrado (`vtcall_ramals_*`). Ela aparece em `voip_calls_list` com `direction: "outbound"`, protocolo `LIG-...`
|
|
609
|
+
(o do botão continua `AAMMDD-NNN`) e sem caixa. O card na conversa sai sem autor, com o nome do atendente.
|
|
610
|
+
|
|
611
|
+
Cuidado ao ler "Feitas atendidas": pelo botão a central marca atendida sempre (é a perna em que o atendente atende o
|
|
612
|
+
próprio ramal); pelo aparelho, só quando o cliente atende. Não compare as duas taxas como se medissem a mesma coisa.
|
|
613
|
+
Ramal sem cadastro não gera registro — se o cliente diz que "liguei e não apareceu", confira os ramais primeiro.
|
|
614
|
+
|
|
603
615
|
## Assinatura eletrônica de contratos (04/09)
|
|
604
616
|
|
|
605
617
|
### `status` do contrato (`signature_envelopes`)
|