@lionchat/mcp-server 0.4.5 → 0.4.7

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.
@@ -68,6 +68,10 @@ Account (conta/empresa)
68
68
  - Query param `page` (1-indexado)
69
69
  - `per_page` padrão 25, máximo 100 (clamp server-side)
70
70
  - Resposta inclui `meta` com contadores globais
71
+ - **Atenção:** o tamanho da página é por-endpoint e às vezes **FIXO**, ignorando `per_page`:
72
+ - **Conversas** = 25 fixo (`app/finders/conversation_finder.rb:250`)
73
+ - **Contatos** = 15 fixo (`contacts_controller.rb:12`)
74
+ - Nesses casos, para pegar mais resultados pagine por `page` — aumentar `per_page` não tem efeito.
71
75
 
72
76
  ### Autenticação
73
77
  - Header `api_access_token` (NUNCA na URL)
@@ -121,10 +125,28 @@ Mais em `lionchat://docs/glossary` → "Quando usar campo nativo vs custom_attri
121
125
 
122
126
  ## 4. Workflows típicos
123
127
 
124
- ### Listar conversas abertas
125
- ```
126
- lionchat_conversations_list (status: 'open', assignee_type: 'all')
127
- ```
128
+ ### Listar / buscar conversas (use `lionchat_conversations_list`)
129
+
130
+ Combine os filtros conforme o pedido do usuário:
131
+
132
+ | Usuário pede | Parâmetros |
133
+ |---|---|
134
+ | Conversas abertas | `status: 'open'` (já é o padrão) |
135
+ | Mais recentes / últimas | `sort_by: 'last_activity_at_desc'` (padrão) |
136
+ | Últimas criadas | `sort_by: 'created_at_desc'` |
137
+ | Mais antigas | `sort_by: 'created_at_asc'` |
138
+ | **Não lidas** | `conversation_type: 'unread'` |
139
+ | **Não atendidas** (sem 1ª resposta) | `conversation_type: 'unattended'` |
140
+ | Minhas conversas | `assignee_type: 'me'` |
141
+ | Sem dono / não atribuídas | `assignee_type: 'unassigned'` |
142
+ | Esperando há mais tempo | `sort_by: 'waiting_since_desc'` |
143
+ | Por prioridade | `sort_by: 'priority_desc'` |
144
+ | Todas (qualquer status) | `status: 'all'` |
145
+
146
+ Notas:
147
+ - Para SÓ a contagem (sem listar), use `lionchat_conversations_meta` com os mesmos filtros (ex: `conversation_type: 'unread'` conta as não lidas).
148
+ - **Não existe filtro nativo de "lidas".** Para isso, liste e considere lidas as conversas com `unread_count == 0` no payload.
149
+ - `lionchat_conversations_unread` é AÇÃO DE ESCRITA (marca como não lida) — NÃO use para buscar.
128
150
 
129
151
  ### Ver mensagens de uma conversa
130
152
  ```
@@ -139,10 +161,25 @@ lionchat_conversations_messages_list (conversation_id: 123)
139
161
  ```
140
162
 
141
163
  ### Criar card Kanban a partir de uma conversa
142
- ```
143
- 1. lionchat_kanban_v2_create (account_id, funnel_id, contact_id, conversation_id, stage)
164
+
165
+ Use `lionchat_kanban_items_create`. NÃO existem campos `contact_id`/`conversation_id`/`stage` — o contato vem pela conversa vinculada (`conversation_display_id`) e a etapa é o slug em `funnel_stage`.
166
+
167
+ ```json
168
+ POST /api/v1/accounts/{account_id}/kanban_items
169
+ {
170
+ "kanban_item": {
171
+ "funnel_id": 12,
172
+ "funnel_stage": "novo-lead",
173
+ "position": 0,
174
+ "item_details": { "title": "Negociação — Fulano" },
175
+ "conversation_display_id": 123
176
+ }
177
+ }
144
178
  ```
145
179
 
180
+ - `conversation_display_id` é **opcional** (card sem conversa vinculada é válido).
181
+ - Ignore as ferramentas `lionchat_kanban_v2_*` — apontam para controllers inexistentes.
182
+
146
183
  ### Relatório de produtividade
147
184
  ```
148
185
  1. lionchat_reports_summary (visão geral)
@@ -180,13 +217,44 @@ lionchat_conversations_messages_list (conversation_id: 123)
180
217
  | Rate limit escrita | 600/min por token |
181
218
  | Loop detection | bloqueia chamada idêntica >10x em 10s |
182
219
 
