@lionchat/mcp-server 1.8.1 → 1.10.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 +30 -2
- package/dist/docs/resources/api-conventions.md +14 -4
- package/dist/docs/resources/best-practices.md +82 -10
- package/dist/docs/resources/conversation-flows.md +73 -12
- package/dist/docs/resources/data-model.md +12 -4
- package/dist/docs/resources/flowbuilder-design-guide.md +68 -14
- package/dist/docs/resources/glossary.md +22 -8
- package/dist/docs/resources/kanban-deep-dive.md +35 -3
- package/dist/docs/resources/reports-guide.md +117 -16
- package/dist/tools.js +51 -9
- package/dist/tools.js.map +1 -1
- package/endpoints.json +333 -120
- package/package.json +1 -1
|
@@ -227,9 +227,15 @@ POST /api/v1/accounts/{account_id}/kanban_items
|
|
|
227
227
|
### Relatório de produtividade
|
|
228
228
|
```
|
|
229
229
|
1. lionchat_reports_summary (visão geral)
|
|
230
|
-
2. lionchat_reports_list (por agente — passa since/until
|
|
230
|
+
2. lionchat_reports_list (por agente — passa since/until em Unix EM SEGUNDOS)
|
|
231
231
|
3. Comparar avg_first_response_time entre agentes
|
|
232
232
|
```
|
|
233
|
+
**`since`/`until` dos relatórios só aceitam número Unix em SEGUNDOS** (`1782864000` = 01/07/2026 00:00
|
|
234
|
+
UTC), nunca ISO 8601 nem data por extenso — o formato ISO do resto da plataforma (seção 3) NÃO vale
|
|
235
|
+
aqui. Desde 29/07/2026 a API recusa com HTTP 400 (`invalid_date_params`); antes disso o relatório
|
|
236
|
+
voltava ZERADO com HTTP 200 (a conta 19 tinha 4.396 conversas no período e a resposta vinha zero).
|
|
237
|
+
Converta a data do usuário para segundos antes de chamar. Os exports em CSV (agents, inboxes, labels,
|
|
238
|
+
teams, conversations_summary) exigem as DUAS pontas; nos demais, `since` ou `until` sozinho é válido.
|
|
233
239
|
|
|
234
240
|
### Criar campanha de WhatsApp (disparo em massa)
|
|
235
241
|
```
|
|
@@ -242,6 +248,24 @@ POST /api/v1/accounts/{account_id}/kanban_items
|
|
|
242
248
|
Estrutura do audience: `[{type:'Label', id}, {type:'Funnel', id, stages:[], include_won, include_lost}, {type:'ConversationAttribute', key, value}]`.
|
|
243
249
|
QR Code aceita `template_params` com `delay_min/delay_max/variations` (anti-bloqueio) e `bulk_actions`.
|
|
244
250
|
|
|
251
|
+
### Criar campanha de FLUXO (dispara um flow, não uma mensagem) — novo 28/07/2026
|
|
252
|
+
```
|
|
253
|
+
1. lionchat_flows_list com with_campaign_trigger=true&inbox_id=N → flows ELEGÍVEIS
|
|
254
|
+
(vazio? o flow precisa do gatilho 'campaign_trigger' no bloco start — ver flowbuilder-design-guide)
|
|
255
|
+
2. Montar audience + lionchat_campaigns_estimate_audience → mostrar contagem ao usuário
|
|
256
|
+
3. CONFIRMAR com o usuário (é envio em massa — seção 7b)
|
|
257
|
+
4. lionchat_campaigns_create com flow_id + template_params {delay_min, delay_max, daily_cap}
|
|
258
|
+
(SEM message — o texto sai dos blocos do flow)
|
|
259
|
+
5. Acompanhar com lionchat_campaigns_flow_report; parar com lionchat_campaigns_stop_flow
|
|
260
|
+
```
|
|
261
|
+
A campanha inicia o flow para cada pessoa do público, cadenciado (intervalo sorteado entre uma pessoa
|
|
262
|
+
e outra) e com teto de PESSOAS por dia — quem passa do teto entra no dia seguinte, no mesmo horário,
|
|
263
|
+
automaticamente. Vale nas duas famílias de WhatsApp (oficial e QR Code).
|
|
264
|
+
|
|
265
|
+
Ao explicar o resultado, use `flow_report`: `fired` (disparados), `pending` (na fila) e `skips` (quem
|
|
266
|
+
foi pulado, com o motivo). Motivo mais comum: `sessao_ja_ativa` = a pessoa já estava nesse mesmo flow,
|
|
267
|
+
e pular evita que ela receba tudo em dobro. Quem está sendo atendido pela equipe **não** é pulado.
|
|
268
|
+
|
|
245
269
|
### Criar ferramenta da IA (ai_tool)
|
|
246
270
|
```
|
|
247
271
|
1. lionchat_flow_tools_create (tool_name snake_case + tool_description + flow_data com nó end)
|
|
@@ -274,11 +298,15 @@ QR Code aceita `template_params` com `delay_min/delay_max/variations` (anti-bloq
|
|
|
274
298
|
| Limite | Valor |
|
|
275
299
|
|---|---|
|
|
276
300
|
| `per_page` max | 100 |
|
|
277
|
-
| Bulk actions (IDs) max |
|
|
301
|
+
| Bulk actions (IDs) max | 300 por chamada (desde 18/05/2026; passou disso = HTTP 422 `bulk_ids_limit_exceeded`) |
|
|
278
302
|
| Rate limit leitura | 1200/min por token |
|
|
279
303
|
| Rate limit escrita | 600/min por token |
|
|
280
304
|
| Loop detection | bloqueia chamada idêntica >10x em 10s |
|
|
281
305
|
|
|
306
|
+
Exceção ao teto de 300: em ação em massa de **contatos** com `select_all: true` (agir sobre todos os
|
|
307
|
+
contatos do filtro) não há teto de IDs — o servidor refaz o filtro e processa em lotes no fundo. O
|
|
308
|
+
teto de 300 vale para o caminho normal, com a lista de IDs. Kanban usa o mesmo teto no `item_ids`.
|
|
309
|
+
|
|
282
310
|
## 7. Confirmar antes de agir — regra de ouro
|
|
283
311
|
|
|
284
312
|
Antes de QUALQUER ação que escreve, modifica, apaga ou envia, pare e avalie se o pedido está claro e completo. Na dúvida, pergunte — **nunca chute**.
|
|
@@ -349,17 +349,27 @@ Quando rate-limited:
|
|
|
349
349
|
|
|
350
350
|
### Bulk actions
|
|
351
351
|
|
|
352
|
-
`POST /bulk_actions` aceita arrays de IDs. **Máximo
|
|
352
|
+
`POST /bulk_actions` aceita arrays de IDs. **Máximo 300 IDs por chamada** (`MAX_BULK_IDS`, desde
|
|
353
|
+
2026-05-15). Acima disso o servidor recusa com `422` + `code: "bulk_ids_limit_exceeded"` — quebre em
|
|
354
|
+
chamadas de 300.
|
|
355
|
+
|
|
356
|
+
**Exceção:** contato com `select_all: true` (agir sobre TODOS os contatos do filtro) **não tem teto** —
|
|
357
|
+
o servidor resolve pelo próprio filtro (`query` / `labels_filter`), em lotes no fundo, e não usa `ids`.
|
|
353
358
|
|
|
354
359
|
```json
|
|
355
360
|
{
|
|
356
361
|
"type": "Conversation",
|
|
357
|
-
"ids": [1, 2, 3, ...],
|
|
358
|
-
"
|
|
359
|
-
"action_attributes": { "assignee_id": 6 }
|
|
362
|
+
"ids": [1, 2, 3, ...], // max 300
|
|
363
|
+
"fields": { "assignee_id": 6 } // conversa: status, assignee_id, team_id, inbox_id, captain_assistant_id
|
|
360
364
|
}
|
|
361
365
|
```
|
|
362
366
|
|
|
367
|
+
**⚠️ `action_attributes` NÃO EXISTE.** O servidor não lê esse campo em lugar nenhum (só aparece em
|
|
368
|
+
comentário de TODO no código). Mandar a mudança por ali devolve **200 OK e não faz absolutamente nada** —
|
|
369
|
+
falha silenciosa, sem erro na tela. Conversa se altera por `fields` (+ `labels: {add:[], remove:[]}` e
|
|
370
|
+
`snoozed_until`). Em conversa o `action_name` é **ignorado**; ele só tem efeito em **contato**, onde
|
|
371
|
+
`"delete"` apaga os contatos (sem ele, o que vale é `labels: {add: []}`).
|
|
372
|
+
|
|
363
373
|
### Search (overhaul 2026-06-10)
|
|
364
374
|
|
|
365
375
|
`GET /search?q=texto` — busca cross-entidade (conversations, contacts, messages, articles, kanban_items)
|
|
@@ -77,7 +77,14 @@ Conversa com 200 mensagens? **Não baixe tudo**:
|
|
|
77
77
|
## Operações em massa
|
|
78
78
|
|
|
79
79
|
Pra atribuir labels, mover cards, etc em vários itens:
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
- `kanban_bulk_bulk_actions` — **NÃO é de cards do Kanban**, apesar do nome. Ela bate em
|
|
82
|
+
`POST /bulk_actions` e age em **conversas ou contatos** (o `type` é `Conversation` ou `Contact`).
|
|
83
|
+
Usar ela achando que move card não dá erro visível — só não faz o que você queria.
|
|
84
|
+
- Cards do Kanban têm **três** ferramentas próprias, uma por ação:
|
|
85
|
+
- `kanban_bulk_create` → `bulk_move_items` (mover cards de etapa)
|
|
86
|
+
- `kanban_bulk_create_1` → `bulk_assign_agent` (definir o responsável)
|
|
87
|
+
- `kanban_bulk_create_2` → `bulk_set_priority` (definir a prioridade)
|
|
81
88
|
- `kanban_items_kanban_agents_create` em massa (passar array)
|
|
82
89
|
- `automation_rules_*` (criar regras em vez de fazer manualmente)
|
|
83
90
|
|
|
@@ -127,7 +134,32 @@ O `config` DEVE ser enviado como **objeto** (ex.: `config: { disabled_tools: [..
|
|
|
127
134
|
## Limite do prompt e cache OpenAI (2026-05-22)
|
|
128
135
|
|
|
129
136
|
- `config.instructions` (system prompt do agente) aceita até **20.000 caracteres** (antes 10k). Acima de 15k, o frontend mostra aviso "lost in the middle" — prefira colocar instruções críticas no início ou final.
|
|
130
|
-
- **Cache automático** ativo em todos os
|
|
137
|
+
- **Cache automático** ativo em todos os 16 modelos oferecidos na plataforma. Desconto 50-75% no input cachado, sem configuração. Reuso do prompt do agente em múltiplas conversas maximiza a economia.
|
|
138
|
+
|
|
139
|
+
### Lista real de modelos (`config.model`)
|
|
140
|
+
|
|
141
|
+
Estes são os 16 valores que o seletor de modelo do painel oferece (`ModelSelector.vue`). Só recomende
|
|
142
|
+
valores desta lista:
|
|
143
|
+
|
|
144
|
+
| Faixa | Modelos |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Econômicos / rápidos | `gpt-4.1-nano`, `gpt-4o-mini`, `gpt-4.1-mini`, `gpt-5.4-nano`, `gpt-5.4-mini` |
|
|
147
|
+
| Intermediários | `gpt-4o`, `gpt-4.1`, `gpt-5-mini`, `gpt-5.4` |
|
|
148
|
+
| Raciocínio | `o3-mini`, `o4-mini` |
|
|
149
|
+
| Premium | `gpt-5`, `gpt-5.2`, `gpt-5.5`, `o1`, `o3` |
|
|
150
|
+
|
|
151
|
+
**`GPT-5.2 Pro` não existe.** O valor válido é `gpt-5.2`, sem "Pro".
|
|
152
|
+
|
|
153
|
+
> **PERIGO — família `gpt-5.6` (`gpt-5.6-luna`, `gpt-5.6-terra`, `gpt-5.6-sol`): REMOVIDA em 29/07/2026.**
|
|
154
|
+
> Esses modelos **recusam function tools** em `/v1/chat/completions` (erro 400 real: "Function tools with
|
|
155
|
+
> reasoning_effort are not supported"). Como o AI Agente usa ferramentas em **toda** resposta, 100% das
|
|
156
|
+
> chamadas falhavam e a IA ficava **MUDA** — sem erro na tela, sem resposta. Ficou 2 semanas invisível.
|
|
157
|
+
> **Nunca sugira, nunca grave** um `gpt-5.6` — nem se o cliente pedir "o mais novo/mais forte".
|
|
158
|
+
>
|
|
159
|
+
> O campo `config.model` **não tem lista branca no servidor**: ele é `store_accessor` do `config` e
|
|
160
|
+
> aceita qualquer texto sem validar. Modelo inválido salva com 200 e só quebra na hora de responder.
|
|
161
|
+
> Desde 29/07 o erro vira uma **nota privada em português** na conversa (`llm_request_rejected`) — é o
|
|
162
|
+
> primeiro lugar a olhar quando "a IA parou de responder logo depois de mexerem nas configurações".
|
|
131
163
|
|
|
132
164
|
## Follow-up automático multi-etapa (2026-06)
|
|
133
165
|
|
|
@@ -137,21 +169,61 @@ O follow-up do AI Agente agora suporta **cadência de até 3 etapas** via `confi
|
|
|
137
169
|
{ "config": { "feature_follow_up": true, "follow_up_steps": [
|
|
138
170
|
{ "after_minutes": 30, "prompt": "Pergunte gentilmente se ainda tem interesse" },
|
|
139
171
|
{ "after_minutes": 240, "prompt": "Ofereça tirar dúvidas" },
|
|
140
|
-
{ "after_minutes":
|
|
172
|
+
{ "after_minutes": 1110, "prompt": "Última tentativa, despeça-se cordialmente" }
|
|
141
173
|
] } }
|
|
142
174
|
```
|
|
143
175
|
|
|
144
|
-
Regras: cada etapa ≥ 5 min; soma total ≤
|
|
145
|
-
`follow_up_time` + `follow_up_prompt` continuam funcionando como 1 etapa única
|
|
176
|
+
Regras: cada etapa ≥ 5 min; **soma total ≤ 1380 min (23h)**; máx 3 etapas. Campos legados
|
|
177
|
+
`follow_up_time` + `follow_up_prompt` continuam funcionando como 1 etapa única (e obedecem ao **mesmo
|
|
178
|
+
teto de 1380**).
|
|
179
|
+
|
|
180
|
+
> **O teto é 1380, não 1440** (`Captain::Assistant::FOLLOW_UP_MAX_TOTAL_MINUTES`). Motivo: a janela de
|
|
181
|
+
> 24h do WhatsApp conta a partir da **mensagem do cliente**, não do primeiro acompanhamento — 1h de
|
|
182
|
+
> folga evita agendar uma cobrança que a Meta recusaria na hora de enviar. Montar a cadência somando
|
|
183
|
+
> 1440 faz o salvamento ser **recusado**. Pegadinha: a mensagem de erro do campo legado `follow_up_time`
|
|
184
|
+
> ainda diz "must be between 5 and 1440 minutes", mas a validação usa 1380 — não confie no texto do erro.
|
|
185
|
+
|
|
186
|
+
(O exemplo acima soma exatamente 1380: 30 + 240 + 1110.)
|
|
187
|
+
|
|
146
188
|
Dispara quando o CLIENTE fica inativo após resposta da IA. O motor de follow-up é dedicado e
|
|
147
189
|
**só-leitura** (não executa ferramentas, não coleta dado — só redige a mensagem de retomada,
|
|
148
190
|
com saída estruturada garantida). `paused=true` no assistente corta os follow-ups também.
|
|
149
191
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
192
|
+
### "A IA nunca faz o follow-up" — o conserto é de 29/07, NÃO de 24/07
|
|
193
|
+
|
|
194
|
+
**Não repita que isso foi corrigido em 24/07.** A versão de 24/07 agendava o follow-up **dentro** do
|
|
195
|
+
bloco do "Responder na hora": ou seja, ela só funcionava quando esse interruptor estava **LIGADO** —
|
|
196
|
+
justamente o caso que menos precisa dela. Com "Responder na hora" **desligado**, a IA não fala, o único
|
|
197
|
+
outro gatilho (o hook de mensagem enviada) também não dispara, e a conversa fica **com IA ativa e sem
|
|
198
|
+
nenhum acompanhamento, para sempre**. Aconteceu em **140 conversas da conta 52** — todas criadas
|
|
199
|
+
DEPOIS do suposto conserto de 24/07. A ativação **manual pela tela** nunca esteve coberta em nenhuma
|
|
200
|
+
das duas versões até 29/07.
|
|
201
|
+
|
|
202
|
+
**"Responder na hora" e acompanhamento são INDEPENDENTES.** São perguntas diferentes:
|
|
203
|
+
- *Responder na hora* (`captain_reply_now` / `proactive`) = "a IA fala agora, sem esperar o cliente?"
|
|
204
|
+
- *Acompanhamento* (`feature_follow_up`) = "a IA cobra depois, se ninguém responder?"
|
|
205
|
+
|
|
206
|
+
Desligar o primeiro **não** deve desligar o segundo. Nunca oriente o cliente a ligar "Responder na
|
|
207
|
+
hora" para o acompanhamento funcionar.
|
|
208
|
+
|
|
209
|
+
**Corrigido de verdade em 2026-07-29**, nos três caminhos que ativam a IA, com o agendamento **fora**
|
|
210
|
+
do bloco do "Responder na hora":
|
|
211
|
+
- Flow, ação "Ativar IA" (`FlowBuilder::ActionDelegator`) — só em ativação nova (a conversa não tinha IA)
|
|
212
|
+
- Automação, ação "Atribuir assistente" (`AutomationRules::ActionService`)
|
|
213
|
+
- Ativação manual pela tela da conversa (`ConversationsController`) — armado **antes** da checagem do
|
|
214
|
+
"Responder na hora"
|
|
215
|
+
|
|
216
|
+
**Ainda descoberto (não corrigido):** ativar a IA em **lote** (ação em massa de conversas com
|
|
217
|
+
`captain_assistant_id`) **não arma o acompanhamento nem dispara resposta imediata**. Se o cliente
|
|
218
|
+
ativou a IA em massa e reclama que ela não cobra de volta, é isso — e o caminho é reativar pela tela
|
|
219
|
+
ou por automação.
|
|
220
|
+
|
|
221
|
+
Diagnóstico, nesta ordem, quando relatarem "a IA nunca faz o follow-up":
|
|
222
|
+
1. `config.feature_follow_up` está ligado no assistente? E `paused` está desligado? (`paused=true`
|
|
223
|
+
corta os follow-ups)
|
|
224
|
+
2. A ativação veio por **lote**? Então nunca foi armado.
|
|
225
|
+
3. Tem `follow_up_skip_conditions` batendo (etiqueta, atributo, janela de silêncio)?
|
|
226
|
+
4. A soma das etapas passa de 1380 min? Nesse caso o assistente nem salvou a cadência.
|
|
155
227
|
|
|
156
228
|
## Condições para NÃO fazer follow-up + horário de silêncio (2026-07)
|
|
157
229
|
|
|
@@ -98,17 +98,65 @@ Avaliação:
|
|
|
98
98
|
|
|
99
99
|
### 4. Auto-assignment
|
|
100
100
|
|
|
101
|
-
Roda após automation rules.
|
|
101
|
+
Roda após automation rules. Existem **DOIS caminhos vivos** (`Inbox#auto_assignment_v2_enabled?`),
|
|
102
|
+
não um moderno e um legado:
|
|
102
103
|
|
|
103
104
|
```
|
|
104
|
-
SE inbox.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
SE inbox.assignment_policy vinculada:
|
|
106
|
+
→ modo POLÍTICA: round-robin ou balanced, prioridade da fila, capacidade
|
|
107
|
+
SENÃO SE inbox.enable_auto_assignment (botão "Distribuição automática" da própria caixa):
|
|
108
|
+
→ modo SIMPLES: round-robin entre os membros da caixa
|
|
109
|
+
SENÃO:
|
|
110
|
+
→ assignee_id fica null (atribuição manual)
|
|
109
111
|
```
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
Os dois caminhos rodam no MESMO serviço (`AutoAssignment::AssignmentService`), usam o MESMO rodízio
|
|
114
|
+
(`AutoAssignment::RoundRobinSelector` — só a política pode trocá-lo pelo modo `balanced`) e passam
|
|
115
|
+
pelo MESMO freio de rajada
|
|
116
|
+
(`AutoAssignment::RateLimiter`). O que muda é de onde vem a configuração: com política vinculada, ela
|
|
117
|
+
manda em tudo (inclusive em "distribuir para agentes offline"); sem política, vale o que está no
|
|
118
|
+
`auto_assignment_config` da própria caixa.
|
|
119
|
+
|
|
120
|
+
**NUNCA escreva que o modo simples é legado, nem recomende criar Política de Atribuição para quem só
|
|
121
|
+
quer o botão da caixa.** Vincular uma política **esconde a seção inteira de Distribuição Automática da
|
|
122
|
+
tela da caixa** (`CollaboratorsPage.vue`: `showAutoDistribution = !inbox.has_assignment_policy`). O
|
|
123
|
+
cliente perde os controles que estava usando, não entende por que sumiram, e o que ele configurou lá
|
|
124
|
+
passa a ser ignorado (o valor fica gravado e só volta a valer se a política for desvinculada).
|
|
125
|
+
|
|
126
|
+
Regra prática:
|
|
127
|
+
- Cliente quer só "dividir os leads entre a equipe" → **botão da própria caixa**. Não crie política.
|
|
128
|
+
- Cliente quer regra compartilhada entre várias caixas, modo balanced, prioridade de fila ou limite de
|
|
129
|
+
capacidade por atendente → aí sim Política de Atribuição.
|
|
130
|
+
|
|
131
|
+
Quem entra no rodízio: só `inbox_members` com `auto_assignable: true` (supervisor de caixa fica de
|
|
132
|
+
fora) e, se "distribuir para agentes offline" estiver desligado, só quem está online.
|
|
133
|
+
|
|
134
|
+
**LEIA o valor da conta antes de responder — NUNCA recite um número de cabeça.** Os valores reais
|
|
135
|
+
vêm na resposta da API:
|
|
136
|
+
- `has_assignment_policy` (resposta da caixa) diz QUAL dos dois caminhos vale ali. `true` = política
|
|
137
|
+
manda; `false` = vale o botão da própria caixa.
|
|
138
|
+
- Com política vinculada, leia `fair_distribution_limit` (quantas conversas por atendente),
|
|
139
|
+
`fair_distribution_window` (tamanho da janela, **em segundos**) e `assign_offline_agents` na
|
|
140
|
+
resposta da política. O `auto_assignment_config` da caixa é **IGNORADO** nesse caso — não o cite.
|
|
141
|
+
- Sem política, leia as MESMAS chaves dentro do bloco `auto_assignment_config` da caixa
|
|
142
|
+
(`fair_distribution_limit`, `fair_distribution_window`, `assign_offline_agents`).
|
|
143
|
+
- Chave ausente/vazia = vale o padrão de fábrica (hoje 10 conversas a cada 600 segundos) — cite isso
|
|
144
|
+
só como "valor de fábrica, confira o da conta", nunca como se fosse a configuração do cliente.
|
|
145
|
+
|
|
146
|
+
**EXCEÇÃO SEM FREIO (não dá pra descobrir lendo a resposta):** caixa **sem** política vinculada, com
|
|
147
|
+
`assign_offline_agents` LIGADO e **sem número escolhido** em `fair_distribution_limit`, roda **SEM
|
|
148
|
+
FREIO NENHUM** (`AutoAssignment::RateLimiter#resolve_limit`: `return 0 if inbox.assign_offline_agents?`).
|
|
149
|
+
Racional (decisão do dono 29/07): com offline ligado todo membro está sempre na roleta, então a divisão
|
|
150
|
+
já é igual por construção e o freio só atrasaria a entrega. Não existe campo dizendo "sem freio" — o
|
|
151
|
+
bloco vazio parece "padrão", mas não é. Efeito prático: **nessas caixas, dizer ao cliente que existe um
|
|
152
|
+
teto por atendente é MENTIRA**. Número escolhido pelo cliente sempre vence o toggle: se
|
|
153
|
+
`fair_distribution_limit` tem um número MAIOR QUE ZERO, o freio existe mesmo com offline ligado.
|
|
154
|
+
|
|
155
|
+
**Zero também é "sem freio":** na caixa (não na política), `fair_distribution_limit: 0` é a saída
|
|
156
|
+
explícita de quem não quer teto nenhum — o freio fica desligado (`RateLimiter#enabled?` exige limite
|
|
157
|
+
positivo). Ou seja, existem DUAS formas de a caixa ficar sem freio: campo vazio com offline ligado, ou
|
|
158
|
+
zero escrito de propósito. Na Política de Atribuição o zero nem é aceito (o cadastro exige maior que
|
|
159
|
+
zero), então política vinculada SEMPRE tem freio.
|
|
112
160
|
|
|
113
161
|
### 5. Captain (IA Agente)
|
|
114
162
|
|
|
@@ -138,7 +186,10 @@ handoff consultor→gestor). Pendente só acontece por ação explícita:
|
|
|
138
186
|
- Ferramenta nativa da IA (deixar conversa pendente)
|
|
139
187
|
|
|
140
188
|
Comportamentos importantes:
|
|
141
|
-
- Mensagem nova do
|
|
189
|
+
- Mensagem nova do CLIENTE REABRE conversa pendente (volta pra Aberta, com pílula de aviso na
|
|
190
|
+
conversa). Só a do cliente: resposta do ATENDENTE não reabre. Vale desde 30/07/2026 e REVOGA a
|
|
191
|
+
regra anterior de 20/07 ("pendente não reabre") — decisão do dono depois de a conta 19 acumular
|
|
192
|
+
258 clientes esperando sem o time ver. NUNCA responda que a conversa fica parada em Pendente
|
|
142
193
|
- Pendente ATRIBUÍDA notifica o responsável (o gestor recebe o aviso)
|
|
143
194
|
|
|
144
195
|
### 7. Resolução (status 1)
|
|
@@ -186,10 +237,20 @@ Conversation
|
|
|
186
237
|
|
|
187
238
|
### "Conversa não atribuiu ninguém"
|
|
188
239
|
|
|
189
|
-
1. `inbox.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
240
|
+
1. A distribuição está ligada em ALGUM dos dois caminhos? `inbox.assignment_policy` vinculada **ou**
|
|
241
|
+
`inbox.enable_auto_assignment` true. Não existe campo `enable_auto_assignment_v2` — não peça por ele.
|
|
242
|
+
Política vinculada **não é requisito**: caixa sem política e com o botão ligado distribui normalmente.
|
|
243
|
+
2. Se há política: ela está com `enabled: true`? Política desligada = ninguém é atribuído.
|
|
244
|
+
3. Tem `InboxMember` com `auto_assignable: true`? Supervisor de caixa é membro mas **não entra** no rodízio.
|
|
245
|
+
4. Se "distribuir para agentes offline" está desligado, tem alguém **online** agora? Sem ninguém online
|
|
246
|
+
a fila fica parada até alguém logar.
|
|
247
|
+
5. O atendente pode estar no **teto da janela** do freio de rajada (o robô só entrega N conversas por
|
|
248
|
+
atendente/caixa dentro de uma janela deslizante). A vaga volta quando a conversa é resolvida, adiada
|
|
249
|
+
ou vira pendente — e a fila anda sozinha em até 1 minuto. Antes de culpar o freio, confira os
|
|
250
|
+
valores reais da conta e a exceção SEM FREIO (seção 4) — pode não haver teto nenhum ali.
|
|
251
|
+
6. Se a caixa tem **uma única** conversa esperando, o freio **nem se aplica** (`should_apply_rate_limit?`
|
|
252
|
+
exige mais de 1 não atribuída). Nesse caso a causa é outra — não culpe o freio.
|
|
253
|
+
7. Tem `automation_rule` que poderia ter atribuído antes?
|
|
193
254
|
|
|
194
255
|
### "IA não respondeu"
|
|
195
256
|
|
|
@@ -245,15 +245,23 @@ Captain::Assistant
|
|
|
245
245
|
├── config (jsonb) — chaves principais:
|
|
246
246
|
│ model, temperature, instructions (max 20.000 chars), feature_memory, feature_faq,
|
|
247
247
|
│ feature_follow_up (bool), follow_up_steps (array de {after_minutes, prompt} — ATE 3 etapas,
|
|
248
|
-
│ cada uma >= 5 min, soma <=
|
|
248
|
+
│ cada uma >= 5 min, soma <= 1380 min = 23h (2026-07-29 — NAO 1440: a janela de 24h do WhatsApp
|
|
249
|
+
│ conta da MENSAGEM DO CLIENTE, nao do 1o follow-up, entao 1440 disparava com a janela ja fechada.
|
|
250
|
+
│ Somar 1440 = 422. GOTCHA: no campo legado follow_up_time a mensagem de erro do servidor ainda
|
|
251
|
+
│ diz "must be between 5 and 1440 minutes" por engano — o teto que ele aplica e 1380);
|
|
252
|
+
│ legado follow_up_time+follow_up_prompt = 1 etapa),
|
|
249
253
|
│ follow_up_skip_conditions (array — condicoes pra NAO fazer follow-up, logica OU, ate 3; tipos:
|
|
250
254
|
│ label {operator present|absent, labels[]}; contact_attr/conversation_attr {attribute, operator
|
|
251
255
|
│ equal|not_equal|contains|present|blank|gt|lt, value}; time_window {start, end — horas inteiras
|
|
252
256
|
│ 0-23; NAO envia follow-up nesse periodo, janela circular ex 22->7; a IA SEGUE respondendo o
|
|
253
257
|
│ cliente normal — o silencio e so do follow-up}),
|
|
254
|
-
│ feature_pause_on_human_reply (bool — a IA se DESLIGA sozinha na
|
|
255
|
-
│ assume: atendente responde pelo painel (ao vivo) OU mensagem enviada
|
|
256
|
-
│ nota privada, msg da propria IA, automacao, campanha, confirmacao de
|
|
258
|
+
│ feature_pause_on_human_reply (bool, nasce DESLIGADO — quando ligado, a IA se DESLIGA sozinha na
|
|
259
|
+
│ conversa quando um HUMANO assume: atendente responde pelo painel (ao vivo) OU mensagem enviada
|
|
260
|
+
│ do celular. NAO conta: nota privada, msg da propria IA, automacao, campanha, confirmacao de
|
|
261
|
+
│ agendamento. MENSAGEM AGENDADA DEPENDE DE QUEM AGENDOU (2026-07-29): agendada pela IA (a tool
|
|
262
|
+
│ carimba source_type='Captain::Assistant' e o disparo assina como o assistente) NAO desliga;
|
|
263
|
+
│ agendada por ATENDENTE assina como User com Current.user setado no disparo = conta como
|
|
264
|
+
│ atendente ao vivo e DESLIGA a IA da conversa),
|
|
257
265
|
│ activation_label (etiqueta que ativa o agente — unica por conta),
|
|
258
266
|
│ min_response_time / max_response_time (delay humanizado, 1-60s),
|
|
259
267
|
│ disabled_tools (array de IDs de tools desativadas pro assistente),
|
|
@@ -60,7 +60,7 @@ Todo node tem essa estrutura base:
|
|
|
60
60
|
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
**Triggers válidos:** `message_received`, `message_sent`, `conversation_created`, `conversation_resolved`, `conversation_reopened`, `label_added`, `label_removed`, `card_created`, `card_moved`, `conversation_attribute_changed`, `card_attribute_changed`, `contact_attribute_changed`, `date_trigger`, `cron`, `webhook`.
|
|
63
|
+
**Triggers válidos:** `message_received`, `message_sent`, `conversation_created`, `conversation_resolved`, `conversation_reopened`, `label_added`, `label_removed`, `card_created`, `card_moved`, `conversation_attribute_changed`, `card_attribute_changed`, `contact_attribute_changed`, `date_trigger`, `campaign_trigger`, `cron`, `webhook`.
|
|
64
64
|
|
|
65
65
|
**Trigger `message_sent` (novo 2026-06-11):** par do message_received, mas pra mensagens de SAÍDA — dispara quando atendente, celular (eco do WhatsApp) ou a própria IA/flow envia mensagem (nota privada NÃO dispara). Config: `keywords` (opcional) + `match_type` (`contains`|`exact`). Caso de uso clássico: "quando eu responder do celular, desligar a IA". ATENÇÃO: mensagem da IA também dispara — se a ação for desativar a IA, use keywords que só humanos digitam ou aceite que a primeira resposta da IA aciona o flow. Protegido por anti-loop (profundidade 5) e sessão única por conversa+flow; nunca alimenta `waiting_input`.
|
|
66
66
|
|
|
@@ -95,6 +95,35 @@ Todo node tem essa estrutura base:
|
|
|
95
95
|
- **`overwrite_mode` (novo 2026-07-18):** `'replace'` (default) = trocar a data cancela o agendamento anterior (correção de data errada não dispara em dobro). `'keep_both'` = acumula (cada data seu próprio disparo) — **SÓ com `attr_source: 'conversation'`** (vetado no contato) e **incompatível com `repeat_yearly`**. Os disparos futuros pendentes ficam visíveis/canceláveis na aba "Agendados" do editor.
|
|
96
96
|
- **Ver/cancelar os agendamentos (novo 2026-07-20):** `flows_scheduled_firings_list` (flow_id) lista os disparos FUTUROS pendentes do Gatilho de Data (quando vai disparar, atributo/valor que gerou, fonte contato/conversa, contato/conversa alvo). `flows_scheduled_firings_cancel` (flow_id, id) cancela um — se já disparou responde 409 `ja_disparado` e nada muda. Só flows com Gatilho de Data; ADMIN + flowbuilder_manage.
|
|
97
97
|
|
|
98
|
+
**Trigger `campaign_trigger` — Gatilho "Campanha" (novo 2026-07-28):** LIBERA o flow para ser disparado
|
|
99
|
+
por uma **Campanha de Fluxo** (Campanhas > Fluxo). É uma **AUTORIZAÇÃO, não um evento**: sozinho ele
|
|
100
|
+
nunca dispara nada — quem dispara é a campanha, pessoa por pessoa, no ritmo configurado nela.
|
|
101
|
+
|
|
102
|
+
Item (sem `config`): `{ "key": "campaign_trigger" }`
|
|
103
|
+
|
|
104
|
+
- **Sem esse item o flow NÃO aparece na lista da campanha** e `campaigns_create` com `flow_id` responde
|
|
105
|
+
422. É o passo que todo mundo esquece — ao montar um flow para disparo em massa, **ligue este gatilho
|
|
106
|
+
antes de criar a campanha**.
|
|
107
|
+
- Pode conviver com outros gatilhos no mesmo flow (ex.: um flow que responde a mensagem E também pode
|
|
108
|
+
ser disparado em campanha). É **isento** da trava de gatilho duplicado.
|
|
109
|
+
- Só flow de conversa (`flow_type: conversation`); flow de grupo funciona, mas o público é de contatos.
|
|
110
|
+
- Em caixa WhatsApp **oficial**, o primeiro bloco de mensagem de **CADA CAMINHO** que sai do Início
|
|
111
|
+
precisa abrir com **template aprovado** — não só o primeiro bloco que o flow encontra. Com
|
|
112
|
+
randomizador (teste A/B) ou condição logo no começo, **todas** as variações são conferidas: basta
|
|
113
|
+
UMA sem template pra campanha ser recusada na criação (422). O erro nomeia os blocos culpados pelo
|
|
114
|
+
rótulo, teto de 5: `must start with an approved WhatsApp template on official inboxes (no template
|
|
115
|
+
in: <rótulos>)`. O que vale é o **primeiro item** do `messageItems` daquele bloco, e os tipos
|
|
116
|
+
aceitos são `template` (o que a tela grava) e `whatsapp_template` (legado). Flow sem nenhum bloco
|
|
117
|
+
de mensagem não tem o que validar. Em QR Code não existe essa regra.
|
|
118
|
+
(Corrigido em 2026-07-29: até então a checagem aceitava só `whatsapp_template` — nome que a tela
|
|
119
|
+
NUNCA gravou — e olhava um caminho só; recusava 100% das caixas oficiais.)
|
|
120
|
+
- Como a campanha inicia o flow com o contato **sem ter mandado nada**, escreva o 1º bloco assumindo
|
|
121
|
+
contato frio (apresente-se, dê contexto) — diferente de um flow de atendimento, que responde a alguém.
|
|
122
|
+
|
|
123
|
+
Receita completa: `flows_create`/`flows_update` com o item no start → `flows_list` com
|
|
124
|
+
`with_campaign_trigger=true&inbox_id=N` para confirmar que ficou elegível → `campaigns_create` com
|
|
125
|
+
`flow_id`. Acompanhar: `campaigns_flow_report`. Interromper: `campaigns_stop_flow`.
|
|
126
|
+
|
|
98
127
|
**Trigger `webhook` — Webhook Universal EMBUTIDO (novo 2026-06):** o flow pode ser disparado por um webhook próprio, criado automaticamente. Receita via API:
|
|
99
128
|
1. Criar o flow normalmente (`flows_create`).
|
|
100
129
|
2. `POST /custom_webhook_integrations` com `{ "custom_webhook_integration": { "flow_id": <id do flow> } }` — o sistema cria a integração embutida (idempotente: repetir retorna a mesma; nome automático "Flow: <nome>"; auto-mapeia todos os eventos → este flow) e retorna a URL única do webhook.
|
|
@@ -102,7 +131,7 @@ Todo node tem essa estrutura base:
|
|
|
102
131
|
4. Salvar o flow (`flows_update`) — o save sincroniza a ativação do webhook embutido (remover o item desativa a integração automaticamente).
|
|
103
132
|
Webhooks embutidos NÃO aparecem na listagem de integrações standalone; excluir o flow destrói o webhook; duplicar o flow NÃO copia o gatilho embutido. Rate limit do endpoint público: 60/min por token.
|
|
104
133
|
|
|
105
|
-
**TRAVA DE GATILHO DUPLICADO (2026-06-16) — leia ANTES de ativar/criar flow ativo:** o sistema BLOQUEIA ter dois flows ATIVOS com o MESMO gatilho na MESMA inbox e mesmo `conversation_mode` (evita o evento disparar dois flows). Colisão = mesmo tipo de gatilho + config cruzando (mesmas keywords/funil/labels/url/etc) + inbox compartilhada + mesmo modo. EXCEÇÕES que podem coexistir: `webhook_received` e `
|
|
134
|
+
**TRAVA DE GATILHO DUPLICADO (2026-06-16) — leia ANTES de ativar/criar flow ativo:** o sistema BLOQUEIA ter dois flows ATIVOS com o MESMO gatilho na MESMA inbox e mesmo `conversation_mode` (evita o evento disparar dois flows). Colisão = mesmo tipo de gatilho + config cruzando (mesmas keywords/funil/labels/url/etc) + inbox compartilhada + mesmo modo. EXCEÇÕES que podem coexistir: `webhook_received`, `manual_trigger` e `campaign_trigger` (este último porque não dispara por evento — é só uma autorização para a campanha).
|
|
106
135
|
- Ao **ativar** (`flows_toggle` inativo→ativo) ou **criar já ativo**: qualquer conflito é barrado.
|
|
107
136
|
- Ao **editar** um flow já ativo: só conflito NOVO é barrado (duplicados que já existiam são preservados).
|
|
108
137
|
- A API responde **422** com `{ "error_code": "flow_trigger_conflict", "conflicts": [{ flow_id, flow_name, trigger_type, inbox_id, inbox_name }] }`.
|
|
@@ -363,7 +392,7 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
|
|
|
363
392
|
|
|
364
393
|
| Key | config esperado | Efeito |
|
|
365
394
|
|---|---|---|
|
|
366
|
-
| `assign_agent` | `{ agent_id }` | Atribui agente humano à conversa. `agent_id` aceita id fixo
|
|
395
|
+
| `assign_agent` | `{ agent_id }` | Atribui agente humano à conversa. `agent_id` aceita id fixo, variável Liquid (ver nota abaixo) OU a string `'nil'` — que REMOVE o responsável da conversa (opção "Nenhum" da UI) |
|
|
367
396
|
| `distribute_agents` | `{ agents: [{ agent_id }], dist_id }` | RODÍZIO (round-robin) de agentes: cada lead vai pro PRÓXIMO da lista na vez (1,2,3,1,2,3). `dist_id` = id fixo da ação (chave do cursor no Redis; gere um único por ação, ex. `"d_ab12cd"`). Ordem da lista = ordem do rodízio; sem porcentagem. DIFERENTE do randomizer mode `distribute_agents` (que é sorteio ponderado) |
|
|
368
397
|
| `assign_team` | `{ team_id }` | Atribui time. `team_id` aceita id fixo OU variável Liquid — ver nota abaixo |
|
|
369
398
|
| `change_status` | `{ status: 'open' \| 'resolved' \| 'pending' \| 'snoozed' }` | Muda status da conversa |
|
|
@@ -380,7 +409,7 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
|
|
|
380
409
|
| `set_kanban_item_status` | `{ status: 'won' \| 'lost' \| 'active' }` | Marca status do card |
|
|
381
410
|
| `set_won` | `{}` | Atalho pra ganho |
|
|
382
411
|
| `set_lost` | `{ reason? }` | Atalho pra perdido |
|
|
383
|
-
| `assign_agent_card` | `{ agent_id }` |
|
|
412
|
+
| `assign_agent_card` | `{ agent_id, mode? }` | Responsável do card. `mode`: `'add'` (default — SOMA na lista), `'replace'` (só o escolhido fica; atribui antes de remover os demais) ou `'remove_all'` (tira TODOS os responsáveis; `agent_id` dispensado) |
|
|
384
413
|
| `add_card_note` | `{ content }` | Nota no card |
|
|
385
414
|
| `add_card_checklist` | `{ template_id, funnel_id?, card_source? }` | Aplica um MODELO de checklist ao card (vira um grupo). `template_id` de `kanban_config.checklist_templates`. Um modelo por bloco (repita o bloco pra mais de um) |
|
|
386
415
|
| `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) |
|
|
@@ -397,6 +426,10 @@ o botão "Usar variável" ao lado do campo alterna lista fixa ↔ variável.
|
|
|
397
426
|
|
|
398
427
|
**`update_attribute` — campos EXATOS:** `attr_source` (`'contact'`, `'conversation'` ou `'card'`), `attr_key` (nome do atributo), `attr_value` (valor). NÃO existem `entity`/`key`/`value` — esses são ignorados e não salvam nada.
|
|
399
428
|
|
|
429
|
+
**Coerção por TIPO (2026-07-29):** em `attr_source: 'contact'|'conversation'`, o `attr_value` de atributo personalizado é coagido pelo tipo da definição antes de gravar: número/moeda/porcentagem viram numérico ("1.234,56" → 1234.56; inteiro fica inteiro), checkbox aceita sim/não/true/false e grava BOOLEANO, lista casa caixa-insensível e grava a opção canônica, data aceita DD/MM/AAAA ou ISO, hora aceita "14:30"/"14h30". Valor irreconhecível grava como veio e o passo mostra nota âmbar no histórico. Card NÃO coage (atributo de card não tem definição tipada no backend).
|
|
430
|
+
|
|
431
|
+
**Campos NATIVOS do contato (2026-07-29):** com `attr_source: 'contact'`, `attr_key` também aceita os paths canônicos `contact.name`, `contact.email`, `contact.cpf`, `contact.cnpj`, `contact.rg`, `contact.passport`, `contact.date_of_birth`, `contact.gender`, `contact.marital_status`, `contact.profession` e `contact.address.{cep,street,number,complement,neighborhood,city,state,country}` — gravam na ficha nativa do contato (não em custom_attributes). Regras: telefone/identifier/etiquetas NÃO são aceitos (mapa fechado — chave fora dele vira atributo personalizado comum); documentos, nascimento, gênero e **e-mail** só preenchem campo VAZIO; nome/profissão/estado civil/endereço sobrescrevem; valor em branco nunca grava; gênero aceita `m|f|o|na` e estado civil `solteiro|casado|uniao_estavel|divorciado|separado|viuvo`; data de nascimento em `dd/mm/aaaa` ou `aaaa-mm-dd`. Recusas aparecem no histórico de execução (passo 'skipped'/'error' com mensagem).
|
|
432
|
+
|
|
400
433
|
**Somar/subtrair NÃO é operação dedicada** — é filtro Liquid no próprio `attr_value`. O valor é resolvido como template antes de salvar. Exemplos:
|
|
401
434
|
|
|
402
435
|
```json
|
|
@@ -476,14 +509,22 @@ NÃO existe `.payload`/`.response` (`{{api_response.payload.x}}` resolve vazio).
|
|
|
476
509
|
|
|
477
510
|
**Modelo por ação — `aiModel` + `aiModelExplicit` (2026-07-13):** TODO modo aceita escolher o modelo
|
|
478
511
|
LLM no próprio nó, com ou sem assistente, em qualquer tipo de flow. Enviar o par:
|
|
479
|
-
`"aiModel": "gpt-4.1-mini"`
|
|
480
|
-
|
|
481
|
-
`
|
|
482
|
-
|
|
483
|
-
conta.
|
|
484
|
-
assistente usaria). Modelo fora da whitelist = ignorado (cai no padrão da conta).
|
|
512
|
+
`"aiModel": "gpt-4.1-mini"` JUNTO de `"aiModelExplicit": true`. SEM a flag o backend ignora o
|
|
513
|
+
aiModel nos modos novos (proteção de nós antigos que carregam aiModel de default nunca honrado).
|
|
514
|
+
`aiModel` vazio/ausente = modelo padrão da conta. Com assistente selecionado, o override também vale
|
|
515
|
+
(sobrepõe o modelo que o caminho do assistente usaria). Modelo fora da whitelist = ignorado (cai no
|
|
516
|
+
padrão da conta).
|
|
485
517
|
Exemplo: `{"aiMode":"sentiment","aiAssistantId":"12","aiModel":"gpt-4.1-mini","aiModelExplicit":true}`.
|
|
486
518
|
|
|
519
|
+
**A whitelist (`FlowBuilder::RawLlmService::SUPPORTED_MODELS`) é uma lista FECHADA de 16 nomes, SEM
|
|
520
|
+
curinga** — não existe "a família gpt-5 inteira":
|
|
521
|
+
`gpt-4o-mini`, `gpt-4o`, `gpt-4.1-nano`, `gpt-4.1-mini`, `gpt-4.1`, `gpt-5-mini`, `gpt-5`,
|
|
522
|
+
`gpt-5.2`, `gpt-5.4-nano`, `gpt-5.4-mini`, `gpt-5.4`, `gpt-5.5`, `o1`, `o3`, `o3-mini`, `o4-mini`.
|
|
523
|
+
A família **`gpt-5.6` (luna/terra/sol) foi REMOVIDA em 2026-07-29**: ela recusa function tools
|
|
524
|
+
(erro 400 na OpenAI), o que deixava o Agente de IA **mudo em 100% das chamadas** — saiu também daqui
|
|
525
|
+
pra não ser oferecida numa tela e faltar na outra. Também não existem `o1-mini`, `gpt-4.5-preview`,
|
|
526
|
+
`gpt-5.2-pro` nem `gpt-5.5-pro` (a OpenAI recusa).
|
|
527
|
+
|
|
487
528
|
**OBRIGATÓRIO via API — `aiAssistantId`:** os modos `generate`/`intent`/`sentiment`/`extract`
|
|
488
529
|
EXIGEM um `aiAssistantId` válido (id de um assistente Captain da conta). Sem ele o nó SAI cedo
|
|
489
530
|
(`action: continue`) sem gravar nada — a variável de saída (`aiResponseVar`/`ai_intent`/`ai_sentiment`)
|
|
@@ -560,15 +601,22 @@ O campo raiz é **`waitMode`**: `"duration"` (default), `"date"` ou `"weekday"`.
|
|
|
560
601
|
"waitMode": "weekday",
|
|
561
602
|
"waitWeekday": 1,
|
|
562
603
|
"waitWeekdayTime": "09:00",
|
|
604
|
+
"waitWeekdayTimeMode": "fixed",
|
|
563
605
|
"waitTimezone": "America/Sao_Paulo"
|
|
564
606
|
}
|
|
565
607
|
}
|
|
566
608
|
```
|
|
567
609
|
|
|
568
|
-
- `waitWeekday`: 0=domingo, 1=segunda... 6=sábado. `waitWeekdayTime` = `"HH:MM"` 24h
|
|
610
|
+
- `waitWeekday`: 0=domingo, 1=segunda... 6=sábado. `waitWeekdayTime` = `"HH:MM"` 24h. `waitTimezone` = mesmo campo do modo date.
|
|
611
|
+
- **Variável no Horário (novo 2026-07-29 — reverte a proibição de 06/07):** o campo `waitWeekdayTime` ACEITA variável `{{ }}`, com o mesmo contrato dos campos do modo `date`. O modo mora em **`waitWeekdayTimeMode`**: `"fixed"` (default, e o que vale quando a chave está ausente) ou `"variable"` — espelho exato do `waitTimeMode`.
|
|
612
|
+
- **Mandar a variável SEM `"waitWeekdayTimeMode": "variable"` não funciona e não avisa:** o motor trata o campo como texto fixo, a chave `{{ }}` nunca é resolvida e o horário calculado não é o pretendido. Não há erro de validação, nem na API nem na tela. Como o MCP escreve o JSON do fluxo direto, sem passar pela tela, esse par é obrigatório.
|
|
613
|
+
- Exemplo: `{"waitMode":"weekday","waitWeekday":1,"waitWeekdayTime":"{{contact.custom_attribute.horario_retorno}}","waitWeekdayTimeMode":"variable","waitTimezone":"America/Sao_Paulo"}`.
|
|
614
|
+
- Resolve UMA única vez, na entrada do node (quem já está esperando mantém o valor capturado). Aceita `"14:30"`, `"9:30"`, `"14h30"`, `"14h"`, `"14:30:00"` — ou seja, atributo tipo `time` (Hora 24h). Valor ISO de atributo `date`/`datetime` é rejeitado (mesma regra do campo Horário do modo `date`).
|
|
615
|
+
- Variável que não resolve pra horário válido → saída `error` (payload `invalid_variable_datetime`, `field: waitWeekdayTime`).
|
|
616
|
+
- Modo `fixed` NUNCA emite erro: horário em branco cai no default `09:00` — flow antigo intacto.
|
|
569
617
|
- (Os nomes `targetWeekday`/`targetHour` NÃO existem — eram um erro de documentação antiga.)
|
|
570
618
|
|
|
571
|
-
**Handles que SAEM:** `success` (+ `error`, emitido
|
|
619
|
+
**Handles que SAEM:** `success` (+ `error`, emitido quando um campo em modo `variable` resolve pra valor inválido — vale nos DOIS modos, `date` e `weekday`; em modo `fixed` o `error` nunca é emitido).
|
|
572
620
|
|
|
573
621
|
### 2.9 `set_variable`
|
|
574
622
|
|
|
@@ -600,13 +648,19 @@ O campo raiz é **`waitMode`**: `"duration"` (default), `"date"` ou `"weekday"`.
|
|
|
600
648
|
"label": "A/B test mensagem",
|
|
601
649
|
"mode": "branches",
|
|
602
650
|
"branches": [
|
|
603
|
-
{ "id": "A", "label": "Variante A", "
|
|
604
|
-
{ "id": "B", "label": "Variante B", "
|
|
651
|
+
{ "id": "A", "label": "Variante A", "percent": 50 },
|
|
652
|
+
{ "id": "B", "label": "Variante B", "percent": 50 }
|
|
605
653
|
]
|
|
606
654
|
}
|
|
607
655
|
}
|
|
608
656
|
```
|
|
609
657
|
|
|
658
|
+
**O campo é `percent`, NÃO `weight` (2026-07-30):** a tela sempre gravou `percent` (`randomizerBranches`
|
|
659
|
+
do FlowNodeConfigModal) e o motor lia só `weight` — nenhum node configurado pela tela funcionava.
|
|
660
|
+
Corrigido em 30/07: o motor lê `percent` e aceita `weight` só como dado antigo. **Escreva sempre
|
|
661
|
+
`percent`** — randomizador criado com `weight` até roda, mas abre com as porcentagens EM BRANCO na
|
|
662
|
+
tela do cliente (a tela lê só `percent`).
|
|
663
|
+
|
|
610
664
|
**Modo `distribute_agents` (sorteio PONDERADO):** `data.mode: 'distribute_agents'` + `data.agents: [{ agent_id, percent }]` — sorteia UM agente por probabilidade (percentuais somando 100) e atribui automaticamente. Isso é SORTEIO, não rodízio: no curto prazo pode cair no mesmo agente várias vezes seguidas. Pra RODÍZIO EXATO (cada um na vez), use a AÇÃO `distribute_agents` no node `action` (ver seção 2.5) — não este modo.
|
|
611
665
|
|
|
612
666
|
**Handles que SAEM:** o `id` de cada branch (`A`, `B`, ...). Em `mode: 'distribute_agents'` é `success`.
|
|
@@ -214,7 +214,10 @@ os de `user_ids` viram membros comuns. A resposta traz `is_supervisor` (boolean)
|
|
|
214
214
|
- `config.feature_memory`: gera notas no contato ao resolver conversas
|
|
215
215
|
- `config.feature_faq`: gera FAQs sugeridas ao resolver conversas
|
|
216
216
|
- `config.feature_follow_up` + `config.follow_up_steps`: follow-up automático em cadência de até
|
|
217
|
-
3 etapas (cada ≥5 min, soma ≤24h) quando o cliente some — motor dedicado
|
|
217
|
+
3 etapas (cada ≥5 min, **soma ≤1380 min = 23h**, não 24h) quando o cliente some — motor dedicado
|
|
218
|
+
só-leitura. O teto é 1380 porque a janela de 24h do WhatsApp conta da **mensagem do cliente**, não do
|
|
219
|
+
primeiro follow-up; somar 1440 é recusado com 422 (e no campo legado `follow_up_time` a mensagem de
|
|
220
|
+
erro ainda diz "1440" por engano — o teto aplicado é 1380)
|
|
218
221
|
- `config.follow_up_skip_conditions`: array (até 3, lógica OU) de condições pra NÃO fazer follow-up.
|
|
219
222
|
Tipos: `label`, `contact_attr`/`conversation_attr`, e `time_window` (horário de silêncio: `start`/`end`
|
|
220
223
|
horas 0-23, janela circular — no período não envia follow-up, mas a IA segue respondendo o cliente)
|
|
@@ -223,8 +226,12 @@ os de `user_ids` viram membros comuns. A resposta traz `is_supervisor` (boolean)
|
|
|
223
226
|
com 422 "step 1 must be at least 5 minutes"). `feature_pause_on_human_reply`,
|
|
224
227
|
`feature_follow_up` e demais booleans de config aceitam update direto via
|
|
225
228
|
`captain_assistants_update` (merge parcial de config).
|
|
226
|
-
- `config.feature_pause_on_human_reply`: bool — a IA se desliga sozinha na conversa
|
|
227
|
-
assume (atendente responde pelo painel OU mensagem do celular); nota
|
|
229
|
+
- `config.feature_pause_on_human_reply`: bool (nasce desligado) — a IA se desliga sozinha na conversa
|
|
230
|
+
quando um humano assume (atendente responde pelo painel OU mensagem do celular); nota
|
|
231
|
+
privada/automação/campanha não contam. **Mensagem agendada depende de quem agendou (2026-07-29):**
|
|
232
|
+
agendada **pela IA** não desliga (o disparo assina como o assistente); agendada **por um atendente**
|
|
233
|
+
conta como atendente ao vivo e **DESLIGA** a IA da conversa — na prática, programar uma mensagem em
|
|
234
|
+
nome do atendente numa conversa com IA ativa tira a IA dali quando ela for enviada
|
|
228
235
|
- `config.model`: modelo OpenAI usado (gpt-4o, gpt-4o-mini)
|
|
229
236
|
- `config.temperature`: 0.0-1.0
|
|
230
237
|
- `config.instructions`: prompt sistema (Liquid template, até 20.000 chars)
|
|
@@ -373,16 +380,23 @@ Campos MUTÁVEIS (atualizáveis livremente por IA/integração): `marital_status
|
|
|
373
380
|
|
|
374
381
|
`custom_attribute_definitions` (model=`contact_attribute`) fica reservado a dado de NEGÓCIO genérico que não tem modelo dedicado (ex: "plano contratado", "nicho do cliente").
|
|
375
382
|
|
|
376
|
-
## Ligações —
|
|
383
|
+
## Ligações — CINCO sistemas distintos (não confundir!)
|
|
377
384
|
|
|
378
385
|
| Sistema | O que é | Tools | Canal |
|
|
379
386
|
|---|---|---|---|
|
|
380
|
-
| **Ligação WP (Wavoip)** | Voz pelo WhatsApp em caixa **QR Code/WAHA** — QR conecta um "device" Wavoip; widget de discagem, gravação e transcrição automática | **NENHUMA — sem tool no MCP** (ver nota abaixo) | WhatsApp QR Code |
|
|
387
|
+
| **Ligação WP (Wavoip)** | Voz pelo WhatsApp em caixa **QR Code/WAHA** — QR conecta um "device" Wavoip (pago, por caixa); widget de discagem, gravação e transcrição automática | **NENHUMA — sem tool no MCP** (ver nota abaixo) | WhatsApp QR Code |
|
|
388
|
+
| **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 |
|
|
381
389
|
| **WhatsApp Calling (Cloud)** | Voz pelo WhatsApp na **API oficial** (enterprise) | `whatsapp_calls_*` + enable/disable_whatsapp_calling | WhatsApp oficial |
|
|
382
|
-
| **VoIP (Zenvia)** | Telefonia COMUM (ramais, saldo, recarga) | `voip_*` | Telefone |
|
|
390
|
+
| **VoIP (Zenvia)** | Telefonia COMUM com softphone no navegador (ramais, saldo, recarga) | `voip_*` | Telefone |
|
|
391
|
+
| **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) |
|
|
383
392
|
|
|
384
|
-
Quando o usuário falar "ligação", descubra o canal: caixa QR Code → Wavoip
|
|
385
|
-
|
|
393
|
+
Quando o usuário falar "ligação", descubra o canal ANTES de responder: caixa QR Code → Wavoip **ou**
|
|
394
|
+
LionCalls (dois motores diferentes para o MESMO tipo de caixa — veja qual está conectado nela); caixa
|
|
395
|
+
oficial → `whatsapp_calls`; telefone/ramal → Zenvia (softphone) ou VTCall (PABX).
|
|
396
|
+
|
|
397
|
+
**Histórico é compartilhado.** Wavoip, LionCalls, Zenvia e VTCall gravam na MESMA tabela de ligações,
|
|
398
|
+
então `voip_calls_list` (`/voip/calls`) devolve as quatro juntas — mesmo as que não têm tool própria.
|
|
399
|
+
Só o WhatsApp Calling (Cloud) fica fora, em `whatsapp_calls_*`.
|
|
386
400
|
|
|
387
401
|
**Wavoip — SEM TOOL NO MCP (decisão do dono, 2026-07-16).** As 8 tools `wavoip_*` (status, token,
|
|
388
402
|
calls, connect, disconnect, settings, should_ring, flag_rejected) foram REMOVIDAS dos dois conectores.
|