183
- ## 7. Quando NÃO chamar nenhuma ferramenta
220
+ ## 7. Confirmar antes de agir — regra de ouro
221
+
222
+ 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**.
223
+
224
+ **Sempre confirme em linguagem natural (resumindo o efeito) antes de executar quando:**
225
+
226
+ - **(a) A ação é destrutiva/irreversível** — qualquer `*_destroy`, `*_bulk_actions`, apagar contato (cascateia para as conversas dele), mudar status em massa.
227
+ - **(b) Envia mensagem ao cliente final** — `lionchat_conversations_messages_create` com `message_type` outgoing, `*_scheduled_messages_*`, templates WhatsApp, campanhas/disparos.
228
+ - **(c) Altera config** — conta, inbox, funil, automação, IA Agente (`captain_assistants_update`).
229
+ - **(d) O pedido é ambíguo/incompleto** — falta ID, período, canal, destinatário, ou há mais de uma interpretação possível.
230
+
231
+ **Como confirmar:** descreva o efeito em linguagem do usuário e aguarde o "sim". Ex.: *"Vou apagar o contato #312 — isso remove as 4 conversas dele também. Confirma?"*. Se faltar dado, peça só o que falta (1-2 perguntas), nunca um questionário.
232
+
233
+ **Aja sem perguntar apenas em operações idempotentes:** leitura, busca, resumo, classificação.
234
+
235
+ **NUNCA sobrescreva por inferência dados já preenchidos.** Em especial, **NUNCA sobrescreva telefone ou e-mail existente de um contato** — se já há valor preenchido, pergunte antes de alterar.
236
+
237
+ ## 8. Índice de roteamento (intenção → tool certa)
238
+
239
+ Antes de escolher a ferramenta, cheque se a intenção bate com a coluna da direita. Erros comuns aqui levam a ações de escrita disfaradas de leitura.
240
+
241
+ | Intenção do usuário | Tool certa | Cuidado |
242
+ |---|---|---|
243
+ | Listar / ver não lidas | `lionchat_conversations_list` (`conversation_type: 'unread'`) ou `lionchat_conversations_meta` | NÃO use `lionchat_conversations_unread` — é AÇÃO DE ESCRITA (marca como não-lida) |
244
+ | Buscar por texto (nome, telefone, conteúdo) | `lionchat_conversations_search` / `lionchat_contacts_search` | NÃO baixe tudo com `*_list` e filtre na mão |
245
+ | Só a contagem / números | `lionchat_conversations_meta` / `lionchat_reports_summary` | Não liste o conteúdo completo |
246
+ | Criar contato | `lionchat_contacts_create` base (path `/contacts`) | NÃO use as variantes `_1` … `_9` |
247
+ | Pausar a IA agora (botão de pânico) | `lionchat_captain_assistants_update` (`paused: true`, top-level) | — |
248
+ | Anexar arquivo a uma mensagem | `lionchat_upload_create` primeiro, depois usar o retorno em `lionchat_conversations_messages_create` | — |
249
+ | Marcar conversa como **lida** | `lionchat_conversations_update_last_seen` | NÃO use `unread` (esse marca como NÃO-lida) |
250
+
251
+ ## 9. Quando NÃO chamar nenhuma ferramenta
184
252
 
185
253
  - Conversa social: "oi", "bom dia", "obrigado" → responda direto, não invoque tools
186
254
  - Pergunta conceitual: "o que é Kanban?" → explique do seu conhecimento, não chame nenhum endpoint
187
255
  - O usuário já tem a info na tela e está pedindo interpretação → use o que ele já mostrou
188
256
 
189
- ## 8. Resources disponíveis (leia sob demanda)
257
+ ## 10. Resources disponíveis (leia sob demanda)
190
258
 
191
259
  Quando precisar de detalhes profundos, leia um destes documents via `resources/read`:
192
260
 
@@ -201,7 +269,7 @@ Quando precisar de detalhes profundos, leia um destes documents via `resources/r
201
269
  - `lionchat://docs/flowbuilder-design-guide` — **OBRIGATÓRIO** antes de criar/editar fluxo: schema de nodes, handles expostos por tipo, layout/positioning, erros comuns, checklist
202
270
  - `lionchat://docs/flowbuilder-patterns` — 10 templates de fluxos prontos pra adaptar (saudação, captura, qualificação, CSAT, IA, etc)
203
271
 
204
- ## 9. Prompts pré-prontos (workflows automatizados)
272
+ ## 11. Prompts pré-prontos (workflows automatizados)
205
273
 
206
274
  O usuário pode invocar via slash command (`/nome_do_prompt`):
207
275
 
@@ -228,7 +296,7 @@ Quando o cliente pedir pra criar um fluxo:
228
296
  7. **NUNCA invente** `sourceHandle` — use só os listados no design guide por tipo de node (ex: condition usa `cond_0`/`cond_1`/`default`, não IDs livres)
229
297
  8. Antes de chamar `flows_create`, apresente um resumo em **linguagem natural** do que vai criar e confirme com o cliente
230
298
 
231
- ## 10. Idioma das respostas
299
+ ## 12. Idioma das respostas
232
300
 
233
301
  Sempre responda em **PT-BR**. Quando mencionar termos técnicos padronizados da API, mantenha em inglês entre parênteses na primeira ocorrência:
234
302
  - "conversa aberta (open)"
@@ -237,7 +305,7 @@ Sempre responda em **PT-BR**. Quando mencionar termos técnicos padronizados da
237
305
 
238
306
  Isso ajuda o usuário a casar o que você diz com a API/documentação.
239
307
 
240
- ## 11. Quando errar uma chamada
308
+ ## 13. Quando errar uma chamada
241
309
 
242
310
  Se uma ferramenta retornar erro:
243
311
  - `HTTP 401/403`: você ou o usuário perdeu permissão → reporte claramente, não retente
@@ -9,5 +9,5 @@
9
9
  "required": true
10
10
  }
11
11
  ],
12
- "template": "Gere visão **360° do cliente** identificado por: **{{contact_identifier}}**\n\n## Passos\n\n1. **Localize o contato** (use `lionchat_contacts_search` com `q={{contact_identifier}}`):\n - Se múltiplos resultados, mostre os top 3 e peça confirmação\n - Se nenhum, reporte e pare\n\n2. **Detalhes do contato** (use `lionchat_contacts_show`):\n - Nome, email, telefone, identifier\n - custom_attributes (importante! pode ter dados de negócio)\n - Empresa vinculada (se houver)\n - Data de criação no LionChat\n\n3. **Histórico de conversas** (use `lionchat_contacts_list` com filtro contact_id):\n - Total de conversas com esse cliente\n - Status atual: quantas abertas, resolvidas, pendentes\n - Últimas 5 conversas (com data e display_id)\n\n4. **Conversa mais recente** (use `lionchat_conversations_messages_list` da última conversa):\n - Pegue as últimas 10 mensagens pra contexto\n - Identifique tom (cordial, irritado, satisfeito)\n - Status atual e último agente atribuído\n\n5. **Cards Kanban vinculados** (use `lionchat_kanban_items_filter` por conversation_display_ids):\n - Em quais funis está\n - Etapas atuais\n - Valor total no pipeline\n\n6. **CSAT histórico** (use `lionchat_csat_list` filtrando por contact_id se possível, senão por conversas dele):\n - Notas recebidas, feedbacks\n - Tendência (melhorando/piorando)\n\n7. **Engajamento** (calcule a partir dos dados):\n - Frequência de interação (conversas/mês)\n - Tempo desde última conversa (sinal de churn?)\n - Taxa de resposta dele (incoming vs outgoing)\n\n## Apresentação\n\n```markdown\n# 👤 Cliente: [Nome] — Visão 360°\n\n## 📇 Identificação\n- **Email:** ...\n- **Telefone:** ...\n- **Empresa:** ...\n- **Cadastrado em:** DD/MM/YYYY (X meses atrás)\n- **Custom:** [campos relevantes do custom_attributes]\n\n## 📊 Status\n- **Health Score:** 🟢 Saudável / 🟡 Atenção / 🔴 Risco\n- **Última interação:** DD/MM/YYYY (X dias atrás)\n- **Conversas totais:** N | Abertas: M | Resolvidas: K\n- **CSAT médio:** X⭐ (baseado em N respostas)\n\n## 💰 Pipeline (Kanban)\n- **Funil [X]:** Etapa Y — R$ Z (parado há W dias)\n- **Funil [X2]:** Etapa Y2 — R$ Z2\n\n## 💬 Última Conversa (#display_id)\n- **Data:** DD/MM HH:MM\n- **Status:** Aberta/Resolvida\n- **Agente:** Nome\n- **Resumo (2-3 linhas):** O que aconteceu na última interação\n- **Tom percebido:** Cordial / Frustrado / Satisfeito\n\n## ⚠️ Sinais de Alerta\n[Lista sinais detectados:]\n- Sem interação há >30 dias\n- Última CSAT foi 2⭐\n- Card parado em \"Negociação\" há 21 dias\n- Cliente comentou \"vou avaliar concorrentes\"\n\n## 💡 Próximos Passos Sugeridos\n[2-3 ações concretas:]\n1. Ligar pra entender por que parou de responder\n2. Mover card pra etapa \"Em risco\"\n3. Enviar pesquisa de feedback\n```\n\n## Health Score (como calcular)\n\n- 🟢 **Saudável:** última interação <30d + CSAT ≥4 + sem sinais negativos\n- 🟡 **Atenção:** última interação 30-90d OU CSAT 3-3.9 OU card parado >14d\n- 🔴 **Risco:** última interação >90d OU CSAT <3 OU múltiplos cards parados OU mensagens frustradas recentes\n\n## Importante\n\n- Seja honesto no Health Score. Não infle: 🟢 só se realmente saudável.\n- **Não invente sinais.** Só liste o que viu nos dados.\n- Se cliente é novo (<30 dias), avise: \"Pouco histórico, score preliminar\".\n- Se múltiplos contatos com o mesmo nome, peça pra desambiguar antes de continuar.\n- Datas em PT-BR (DD/MM/YYYY)."
12
+ "template": "Gere visão **360° do cliente** identificado por: **{{contact_identifier}}**\n\n## Passos\n\n1. **Localize o contato** (use `lionchat_contacts_search` com `q={{contact_identifier}}`):\n - Se múltiplos resultados, mostre os top 3 e peça confirmação\n - Se nenhum, reporte e pare\n\n2. **Detalhes do contato** (use `lionchat_contacts_show`):\n - Nome, email, telefone, identifier\n - custom_attributes (importante! pode ter dados de negócio)\n - Empresa vinculada (se houver)\n - Data de criação no LionChat\n\n3. **Histórico de conversas** (use `lionchat_contacts_list_1` — GET /contacts/{id}/conversations — passando o `id` do contato):\n - Total de conversas com esse cliente\n - Status atual: quantas abertas, resolvidas, pendentes\n - Últimas 5 conversas (com data e display_id)\n\n4. **Conversa mais recente** (use `lionchat_conversations_messages_list` da última conversa):\n - Pegue as últimas 10 mensagens pra contexto\n - Identifique tom (cordial, irritado, satisfeito)\n - Status atual e último agente atribuído\n\n5. **Cards Kanban vinculados** (use `lionchat_kanban_items_filter` por conversation_display_ids):\n - Em quais funis está\n - Etapas atuais\n - Valor total no pipeline\n\n6. **CSAT histórico** (use `lionchat_csat_list` filtrando por contact_id se possível, senão por conversas dele):\n - Notas recebidas, feedbacks\n - Tendência (melhorando/piorando)\n\n7. **Engajamento** (calcule a partir dos dados):\n - Frequência de interação (conversas/mês)\n - Tempo desde última conversa (sinal de churn?)\n - Taxa de resposta dele (incoming vs outgoing)\n\n## Apresentação\n\n```markdown\n# 👤 Cliente: [Nome] — Visão 360°\n\n## 📇 Identificação\n- **Email:** ...\n- **Telefone:** ...\n- **Empresa:** ...\n- **Cadastrado em:** DD/MM/YYYY (X meses atrás)\n- **Custom:** [campos relevantes do custom_attributes]\n\n## 📊 Status\n- **Health Score:** 🟢 Saudável / 🟡 Atenção / 🔴 Risco\n- **Última interação:** DD/MM/YYYY (X dias atrás)\n- **Conversas totais:** N | Abertas: M | Resolvidas: K\n- **CSAT médio:** X⭐ (baseado em N respostas)\n\n## 💰 Pipeline (Kanban)\n- **Funil [X]:** Etapa Y — R$ Z (parado há W dias)\n- **Funil [X2]:** Etapa Y2 — R$ Z2\n\n## 💬 Última Conversa (#display_id)\n- **Data:** DD/MM HH:MM\n- **Status:** Aberta/Resolvida\n- **Agente:** Nome\n- **Resumo (2-3 linhas):** O que aconteceu na última interação\n- **Tom percebido:** Cordial / Frustrado / Satisfeito\n\n## ⚠️ Sinais de Alerta\n[Lista sinais detectados:]\n- Sem interação há >30 dias\n- Última CSAT foi 2⭐\n- Card parado em \"Negociação\" há 21 dias\n- Cliente comentou \"vou avaliar concorrentes\"\n\n## 💡 Próximos Passos Sugeridos\n[2-3 ações concretas:]\n1. Ligar pra entender por que parou de responder\n2. Mover card pra etapa \"Em risco\"\n3. Enviar pesquisa de feedback\n```\n\n## Health Score (como calcular)\n\n- 🟢 **Saudável:** última interação <30d + CSAT ≥4 + sem sinais negativos\n- 🟡 **Atenção:** última interação 30-90d OU CSAT 3-3.9 OU card parado >14d\n- 🔴 **Risco:** última interação >90d OU CSAT <3 OU múltiplos cards parados OU mensagens frustradas recentes\n\n## Importante\n\n- Seja honesto no Health Score. Não infle: 🟢 só se realmente saudável.\n- **Não invente sinais.** Só liste o que viu nos dados.\n- Se cliente é novo (<30 dias), avise: \"Pouco histórico, score preliminar\".\n- Se múltiplos contatos com o mesmo nome, peça pra desambiguar antes de continuar.\n- Datas em PT-BR (DD/MM/YYYY)."
13
13
  }
@@ -19,5 +19,5 @@
19
19
  "required": false
20
20
  }
21
21
  ],
22
- "template": "Identifique contatos da conta que estão inativos há {{days_inactive|30}} dias ou mais.\n\nCritérios:\n- Tiveram pelo menos {{min_past_conversations|1}} conversa(s) anteriormente (mostra que já são leads ativos, não desconhecidos)\n- Última conversa marcada como `resolved` ou sem mensagens novas há {{days_inactive|30}}+ dias\n- Excluir contatos com labels: {{exclude_labels|none}}\n\nPra cada contato encontrado:\n1. Nome, telefone/email\n2. Última conversa (data, motivo do contato anterior se possível inferir)\n3. Canal favorito de contato (WhatsApp, Email, etc)\n4. Sugestão de abordagem personalizada:\n - Se foi cliente: \"como pode ter ido com X que você comprou?\"\n - Se foi lead frio: \"vimos seu interesse em Y, ainda faz sentido?\"\n - Se foi pós-venda: \"vamos renovar?\" / \"feedback?\"\n\nApresentar no formato:\n- Resumo: X contatos identificados\n- Top 10 mais relevantes (priorizar valor histórico, segmento alto, label 'cliente')\n- Sugestão de mensagem template pra reativação em massa (se o usuário quiser, podemos criar campanha)"
22
+ "template": "Identifique contatos da conta que estão inativos há {{days_inactive|30}} dias ou mais.\n\nComo buscar (use `lionchat_contacts_filter`):\n- Atributo `last_activity_at` com operador `days_before` e valor `{{days_inactive|30}}` — retorna contatos cuja última atividade foi há {{days_inactive|30}}+ dias\n- Para excluir labels, adicione condição `labels` com operador `not_equal_to` pra cada label em {{exclude_labels|none}}\n- Combine as condições com query_operator `and`\n\nCritérios adicionais (filtre o resultado):\n- Tiveram pelo menos {{min_past_conversations|1}} conversa(s) anteriormente (mostra que já são leads ativos, não desconhecidos) — confira via `lionchat_contacts_list_1` (GET /contacts/{id}/conversations) por contato\n- Excluir contatos com labels: {{exclude_labels|none}}\n\nPra cada contato encontrado:\n1. Nome, telefone/email\n2. Última conversa (data, motivo do contato anterior se possível inferir)\n3. Canal favorito de contato (WhatsApp, Email, etc)\n4. Sugestão de abordagem personalizada:\n - Se foi cliente: \"como pode ter ido com X que você comprou?\"\n - Se foi lead frio: \"vimos seu interesse em Y, ainda faz sentido?\"\n - Se foi pós-venda: \"vamos renovar?\" / \"feedback?\"\n\nApresentar no formato:\n- Resumo: X contatos identificados\n- Top 10 mais relevantes (priorizar valor histórico, segmento alto, label 'cliente')\n- Sugestão de mensagem template pra reativação em massa (se o usuário quiser, podemos criar campanha)"
23
23
  }
@@ -9,5 +9,5 @@
9
9
  "required": false
10
10
  }
11
11
  ],
12
- "template": "Gere um **Relatório de Produtividade da Equipe** dos últimos {{period_days|7}} dias.\n\n## Passos a executar\n\n1. **Calcule o período:** since = {{period_days|7}} dias atrás (Unix timestamp em segundos), until = agora\n\n2. **Resumo geral da conta** (use `lionchat_reports_summary` com type='account', business_hours=true):\n - Total de conversas, taxa de resolução, tempo médio de primeira resposta e resolução\n - Compare com período anterior (campo `previous` da resposta)\n\n3. **Performance por agente** (use `lionchat_reports_list` com type='agent', mesmo since/until):\n - Liste cada agente com: conversas atendidas, avg_first_response_time, avg_resolution_time, CSAT médio\n - Identifique TOP 3 (por volume + qualidade combinados)\n - Identifique 3 com maior tempo de resposta (potenciais gargalos)\n\n4. **CSAT** (use `lionchat_csat_metrics` com mesmo período):\n - Média geral, distribuição (5⭐, 4⭐, ...)\n - Taxa de resposta CSAT\n\n5. **SLA** (use `lionchat_sla_metrics` se disponível):\n - Hit rate, breach count\n\n## Como apresentar\n\nFormate em **markdown** com:\n\n```markdown\n# 📊 Relatório de Produtividade — Últimos {{period_days|7}} dias\n\n## Visão Geral\n| Métrica | Esta semana | Semana anterior | Variação |\n|---|---|---|---|\n| Conversas | X | Y | +/-Z% |\n| Tempo médio 1ª resposta | X min | Y min | +/-Z% |\n| Tempo médio resolução | X h | Y h | +/-Z% |\n| Taxa de resolução | X% | Y% | +/-Z% |\n\n## Top 3 Agentes\n1. **Nome** — X conversas, Y min 1ª resposta, CSAT Z⭐\n2. ...\n3. ...\n\n## Pontos de Atenção\n- Agente XYZ com tempo de resposta 3x acima da média\n- Inbox WhatsApp com volume crescendo\n\n## CSAT\n- Média: X⭐ (anterior: Y⭐)\n- Taxa de resposta: Z%\n\n## SLA\n- Hits: X% (meta 95%)\n- Breaches: Y conversas\n```\n\n## Conversões importantes\n\n- TODOS os tempos retornados estão em **segundos**. Converta:\n - < 60s → mostre em segundos\n - 60-3600s → mostre em minutos (`X min`)\n - 3600-86400s → mostre em horas (`X h Y min`)\n - \\> 86400s → mostre em dias\n- Datas: use formato brasileiro (DD/MM ou DD/MM HH:MM)\n- Taxas (CSAT, SLA hit rate): mostre como %\n\n## Importante\n\n- Se não tiver dados suficientes (período sem atividade), diga claramente em vez de inventar números\n- Se algum agente tem 0 conversas, OMITA da listagem em vez de mostrar zerado (não inflar lista)\n- Use emojis com moderação (📊 ⭐ 🚨) — máximo 1 por seção"
12
+ "template": "Gere um **Relatório de Produtividade da Equipe** dos últimos {{period_days|7}} dias.\n\n## Passos a executar\n\n1. **Calcule o período:** since = {{period_days|7}} dias atrás (Unix timestamp em segundos), until = agora\n\n2. **Resumo geral da conta** (use `lionchat_reports_summary` com type='account', business_hours=true):\n - Total de conversas, taxa de resolução, tempo médio de primeira resposta e resolução\n - Compare com período anterior (campo `previous` da resposta)\n\n3. **Performance por agente** (use `lionchat_reports_list` com type='agent', mesmo since/until):\n - Liste cada agente com: conversas atendidas, avg_first_response_time, avg_resolution_time\n - ATENÇÃO: o relatório por agente NÃO traz CSAT médio nem status online. Pegue o CSAT por agente separadamente: `lionchat_csat_list` filtrando por `user_ids` (id do agente) no mesmo período e calcule a média das notas dele.\n - Identifique TOP 3 (por volume + qualidade combinados)\n - Identifique 3 com maior tempo de resposta (potenciais gargalos)\n\n4. **CSAT** (use `lionchat_csat_metrics` com mesmo período):\n - Média geral, distribuição (5⭐, 4⭐, ...)\n - Taxa de resposta CSAT\n\n5. **SLA** (use `lionchat_sla_metrics` se disponível):\n - Hit rate, breach count\n\n## Como apresentar\n\nFormate em **markdown** com:\n\n```markdown\n# 📊 Relatório de Produtividade — Últimos {{period_days|7}} dias\n\n## Visão Geral\n| Métrica | Esta semana | Semana anterior | Variação |\n|---|---|---|---|\n| Conversas | X | Y | +/-Z% |\n| Tempo médio 1ª resposta | X min | Y min | +/-Z% |\n| Tempo médio resolução | X h | Y h | +/-Z% |\n| Taxa de resolução | X% | Y% | +/-Z% |\n\n## Top 3 Agentes\n1. **Nome** — X conversas, Y min 1ª resposta, CSAT Z⭐\n2. ...\n3. ...\n\n## Pontos de Atenção\n- Agente XYZ com tempo de resposta 3x acima da média\n- Inbox WhatsApp com volume crescendo\n\n## CSAT\n- Média: X⭐ (anterior: Y⭐)\n- Taxa de resposta: Z%\n\n## SLA\n- Hits: X% (meta 95%)\n- Breaches: Y conversas\n```\n\n## Conversões importantes\n\n- TODOS os tempos retornados estão em **segundos**. Converta:\n - < 60s → mostre em segundos\n - 60-3600s → mostre em minutos (`X min`)\n - 3600-86400s → mostre em horas (`X h Y min`)\n - \\> 86400s → mostre em dias\n- Datas: use formato brasileiro (DD/MM ou DD/MM HH:MM)\n- Taxas (CSAT, SLA hit rate): mostre como %\n\n## Importante\n\n- Se não tiver dados suficientes (período sem atividade), diga claramente em vez de inventar números\n- Se algum agente tem 0 conversas, OMITA da listagem em vez de mostrar zerado (não inflar lista)\n- Use emojis com moderação (📊 ⭐ 🚨) — máximo 1 por seção"
13
13
  }
@@ -14,5 +14,5 @@
14
14
  "required": false
15
15
  }
16
16
  ],
17
- "template": "Analise a carga de trabalho da equipe no período: {{period|week}}.\n\n{{team_id|Para todos os times.}}\nFiltrar por team_id={{team_id}} se especificado.\n\nDados a coletar pra cada agente:\n1. Conversas atribuídas (open + pending)\n2. Conversas resolvidas no período\n3. Tempo médio de primeira resposta\n4. Tempo médio de resolução\n5. % de conversas com CSAT (se houver)\n6. Status atual: online / offline / busy\n\nApresentar:\n\n## Visão Geral\n- Total de conversas ativas\n- Agentes online vs offline\n- Conversas sem atribuição (backlog)\n\n## Distribuição por Agente (tabela)\n| Agente | Ativas | Resolvidas | T. Resposta | T. Resolução | Status |\n\n## Identificar:\n- **Sobrecarregados**: > 1.5x média de conversas ativas → risco de queda na qualidade\n- **Subutilizados**: < 0.5x média → podem absorver mais\n- **Agentes offline com conversas pendentes**: redistribuir urgente\n- **Conversas órfãs**: sem assignee há > 1h → atribuir agora\n\n## Recomendações de ação\n- Lista de conversas pra reatribuir (com de → para)\n- Sugestão de revisão de assignment policy se houver desbalanceamento crônico\n- Conversas que talvez devam ser fechadas (sem resposta do cliente há > 7 dias)\n\nFinalizar perguntando se o usuário quer aplicar as redistribuições sugeridas."
17
+ "template": "Analise a carga de trabalho da equipe no período: {{period|week}}.\n\n{{team_id|Para todos os times.}}\nFiltrar por team_id={{team_id}} se especificado.\n\nDados a coletar pra cada agente:\n1. Conversas atribuídas (open + pending)\n2. Conversas resolvidas no período\n3. Tempo médio de primeira resposta\n4. Tempo médio de resolução\n5. % de conversas com CSAT (se houver) — NÃO vem do relatório por agente; pegue via `lionchat_csat_list` filtrando por `user_ids` (id do agente) no período\n6. Status atual: online / offline / busy — NÃO vem do relatório por agente; pegue via `lionchat_agent_availability_list`\n\nApresentar:\n\n## Visão Geral\n- Total de conversas ativas\n- Agentes online vs offline\n- Conversas sem atribuição (backlog)\n\n## Distribuição por Agente (tabela)\n| Agente | Ativas | Resolvidas | T. Resposta | T. Resolução | Status |\n\n## Identificar:\n- **Sobrecarregados**: > 1.5x média de conversas ativas → risco de queda na qualidade\n- **Subutilizados**: < 0.5x média → podem absorver mais\n- **Agentes offline com conversas pendentes**: redistribuir urgente\n- **Conversas órfãs**: sem assignee há > 1h → atribuir agora\n\n## Recomendações de ação\n- Lista de conversas pra reatribuir (com de → para)\n- Sugestão de revisão de assignment policy se houver desbalanceamento crônico\n- Conversas que talvez devam ser fechadas (sem resposta do cliente há > 7 dias)\n\nFinalizar perguntando se o usuário quer aplicar as redistribuições sugeridas."
18
18
  }
@@ -124,8 +124,16 @@ A KEY é o slug interno (`novo_lead`). O `name` é o que aparece na UI ("Novo Le
124
124
 
125
125
  ### Query params padrão
126
126
 
127
- - `page` (int, 1-indexado, default 1)
128
- - `per_page` (int, default varia por endpoint, máximo 100 clamp server-side)
127
+ - `page` (int, 1-indexado, default 1) — **sempre respeitado**
128
+ - `per_page` (int, máximo 100 clamp server-side) — **page-size é por-endpoint e às vezes FIXO**
129
+
130
+ > **Atenção: vários endpoints IGNORAM `per_page` e usam um tamanho fixo.** Exemplos confirmados:
131
+ > - **Conversas** (`conversations_list`): 25 fixo (`CONVERSATION_RESULTS_PER_PAGE`, default 25) — `per_page` é ignorado.
132
+ > - **Contatos** (`contacts_list`): 15 fixo (`RESULTS_PER_PAGE = 15`) — `per_page` é ignorado.
133
+ > - **Busca de mensagens** (`messages_search`): 20 fixo.
134
+ >
135
+ > Nesses casos, NÃO confie em `per_page` pra trazer 100 de uma vez — pagine via `page`
136
+ > (`page=1`, `page=2`, ...) e use `meta.total_count` pra saber quando parar.
129
137
 
130
138
  ### Resposta típica
131
139
 
@@ -156,7 +164,7 @@ Ou para listas grandes (cursors):
156
164
 
157
165
  ### Estratégia recomendada
158
166
 
159
- - Para listar tudo: **NÃO** itere infinitamente. Sempre limite (`per_page=100` + 1 página é geralmente suficiente).
167
+ - Para listar tudo: **NÃO** itere infinitamente. Quando o endpoint respeita `per_page`, `per_page=100` + 1 página é geralmente suficiente. Quando o page-size é FIXO (conversas=25, contatos=15, busca de msg=20), pagine por `page` e pare ao bater `meta.total_count`.
160
168
  - Para "quantos X tem": prefira endpoints `meta` ou `count`.
161
169
  - Para encontrar X específico: prefira `search` ou `filter` (server-side).
162
170
 
@@ -21,7 +21,7 @@ A maioria dos endpoints aceita parâmetros pra filtrar antes de retornar:
21
21
  |---|---|
22
22
  | `conversations_list` | `status` (open/resolved), `assignee_type`, `inbox_id`, `team_id`, `labels`, `q` (busca) |
23
23
  | `contacts_list` | `q` (nome/email/telefone), `include_contact_inboxes` |
24
- | `messages_search` | `q`, `conversation_id`, `before`, `after` |
24
+ | `messages_search` | `q`, `file_type[]`, `private_only`, `page` (busca dentro de UMA conversa) |
25
25
  | `kanban_items_list` | `funnel_id`, `funnel_stage`, `assigned_agent_id` |
26
26
  | `reports_list_*` | `since`, `until`, `metric`, `type` |
27
27
 
@@ -64,8 +64,12 @@ Quando existir endpoint específico, prefira:
64
64
  Conversa com 200 mensagens? **Não baixe tudo**:
65
65
 
66
66
  1. `conversations_show` (1 chamada) → metadata + última mensagem
67
- 2. Se precisa contexto: pegue só as últimas 20-30 mensagens com `before` ou `page`
68
- 3. Pra análise completa, use `conversations_transcript` (formato condensado)
67
+ 2. Se precisa contexto: pegue só as últimas 20-30 mensagens com `conversations_messages_list` (use `page`)
68
+ 3. Pra análise/resumo completo, use `conversations_messages_list` paginando — é a fonte do texto.
69
+
70
+ > **Atenção:** `conversations_transcript` NÃO retorna o texto da conversa pra você ler. Ele EXIGE o
71
+ > parâmetro `email` e apenas dispara um e-mail (via `ConversationReplyMailer`) com a transcrição —
72
+ > responde `head :ok`, sem corpo. Para LER ou RESUMIR uma conversa, use `conversations_messages_list`.
69
73
 
70
74
  ## Operações em massa
71
75
 
@@ -78,9 +82,10 @@ NÃO faça 50 PATCH requests individuais — bate rate limit.
78
82
 
79
83
  ## Rate limiting
80
84
 
81
- LionChat tem rate limit por API token. Limites típicos:
82
- - ~500 req/min para read
83
- - ~100 req/min para write
85
+ LionChat tem rate limit por API token (fonte única: `api-conventions.md` → seção Rate Limiting):
86
+ - 1200 req/min para leitura (reads)
87
+ - 600 req/min para escrita (writes)
88
+ - Detecção de loop: 10 chamadas idênticas em 10 seg
84
89
 
85
90
  Se receber `429 Too Many Requests`:
86
91
  - Espere o `Retry-After` header
@@ -157,6 +162,15 @@ Nunca hard-code esses dados em respostas geradas.
157
162
  2. `reports_list_*` (agent_overview) com `since=7d_ago`, `until=now`
158
163
  3. Sintetize: agente X com Y resoluções, tempo médio Z
159
164
 
165
+ ### "Enviar mensagem com anexo (imagem/arquivo)"
166
+ 1. `upload_create` (a partir de arquivo OU de uma URL) → resposta traz `file_url` e `blob_id`
167
+ 2. `conversations_messages_create` passando o `blob_id` (signed_id) dentro do array `attachments`:
168
+ ```json
169
+ { "content": "Segue o documento", "message_type": "outgoing", "attachments": ["<blob_id>"] }
170
+ ```
171
+ Cada item de `attachments` é o `blob_id` retornado pelo upload. Pode mandar vários.
172
+ 3. O mesmo padrão vale pra anexar mídia num card do Kanban (use o `blob_id` no campo de anexo do card).
173
+
160
174
  ## Cuidados com tools de criação
161
175
 
162
176
  Endpoints `create_*` modificam dados reais. Antes de chamar:
@@ -164,6 +178,26 @@ Endpoints `create_*` modificam dados reais. Antes de chamar:
164
178
  - Verifique se o recurso já existe (evite duplicatas)
165
179
  - Use `dry_run` quando disponível
166
180
 
181
+ ### Wrappers de body obrigatórios (exceções à regra "use raiz")
182
+
183
+ A regra geral é mandar o body na raiz (ver `api-conventions.md`). Mas três endpoints usam
184
+ `params.require(...)` no controller e **exigem o wrapper nomeado** — sem ele dá `400`/`422`:
185
+
186
+ | Tool | Wrapper obrigatório | Controller |
187
+ |---|---|---|
188
+ | `companies_create` / `companies_update` | `{ "company": { ... } }` | `params.require(:company)` |
189
+ | `canned_responses_create` | `{ "canned_response": { ... } }` | `params.require(:canned_response)` |
190
+ | `scheduled_messages_create` (dentro de conversa) | `{ "scheduled_message": { ... } }` | `params.require(:scheduled_message)` |
191
+
192
+ Exemplo:
193
+
194
+ ```json
195
+ { "company": { "name": "Acme", "domain": "acme.com" } }
196
+ ```
197
+
198
+ > Observação: o endpoint `scheduled_messages` de nível raiz (fora de conversa) usa `params.permit`
199
+ > sem wrapper. A exigência de wrapper vale para o criar-dentro-da-conversa.
200
+
167
201
  ## Erros comuns a evitar
168
202
 
169
203
  | Erro | Como evitar |
@@ -195,6 +195,102 @@ Conversation
195
195
  2. Tem SLA policy ativando state change?
196
196
  3. Veio de external API call?
197
197
 
198
+ ## Receitas de ação (qual tool usar pra cada coisa)
199
+
200
+ ### ENVIAR MENSAGEM (`conversations_messages_create`)
201
+
202
+ Endpoint: `POST /api/v1/accounts/{account_id}/conversations/{conversation_id}/messages`
203
+ Os campos vão na **raiz** do payload (NÃO há wrapper). Construídos pelo `Messages::MessageBuilder`.
204
+
205
+ **Payload mínimo (resposta de texto pro cliente):**
206
+ ```json
207
+ {
208
+ "content": "Olá! Como posso ajudar?",
209
+ "message_type": "outgoing"
210
+ }
211
+ ```
212
+
213
+ **Campos:**
214
+
215
+ | Campo | Obrigatório | Detalhe |
216
+ |---|---|---|
217
+ | `content` | sim* | Texto da mensagem (*pode ser vazio se houver `attachments`) |
218
+ | `message_type` | recomendado | `outgoing` (padrão, resposta do agente) ou `incoming`. **`incoming` SÓ é permitido em inbox do tipo API** — em outros canais levanta erro |
219
+ | `private` | não | `true` = nota interna (só agentes veem, não vai pro cliente). Default `false` |
220
+ | `attachments` | não | Array de arquivos (upload) ou `signed_id` (ActiveStorage). Escolhe endpoint de mídia pelo MIME |
221
+ | `content_attributes.in_reply_to` | não | ID da mensagem que está sendo respondida/citada |
222
+ | `template_params` | não | Parâmetros de template WhatsApp (header/body/buttons). JSON |
223
+ | `cc_emails` / `bcc_emails` / `to_emails` | não | SÓ em inbox Email. Lista separada por vírgula |
224
+
225
+ **Nota interna (não vai pro cliente):**
226
+ ```json
227
+ { "content": "Cliente parece irritado, tratar com cuidado", "private": true }
228
+ ```
229
+
230
+ ### AÇÃO → TOOL (mapa rápido)
231
+
232
+ NÃO confunda com `conversations_update`: o update só altera **prioridade** e **IA Agente**
233
+ (`captain_assistant_id`). Para mudar status, prioridade ou atribuição use as tools abaixo.
234
+
235
+ | Ação desejada | Tool / endpoint | Parâmetros |
236
+ |---|---|---|
237
+ | Resolver conversa | `toggle_status` (POST `.../toggle_status`) | `status: "resolved"` |
238
+ | Reabrir conversa | `toggle_status` | `status: "open"` |
239
+ | Marcar como pendente | `toggle_status` | `status: "pending"` |
240
+ | Adiar (snooze) | `toggle_status` | `status: "snoozed"` + `snoozed_until` (datetime futuro) |
241
+ | Mudar prioridade | `toggle_priority` (POST `.../toggle_priority`) | `priority`: `urgent`/`high`/`medium`/`low`/`nil` |
242
+ | Atribuir agente | `assignments` (POST `.../assignments`) | `assignee_id` |
243
+ | Atribuir time | `assignments` | `team_id` |
244
+ | Ativar/trocar IA Agente | `conversations_update` (PATCH) | `captain_assistant_id` |
245
+ | Desativar IA Agente | `conversations_update` (PATCH) | `captain_assistant_id: 0` (ou null) |
246
+
247
+ **IMPORTANTE:** `conversations_update` (PATCH) só aceita `priority` e `captain_assistant_id`
248
+ (ver `permitted_update_params`, conversations_controller.rb). Mandar `status` no update **não muda
249
+ o status** — é ignorado. Use sempre `toggle_status` para status.
250
+
251
+ **Snooze exige `snoozed_until`:**
252
+ ```json
253
+ POST .../conversations/{id}/toggle_status
254
+ { "status": "snoozed", "snoozed_until": "2026-06-01T09:00:00Z" }
255
+ ```
256
+
257
+ ### MARCAR COMO LIDA vs NÃO-LIDA
258
+
259
+ | Ação | Tool / endpoint | Efeito |
260
+ |---|---|---|
261
+ | Marcar como **lida** | `update_last_seen` (POST `.../update_last_seen`) | Atualiza `agent_last_seen_at`; em canal WAHA dispara check azul no WhatsApp |
262
+ | 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 |
263
+
264
+ Ambas são **ações de escrita** (POST). `unread` é o oposto de `update_last_seen`, não uma consulta.
265
+
266
+ ### AGENDAR MENSAGEM (`scheduled_messages_create`)
267
+
268
+ Endpoint: `POST /api/v1/accounts/{account_id}/conversations/{conversation_id}/scheduled_messages`
269
+
270
+ **ATENÇÃO:** diferente do padrão "raiz" das mensagens normais, aqui o payload **EXIGE o wrapper**
271
+ `scheduled_message` (`params.require(:scheduled_message)`).
272
+
273
+ **Payload:**
274
+ ```json
275
+ {
276
+ "scheduled_message": {
277
+ "content": "Lembrete: sua consulta é amanhã às 14h",
278
+ "scheduled_at": "2026-06-01T14:00:00Z",
279
+ "inbox_id": 12
280
+ }
281
+ }
282
+ ```
283
+
284
+ | Campo | Obrigatório | Detalhe |
285
+ |---|---|---|
286
+ | `content` | sim | Texto a enviar |
287
+ | `scheduled_at` | sim | Quando enviar (datetime) |
288
+ | `inbox_id` | sim | Caixa de entrada por onde sair |
289
+ | `is_recurrent` | não | `true` ativa recorrência |
290
+ | `period` | não | Período da recorrência (ex.: `daily`, `weekly`) — vira `{ type: ... }` |
291
+ | `recurrence_count` | não | Quantas vezes repetir |
292
+ | `template_params` | não | Parâmetros de template WhatsApp |
293
+
198
294
  ## Eventos importantes (pra automation)
199
295
 
200
296
  | Evento | Quando dispara |
@@ -145,6 +145,10 @@ Company
145
145
  └── domain
146
146
  ```
147
147
 
148
+ **`additional_attributes` vs `custom_attributes` no Contato:**
149
+ - `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.
150
+ - `custom_attributes` (jsonb): dado de negócio livre, definido pelo cliente via Atributos Customizados.
151
+
148
152
  **Padrões de `source_id` por canal:**
149
153
  - Waha: `5511999999999@c.us` (1-on-1) ou `120363xxx@g.us` (grupo) ou `XXXX@lid` (LID)
150
154
  - WhatsApp Cloud: `5511999999999` (E.164 sem prefixo)
@@ -272,12 +276,22 @@ Macro
272
276
  AccountTask (agenda interna)
273
277
  ├── id (PK)
274
278
  ├── account_id (FK)
275
- ├── user_id (FK → User, assignee)
279
+ ├── user_id (FK → User, optional)
280
+ ├── created_by (FK → User)
276
281
  ├── title
277
282
  ├── description
278
- ├── due_at (datetime)
279
- ├── completed_at (datetime, nullable)
280
- └── color (string)
283
+ ├── task_type (string: task/follow_up/call/meeting/video_call/deadline/custom)
284
+ ├── priority (string: none/low/medium/high/urgent)
285
+ ├── status (enum: pending=0 (default) / completed=1 / cancelled=2 / snoozed=3)
286
+ ├── scheduled_at (datetime) — data/hora da tarefa
287
+ ├── duration_minutes (int, default por task_type: task/follow_up=15, call=30, meeting/video_call=60)
288
+ ├── snoozed_until (datetime, se status=snoozed)
289
+ ├── recurrence_type (string nullable: daily/weekly/biweekly/monthly/yearly; nil = tarefa única)
290
+ ├── recurrence_count (int nullable, 1-365)
291
+ ├── meeting_url (string, sala de reunião call/meeting)
292
+ ├── guest_emails (jsonb array, convidados extras — máx 20)
293
+ ├── conversation_id / contact_id / linked_kanban_item_id (FK opcionais)
294
+ └── assignees (has_many → User, via account_task_assignments)
281
295
 
282
296
  BookingEventType (template de agendamento)
283
297
  ├── id (PK)