{"openapi":"3.0.3","info":{"title":"API Pública do ChatJuridico","x-logo":{"url":"https://app.chatjuridico.com/images/chat-juridico-square-logo.svg","altText":"ChatJuridico"},"description":"## API REST Pública do ChatJuridico\n\nAcesse seus contatos, conversas e mensagens de forma programática.\nIntegre o ChatJuridico com suas ferramentas de automação como n8n, Make, Zapier ou diretamente no seu código.\n\n### Autenticação\n\nTodas as requisições exigem uma chave de API no header `X-API-Key`:\n\n```\nX-API-Key: cj_live_sua_chave_aqui\n```\n\nAs chaves podem ser criadas e gerenciadas no painel do ChatJuridico em **Configurações > Developers**.\n\n### Limites de Requisição\n\nA API utiliza limites diferenciados por tipo de operação para proteger a qualidade do número WhatsApp:\n\n| Operação | Por Minuto | Por Dia |\n|----------|-----------|---------|\n| Leitura (contatos, conversas, mensagens) | 100/min | — |\n| Envio de mensagens (texto, mídia) | 20/min | 500/dia |\n| Envio de templates | 5/min | 500/dia |\n| Cadastro de processo (`POST /v1/lawsuits`) | 20/min | 500/dia |\n| Gravações de processo (atualizar, desvincular, definir/limpar ativo) | 60/min | — |\n\nAo atingir o limite, a API retorna `429 Too Many Requests`.\nHeaders de resposta incluem: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`,\ne para endpoints de envio: `X-RateLimit-Daily-Limit`, `X-RateLimit-Daily-Remaining`.\n\n### Paginação\n\nEndpoints de listagem utilizam paginação baseada em cursor.\nUse o parâmetro `cursor` com o valor de `pagination.next_cursor` para buscar a próxima página.\n\n**Exemplo:**\n```\nGET /v1/contacts?limit=50&cursor=uuid-do-proximo\n```\n\n### Idempotência\n\nRequisições de mutação (POST, PUT, PATCH, DELETE) suportam o header `X-Idempotency-Key`.\nSe a mesma chave for reenviada em até 24 horas, a resposta original é retornada sem reprocessamento.\nRecomendado para evitar duplicações em caso de timeout ou retry.\n\n### Rastreamento\n\nEnvie o header `X-Request-Id` com um UUID para correlacionar requisições nos logs.\nSe não fornecido, um UUID é gerado automaticamente. O valor é retornado no header da resposta.\n\n### Escopos\n\nAs chaves de API são vinculadas a permissões específicas:\n\n| Escopo | Descrição |\n|--------|-----------|\n| `contacts:read` | Consultar contatos |\n| `contacts:write` | Criar, editar e excluir contatos |\n| `contacts:delete` | Excluir contatos em massa (não incluído em `contacts:write`) |\n| `conversations:read` | Consultar conversas |\n| `conversations:write` | Criar e atualizar conversas |\n| `messages:read` | Consultar mensagens |\n| `messages:send` | Enviar mensagens via WhatsApp |\n| `tags:read` | Consultar tags do escritório |\n| `tags:write` | Criar, editar e excluir tags |\n| `funnel:read` | Consultar etapas do funil |\n| `funnel:write` | Mover conversas entre etapas |\n| `webhooks:manage` | Gerenciar webhooks (criar, editar, excluir) |\n| `campaigns:read` | Consultar campanhas e métricas de envio |\n| `campaigns:write` | Criar, editar, excluir e enviar campanhas |\n| `automations:read` | Consultar automações e execuções |\n| `automations:write` | Criar, editar e excluir automações |\n| `shortcuts:read` | Consultar atalhos de mensagem |\n| `shortcuts:write` | Criar, editar e excluir atalhos |\n| `instances:write` | Reconectar instâncias e gerar QR code |\n| `agents:read` | Consultar sessões do agente IA e simulações |\n| `agents:write` | Completar sessões, enviar feedback e controlar agente |\n| `analytics:read` | Consultar métricas e relatórios analíticos |\n| `signatures:read` | Consultar documentos e assinaturas eletrônicas |\n| `signatures:write` | Criar, enviar e gerenciar assinaturas |\n| `payments:read` | Consultar cobranças, dashboard financeiro e clientes |\n| `payments:write` | Criar e cancelar cobranças, agendar lembretes |\n| `calendar:read` | Consultar calendários e eventos |\n| `calendar:write` | Criar, editar e excluir eventos, gerenciar participantes, cores e a própria visibilidade |\n| `calendar:admin` | Gerenciar o compartilhamento de calendários (permissões/ACL) — requer administrador |\n| `integrations:read` | Consultar status de integrações (Asaas, Google Calendar) |\n| `integrations:write` | Ativar e desativar integrações |\n| `lawsuits:read` | Consultar os processos do escritório vinculados aos contatos (situação e movimentações) |\n| `lawsuits:write` | Criar e gerenciar os processos do escritório |\n| `cases:read` | Consultar os casos do escritório (processos administrativos, sem CNJ) e suas partes |\n| `cases:write` | Criar e gerenciar casos, partes e vínculos com conversas |\n| `flows:read` | Consultar formulários do WhatsApp (Flows), pré-visualização e respostas recebidas |\n| `flows:write` | Criar, editar, duplicar e enviar formulários do WhatsApp (Flows) |\n| `flows:publish` | Publicar, implantar nova versão, descontinuar e excluir formulários do WhatsApp (Flows) |\n\n### Agentes de IA (MCP)\n\nA API pode ser conectada diretamente a agentes de IA como **Claude**, **Cursor**, **ChatGPT** e outros\nvia [Model Context Protocol (MCP)](https://modelcontextprotocol.io). Cada endpoint se torna\nautomaticamente uma ferramenta que o agente pode usar.\n\n**Endpoint MCP:** `https://api.jur.chat/mcp`\n\nBasta sua chave de API e a URL acima. Não é necessário instalar nada localmente.\n\nO servidor MCP também inclui:\n- **Instruções de domínio** — contexto sobre o modelo de dados, diferenças entre provedores (Meta vs UAZAPI) e convenções\n- **Prompts de workflow** — guias passo-a-passo para tarefas comuns (enviar mensagem, classificar conversa, configurar webhook, etc.)\n\n#### Claude.ai (Web) — Recomendado\n\n1. Acesse **Configurações > Conectores** no Claude.ai\n2. Clique em **\"Adicionar conector personalizado\"**\n3. Cole a URL: `https://api.jur.chat/mcp`\n4. Clique em **\"Adicionar\"**\n5. Ao conectar, você será redirecionado para inserir sua chave de API do ChatJurídico\n\nNenhuma configuração técnica necessária. A autenticação é automática via OAuth 2.1.\n\n#### ChatGPT (Developer Mode)\n\n1. Acesse **Settings > Advanced Settings** e ative **Developer Mode**\n2. Vá em **Settings > Connectors > Create**\n3. Preencha:\n   - Nome: `ChatJuridico`\n   - URL: `https://api.jur.chat/mcp`\n   - Autenticação: informe sua chave de API\n4. Em uma nova conversa, clique no **+** ao lado do composer\n5. Selecione **\"More\"** e escolha o conector ChatJuridico\n\nDisponível para assinantes Plus, Pro, Business e Enterprise.\n\n#### Claude Desktop (Aplicativo)\n\nAdicione ao seu `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"chatjuridico\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"mcp-remote\",\n        \"https://api.jur.chat/mcp\",\n        \"--header\",\n        \"X-API-Key: cj_live_sua_chave_aqui\"\n      ]\n    }\n  }\n}\n```\n\n#### Cursor IDE\n\nCrie um arquivo `.cursor/mcp.json` na raiz do projeto:\n\n```json\n{\n  \"mcpServers\": {\n    \"chatjuridico\": {\n      \"url\": \"https://api.jur.chat/mcp\",\n      \"headers\": {\n        \"X-API-Key\": \"cj_live_sua_chave_aqui\"\n      }\n    }\n  }\n}\n```\n\n#### Claude Code (Terminal)\n\n```bash\nclaude mcp add --transport http chatjuridico https://api.jur.chat/mcp \\\n  --header \"X-API-Key: cj_live_sua_chave_aqui\"\n```\n\n#### VS Code (GitHub Copilot)\n\nCrie um arquivo `.vscode/mcp.json` na raiz do projeto:\n\n```json\n{\n  \"servers\": {\n    \"chatjuridico\": {\n      \"type\": \"http\",\n      \"url\": \"https://api.jur.chat/mcp\",\n      \"headers\": {\n        \"X-API-Key\": \"cj_live_sua_chave_aqui\"\n      }\n    }\n  }\n}\n```\n\nRequer GitHub Copilot ativo e VS Code 1.102+.\n\n#### Windsurf\n\nAdicione ao arquivo `~/.windsurf/mcp_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"chatjuridico\": {\n      \"serverUrl\": \"https://api.jur.chat/mcp\",\n      \"headers\": {\n        \"X-API-Key\": \"cj_live_sua_chave_aqui\"\n      }\n    }\n  }\n}\n```\n\n#### Qualquer cliente MCP\n\nEnvie requisições `POST` para `https://api.jur.chat/mcp` com o header `X-API-Key`\nou com um Bearer token obtido via OAuth 2.1.\nO endpoint utiliza o transporte Streamable HTTP do protocolo MCP.\n\n#### Ferramentas Compostas (Composite Tools)\n\nO servidor MCP inclui ferramentas compostas que agregam múltiplas chamadas\nde API em uma única operação, otimizando a experiência do agente:\n\n- **visao_completa_cliente** — Visão 360° de um contato (dados, conversas, mensagens, anotações)\n- **contexto_conversa** — Contexto completo de uma conversa com mensagens recentes\n- **resumo_diario** — Briefing matinal com conversas pendentes e follow-ups\n- **resumo_pipeline** — Visão do funil com contagem por etapa\n- **conversas_pendentes** — Dashboard de conversas pendentes agrupadas por membro\n- **preparo_intake** — Dados de referência para onboarding de novo cliente\n- **resumo_automacoes** — Saúde das automações com estatísticas de execução\n- **busca_inteligente** — Busca de conversas com fallback automático\n- **classificar_e_atribuir** — Classificação e atribuição em uma chamada\n- **carga_equipe** — Distribuição de carga por membro da equipe\n- **metricas_leads** — Métricas de leads recentes\n- **ler_midia_mensagem** — Leitura de mídias: transcrição de áudio, visualização de imagens, extração de texto de PDFs\n- **listar_midias_conversa** — Listagem de todos os arquivos e mídias de uma conversa\n- **status_integracao** — Diagnóstico completo da integração (saúde, números, equipe, não lidas, capacidades)\n- **listar_rascunhos** — Rascunhos de mensagem pendentes com preview e nome do contato\n- **configurar_perfil** — Altera o perfil de ferramentas ativas (essencial/vendas/atendimento/completo)\n- **executar_rotina** — Executa rotinas multi-passo no servidor (prioridades_dia, fechamento_dia, etc.)\n- **listar_rotinas** — Lista rotinas disponíveis (built-in e personalizadas)\n- **salvar_rotina** — Salva rotinas personalizadas do escritório\n- **agendar_rotina** — Agenda a execução automática de uma rotina em um horário diário (ex.: `prioridades_dia` às 18:00, fuso `America/Sao_Paulo`)\n- **listar_rotinas_agendadas** / **cancelar_rotina_agendada** — Gerenciam os agendamentos de rotina do escritório\n\n#### Processos do Escritório\n\nFerramentas para consultar os processos jurídicos **cadastrados no escritório**\n(a área do ícone de balança), vinculados a cada contato — distinto da pesquisa\npública de jurisprudência. Requerem o escopo `lawsuits:read`. Todas filtram por\nescritório (isolamento por `office_id`) e redigem processos em segredo de justiça\n(mantêm CNJ/tribunal/situação; omitem partes e movimentações).\n\nO objeto **processo** retornado por estas ferramentas contém: `numero_cnj`,\n`titulo_polo_ativo`, `titulo_polo_passivo`, `tribunal_origem_sigla`,\n`classe_processual`, `assunto_principal`, `situacao`, `valor_causa`,\n`data_ultima_movimentacao`, `segredo_de_justica`, `vinculo`\n`{ office_lawsuit_id, status_operacional, client_polo, source, advogado_user_id }`\ne `ultimas_movimentacoes[]` `{ data, tipo, conteudo, classificacao_nome, fonte_nome }`.\n\n- **processos_do_contato** — Processos vinculados a um contato/conversa, com as últimas movimentações (andamento). Ideal para responder \"qual o andamento do meu processo?\".\n  - Parâmetros: `conversaId` **ou** `contatoId` (informe um); `max_movimentacoes` (padrão `5`, máx `20`).\n  - Resposta: `{ total, processos[], fonte: \"cadastro_escritorio\" }`.\n- **consultar_processo_escritorio** — Um processo do escritório por número CNJ.\n  - Parâmetros: `cnj` (`NNNNNNN-DD.AAAA.J.TR.OOOO` ou 20 dígitos); `max_movimentacoes` (padrão `10`, máx `30`).\n  - Resposta: `{ encontrado: true, processo }` ou `{ encontrado: false, cnj, dica }` quando o CNJ não está cadastrado no escritório. Diferente de `consultar_processo_publico`, que consulta a base pública (DataJud/CNJ).\n- **buscar_processos_escritorio** — Busca paginada nos processos do escritório.\n  - Parâmetros: `parte` (substring no nome de uma das partes), `status` (status operacional), `advogadoId` (advogado responsável), `limit` (padrão `20`, máx `50`), `cursor` (paginação keyset — repasse `next_cursor`).\n  - Resposta: `{ total, processos[], next_cursor }`.\n\n#### Casos do Escritório\n\nFerramentas para os **casos** do escritório — processos administrativos,\nextrajudiciais, consultivos ou de prospecção, **sem número CNJ** e sem\nmonitoramento judicial (o que tem CNJ é \"processo\", acima). Um caso tem\n`title`, `status` do kanban (`novo`, `em_andamento`, `aguardando`,\n`concluido`, `arquivado`), `folder_number` (pasta), `tags` (etiquetas),\n`responsible_user_id`, `parties[]` (`{ name, role, contact_id, qualification,\ndocument }`) e `chats_id[]` (conversas vinculadas). Requerem `cases:read` /\n`cases:write`. Todas filtram por escritório.\n\n#### Pesquisa Pública — Jurisprudência\n\nFerramentas para consultar jurisprudência em **quatro** fontes orquestradas em paralelo: **DataJud** (CNJ, 91 tribunais — apenas metadados), **LexML** (súmulas, repetitivos e OJs federais com ementa), **STJ ScON** (acórdãos do STJ com ementa completa via bypass configurável de Cloudflare) e **TJPR** (acórdãos do TJPR com ementa via portal + fallback LexML). O `SmartRouter` escolhe as fontes conforme o filtro, executa fan-out paralelo, deduplica por CNJ/URN e reordena por BM25 + peso da fonte + recência quando `texto_livre` é informado. **STF não participa do DataJud** — para precedentes vinculantes do STF use LexML.\n\n- **buscar_jurisprudencia** — Busca multi-fonte por **um** filtro (`cnj`, `parte`, `classe_codigo`, `assunto_codigo` ou `texto_livre`). Aceita `tribunais` (CSV — inclua `tjpr` para fixar TJPR), janela `data_inicio`/`data_fim` (YYYY-MM-DD) e dois overrides opcionais:\n  - `fonte`: `auto` (padrão — roteador decide) | `datajud` | `lexml` | `stj_scon` | `tjpr` | `multi` (todas).\n  - `modo`: `auto` | `metadata` | `ementa` | `precedente` **mais** modos especializados de pesquisa:\n    - `tese_caso` (alias de `auto`, padrão recomendado)\n    - `tributaria` — força STJ ScON + LexML (Repetitivos / Repercussão Geral)\n    - `viabilidade` — fan-out mais amplo para panorama\n    - `firac_contraria` — só ementa; promove julgados adversos (`improcedente`/`indeferido`/`inadmissível`) via boost de palavras-chave\n    - `fundamentacao_peca` — só ementa; aplica cap automático de 1 superior (STJ/STF) + 1 local\n\n  A resposta inclui `fontes_consultadas` (multi-fonte: telemetria `{fonte,hits,latency_ms,ok,warnings,errors}` por fonte), `fontes_consultadas_names` e `schema_version`. Cada hit também pode trazer `comarca` e `citacao_abnt`, além de `tem_ementa`, `ementa_excerpt` (preview ~600 caracteres), `ementa_completa` (texto íntegro), `link_inteiro_teor`, `relator`, `data_julgamento` e `data_publicacao`. `citacao_abnt` já vem pronta para citação: TJPR usa `(TJ-PR <cnj> <comarca>, Relator: …, Data de Julgamento: DD/MM/AAAA, <órgão>, Data de Publicação: DD/MM/AAAA).`; STJ ScON usa `(STJ - <classe> <num>, Rel. …, <órgão>, j. DD/MM/AAAA, DJe DD/MM/AAAA).`; LexML usa `(<tribunal> - <urn>, …)`. **Alertas automáticos de tema** (Tese do Século, Tema 566/1099, RE 851.108/796.376, JCP, ágio, IBS/CBS, transação tributária) são prepended em `_warnings` com prefixo `tema:` quando a query bate em padrões conhecidos. Cache de 5 min escopado por escritório, até 50 hits por chamada.\n- **consultar_processo_publico** — Drill-down em um processo específico por número CNJ (formato `NNNNNNN-DD.AAAA.J.TR.OOOO` ou 20 dígitos). Continua em DataJud (lookup por CNJ é o melhor caso da API pública).\n\n> **Quando a ementa aparece:** queries com `texto_livre` retornam ementas reais via LexML, STJ ScON (requer `STJ_SCON_FETCHER=flaresolverr|zenrows`) e TJPR. Sem bypass de Cloudflare, STJ ScON degrada graciosamente e a busca continua em DataJud + LexML + TJPR.\n\n> **Como o LLM deve consumir o payload (regra dura):** (1) anunciar o `modo` aplicado e relaiar qualquer alerta `tema:`; (2) para cada hit, respeitando o cap do modo, renderizar `ementa_completa` literalmente — nunca parafrasear, resumir ou cortar — seguida de `citacao_abnt` exatamente como retornada; se `ementa_completa` não existir, usar `ementa_excerpt` e dizer `(ementa truncada)`; (3) sintetizar o entendimento dominante (e divergência se aparecer nos hits); (4) checklist final (aderência fática · atualidade · tribunal correto · ementa completa). **Nunca complementar com julgados de memória**; se retornarem poucos hits, dizer isso explicitamente e recomendar uma busca refinada.\n\n#### Perfis de Ferramentas (Token Efficiency)\n\nO servidor MCP suporta perfis para reduzir o consumo de tokens:\n\n| Perfil | Ferramentas | Uso |\n|--------|-------------|-----|\n| `completo` | ~90 | Padrão — todas as ferramentas |\n| `essencial` | ~15 | Briefing, busca, envio, classificação |\n| `vendas` | ~25 | Essencial + funil, tags, contatos |\n| `atendimento` | ~25 | Essencial + mensagens, anotações, atalhos |\n\nAtive via header `X-MCP-Profile: essencial` ou use a ferramenta `configurar_perfil`.\n\n#### Rotinas (Server-Side Workflows)\n\nRotinas executam múltiplas ferramentas em paralelo no servidor com uma única chamada:\n\n| Rotina | Descrição |\n|--------|-----------|\n| `prioridades_dia` | Briefing matinal: não lidas, pipeline, pendências, rascunhos |\n| `fechamento_dia` | Resumo de fim de dia: atividade, pendências, prioridades |\n| `visao_vendas` | Painel de vendas: funil, leads, carga da equipe |\n\nUse `executar_rotina(name=\"prioridades_dia\")` ou crie rotinas personalizadas com `salvar_rotina`.\n\n#### Recursos MCP (Dados de Referência)\n\nRecursos são dados consultados sob demanda pelo agente, sem contar como chamada de ferramenta:\n\n| URI | Conteúdo |\n|-----|----------|\n| `chatjuridico://tags` | Tags disponíveis |\n| `chatjuridico://funnel-stages` | Etapas do funil |\n| `chatjuridico://members` | Membros da equipe |\n| `chatjuridico://instances` | Números WhatsApp |\n| `chatjuridico://templates` | Templates WhatsApp |\n| `chatjuridico://shortcuts` | Atalhos de mensagem |\n| `chatjuridico://workflows` | Guia detalhado de workflows avançados |\n| `chatjuridico://tool-catalog` | Catálogo de ferramentas por categoria |\n","version":"1.0.0","contact":{"name":"Suporte ChatJuridico","email":"suporte@chatjuridico.com.br"},"license":{"name":"Proprietária"}},"externalDocs":{"description":"Central de Ajuda do ChatJuridico","url":"https://chatjuridico.com/ajuda"},"servers":[{"url":"https://api.jur.chat","description":"Produção"}],"security":[{"ApiKeyAuth":[]}],"x-tagGroups":[{"name":"Comunicação","tags":["Contatos","Conversas","Mensagens","Media","MensagensAgendadas","Rascunhos","Templates","Campanhas"]},{"name":"CRM","tags":["Tags","Funnels","FunnelStages","Webhooks","Anotações","Membros","Instancias","Processos"]},{"name":"Automação","tags":["Automações","Atalhos","Formularios"]},{"name":"Analíticos","tags":["Analiticos"]},{"name":"Assinaturas & Cobranças","tags":["Assinaturas","AssinaturasAutentique","Cobrancas"]},{"name":"IA & Agente","tags":["SessoesAgente","Simulacoes","FAQ"]},{"name":"Agenda","tags":["Calendario"]},{"name":"Integrações","tags":["Integracoes","IntegracoesAutentique","IntegracoesAsaas","IntegracoesGoogleCalendar"]},{"name":"Administração","tags":["ChavesAPI"]},{"name":"Sistema","tags":["Uso","Saude"]}],"tags":[{"name":"Contatos","description":"Gerencie os contatos do seu escritório. Contatos representam clientes, leads ou qualquer pessoa\ncom quem seu escritório interage via WhatsApp.\n"},{"name":"Processos","x-displayName":"Processos do Escritório","description":"Consulte os processos jurídicos cadastrados no escritório (a área do ícone\nde balança), vinculados aos contatos. Endpoints somente-leitura: busca\npaginada, consulta por CNJ e processos vinculados a um contato ou conversa,\ncom as movimentações (andamento) mais recentes. Distinto da pesquisa\npública de jurisprudência. Requer o escopo `lawsuits:read`.\n"},{"name":"Casos","x-displayName":"Casos do Escritório","description":"Gerencie os **casos** do escritório: processos administrativos,\nextrajudiciais, consultivos ou de prospecção — sem número CNJ e sem\nmonitoramento judicial (para os judiciais veja \"Processos\"). Cada caso\ntem status de kanban, pasta, etiquetas, responsável, partes (cliente e\ndemais envolvidos) e conversas vinculadas. Requer `cases:read` /\n`cases:write`.\n"},{"name":"ConvitesParceiro","x-displayName":"Convites de Parceiro","description":"Convites de teste que o escritório PARCEIRO cria por conta própria: um\ncadastro de 30 dias para a banca que ele está indicando, sem passar por\nnós. O escritório informa apenas quem está convidando — plano, valor e\nduração do teste são definidos pelo servidor.\n\nDisponível só para escritórios com o módulo habilitado (acordo comercial,\nnão recurso de plano); sem ele todo endpoint responde 403\n`feature_disabled`. Requer `partner_invites:read` / `partner_invites:write`.\n\nA chave age como o USUÁRIO QUE A CRIOU: ele precisa ser membro do\nescritório da chave — o mesmo que a tela exige. Se sair do escritório, a\nchave para de valer aqui na hora (403 `partner_invites_not_entitled`), sem\ndepender de revogação manual.\n"},{"name":"Conversas","description":"Acesse as conversas do WhatsApp do seu escritório. Cada conversa está vinculada a um contato\ne a um número de WhatsApp.\n"},{"name":"Mensagens","description":"Leia e envie mensagens nas conversas do WhatsApp. As mensagens são enviadas de forma\nassíncrona via fila e o status é atualizado automaticamente.\n"},{"name":"Media","x-displayName":"Upload de Mídia","description":"Faça upload de arquivos para enviar como mídia em mensagens do WhatsApp.\n\n**Fluxo de uso:**\n1. Faça upload do arquivo via `POST /v1/media/upload` (base64, até ~4.5MB) ou\n   solicite uma URL pré-assinada via `POST /v1/media/upload-url` (qualquer tamanho).\n2. Use a `media_url` retornada como `file_url` ao enviar mensagem.\n\nLimites por tipo: imagens 5MB, áudio 16MB, vídeo 16MB, documentos 100MB.\nRequer o escopo `messages:send`.\n"},{"name":"Instancias","x-displayName":"Números WhatsApp","description":"Consulte os números de WhatsApp conectados ao seu escritório.\nO `id` de um número é necessário para criar conversas e enviar mensagens.\nRequer o escopo `conversations:read`.\n"},{"name":"ChavesAPI","x-displayName":"Chaves de API","description":"Gerencie as chaves de acesso à API do seu escritório. Cada chave possui escopos específicos\ne pode ser revogada individualmente. Esses endpoints requerem autenticação via Bearer JWT.\n"},{"name":"Tags","description":"Gerencie as tags do seu escritório para classificar conversas.\nTags podem ser usadas para organizar e filtrar conversas no CRM.\n"},{"name":"Funnels","x-displayName":"Funis","description":"Consulte os funis de vendas do seu escritório. Cada funil contém etapas\npara organizar o fluxo de atendimento. Requer o escopo `funnel:read`.\n"},{"name":"FunnelStages","x-displayName":"Etapas do Funil","description":"Gerencie as etapas do funil de vendas do seu escritório.\nUse para mover conversas entre etapas via API.\n"},{"name":"Webhooks","description":"Registre endpoints para receber notificações em tempo real quando eventos ocorrerem.\n\n## Formato da Entrega\n\nQuando um evento ocorre, o ChatJuridico envia uma requisição `POST` para a URL do webhook com:\n\n**Headers:**\n- `Content-Type: application/json`\n- `X-Webhook-Event`: tipo do evento (ex: `message.received`)\n- `X-Webhook-Signature`: assinatura HMAC-SHA256 do corpo (hex)\n\n**Corpo (JSON):**\n```json\n{\n  \"event\": \"message.received\",\n  \"data\": { ... },\n  \"timestamp\": 1712345678\n}\n```\n\n- `event` (string): tipo do evento\n- `data` (object): entidade completa do evento, sem campos internos\n- `timestamp` (integer): Unix epoch em segundos\n\n**Campos excluídos de `data`:** `file_thumb_base64`, `template_components`,\n`template_parameters`, `template_metadata`, `interactive_data`, `referral_data`, `call_log`.\n\n### Consumidores estritos (`payload_fields`)\n\nPor padrão o `data` traz **todos** os campos da entidade (incluindo colunas internas).\nSe o seu consumidor **rejeita campos desconhecidos** — por exemplo Java/Jackson com\n`FAIL_ON_UNKNOWN_PROPERTIES` (o padrão), ou um model Pydantic estrito — ele vai\nresponder `400` ao encontrar qualquer campo que não declare.\n\nUse `payload_fields` ao criar/atualizar o webhook para receber **apenas** os campos\nque você declara:\n\n```json\n{ \"payload_fields\": [\"id\", \"conversation_id\", \"type\", \"content\", \"direction\", \"status\"] }\n```\n\nEnviar um **subconjunto** do seu model é sempre seguro (campos ausentes viram `null`);\no que quebra é o contrário — campos **extras** que ele não conhece.\nAlternativa (recomendada, no seu lado): `@JsonIgnoreProperties(ignoreUnknown = true)`.\n\n## Tipos de Evento\n\n| Evento | Descrição |\n|--------|-----------|\n| `message.sent` | Mensagem enviada pelo escritório |\n| `message.received` | Mensagem recebida de um contato |\n| `message.reply_captured` | Resposta capturada de um fluxo `collect-reply` (ver `POST /conversations/{id}/collect-reply`). Carrega `reply_to_token`. Uma mensagem classificada aqui **não** dispara também `message.received`. |\n| `message.status_changed` | Status da mensagem alterado (delivered, read, failed) |\n| `conversation.created` | Nova conversa criada |\n| `conversation.updated` | Conversa atualizada (etapa, responsável, status) |\n| `contact.created` | Novo contato criado |\n| `contact.updated` | Contato atualizado |\n| `scheduled_message.sent` | Mensagem agendada enviada com sucesso |\n| `scheduled_message.failed` | Mensagem agendada falhou |\n| `campaign.completed` | Campanha finalizou todos os envios |\n| `api_key.created` | Nova chave de API criada |\n| `api_key.revoked` | Chave de API revogada |\n| `automation.executed` | Automação executou uma ação |\n| `automation.activated` | Automação ativada |\n| `automation.deactivated` | Automação desativada |\n\n## Verificação HMAC-SHA256\n\nValide a assinatura comparando o header `X-Webhook-Signature` com o HMAC-SHA256\ndo corpo da requisição usando o `secret` do webhook:\n\n**Python:**\n```python\nimport hmac, hashlib\n\ndef verify_signature(body: bytes, secret: str, signature: str) -> bool:\n    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(expected, signature)\n```\n\n**Node.js:**\n```javascript\nconst crypto = require('crypto');\nfunction verifySignature(body, secret, signature) {\n  const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));\n}\n```\n"},{"name":"MensagensAgendadas","x-displayName":"Mensagens Agendadas","description":"Agende mensagens para envio futuro em conversas existentes. Mensagens agendadas são\nprocessadas automaticamente no horário programado.\n\n- **Números Meta**: suporta agendamento de templates (message_type: `template`).\n- **Números UAZAPI**: suporta agendamento de texto e mídia (message_type: `text`, `image`, etc.).\n\nRequer o escopo `messages:send`.\n"},{"name":"Templates","x-displayName":"Templates WhatsApp","description":"Consulte os templates de mensagem aprovados pela Meta disponíveis nos seus números WhatsApp.\nTemplates são necessários para iniciar conversas fora da janela de 24h do WhatsApp.\n\n**Disponível apenas para números Meta (WhatsApp Cloud API).**\n\nRequer o escopo `messages:read`.\n"},{"name":"Campanhas","description":"Acompanhe as campanhas de envio em massa do seu escritório. Consulte status,\ndestinatários e métricas de entrega de cada campanha.\n\n**Disponível apenas para números Meta (WhatsApp Cloud API).** Números UAZAPI não possuem infraestrutura de campanhas.\n\nRequer o escopo `campaigns:read`.\n"},{"name":"Uso","x-displayName":"Uso da API","description":"Consulte estatísticas de uso da sua chave de API e os limites de requisição configurados.\nRequer o escopo `contacts:read`.\n"},{"name":"Automações","description":"Gerencie as automações do seu escritório. Automações permitem criar fluxos automatizados\nbaseados em gatilhos (nova mensagem, palavra-chave, mudança de etapa no funil, etc.)\ne ações (enviar mensagem, atualizar CRM, adicionar tag, etc.).\n\nRequer os escopos `automations:read` e/ou `automations:write`.\n"},{"name":"Atalhos","x-displayName":"Atalhos de Mensagem","description":"Gerencie atalhos de mensagem do seu escritório. Atalhos são textos pré-definidos\nacionados por gatilhos (ex: `/saudacao`) para agilizar o atendimento.\n\nRequer os escopos `shortcuts:read` e/ou `shortcuts:write`.\n"},{"name":"Anotações","x-displayName":"Anotações Internas","description":"Gerencie anotações internas nas conversas do seu escritório.\nAnotações são notas visíveis apenas para o time, úteis para registrar\ninformações de acompanhamento, observações e contexto sobre o atendimento.\n\nRequer os escopos `conversations:read` e/ou `conversations:write`.\n"},{"name":"Rascunhos","x-displayName":"Rascunhos de Mensagem","description":"Gerencie rascunhos de mensagem nas conversas do seu escritório.\nRascunhos permitem salvar textos de mensagem para envio posterior,\ncom sincronização automática com o compositor da interface.\n\nCada conversa permite um rascunho por usuário (constraint única).\nA API usa um identificador fixo para rascunhos criados via API.\n\nRequer os escopos `conversations:read` e/ou `conversations:write`.\n"},{"name":"Membros","x-displayName":"Membros do Escritório","description":"Consulte os membros do seu escritório. Útil para resolver `accountable_id`\nde conversas e listar membros disponíveis para atribuição.\n\nRequer o escopo `conversations:read`.\n"},{"name":"SessoesAgente","x-displayName":"Sessões do Agente IA","description":"Consulte e gerencie as sessões do agente de IA do seu escritório.\nCada sessão representa uma interação completa do agente com um contato,\nincluindo classificação, coleta de dados e resumo da conversa.\n\nTambém inclui controle do agente por conversa (ativar/desativar),\navaliações de qualidade e follow-ups agendados.\n\nRequer os escopos `agents:read` e/ou `agents:write`.\n"},{"name":"Simulacoes","x-displayName":"Simulações do Agente","description":"Ambiente sandbox para testar o agente de IA sem afetar conversas reais.\nCrie sessões de simulação, envie mensagens de teste e avalie o comportamento\ndo agente antes de ativá-lo em produção.\n\nRequer os escopos `agents:read` e/ou `agents:write`.\n"},{"name":"Analiticos","x-displayName":"Analíticos","description":"Métricas e relatórios analíticos do escritório. Inclui dashboard geral, desempenho da equipe,\nmétricas de contatos, análise de funil, tempos de resposta, log de interações e métricas de anúncios.\n\nTodos os endpoints aceitam os parâmetros de período: `start_date`, `end_date`, `period` (week/month/quarter/year)\ne `instance_id` para filtrar por número WhatsApp. Use `compare=true` para incluir comparação com período anterior.\n\nRequer o escopo `analytics:read`.\n"},{"name":"Assinaturas","x-displayName":"Assinaturas Eletrônicas","description":"Gerencie documentos para assinatura eletrônica.\nCiclo completo: criar rascunho → upload de arquivo → enviar para assinatura → acompanhar status → baixar documento assinado.\n\nStatus do documento: `draft` → `pending` → `partially_signed` → `signed` (ou `rejected`/`expired`/`cancelled`).\nStatus do signatário: `new` → `sent` → `viewed` → `signed` (ou `rejected`).\n\nRequer os escopos `signatures:read` e/ou `signatures:write`.\n"},{"name":"AssinaturasAutentique","x-displayName":"Assinaturas Eletrônicas (Autentique)","description":"Gerencie documentos para assinatura eletrônica via integração com Autentique.\nCiclo: upload de arquivo → criar documento → acompanhar status → baixar documento assinado.\n\nStatus do documento: `draft` → `pending` → `partially_signed` → `signed` (ou `rejected`/`expired`/`cancelled`).\n\nRequer os escopos `signatures:read` e/ou `signatures:write`.\n"},{"name":"IntegracoesAutentique","x-displayName":"Integração Autentique","description":"Gerencie a integração com o Autentique para assinaturas eletrônicas.\nAtive, desative ou verifique o status da conexão com sua conta Autentique.\n\nRequer os escopos `signatures:read` e/ou `signatures:write`.\n"},{"name":"Cobrancas","x-displayName":"Cobranças (Asaas)","description":"Gerencie cobranças financeiras via integração com Asaas.\nCiclo completo: criar cobrança → acompanhar status → receber pagamento → dashboard financeiro.\n\n**Tipos de cobrança:** PIX, BOLETO, CREDIT_CARD (cartão de crédito).\n**Status:** PENDING → RECEIVED/CONFIRMED (ou OVERDUE → CANCELLED/REFUNDED).\n\n**Limites CDC (Código de Defesa do Consumidor):**\n- Juros: máximo 1% ao mês\n- Multa: máximo 2% sobre o valor\n\nRequer os escopos `payments:read` e/ou `payments:write`.\nEndpoints internos (JWT) verificam permissões granulares por usuário: `view_payments`, `create_payments`, `cancel_payments`.\n"},{"name":"IntegracoesAsaas","x-displayName":"Integração Asaas","description":"Gerencie a integração com o gateway de pagamentos Asaas.\nAtive, desative ou verifique o status da conexão com sua conta Asaas.\n\nSuporta ambientes `production` e `sandbox`. A detecção de ambiente é automática a partir da chave de API.\n\nRequer os escopos `integrations:read` e/ou `integrations:write`.\n"},{"name":"Integracoes","x-displayName":"Integração de Assinaturas","description":"Gerencie integrações externas do escritório. Suporta integração de assinaturas eletrônicas\ne com Asaas para cobranças financeiras.\n\nUse os endpoints de integração para ativar/desativar conexões, verificar status e sincronizar dados.\n\nRequer os escopos `signatures:read`/`signatures:write` (assinaturas) ou `integrations:read`/`integrations:write` (Asaas).\n"},{"name":"Calendario","x-displayName":"Agenda (Calendário)","description":"Gerencie calendários e eventos do escritório. Suporta calendários nativos e\nsincronização com Google Calendar (via integração).\n\nEventos recorrentes são expandidos automaticamente na listagem — cada ocorrência\naparece como um evento individual com ID sintético (`masterId::slotKey`).\nPara editar/excluir uma ocorrência específica, use `scope: only_this` e\n`occurrence_start`. Para toda a série, `scope: all`. Para dividir a série,\n`scope: this_and_following`.\n\n**Nota:** Eventos em calendários do Google com permissão de escrita\n(source=app ou google_synced) podem ser criados, editados e excluídos via API —\nas alterações são enviadas ao Google Agenda. Calendários do Google conectados\ncomo somente leitura ou disponibilidade não aceitam criação, seus eventos\n(google_external) são somente-leitura, e nenhum evento pode ser movido para\nou de um calendário do Google (crie o evento diretamente nele).\n\nRequer os escopos `calendar:read` e/ou `calendar:write`.\n"},{"name":"IntegracoesGoogleCalendar","x-displayName":"Integração Google Calendar","description":"Consulte o status da integração com Google Calendar e as assinaturas de calendários\nsincronizados. Endpoints somente leitura.\n\nRequer o escopo `integrations:read`.\n"},{"name":"Formularios","x-displayName":"Formulários do WhatsApp (Flows)","description":"Formulários interativos do WhatsApp (Meta WhatsApp Flows): o cliente\npreenche campos dentro do próprio WhatsApp e a resposta volta estruturada.\nCiclo: criar (`DRAFT`) → enviar o JSON → publicar (`PUBLISHED`,\nirreversível) → enviar para conversas → ler respostas. Para mudar um\nformulário publicado use `deployFormulario` (o `id` não muda).\n\n**Só números da API oficial da Meta (WhatsApp Cloud API).** Exige a flag\ndo escritório `whatsapp_flows_enabled` (sem ela: 403 `feature_disabled`).\nRequer `flows:read` / `flows:write`; publicar, implantar, descontinuar e\nexcluir exigem `flows:publish`.\n"},{"name":"Saude","x-displayName":"Saúde do Serviço","description":"Verifique o status e a disponibilidade do serviço. Não requer autenticação."},{"name":"FAQ","x-displayName":"Perguntas Frequentes (FAQ)","description":"Gerencie a base de perguntas frequentes do agente IA do escritório.\nCada agente pode ter até 100 itens de FAQ com perguntas e respostas.\n\nInclui endpoints de geração por IA (OpenAI) e sugestão automática\na partir de conversas recentes.\n\nRequer os escopos `agents:read` e/ou `agents:write`.\n"},{"name":"Documentos","x-displayName":"Documentos (v2)","description":"Gere e gerencie documentos jurídicos com identidade visual do escritório\ne propriedades personalizadas. Inclui templates Markdown, versões,\nrevisão antes de exportar (OAB) e integração com assinatura digital.\n\nRequer os escopos `documents:read`, `documents:write`,\n`custom_properties:read` ou `custom_properties:write`.\n\nPlano: docs/document-generation/05-API-AND-UI-CONTRACT.md.\n"}],"paths":{"/v1/lawsuits":{"get":{"operationId":"listarProcessos","summary":"Listar/buscar processos do escritório","description":"Lista os processos jurídicos **cadastrados no escritório** (a área do\nícone de balança), vinculados aos contatos. Distinto da pesquisa pública\nde jurisprudência. Requer o escopo `lawsuits:read`.\n\n- Informe `conversation_id` para os processos vinculados a uma conversa,\n  ou `contact_id` para os de um contato — ambos retornam as últimas\n  movimentações (andamento) de cada processo.\n- Sem esses filtros, faz uma **busca paginada** por nome de parte\n  (`parte`), status operacional (`status`) e/ou advogado responsável\n  (`advogado_id`).\n\nProcessos em segredo de justiça são retornados com os detalhes omitidos\n(apenas CNJ, tribunal, situação e a flag `segredo_de_justica`).\n","tags":["Processos"],"parameters":[{"name":"conversation_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Retorna os processos vinculados a esta conversa (com movimentações). Tem precedência sobre `contact_id`."},{"name":"contact_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Retorna os processos vinculados a este contato (com movimentações)"},{"name":"max_movements","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":20,"default":5},"description":"Movimentações recentes por processo (apenas com `conversation_id`/`contact_id`)"},{"name":"parte","in":"query","required":false,"schema":{"type":"string"},"description":"Filtra por substring no nome de uma das partes (polo ativo ou passivo)"},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Status operacional do processo no escritório (ex. CONHECIMENTO, RECURSAL, EXECUCAO, ENCERRADO, RASCUNHO)"},{"name":"advogado_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Filtra pelo advogado responsável (UUID do usuário)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":50,"default":20},"description":"Número máximo de processos por página"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Cursor para a próxima página (valor de `pagination.next_cursor`)"}],"responses":{"200":{"description":"Lista paginada de processos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lawsuit"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarProcesso","summary":"Registrar um processo (e opcionalmente vincular)","description":"Consulta o CNJ na base pública (Escavador — consulta paga), registra\no processo no escritório e, opcionalmente, vincula a uma conversa\n(`conversation_id`) ou à conversa principal de um contato (`contact_id`).\nCom `contact_id`, vincula apenas a primeira conversa; para vincular\noutras, chame novamente com cada `conversation_id`. Requer o escopo\n`lawsuits:write`. A consulta ao Escavador é síncrona; quando indisponível,\no processo é criado como rascunho e atualizado de forma assíncrona.\n","tags":["Processos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLawsuitRequest"}}}},"responses":{"201":{"description":"Processo registrado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/LawsuitCreateResult"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/lawsuits/{cnj}":{"get":{"operationId":"consultarProcessoEscritorio","summary":"Consultar um processo do escritório por CNJ","description":"Retorna um único processo cadastrado no escritório pelo número CNJ\n(formatado `NNNNNNN-DD.AAAA.J.TR.OOOO` ou 20 dígitos), com metadados e as\nmovimentações mais recentes. Diferente da consulta pública (DataJud/CNJ):\nsó retorna se o processo estiver cadastrado neste escritório. Requer o\nescopo `lawsuits:read`.\n","tags":["Processos"],"parameters":[{"name":"cnj","in":"path","required":true,"schema":{"type":"string"},"description":"Número CNJ formatado ou 20 dígitos"},{"name":"max_movements","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":30,"default":10},"description":"Quantidade de movimentações recentes a incluir"}],"responses":{"200":{"description":"Dados do processo","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Lawsuit"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarProcessoEscritorio","summary":"Atualizar o vínculo do processo com o escritório","description":"Atualiza `client_polo`, `status_operacional` e/ou `advogado_user_id` do\nvínculo do processo (identificado pelo CNJ) com o escritório. Para limpar\no responsável, envie `advogado_user_id: \"__LIMPAR__\"` (via MCP, onde null\né ignorado) ou `null` (via REST). Requer `lawsuits:write`.\n","tags":["Processos"],"parameters":[{"name":"cnj","in":"path","required":true,"schema":{"type":"string"},"description":"Número CNJ formatado ou 20 dígitos"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLawsuitRequest"}}}},"responses":{"200":{"description":"Vínculo atualizado"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"desvincularProcessoConversa","summary":"Desvincular uma conversa do processo","description":"Remove a conversa (`conversation_id`) do processo do escritório. Se for a\núltima conversa vinculada, o vínculo é arquivado. Requer `lawsuits:write`.\n","tags":["Processos"],"parameters":[{"name":"cnj","in":"path","required":true,"schema":{"type":"string"},"description":"Número CNJ formatado ou 20 dígitos"},{"name":"conversation_id","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"Conversa a desvincular"}],"responses":{"200":{"description":"Conversa desvinculada"},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/lawsuits/{cnj}/active":{"put":{"operationId":"definirProcessoAtivo","summary":"Definir o processo ativo da conversa (para a IA)","description":"Define este processo como o **ativo** da conversa — o que a IA usa ao\nresponder sobre andamento. Requer `lawsuits:write`.\n","tags":["Processos"],"parameters":[{"name":"cnj","in":"path","required":true,"schema":{"type":"string"},"description":"Número CNJ formatado ou 20 dígitos"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["conversation_id"],"properties":{"conversation_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Processo ativo definido"},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"limparProcessoAtivo","summary":"Limpar o processo ativo da conversa","description":"Remove este processo (o CNJ informado) dos processos ativos da conversa;\nos demais processos ativos da conversa não são alterados. Retorna 404 se\no CNJ não estiver cadastrado no escritório. Requer `lawsuits:write`.\n","tags":["Processos"],"parameters":[{"name":"cnj","in":"path","required":true,"schema":{"type":"string"},"description":"Número CNJ formatado ou 20 dígitos"},{"name":"conversation_id","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"Conversa cujo processo ativo será limpo"}],"responses":{"200":{"description":"Processo ativo removido"},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/cases":{"get":{"operationId":"listarCasos","summary":"Listar/buscar casos do escritório","description":"Lista os **casos** do escritório (processos administrativos — sem CNJ),\npaginados. Requer `cases:read`.\n\n- `conversation_id` → casos vinculados a uma conversa (tem precedência\n  sobre `contact_id`).\n- `contact_id` → casos vinculados a qualquer conversa do contato (um\n  contato tem uma conversa por número do escritório).\n- Demais filtros combinam: `status`, `source`, `tag` (etiqueta exata),\n  `search` (título ou pasta), `responsible_user_id`.\n","tags":["Casos"],"parameters":[{"name":"conversation_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Casos vinculados a esta conversa"},{"name":"contact_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Casos vinculados às conversas deste contato"},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["novo","em_andamento","aguardando","concluido","arquivado"]},"description":"Coluna do kanban"},{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["manual","advbox","astrea","calculo_juridico"]},"description":"Origem do caso"},{"name":"tag","in":"query","required":false,"schema":{"type":"string"},"description":"Etiqueta exata (ex. \"contrato fechado por ia\")"},{"name":"search","in":"query","required":false,"schema":{"type":"string"},"description":"Substring no título ou no número da pasta"},{"name":"responsible_user_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Responsável (UUID do membro, via `listarMembros`)"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1},"description":"Página (1-based)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Casos por página"}],"responses":{"200":{"description":"Lista paginada de casos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Case"}},"pagination":{"$ref":"#/components/schemas/PagePagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarCaso","summary":"Criar um caso (com partes e vínculos)","description":"Cria um caso manual no escritório. Requer `cases:write`. Não consulta\nnenhuma base externa e não tem custo.\n\n- `contact_id` = \"este contato é o cliente\": adiciona o contato como\n  parte `cliente` e vincula **todas** as conversas dele (uma por número\n  do escritório).\n- `conversation_id` vincula uma conversa específica.\n- `parties[]` adiciona as demais partes na mesma chamada (nome e\n  papel; `contact_id` opcional preenche o nome a partir do contato).\n- `responsible_user_id` deve ser membro do escritório (`listarMembros`).\n- Boas práticas do escritório: título com o nome do cliente, `tags`\n  para a tese e a origem (\"contrato fechado por ia\"), `status:\n  aguardando` enquanto falta assinatura.\n","tags":["Casos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCaseRequest"}}}},"responses":{"201":{"description":"Caso criado (com `parties`)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Case"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/cases/counts":{"get":{"operationId":"contagemCasos","summary":"Contagem de casos por status","description":"Quantos casos o escritório tem em cada coluna do kanban (`novo`,\n`em_andamento`, `aguardando`, `concluido`, `arquivado`). Opcionalmente\nfiltrado por `source`. Requer `cases:read`.\n","tags":["Casos"],"parameters":[{"name":"source","in":"query","required":false,"schema":{"type":"string","enum":["manual","advbox","astrea","calculo_juridico"]},"description":"Origem do caso"}],"responses":{"200":{"description":"Contagem por status","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CaseCounts"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/cases/{id}":{"get":{"operationId":"consultarCaso","summary":"Consultar um caso","description":"Retorna um caso do escritório com suas partes (`parties[]`) e conversas\nvinculadas (`chats_id[]`). Requer `cases:read`.\n","tags":["Casos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID do caso"}],"responses":{"200":{"description":"Dados do caso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Case"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarCaso","summary":"Atualizar um caso (inclui mover no kanban)","description":"Atualização parcial: campos omitidos ficam como estão. Mover de coluna\né `status`. Para limpar o responsável envie\n`responsible_user_id: \"__LIMPAR__\"` (via MCP, onde null é ignorado) ou\n`null` (via REST). Conversas e partes têm endpoints próprios. Requer\n`cases:write`.\n","tags":["Casos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID do caso"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseRequest"}}}},"responses":{"200":{"description":"Caso atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Case"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirCaso","summary":"Excluir um caso","description":"Exclui (soft-delete) um caso do escritório; ele deixa de aparecer junto\ncom suas partes. Não afeta as\nconversas nem os contatos. Requer `cases:write`.\n","tags":["Casos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID do caso"}],"responses":{"200":{"description":"Caso excluído"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/cases/{id}/parties":{"post":{"operationId":"adicionarParteCaso","summary":"Adicionar uma parte ao caso","description":"Adiciona uma parte (cliente, autor, réu, envolvido, testemunha…) ao\ncaso. Com `contact_id`, o nome é preenchido a partir do contato quando\nomitido. Requer `cases:write`.\n","tags":["Casos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID do caso"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CasePartyRequest"}}}},"responses":{"201":{"description":"Parte adicionada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CaseParty"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/cases/{id}/parties/{partyId}":{"delete":{"operationId":"removerParteCaso","summary":"Remover uma parte do caso","description":"Remove uma parte do caso. Requer `cases:write`.","tags":["Casos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID do caso"},{"name":"partyId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da parte (de `parties[].id`)"}],"responses":{"200":{"description":"Parte removida"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/cases/{id}/conversations":{"post":{"operationId":"vincularCasoConversa","summary":"Vincular uma conversa ao caso","description":"Vincula a conversa (`conversation_id`) ao caso, que passa a aparecer no\npainel dessa conversa. Idempotente. Requer `cases:write`.\n","tags":["Casos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID do caso"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["conversation_id"],"properties":{"conversation_id":{"type":"string","format":"uuid","description":"Conversa a vincular"}}}}}},"responses":{"200":{"description":"Conversa vinculada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Case"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/cases/{id}/conversations/{conversationId}":{"delete":{"operationId":"desvincularCasoConversa","summary":"Desvincular uma conversa do caso","description":"Remove a conversa do caso. O caso continua existindo (diferente de\n`desvincularProcessoConversa`, aqui nada é arquivado). Requer\n`cases:write`.\n","tags":["Casos"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID do caso"},{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Conversa a desvincular"}],"responses":{"200":{"description":"Conversa desvinculada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Case"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/health":{"get":{"operationId":"verificarSaude","summary":"Verificar saúde do serviço","description":"Retorna o status de saúde do serviço. Não requer autenticação.","tags":["Saude"],"security":[],"responses":{"200":{"description":"Serviço funcionando normalmente","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"healthy"},"service":{"type":"string","example":"cj-open-api"},"stage":{"type":"string","example":"production"},"timestamp":{"type":"string","format":"date-time","example":"2026-03-20T14:30:00Z"}}},"example":{"status":"healthy","service":"cj-open-api","stage":"production","timestamp":"2026-03-20T14:30:00Z"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/openapi.json":{"get":{"operationId":"obterEspecificacaoAPI","summary":"Obter especificação OpenAPI","description":"Retorna a especificação OpenAPI completa da API em formato JSON.\nÚtil para geração de SDKs, documentação e integração com ferramentas de API.\nNão requer autenticação. A resposta é cacheada por 5 minutos.\n","tags":["Saude"],"security":[],"responses":{"200":{"description":"Especificação OpenAPI em JSON","content":{"application/json":{"schema":{"type":"object","description":"Documento OpenAPI 3.0.3 completo"}}},"headers":{"Cache-Control":{"schema":{"type":"string","example":"public, max-age=300"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/jurisprudencia/health":{"get":{"operationId":"verificarSaudeJurisprudencia","summary":"Verificar saúde da pesquisa de jurisprudência","description":"Retorna o status das fontes externas usadas pela pesquisa de jurisprudência\n(DataJud, LexML, STJ ScON e TJPR), incluindo:\n- estado dos circuit breakers por fonte;\n- modo do fetcher do STJ ScON (`direct`, `chain:direct`, `flaresolverr`, `zenrows`);\n- metadados do snapshot da Tabela de Padronização de Assuntos (TPU).\n\nUsado pelo probe sintético do CloudWatch e pelo dashboard de SLOs.\nNão requer autenticação.\n","tags":["Saude"],"security":[],"responses":{"200":{"description":"Status agregado das fontes de jurisprudência","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JurisprudenciaHealth"},"example":{"status":"ok","service":"cj-open-api","stage":"production","timestamp":"2026-05-06T14:30:00Z","sources":{"stj_scon":{"status":"configured","fetcher_mode":"flaresolverr"},"lexml":{"status":"configured"},"datajud":{"status":"configured"},"tjpr":{"status":"configured"}},"tpu_snapshot":{"assuntos":{"version":"2026-04-01","total":11342},"classes":{"version":"2026-04-01","total":1187}},"circuit_breakers_open":[]}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts":{"get":{"operationId":"listarContatos","summary":"Listar contatos","description":"Retorna uma lista paginada de contatos do seu escritório.\nSuporta busca por nome ou telefone, filtros por múltiplas tags, telefone dedicado e ordenação.\nO Status do contato é `is_customer`: true=Cliente, false=Lead, null=não classificado.\nUse `contact_status` para filtrar e `contarContatos` para totais exatos sem paginar.\n","tags":["Contatos"],"x-codeSamples":[{"lang":"cURL","source":"curl -X GET \"https://api.jur.chat/v1/contacts?limit=50\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\"\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.get(\n    \"https://api.jur.chat/v1/contacts\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    params={\"limit\": 50}\n)\nprint(response.json())\n"}],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/ContactStatus"},{"name":"search","in":"query","description":"Buscar por nome ou telefone do contato","schema":{"type":"string"}},{"name":"tag_id","in":"query","description":"Filtrar contatos por ID de uma tag (para múltiplas tags use `tag_ids`)","schema":{"type":"string","format":"uuid"}},{"name":"tag_ids","in":"query","description":"Filtrar contatos que possuem qualquer uma das tags (UUID separados por vírgula, semântica OR)","schema":{"type":"string"},"example":"uuid1,uuid2"},{"name":"is_active","in":"query","description":"Filtrar por status ativo (`true`) ou inativo (`false`)","schema":{"type":"boolean"}},{"name":"phone","in":"query","description":"Filtrar contatos por número de telefone (busca parcial)","schema":{"type":"string"}},{"name":"sort_by","in":"query","description":"Campo de ordenação dos contatos","schema":{"type":"string","enum":["name","recent","created_at"],"default":"name"}}],"responses":{"200":{"description":"Lista de contatos retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"João Silva","phone":"5511999998888","tags":["cliente-vip"],"stage_id":null,"is_active":true,"is_starred":false,"source":"whatsapp","summary":"Cliente buscando orientação sobre divórcio consensual","created_at":"2026-01-15T10:30:00Z","updated_at":"2026-03-18T14:22:00Z"},{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","name":"Maria Oliveira","phone":"5521988887777","tags":["lead","trabalhista"],"stage_id":"c3d4e5f6-a7b8-9012-cdef-123456789012","is_active":true,"is_starred":true,"source":"api","summary":null,"created_at":"2026-02-20T08:15:00Z","updated_at":"2026-03-19T16:45:00Z"}],"pagination":{"has_more":true,"next_cursor":"b2c3d4e5-f6a7-8901-bcde-f12345678901","limit":50}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarContato","summary":"Criar contato","description":"Cria um novo contato no seu escritório. O campo `phone` deve estar no formato internacional\nsem caracteres especiais (ex: `5511999998888`).\n\n**Semântica por instância**: a unicidade do contato é por par\n`(phone, instance_id)`. O mesmo telefone pode existir como contatos\ndistintos em instâncias diferentes do mesmo escritório.\n\n- Se sua chave de API tem escopo em **uma única instância** e você\n  omitir `instance_id`, o contato é criado nessa instância (default\n  transparente).\n- Se sua chave abrange **múltiplas instâncias** e você omitir\n  `instance_id`, a operação retorna `400` com\n  `code: instance_required` — informe a instância explicitamente.\n- Se você informar um `instance_id` **fora do escopo** da sua chave,\n  a operação retorna `400` com `code: instance_out_of_scope`.\n- Se já existir um contato ativo com o mesmo telefone **na mesma\n  instância**, a operação retorna `409 Conflict` com\n  `code: duplicate_resource`.\n","tags":["Contatos"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/contacts\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"Carlos Santos\", \"phone\": \"5511999998888\"}'\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.post(\n    \"https://api.jur.chat/v1/contacts\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\"name\": \"Carlos Santos\", \"phone\": \"5511999998888\"}\n)\nprint(response.json())\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContact"},"example":{"name":"Carlos Santos","phone":"5531977776666","tags":["novo-lead"],"source":"api"}}}},"responses":{"201":{"description":"Contato criado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Contact"}}},"example":{"data":{"id":"d4e5f6a7-b8c9-0123-defa-456789012345","name":"Carlos Santos","phone":"5531977776666","tags":["novo-lead"],"stage_id":null,"is_active":true,"is_starred":false,"source":"api","summary":null,"created_at":"2026-03-20T15:00:00Z","updated_at":"2026-03-20T15:00:00Z"}}}}},"400":{"description":"Requisição inválida no contexto de escopo de instância. O campo\n`error.code` discrimina o motivo:\n  - `instance_required`: a chave abrange múltiplas instâncias e\n    a requisição não informou `instance_id`.\n  - `instance_out_of_scope`: o `instance_id` informado não é\n    acessível por esta chave de API.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"instance_required":{"summary":"instance_id ausente com chave multi-instância","value":{"error":{"code":"instance_required","message":"instance_id is required: this API key spans multiple instances. Specify which instance the contact should belong to.","status":400}}},"instance_out_of_scope":{"summary":"instance_id fora do escopo da chave","value":{"error":{"code":"instance_out_of_scope","message":"The specified instance_id is not accessible with this API key","status":400}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts/bulk":{"post":{"operationId":"criarContatosEmMassa","summary":"Criar contatos em massa","description":"Cria até 100 contatos em uma única requisição. Cada contato é processado individualmente —\nfalhas em um item não impedem a criação dos demais. Retorna `201` se todos forem criados\ncom sucesso, ou `207 Multi-Status` se houver falhas parciais.\n\n**Semântica por instância**: igual a `POST /v1/contacts`. Cada item\nprecisa resolver para uma única instância dentro do escopo da chave\n(default transparente quando a chave tem uma instância só, explícito\nquando tem várias). Itens individuais podem retornar `instance_required`,\n`instance_out_of_scope` ou `duplicate_resource` no campo `code`\nsem afetar os demais.\n","tags":["Contatos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCreateContacts"},"example":{"contacts":[{"name":"João Silva","phone":"5511999998888","tags":["lead"]},{"name":"Maria Oliveira","phone":"5521988887777"}]}}}},"responses":{"201":{"description":"Todos os contatos criados com sucesso","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCreateContactsResponse"},"example":{"data":{"created":2,"failed":0,"results":[{"index":0,"status":"created","data":{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"João Silva","phone":"5511999998888"}},{"index":1,"status":"created","data":{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","name":"Maria Oliveira","phone":"5521988887777"}}]}}}}},"207":{"description":"Criação parcial — alguns contatos falharam","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCreateContactsResponse"},"example":{"data":{"created":1,"failed":1,"results":[{"index":0,"status":"created","data":{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"João Silva","phone":"5511999998888"}},{"index":1,"status":"error","error":"Telefone já cadastrado"}]}}}}},"400":{"description":"Corpo inválido ou lista vazia","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts/bulk-delete":{"post":{"operationId":"excluirContatosEmMassa","summary":"Excluir contatos em massa","description":"Exclui até 100 contatos em uma única requisição, de forma reversível\n(soft-delete) — a mesma semântica de `DELETE /v1/contacts/{id}`, aplicada\nem lote: as conversas dos contatos são arquivadas, mensagens agendadas são\ncanceladas e destinatários de campanha pendentes são ignorados.\n\nA seleção é **somente por lista explícita de ids** — não existe exclusão\npor filtro. Ids inexistentes, já excluídos ou de outro escritório voltam\nem `not_found`, sem erro.\n\n**Requer o escopo `contacts:delete`** (não incluído em `contacts:write`).\nPara lotes maiores que o limite, divida em várias requisições.\n\n**Desfazer:** a exclusão é reversível por `PATCH /v1/contacts/{id}` com\n`{\"is_active\": true}` — que exige `contacts:write`. Uma chave com apenas\n`contacts:delete` não consegue desfazer a própria exclusão; conceda os\ndois escopos juntos.\n","tags":["Contatos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDeleteContacts"},"example":{"contact_ids":["a1b2c3d4-e5f6-7890-abcd-ef1234567890","b2c3d4e5-f6a7-8901-bcde-f12345678901"]}}}},"responses":{"200":{"description":"Exclusão processada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDeleteContactsResponse"},"example":{"data":{"deleted":2,"not_found":[],"conversations_archived":2}}}}},"400":{"description":"Corpo inválido, lista vazia, acima do limite de 100 ids, ou um id que\nnão é UUID (`code: invalid_uuid`, nomeando o id ofensor)\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts/count":{"get":{"operationId":"contarContatos","summary":"Contar leads e clientes","description":"Retorna a contagem exata de registros de contatos não excluídos no escritório e\nnas instâncias permitidas pela chave. Use contact_status=lead para Leads,\ncustomer para Clientes ou unclassified para não classificados. Sem filtro conta todos.\nNão depende de paginação, não conta pessoas únicas e não confunde null com Lead.\nRequer contacts:read.\n","tags":["Contatos"],"parameters":[{"$ref":"#/components/parameters/ContactStatus"}],"responses":{"200":{"description":"Contagem exata","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["total","contact_status"],"properties":{"total":{"type":"integer","minimum":0},"contact_status":{"type":"string","nullable":true,"enum":["lead","customer","unclassified",null]}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts/bulk-status":{"post":{"operationId":"atualizarStatusContatosEmMassa","summary":"Atualizar status Lead/Cliente em massa","description":"Define o Status do contato para até 100 IDs explícitos: is_customer=true (Cliente),\nfalse (Lead) ou null (limpar classificação). Requer contacts:write.\nResolva a seleção e todas as páginas antes de escrever. Não altera etapas, tags ou mensagens.\nIDs repetidos são deduplicados. IDs inexistentes, excluídos ou fora do escopo retornam\nem not_found; confira updated e not_found antes de afirmar que todos foram alterados.\n","tags":["Contatos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_ids","is_customer"],"properties":{"contact_ids":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string","format":"uuid"}},"is_customer":{"type":"boolean","nullable":true,"description":"true=Cliente, false=Lead, null=limpar classificação; obrigatório."}}}}}},"responses":{"200":{"description":"Resultado da atualização dos IDs acessíveis","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","required":["updated","updated_ids","not_found","is_customer"],"properties":{"updated":{"type":"integer","minimum":0},"updated_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"not_found":{"type":"array","items":{"type":"string","format":"uuid"}},"is_customer":{"type":"boolean","nullable":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts/{id}":{"get":{"operationId":"buscarContato","summary":"Buscar contato por ID","description":"Retorna os dados completos de um contato específico.","tags":["Contatos"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados do contato","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarContato","summary":"Atualizar contato","description":"Atualiza os dados de um contato existente. Envie apenas os campos que deseja alterar.\nStatus do contato: is_customer=true para Cliente, false para Lead, null para limpar.\nNão confundir com etapa do funil, tag, propriedade personalizada ou is_active.\nPara vários IDs, use atualizarStatusContatosEmMassa.\n","tags":["Contatos"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateContact"},"example":{"name":"João Silva Jr.","is_starred":true,"tags":["cliente-vip","cível"]}}}},"responses":{"200":{"description":"Contato atualizado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirContato","summary":"Excluir contato","description":"Remove um contato de forma reversível (soft-delete). O campo `is_active` é definido como `false`.","tags":["Contatos"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Contato excluído com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}},"example":{"data":{"deleted":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts/merge":{"post":{"operationId":"mesclarContatos","summary":"Mesclar dois contatos","description":"Mescla um contato duplicado (\"loser\") em um contato principal (\"keeper\").\nTodas as mensagens, anotações, propostas, processos e demais dados do contato\nduplicado são transferidos para o contato principal. O contato duplicado é\ndesativado após a mesclagem.\n\nAmbos os contatos devem pertencer ao mesmo escritório e à mesma instância.\nEsta operação é irreversível.\n\nRequer o escopo `contacts:write`.\n","tags":["Contatos"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/contacts/merge\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"keeper_id\": \"uuid-contato-principal\", \"loser_id\": \"uuid-contato-duplicado\"}'\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.post(\n    \"https://api.jur.chat/v1/contacts/merge\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\n        \"keeper_id\": \"uuid-contato-principal\",\n        \"loser_id\": \"uuid-contato-duplicado\"\n    }\n)\nprint(response.json())\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["keeper_id","loser_id"],"properties":{"keeper_id":{"type":"string","format":"uuid","description":"ID do contato principal que será mantido"},"loser_id":{"type":"string","format":"uuid","description":"ID do contato duplicado que será absorvido"}}},"example":{"keeper_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","loser_id":"f9e8d7c6-b5a4-3210-fedc-ba0987654321"}}}},"responses":{"200":{"description":"Contatos mesclados com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"success":{"type":"boolean"},"keeper_id":{"type":"string","format":"uuid"},"loser_id":{"type":"string","format":"uuid"},"chat_merged":{"type":"boolean","description":"Se uma conversa foi mesclada"},"messages_moved":{"type":"integer","description":"Quantidade de mensagens transferidas"},"fields_merged":{"type":"array","items":{"type":"string"},"description":"Campos do perfil preenchidos a partir do contato duplicado"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Validação falhou (contatos de instâncias diferentes, inativos, etc.)"},"500":{"$ref":"#/components/responses/InternalError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/contacts/duplicates":{"get":{"operationId":"buscarContatosDuplicados","summary":"Buscar contatos duplicados","description":"Identifica contatos potencialmente duplicados no escritório.\nDetecta variantes do 9º dígito brasileiro, telefones idênticos em contatos\ndiferentes e colisões de BSUID. Retorna pares agrupados com sugestão de\nqual contato manter.\n\nRequer o escopo `contacts:read`.\n","tags":["Contatos"],"x-codeSamples":[{"lang":"cURL","source":"curl -X GET \"https://api.jur.chat/v1/contacts/duplicates?limit=50\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\"\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.get(\n    \"https://api.jur.chat/v1/contacts/duplicates\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    params={\"limit\": 50}\n)\nprint(response.json())\n"}],"parameters":[{"name":"limit","in":"query","description":"Número máximo de pares duplicados a retornar","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Lista de contatos duplicados encontrados","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"duplicates":{"type":"array","items":{"type":"object","properties":{"group_id":{"type":"integer"},"match_reason":{"type":"string","enum":["phone_variant","exact_phone","bsuid_collision"]},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"phone":{"type":"string"},"has_chat":{"type":"boolean"},"message_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}}},"suggested_keeper":{"type":"string","format":"uuid","description":"ID do contato sugerido para manter"}}}},"total_found":{"type":"integer"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/contacts/{id}/profile-picture":{"post":{"operationId":"atualizarFotoPerfilContato","summary":"Atualizar foto de perfil do contato","description":"Busca e atualiza a foto de perfil do contato a partir do WhatsApp.\nRequer o escopo `contacts:write`.\n","tags":["Contatos"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Foto de perfil atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"description":"Contato sem número de telefone"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"description":"Serviço de foto de perfil indisponível"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations":{"get":{"operationId":"listarConversas","summary":"Listar conversas","description":"Retorna uma lista paginada de conversas do WhatsApp com filtros avançados.\nFiltre por contato, número WhatsApp, tags, estágio do funil, status de leitura, responsável, status do agente IA, e mais.\nOrdene por recência (`last_message_at`), nome do contato ou data de criação.\n","tags":["Conversas"],"x-codeSamples":[{"lang":"cURL","source":"curl -X GET \"https://api.jur.chat/v1/conversations?limit=20\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\"\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.get(\n    \"https://api.jur.chat/v1/conversations\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    params={\"limit\": 20}\n)\nprint(response.json())\n"}],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"},{"name":"contact_id","in":"query","description":"Filtrar conversas de um contato específico","schema":{"type":"string","format":"uuid"}},{"name":"is_archived","in":"query","description":"Filtrar por conversas arquivadas (`true`) ou ativas (`false`)","schema":{"type":"boolean"}},{"name":"instance_id","in":"query","description":"Filtrar por número de WhatsApp","schema":{"type":"string","format":"uuid"}},{"name":"tag_ids","in":"query","description":"Filtrar conversas que possuem qualquer uma das tags (UUID separados por vírgula, semântica OR)","schema":{"type":"string"},"example":"uuid1,uuid2"},{"name":"stage_id","in":"query","description":"Filtrar conversas por estágio do funil (UUID do estágio)","schema":{"type":"string","format":"uuid"}},{"name":"search","in":"query","description":"Buscar conversas pelo nome ou telefone do contato","schema":{"type":"string"}},{"name":"has_unread","in":"query","description":"Filtrar por conversas com mensagens não lidas (`true`) ou todas lidas (`false`)","schema":{"type":"boolean"}},{"name":"date_start","in":"query","description":"Filtrar conversas com última mensagem a partir desta data (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"date_end","in":"query","description":"Filtrar conversas com última mensagem até esta data (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"sort_by","in":"query","description":"Campo de ordenação das conversas","schema":{"type":"string","enum":["recent","name","created_at"],"default":"recent"}},{"name":"responsible_id","in":"query","description":"Filtrar por responsável. Use um UUID do usuário ou `none` para conversas sem responsável","schema":{"type":"string"}},{"name":"is_waiting_human","in":"query","description":"Filtrar conversas aguardando atendimento humano","schema":{"type":"boolean"}},{"name":"agent_is_active","in":"query","description":"Filtrar por status do agente IA (`true` = IA ativa, `false` = IA inativa)","schema":{"type":"boolean"}},{"name":"is_ctwa_origin","in":"query","description":"Filtrar conversas originadas de anúncios Click-to-WhatsApp","schema":{"type":"boolean"}},{"name":"customer_service_window_status","in":"query","description":"Filtrar conversas pelo status da janela de atendimento de 24h. Aplica-se apenas a instâncias Meta.\n","schema":{"type":"string","enum":["open","expiring","closed"]}},{"name":"free_entry_point_window_open","in":"query","description":"Filtrar pela janela gratuita de anúncio Click-to-WhatsApp da Meta (`free_entry_point_window_open`). `true` = mensagens enviadas agora são grátis; `false` = janela encerrada ou nunca aberta. Aplica-se apenas a instâncias Meta.\n","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Lista de conversas retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"example":{"data":[{"id":"e5f6a7b8-c9d0-1234-efab-567890123456","contact_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","instance_id":"f6a7b8c9-d0e1-2345-fabc-678901234567","last_message_at":"2026-03-20T14:15:00Z","last_message_text":"Bom dia, doutor! Preciso agendar uma consulta.","last_message_type":"text","is_archived":false,"stage_id":null,"unread_count":2,"phone":"5511999998888","created_at":"2026-01-15T10:30:00Z","customer_service_window_status":null}],"pagination":{"has_more":false,"next_cursor":null,"limit":50}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarConversa","summary":"Criar conversa","description":"Abre a conversa de um contato no número de WhatsApp ao qual ele pertence.\n\n**CONTATO ≠ PESSOA — não confunda:**\n\n- Um **contato** está vinculado a **UM único número** (`contacts` é `UNIQUE(phone, instance_id)`) e tem no máximo **UMA** conversa (`chats` é `UNIQUE(contact_id)`).\n- A mesma **pessoa** (mesmo telefone) tem **um contato — e uma conversa — POR NÚMERO** do escritório. Isso é normal e esperado.\n- Para falar com essa pessoa por **outro** número, use o **contato daquele número**. **NÃO** reutilize o mesmo `contact_id` trocando o `instance_id`: o contato não muda de número.\n\nPortanto `instance_id` deve ser o número ao qual o contato **já pertence**:\n\n- Conversa já existe para o contato: ela é retornada (idempotente).\n- `instance_id` **diferente** do número do contato: `409 Conflict` com `code: contact_bound_to_other_instance`. A mensagem indica o número real do contato e, quando existe, o `contact_id` **irmão** no número solicitado — use esse.\n- Conversa existe mas em um número **fora do escopo da chave de API**: `409 Conflict` com `code: conversation_exists_other_instance`.\n","tags":["Conversas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversation"},"example":{"contact_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","instance_id":"f6a7b8c9-d0e1-2345-fabc-678901234567"}}}},"responses":{"201":{"description":"Conversa criada com sucesso (ou retornada como idempotente quando já existia para o mesmo `(contact_id, instance_id)`)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/count":{"get":{"operationId":"contarConversas","summary":"Contar conversas","description":"Retorna a contagem exata de conversas que correspondem aos filtros informados.\nUsa `SELECT COUNT(*)` diretamente no banco, sem limite de paginação — ideal para dashboards, resumos de pipeline e métricas em tempo real.\nAceita os mesmos filtros de `listarConversas` (exceto `cursor`, `limit`, `sort_by` e `search`).\n","tags":["Conversas"],"parameters":[{"name":"stage_id","in":"query","description":"Filtrar conversas por estágio do funil (UUID do estágio)","schema":{"type":"string","format":"uuid"}},{"name":"instance_id","in":"query","description":"Filtrar por um único número de WhatsApp (instance `unique_id`). Por padrão a\ncontagem soma todos os números do escritório; informe este parâmetro para contar\num número específico, reproduzindo a visão do quadro do funil. Números fora do\nescritório/escopo da chave retornam contagem 0.\n","schema":{"type":"string","format":"uuid"}},{"name":"tag_ids","in":"query","description":"Filtrar conversas que possuem qualquer uma das tags (UUID separados por vírgula, semântica OR)","schema":{"type":"string"},"example":"uuid1,uuid2"},{"name":"is_archived","in":"query","description":"Filtrar por conversas arquivadas (`true`) ou ativas (`false`)","schema":{"type":"boolean"}},{"name":"has_unread","in":"query","description":"Filtrar por conversas com mensagens não lidas","schema":{"type":"boolean"}},{"name":"responsible_id","in":"query","description":"Filtrar por responsável. Use um UUID do usuário ou `none` para conversas sem responsável","schema":{"type":"string"}}],"responses":{"200":{"description":"Contagem retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"count":{"type":"integer","description":"Número total de conversas que correspondem aos filtros","example":3277}}}}},"example":{"data":{"count":3277}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/search":{"post":{"operationId":"buscarConversasAvancado","summary":"Busca avançada de conversas","description":"Endpoint de busca estruturada para consultas avançadas que não podem ser expressas com os parâmetros GET.\nAceita filtros com operadores, ordenação multi-campo e paginação por cursor.\nCampos permitidos: unique_id, contact_id, instance_id, last_message_at, last_message_text,\nlast_message_type, is_archived, office_stage_id, tags_ids, unread_messages_count,\nphone_id, created_at, accountable_id, agent_is_active_local, is_waiting_human, is_ctwa_origin, customer_service_window_status.\nOperadores: eq, neq, gt, gte, lt, lte, in, overlaps, ilike, is_null.\n","tags":["Conversas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSearchRequest"},"example":{"filters":[{"field":"tags_ids","operator":"overlaps","value":["uuid-da-tag-1","uuid-da-tag-2"]},{"field":"office_stage_id","operator":"eq","value":"uuid-do-estagio"},{"field":"last_message_at","operator":"gte","value":"2026-03-01T00:00:00Z"}],"sort":[{"field":"last_message_at","direction":"desc"}],"limit":50}}}},"responses":{"200":{"description":"Resultados da busca retornados com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}":{"get":{"operationId":"buscarConversa","summary":"Buscar conversa por ID","description":"Retorna os dados completos de uma conversa específica, incluindo última mensagem e contagem de não lidas.","tags":["Conversas"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Dados da conversa","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarConversa","summary":"Atualizar conversa","description":"Atualiza tags, etapa do funil, status de arquivamento, responsável e/ou departamento\n(fila) de uma conversa. Envie apenas os campos que deseja alterar.\n","tags":["Conversas"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConversation"},"example":{"tag_ids":["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],"stage_id":"c3d4e5f6-a7b8-9012-cdef-123456789012","is_archived":false,"accountable_id":"d4e5f6a7-b8c9-0123-def0-456789abcdef"}}}},"responses":{"200":{"description":"Conversa atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/transfer":{"post":{"operationId":"transferirConversa","summary":"Delegar conversa para membro e/ou departamento","description":"Delega uma conversa para um membro da equipe (`member_id`), um departamento/fila\n(`department_id`), ou ambos. Informe ao menos um dos dois. Qualquer um dos campos\naceita `null` para LIMPAR a atribuição atual (enviar `member_id` não mexe no\ndepartamento e vice-versa — só o campo presente é alterado).\nOpcionalmente cria uma anotação com o motivo da transferência.\nResolva `department_id` via `GET /v1/departments`.\nRequer o escopo `conversations:write`.\n","tags":["Conversas"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Informe member_id e/ou department_id (ao menos um).","properties":{"member_id":{"type":"string","format":"uuid","nullable":true,"description":"UUID do membro para delegar (null para limpar o responsável)"},"department_id":{"type":"string","format":"uuid","nullable":true,"description":"UUID do departamento/fila para delegar (null para limpar)"},"note":{"type":"string","maxLength":5000,"description":"Nota opcional sobre a transferência (cria anotação)"}}}}}},"responses":{"200":{"description":"Conversa transferida com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Conversation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"Corpo da requisição inválido"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/messages":{"get":{"operationId":"listarMensagens","summary":"Listar mensagens","description":"Retorna uma lista paginada de mensagens de uma conversa, ordenadas da mais recente para a mais antiga.\nFiltre por direção (`from_me`), tipo de mensagem, intervalo de datas, ou busque por texto no conteúdo.\n","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","description":"Número máximo de mensagens por página","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"$ref":"#/components/parameters/CursorParam"},{"name":"from_me","in":"query","description":"Filtrar por direção — `true` para enviadas, `false` para recebidas","schema":{"type":"boolean"}},{"name":"search","in":"query","description":"Buscar mensagens por texto no conteúdo (mínimo 2 caracteres)","schema":{"type":"string","minLength":2}},{"name":"type","in":"query","description":"Filtrar por tipo de mensagem","schema":{"type":"string","enum":["text","image","audio","video","document","template","sticker","interactive"]}},{"name":"after","in":"query","description":"Filtrar mensagens criadas a partir desta data (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"before","in":"query","description":"Filtrar mensagens criadas até esta data (ISO 8601)","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Lista de mensagens retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"example":{"data":[{"id":"a7b8c9d0-e1f2-3456-abcd-789012345678","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","text":"Bom dia, doutor! Preciso agendar uma consulta.","type":"text","from_me":false,"file_url":null,"file_type":null,"file_name":null,"status":"read","created_at":"2026-03-20T14:15:00Z"},{"id":"b8c9d0e1-f2a3-4567-bcde-890123456789","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","text":"Bom dia! Claro, posso agendar para amanhã às 10h. Funciona?","type":"text","from_me":true,"file_url":null,"file_type":null,"file_name":null,"status":"delivered","created_at":"2026-03-20T14:18:00Z"}],"pagination":{"has_more":false,"next_cursor":null,"limit":100}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"enviarMensagem","summary":"Enviar mensagem","description":"Envia uma nova mensagem via WhatsApp em uma conversa existente.\nA mensagem é colocada em fila e enviada de forma assíncrona.\nO status inicial é `pending` e será atualizado para `sent`, `delivered` ou `read` conforme o progresso.\n\n**Templates:** Para enviar mensagens de template (necessárias para iniciar conversas fora da janela de 24h),\nuse `type: \"template\"` com o objeto `template`. O template deve estar aprovado pela Meta.\n\n**Preenchendo variáveis do template:** Consulte os `components` do template (via `listarTemplates`).\nO texto do corpo contém placeholders como `{{1}}`, `{{2}}`. Mapeie cada `{{N}}` para `body_N` no\nobjeto `parameters`: `{{1}}` → `body_1`, `{{2}}` → `body_2`. Para cabeçalhos de texto: `header_1`, etc.\nSempre use os dados reais do contato (nome, horário, etc.), NUNCA o placeholder `{1}` ou `{{1}}`.\n\n**Validação:** A API valida que todos os placeholders `{{N}}` no corpo do template possuem o\nparâmetro `body_N` correspondente. Se faltar algum, retorna **422** com mensagem indicando os\nparâmetros faltantes. Essa validação não se aplica ao override via `components` (formato Meta avançado).\n\n**Limites:** Mensagens de texto/mídia: 20/min, 500/dia. Templates: 5/min (limite adicional).\n","tags":["Mensagens"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/conversations/{conversation_id}/messages\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"type\": \"text\", \"content\": \"Olá!\"}'\n"},{"lang":"Python","source":"import requests\n\nconversation_id = \"uuid-da-conversa\"\nresponse = requests.post(\n    f\"https://api.jur.chat/v1/conversations/{conversation_id}/messages\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\"type\": \"text\", \"content\": \"Olá!\"}\n)\nprint(response.json())\n"}],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessage"},"examples":{"texto":{"summary":"Mensagem de texto simples","value":{"text":"Olá! Sua consulta está agendada para amanhã às 10h. Confirma?","type":"text"}},"template_simples":{"summary":"Template com parâmetros simples","value":{"type":"template","template":{"name":"appointment_reminder","language":"pt_BR","parameters":{"body_1":"João","body_2":"10h"}}}},"template_avancado":{"summary":"Template com componentes Meta raw","value":{"type":"template","template":{"name":"appointment_reminder","language":"pt_BR","components":[{"type":"body","parameters":[{"type":"text","text":"João"},{"type":"text","text":"10h"}]}]}}}}}}},"responses":{"201":{"description":"Mensagem enfileirada para envio com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Message"}}},"examples":{"texto":{"summary":"Mensagem de texto","value":{"data":{"id":"c9d0e1f2-a3b4-5678-cdef-901234567890","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","text":"Olá! Sua consulta está agendada para amanhã às 10h. Confirma?","type":"text","from_me":true,"file_url":null,"file_type":null,"file_name":null,"status":"sent","template_name":null,"template_category":null,"estimated_cost_brl":null,"created_at":"2026-03-20T15:30:00Z"}}},"template":{"summary":"Mensagem de template (com custo estimado)","value":{"data":{"id":"d0e1f2a3-b4c5-6789-defa-012345678901","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","text":"Olá João! Sua consulta está agendada para amanhã às 10h.","type":"template","from_me":true,"file_url":null,"file_type":null,"file_name":null,"status":"sent","template_name":"appointment_reminder","template_category":"UTILITY","estimated_cost_brl":0.035,"free_entry_point":false,"created_at":"2026-03-23T17:00:00Z"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Conflito. Em ambos os casos NENHUMA mensagem é criada e a conversa não é alterada.\n\n`code: \"service_window_closed\"` — janela de atendimento de 24h fechada (Meta): texto livre não pode ser entregue; envie um template aprovado para reabrir a conversa. A janela gratuita de anúncio Click-to-WhatsApp NÃO libera texto livre; quando ela está aberta, `error.details` traz `free_entry_point_window_open: true` e `free_entry_point_window_expires_at`, e o template enviado até lá sai grátis.\n\n`code: \"instance_mismatch\"` — o `instance_id` informado não corresponde ao número da conversa. O envio foi RECUSADO para não sair pelo número errado. `error.details` traz `conversation_instance_id` e `requested_instance_id`. Use a conversa que pertence ao número pretendido — a mesma pessoa tem uma conversa por número.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"instance_mismatch":{"summary":"Número errado — nada foi enviado","value":{"error":{"code":"instance_mismatch","message":"This conversation belongs to WhatsApp number <uuid-a>, but the request asked to send from <uuid-b>. Nothing was sent.","status":409,"details":{"conversation_instance_id":"<uuid-a>","requested_instance_id":"<uuid-b>"}}}},"service_window_closed_free_entry_point":{"summary":"Janela de 24h fechada, janela gratuita de anúncio aberta — envie um template (grátis)","value":{"error":{"code":"service_window_closed","message":"Customer service window is closed; free-text cannot be delivered. Send an approved template to re-open the conversation. This contact came from a Click-to-WhatsApp ad and its free entry point window is open until 2026-10-15T14:03:00Z: templates sent until then are free of charge.","status":409,"details":{"free_entry_point_window_open":true,"free_entry_point_window_expires_at":"2026-10-15T14:03:00Z","free_entry_point_reply_deadline":null}}}}}}}},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/message-sequence":{"post":{"operationId":"criarSequenciaMensagens","summary":"Enviar e agendar sequência de mensagens","description":"Envia mensagens imediatas e agenda mensagens futuras em uma única chamada.\nUse para fluxos como confirmação de compromisso e lembrete 1 hora antes.\n\nMáximo de 10 itens por requisição. `idempotency_key` e `client_item_id`\nevitam duplicação em retries. A resposta é por item e pode conter sucesso\nparcial.\n\nPara instâncias Meta, mensagens futuras devem usar template aprovado que\nresolva para `template_id`. Mensagens futuras de texto/mídia só devem ser\nusadas em provedores que possuam executor de mensagens agendadas.\n","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageSequenceRequest"},"example":{"idempotency_key":"appointment-123","items":[{"client_item_id":"confirm-now","message":{"type":"text","text":"Agendamento confirmado para amanhã às 10h."}},{"client_item_id":"reminder-1h","send_at":"2026-04-30T12:00:00Z","message":{"type":"template","template":{"name":"appointment_reminder","language":"pt_BR","parameters":{"body_1":"João","body_2":"10h"}}}}]}}}},"responses":{"201":{"description":"Sequência processada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MessageSequenceResponse"}}}}}},"207":{"description":"Sequência processada com sucesso parcial","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MessageSequenceResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/collect-reply":{"post":{"operationId":"coletarResposta","summary":"Enviar prompt e capturar a próxima resposta do contato","description":"Envia uma mensagem (texto ou menu interativo) e registra que a **próxima** mensagem\nrecebida do contato nesta conversa é a resposta aguardada — de qualquer tipo (texto,\náudio, etc.). Quando o contato responde, a plataforma correlaciona a mensagem de forma\natômica e entrega um webhook `message.reply_captured` contendo o `token` informado.\n\nResolve o padrão \"pergunta → coleta de resposta livre\" (ex.: pedir o motivo de um não\ncomparecimento) sem que o seu sistema precise guardar estado nem assinar todas as\nmensagens recebidas. Filtre a entrega com `filters.reply` no webhook.\n\nReenviar um `collect-reply` para o mesmo contato substitui a expectativa anterior. A\nexpectativa expira após `ttl_seconds`. Requer o escopo `messages:send`.\n","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectReplyRequest"},"example":{"prompt":{"type":"text","text":"Por favor, explique o motivo do não comparecimento:"},"token":"no-show:REC-8821","ttl_seconds":1800,"metadata":{"record_id":"REC-8821"}}}}},"responses":{"201":{"description":"Prompt enviado e expectativa registrada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CollectReplyResponse"}}},"example":{"data":{"expectation_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","token":"no-show:REC-8821","expires_at":"2026-07-03T12:30:00Z","prompt_message_id":"f0e1d2c3-b4a5-6789-0abc-def123456789"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"cancelarColetaResposta","summary":"Cancelar a expectativa de resposta ativa","description":"Cancela a expectativa de resposta ativa do contato desta conversa. Requer o escopo `messages:send`.","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Resultado do cancelamento","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"cancelled":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/messages/{id}":{"get":{"operationId":"buscarMensagem","summary":"Buscar mensagem por ID","description":"Retorna os dados completos de uma mensagem específica, incluindo status de entrega e arquivos anexados.","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}},{"name":"id","in":"path","required":true,"description":"ID da mensagem","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Dados da mensagem","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Message"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirMensagem","summary":"Excluir mensagem","description":"Exclui uma mensagem enviada pelo escritório (from_me=true). A mensagem é removida localmente e revogada no WhatsApp.\nRequer o escopo `messages:send`.\n","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}},{"name":"id","in":"path","required":true,"description":"ID da mensagem","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Mensagem excluída com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Apenas mensagens enviadas pelo escritório podem ser excluídas"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/messages/{id}/react":{"post":{"operationId":"reagirMensagem","summary":"Reagir a uma mensagem","description":"Envia uma reação (emoji) a uma mensagem específica via WhatsApp.\nRequer o escopo `messages:send`.\n","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}},{"name":"id","in":"path","required":true,"description":"ID da mensagem","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["emoji"],"properties":{"emoji":{"type":"string","description":"Emoji para reação (ex. 👍)","minLength":1,"maxLength":10}}}}}},"responses":{"200":{"description":"Reação enviada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid"},"emoji":{"type":"string"},"status":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"description":"Provedor WhatsApp indisponível"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/messages/{id}/transcribe":{"post":{"operationId":"transcreverMensagem","summary":"Transcrever mensagem de áudio","description":"Solicita a transcrição de uma mensagem de áudio. Se já existir transcrição, retorna imediatamente.\nCaso contrário, transcreve de forma síncrona (Whisper) e retorna o texto na mesma resposta;\náudios longos podem responder 504.\nRequer o escopo `messages:read`.\n","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}},{"name":"id","in":"path","required":true,"description":"ID da mensagem","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Transcrição retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid"},"transcription":{"type":"string","nullable":true},"status":{"type":"string","enum":["completed","processing","failed"]}}}}}}}},"400":{"description":"Mensagem não é do tipo áudio"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"description":"Serviço de transcrição indisponível"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/messages/{id}/media":{"get":{"operationId":"lerMidiaMensagem","summary":"Obter mídia da mensagem","description":"Retorna a URL e metadados da mídia anexada a uma mensagem.\nRequer o escopo `messages:read`.\n","tags":["Mensagens"],"parameters":[{"name":"conversation_id","in":"path","required":true,"description":"ID da conversa","schema":{"type":"string","format":"uuid"}},{"name":"id","in":"path","required":true,"description":"ID da mensagem","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Dados da mídia retornados com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid"},"file_url":{"type":"string","nullable":true},"file_type":{"type":"string","nullable":true},"file_name":{"type":"string","nullable":true},"file_size":{"type":"integer","nullable":true},"mimetype":{"type":"string","nullable":true},"audio_transcription":{"type":"string","nullable":true},"file_duration_seconds":{"type":"number","nullable":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/status":{"get":{"operationId":"listarStatus","summary":"Listar status publicados","description":"Lista os status (stories) do WhatsApp publicados por este escritório, do mais\nrecente ao mais antigo. Cada registro traz o tipo, o texto/mídia, a audiência e\no resultado do envio (`queued`, `sent` ou `failed`).\n\nRequer o escopo `messages:read`.\n","tags":["Mensagens"],"parameters":[{"name":"instance_id","in":"query","required":false,"description":"Filtra pelos status publicados por um número específico.","schema":{"type":"string"}},{"name":"status","in":"query","required":false,"description":"Filtra pelo resultado do envio.","schema":{"type":"string","enum":["queued","sent","failed"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"maximum":500}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de status","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StatusPost"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"publicarStatus","summary":"Publicar status (stories)","description":"Publica um status (story) do WhatsApp. Diferente de uma mensagem, um status é uma\n**transmissão** — não pertence a nenhuma conversa e é visível para os contatos do\nnúmero (ou para uma audiência específica via `recipients`).\n\n**Apenas números uazapi.** A Cloud API da Meta não possui endpoint de status, então\ninstâncias `meta` são rejeitadas com **400** (`status_provider_unsupported`).\n\n**Tipos:** `text` (com `background_color` e `font`), `image`/`video` (com `text` como\nlegenda opcional), `audio`/`myaudio`/`ptt`. Para tipos de mídia, `file_url` é obrigatório.\n\n**Audiência:** omita `recipients` para transmitir a todos os contatos; informe `recipients`\n(números ou JIDs) para restringir; use `max_recipients` como limite de segurança para\naudiências muito grandes (o WhatsApp pode bloquear status com milhares de destinatários).\n\nO envio é **assíncrono**: retorna **202** com o registro `queued`, atualizado depois para\n`sent`/`failed` (consulte via `GET /v1/status`).\n\nRequer o escopo `messages:send`.\n","tags":["Mensagens"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/status\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"instance_id\": \"uuid-da-instancia\", \"type\": \"text\", \"text\": \"Novidades!\", \"background_color\": 7, \"font\": 1}'\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.post(\n    \"https://api.jur.chat/v1/status\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\n        \"instance_id\": \"uuid-da-instancia\",\n        \"type\": \"image\",\n        \"file_url\": \"https://cdn.exemplo.com/promo.jpg\",\n        \"text\": \"Promoção!\",\n    },\n)\nprint(response.json())\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendStatus"},"examples":{"texto":{"summary":"Status de texto (todos os contatos)","value":{"instance_id":"uuid-da-instancia","type":"text","text":"Novidades chegando!","background_color":7,"font":1}},"imagem":{"summary":"Status de imagem para contatos selecionados","value":{"instance_id":"uuid-da-instancia","type":"image","file_url":"https://cdn.exemplo.com/promo.jpg","text":"Promoção!","recipients":["5511999999999","5511888888888@s.whatsapp.net"],"max_recipients":100}}}}}},"responses":{"202":{"description":"Status enfileirado para publicação","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/StatusPost"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/media/upload":{"post":{"operationId":"uploadMedia","summary":"Upload de mídia (base64)","description":"Faz upload de um arquivo codificado em base64 para o armazenamento do ChatJuridico.\nRetorna uma URL pública (`media_url`) que pode ser usada como `file_url` ao enviar mensagens.\n\nIdeal para arquivos pequenos (até ~4.5MB). Para arquivos maiores, use `POST /v1/media/upload-url`.\n\n**Tipos suportados:**\n- **image**: png, jpeg, gif, webp, bmp (máx. 5MB)\n- **audio**: mp3, wav, ogg, m4a, aac, flac (máx. 16MB)\n- **video**: mp4, avi, mov, webm (máx. 16MB)\n- **document**: pdf, doc, docx, xls, xlsx, ppt, pptx, txt, rtf (máx. 100MB)\n\nRequer o escopo `messages:send`.\n","tags":["Media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUploadRequest"},"example":{"file_name":"contrato.pdf","media_type":"document","data":"JVBERi0xLjQKMSAwIG9iago8PC..."}}}},"responses":{"201":{"description":"Arquivo enviado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MediaUploadResponse"}}},"example":{"data":{"media_url":"https://storage-meta.chatjuridico.com/api-uploads/office123/document/2026/04/07/abc123.pdf","media_type":"document","content_type":"application/pdf","file_size":234567,"file_name":"contrato.pdf"}}}}},"400":{"description":"Arquivo inválido (tipo não suportado, base64 inválido)"},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"Arquivo excede o tamanho máximo permitido para o tipo de mídia"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/media/upload-url":{"post":{"operationId":"getMediaUploadUrl","summary":"Obter URL pré-assinada para upload","description":"Gera uma URL pré-assinada para upload direto ao armazenamento, sem passar pelo servidor.\nO cliente faz `PUT` na `upload_url` retornada com o conteúdo binário do arquivo e o header `Content-Type`.\n\nSuporta arquivos de qualquer tamanho (até 100MB para documentos).\nA URL expira em 1 hora.\n\n**Exemplo de upload via curl:**\n```\ncurl -X PUT \"<upload_url>\" \\\n  -H \"Content-Type: video/mp4\" \\\n  --data-binary @video.mp4\n```\n\nRequer o escopo `messages:send`.\n","tags":["Media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUploadUrlRequest"},"example":{"file_name":"video.mp4","media_type":"video","content_type":"video/mp4"}}}},"responses":{"200":{"description":"URL pré-assinada gerada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MediaUploadUrlResponse"}}},"example":{"data":{"upload_url":"https://r2-endpoint.example.com/presigned-put?X-Amz-Signature=...","media_url":"https://storage-meta.chatjuridico.com/api-uploads/office123/video/2026/04/07/def456.mp4","content_type":"video/mp4","expires_in":3600}}}}},"400":{"description":"Tipo de mídia ou content_type inválido"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/instances":{"get":{"operationId":"listarInstancias","summary":"Listar números WhatsApp","description":"Retorna todos os números de WhatsApp conectados ao seu escritório.\nUse o `id` retornado como `instance_id` ao criar conversas ou filtrar por número.\n","tags":["Instancias"],"responses":{"200":{"description":"Lista de números WhatsApp retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Instance"}}}},"example":{"data":[{"id":"f6a7b8c9-d0e1-2345-fabc-678901234567","name":"Atendimento Principal","phone_id":"5511999998888","display_phone_number":"+55 11 99999-8888","is_connected":true,"api_provider":"meta","created_at":"2025-06-15T10:00:00Z"},{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Comercial","phone_id":"5521988887777","display_phone_number":"+55 21 98888-7777","is_connected":true,"api_provider":"meta","created_at":"2025-09-01T14:30:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/instances/{id}":{"get":{"operationId":"buscarInstancia","summary":"Buscar número WhatsApp por ID","description":"Retorna os dados de um número WhatsApp específico, incluindo status de conexão e telefone.","tags":["Instancias"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados do número WhatsApp","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Instance"}}},"example":{"data":{"id":"f6a7b8c9-d0e1-2345-fabc-678901234567","name":"Atendimento Principal","phone_id":"5511999998888","display_phone_number":"+55 11 99999-8888","is_connected":true,"api_provider":"meta","created_at":"2025-06-15T10:00:00Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/instances/{id}/reconnect":{"post":{"operationId":"reconectarInstancia","summary":"Reconectar instância WhatsApp","description":"Inicia a reconexão de uma instância WhatsApp. O processo é assíncrono.\nRequer o escopo `instances:write`.\n","tags":["Instancias"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Reconexão iniciada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"instance_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"message":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"description":"Serviço de reconexão indisponível"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/instances/{id}/qr-code":{"get":{"operationId":"obterQRCodeInstancia","summary":"Obter QR code da instância","description":"Gera e retorna o QR code para conectar uma instância UAZAPI.\nNão disponível para instâncias Meta (WhatsApp Cloud API).\nRequer o escopo `instances:write`.\n","tags":["Instancias"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"QR code gerado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"instance_id":{"type":"string","format":"uuid"},"qr_code":{"type":"string","description":"QR code em base64","nullable":true},"qr_code_url":{"type":"string","description":"URL para visualizar o QR code","nullable":true}}}}}}}},"400":{"description":"QR code não disponível para instâncias Meta"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"description":"Serviço de QR code indisponível"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/instances/{id}/status":{"get":{"operationId":"statusInstancia","summary":"Obter status da instância","description":"Retorna o status detalhado de conexão de uma instância WhatsApp.\nRequer o escopo `conversations:read`.\n","tags":["Instancias"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Status da instância retornado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"instance_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"phone_id":{"type":"string"},"display_phone_number":{"type":"string"},"is_connected":{"type":"boolean"},"api_provider":{"type":"string","enum":["meta","uazapi"]}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/admin/api-keys":{"get":{"operationId":"listarChavesAPI","summary":"Listar chaves de API","description":"Retorna todas as chaves de API do seu escritório.\nRequer autenticação via Bearer JWT (token de sessão do painel).\n","tags":["ChavesAPI"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Lista de chaves de API","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}},"example":{"data":[{"unique_id":"d0e1f2a3-b4c5-6789-defa-012345678901","name":"Integração n8n","key_prefix":"cj_live_x8k2","scopes":["contacts:read","conversations:read","messages:read"],"is_active":true,"last_used_at":"2026-03-20T12:00:00Z","request_count":1523,"created_at":"2026-02-01T09:00:00Z","created_by":"a1b2c3d4-e5f6-7890-abcd-ef0123456789","created_by_name":"João Silva","expires_at":null}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarChaveAPI","summary":"Criar chave de API","description":"Cria uma nova chave de API. A chave completa é retornada **apenas uma vez** na resposta.\nGuarde-a em local seguro — não será possível visualizá-la novamente.\n","tags":["ChavesAPI"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKey"},"example":{"name":"Integração CRM","scopes":["contacts:read","contacts:write","conversations:read"]}}}},"responses":{"201":{"description":"Chave de API criada (inclui a chave completa)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApiKeyWithSecret"}}},"example":{"data":{"unique_id":"e1f2a3b4-c5d6-7890-efab-123456789012","name":"Integração CRM","key_prefix":"cj_live_m4n7","scopes":["contacts:read","contacts:write","conversations:read"],"is_active":true,"last_used_at":null,"request_count":0,"created_at":"2026-03-20T15:00:00Z","created_by":"a1b2c3d4-e5f6-7890-abcd-ef0123456789","created_by_name":"Maria Oliveira","expires_at":null,"raw_key":"cj_live_m4n7p9q2r5s8t1u4v7w0x3y6z9a2b5c8"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/admin/api-keys/{id}":{"delete":{"operationId":"revogarChaveAPI","summary":"Revogar chave de API","description":"Revoga permanentemente uma chave de API. Todas as requisições futuras\nusando esta chave serão rejeitadas com `401 Unauthorized`.\n**Esta ação não pode ser desfeita.**\n","tags":["ChavesAPI"],"security":[{"BearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Chave revogada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"revoked":{"type":"boolean","example":true}}}}},"example":{"data":{"revoked":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/admin/offices/{id}/quota":{"get":{"operationId":"obterCotaEscritorio","summary":"Obter cota de uso do escritório","description":"Retorna a configuração de cota e uso atual de tokens para um escritório.\nInclui uso do mês corrente, saldo de créditos e projeção de esgotamento.\n**Requer chave de API interna (key_type=internal).**\n","tags":["UsageAdmin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cota e uso do escritório","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"office_id":{"type":"string","format":"uuid"},"plan_tier":{"type":"string","enum":["free","starter","pro","enterprise","custom"]},"monthly_token_limit":{"type":"integer"},"grace_percent":{"type":"integer"},"is_unlimited":{"type":"boolean"},"current_usage":{"type":"object","properties":{"used":{"type":"integer"},"limit":{"type":"integer"},"percent":{"type":"integer"},"remaining":{"type":"integer"}}},"credits":{"type":"object","properties":{"balance":{"type":"integer"}}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"atualizarCotaEscritorio","summary":"Atualizar cota de uso do escritório","description":"Cria ou atualiza a configuração de cota de tokens para um escritório.\nPermite definir o plano (tier), limite mensal e margem de tolerância.\n**Requer chave de API interna (key_type=internal).**\n","tags":["UsageAdmin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan_tier":{"type":"string","enum":["free","starter","pro","enterprise","custom"]},"monthly_token_limit":{"type":"integer","minimum":0},"monthly_tool_limit":{"type":"integer","minimum":0,"nullable":true},"grace_percent":{"type":"integer","minimum":0,"maximum":100},"is_unlimited":{"type":"boolean"}}},"example":{"plan_tier":"starter","monthly_token_limit":2000000,"grace_percent":10,"is_unlimited":false}}}},"responses":{"200":{"description":"Cota atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"updated":{"type":"boolean"},"office_id":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/admin/offices/{id}/credits":{"post":{"operationId":"concederCreditosEscritorio","summary":"Conceder créditos de tokens ao escritório","description":"Adiciona créditos de tokens ao saldo do escritório. Os créditos\nsão somados ao limite mensal para calcular o limite efetivo.\n**Requer chave de API interna (key_type=internal).**\n","tags":["UsageAdmin"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount","description"],"properties":{"amount":{"type":"integer","minimum":1,"description":"Quantidade de tokens a conceder"},"description":{"type":"string","maxLength":500,"description":"Motivo da concessão de créditos"}}},"example":{"amount":500000,"description":"Beta power user - créditos adicionais para teste"}}}},"responses":{"201":{"description":"Créditos concedidos com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"granted":{"type":"boolean"},"office_id":{"type":"string"},"amount":{"type":"integer"},"new_balance":{"type":"integer"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/tags":{"get":{"operationId":"listarTags","summary":"Listar tags","description":"Retorna todas as tags do seu escritório. Requer o escopo `tags:read`.","tags":["Tags"],"responses":{"200":{"description":"Lista de tags retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Tag"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Cliente VIP","color_hex":"#FF5733","color_name":"vermelho","created_at":"2026-01-10T09:00:00Z"},{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","name":"Lead","color_hex":"#33FF57","color_name":"verde","created_at":"2026-01-10T09:00:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarTag","summary":"Criar tag","description":"Cria uma nova tag no seu escritório. Requer o escopo `tags:write`.\nO campo `name` deve ter entre 1 e 100 caracteres.\n","tags":["Tags"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTag"},"example":{"name":"Urgente","color_id":3}}}},"responses":{"201":{"description":"Tag criada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Tag"}}},"example":{"data":{"id":"c3d4e5f6-a7b8-9012-cdef-123456789012","name":"Urgente","color_hex":"#FFC300","color_name":"amarelo","created_at":"2026-03-23T15:00:00Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/tags/{id}":{"get":{"operationId":"buscarTag","summary":"Buscar tag por ID","description":"Retorna os dados de uma tag específica. Requer o escopo `tags:read`.","tags":["Tags"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados da tag","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Tag"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarTag","summary":"Atualizar tag","description":"Atualiza os dados de uma tag existente. Envie apenas os campos que deseja alterar. Requer o escopo `tags:write`.","tags":["Tags"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTag"},"example":{"name":"VIP Premium"}}}},"responses":{"200":{"description":"Tag atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Tag"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirTag","summary":"Excluir tag","description":"Remove uma tag do escritório. As associações existentes com conversas serão removidas. Requer o escopo `tags:write`.","tags":["Tags"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Tag excluída com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}},"example":{"data":{"deleted":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/funnels":{"get":{"operationId":"listarFunis","summary":"Listar funis","description":"Retorna todos os funis ativos do escritório, ordenados por `sort_order`. Requer o escopo `funnel:read`.","tags":["Funnels"],"responses":{"200":{"description":"Lista de funis retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Funnel"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Funil Principal","description":"Funil padrão do escritório","color_hex":"#3B82F6","is_default":true,"is_active":true,"sort_order":0,"created_at":"2026-01-01T00:00:00Z"},{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","name":"Funil Trabalhista","description":"Funil para casos trabalhistas","color_hex":"#F59E0B","is_default":false,"is_active":true,"sort_order":1,"created_at":"2026-02-01T00:00:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/funnels/{id}":{"get":{"operationId":"buscarFunil","summary":"Buscar funil por ID","description":"Retorna os dados de um funil específico. Requer o escopo `funnel:read`.","tags":["Funnels"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados do funil","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Funnel"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/funnel-stages":{"get":{"operationId":"listarEtapasFunil","summary":"Listar etapas do funil","description":"Retorna todas as etapas ativas do funil de vendas do seu escritório, ordenadas por `stage_order`.\nOpcionalmente filtre por `funnel_id` para listar apenas etapas de um funil específico.\nRequer o escopo `funnel:read`.\n","tags":["FunnelStages"],"parameters":[{"name":"funnel_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Filtrar etapas por funil específico"}],"responses":{"200":{"description":"Lista de etapas retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FunnelStage"}}}},"example":{"data":[{"id":"c3d4e5f6-a7b8-9012-cdef-123456789012","name":"Novo Lead","color_hex":"#3498DB","color_name":"azul","stage_order":1,"is_active":true,"created_at":"2026-01-05T10:00:00Z"},{"id":"d4e5f6a7-b8c9-0123-defa-456789012345","name":"Em Negociação","color_hex":"#F39C12","color_name":"laranja","stage_order":2,"is_active":true,"created_at":"2026-01-05T10:00:00Z"},{"id":"e5f6a7b8-c9d0-1234-efab-567890123456","name":"Fechado","color_hex":"#27AE60","color_name":"verde","stage_order":3,"is_active":true,"created_at":"2026-01-05T10:00:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarEtapaFunil","summary":"Criar etapa do funil","description":"Cria uma nova etapa no funil de vendas. Requer o escopo `funnel:write`.","tags":["FunnelStages"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFunnelStageRequest"},"example":{"name":"Qualificação","color_hex":"#3B82F6","color_name":"azul"}}}},"responses":{"201":{"description":"Etapa criada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FunnelStage"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/funnel-stages/{id}":{"get":{"operationId":"buscarEtapaFunil","summary":"Buscar etapa do funil por ID","description":"Retorna os dados de uma etapa específica do funil. Requer o escopo `funnel:read`.","tags":["FunnelStages"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados da etapa do funil","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FunnelStage"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarEtapaFunil","summary":"Atualizar etapa do funil","description":"Atualiza os dados de uma etapa do funil. Requer o escopo `funnel:write`.","tags":["FunnelStages"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFunnelStageRequest"},"example":{"name":"Em Negociação","color_hex":"#F59E0B"}}}},"responses":{"200":{"description":"Etapa atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FunnelStage"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirEtapaFunil","summary":"Excluir etapa do funil","description":"Remove (desativa) uma etapa do funil. Requer o escopo `funnel:write`.","tags":["FunnelStages"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Etapa excluída com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks":{"get":{"operationId":"listarWebhooks","summary":"Listar webhooks","description":"Retorna os webhooks registrados no seu escritório. Filtre por status ativo/inativo. Requer o escopo `webhooks:manage`.","tags":["Webhooks"],"parameters":[{"name":"is_active","in":"query","description":"Filtrar por webhooks ativos (`true`) ou inativos (`false`)","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Lista de webhooks retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}},"example":{"data":[{"id":"f6a7b8c9-d0e1-2345-fabc-678901234567","url":"https://meu-servidor.com/webhooks/chatjuridico","events":["message.received","conversation.created"],"description":"Webhook principal para CRM","is_active":true,"failure_count":0,"last_delivery_at":"2026-03-23T14:00:00Z","last_failure_at":null,"created_at":"2026-02-01T09:00:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarWebhook","summary":"Criar webhook","description":"Registra um novo webhook para receber notificações de eventos.\nA URL deve utilizar HTTPS. O segredo (`secret`) para validação HMAC-SHA256\né retornado **apenas uma vez** na resposta — guarde-o em local seguro.\nRequer o escopo `webhooks:manage`.\n","tags":["Webhooks"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/webhooks\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://meu-servidor.com/webhooks\",\n    \"events\": [\"message.received\", \"conversation.created\"],\n    \"secret\": \"meu_segredo_hmac\"\n  }'\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.post(\n    \"https://api.jur.chat/v1/webhooks\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\n        \"url\": \"https://meu-servidor.com/webhooks\",\n        \"events\": [\"message.received\", \"conversation.created\"],\n        \"secret\": \"meu_segredo_hmac\"\n    }\n)\nprint(response.json())\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhook"},"example":{"url":"https://meu-servidor.com/webhooks/chatjuridico","events":["message.received","conversation.created"],"description":"Webhook principal para CRM","filters":{"message_types":["interactive"],"interactive":{"button_ids":["sim"]}}}}}},"responses":{"201":{"description":"Webhook criado com sucesso (inclui o segredo HMAC)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookWithSecret"}}},"example":{"data":{"id":"f6a7b8c9-d0e1-2345-fabc-678901234567","url":"https://meu-servidor.com/webhooks/chatjuridico","events":["message.received","conversation.created"],"description":"Webhook principal para CRM","is_active":true,"failure_count":0,"last_delivery_at":null,"last_failure_at":null,"created_at":"2026-03-23T15:00:00Z","secret":"whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}":{"get":{"operationId":"buscarWebhook","summary":"Buscar webhook por ID","description":"Retorna os dados de um webhook específico. Requer o escopo `webhooks:manage`.","tags":["Webhooks"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados do webhook","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Webhook"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarWebhook","summary":"Atualizar webhook","description":"Atualiza os dados de um webhook existente. Envie apenas os campos que deseja alterar. Requer o escopo `webhooks:manage`.","tags":["Webhooks"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhook"},"example":{"events":["message.received","message.sent","conversation.created"],"is_active":true}}}},"responses":{"200":{"description":"Webhook atualizado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Webhook"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirWebhook","summary":"Excluir webhook","description":"Remove permanentemente um webhook. Nenhuma notificação será mais enviada para este endpoint. Requer o escopo `webhooks:manage`.","tags":["Webhooks"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Webhook excluído com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}},"example":{"data":{"deleted":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/webhooks/{id}/test":{"post":{"operationId":"testarWebhook","summary":"Enviar evento de teste","description":"Envia um evento de teste para o webhook especificado.\nÚtil para validar a configuração do endpoint antes de ativá-lo em produção.\nRequer o escopo `webhooks:manage`.\n","tags":["Webhooks"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Evento de teste enviado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"delivered":{"type":"boolean","description":"Se o evento foi entregue com sucesso","example":true},"status_code":{"type":"integer","description":"Código HTTP retornado pelo endpoint","example":200},"response_time_ms":{"type":"integer","description":"Tempo de resposta em milissegundos","example":120}}}}},"example":{"data":{"delivered":true,"status_code":200,"response_time_ms":120}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/admin/logs":{"get":{"operationId":"listarLogsRequisicoes","summary":"Listar logs de requisições","description":"Retorna os logs de requisições feitas à API do seu escritório.\nRequer autenticação via Bearer JWT (token de sessão do painel).\n","tags":["ChavesAPI"],"security":[{"BearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"name":"offset","in":"query","description":"Deslocamento para paginação","schema":{"type":"integer","minimum":0,"default":0}},{"name":"api_key_id","in":"query","description":"Filtrar por ID da chave de API","schema":{"type":"string","format":"uuid"}},{"name":"method","in":"query","description":"Filtrar por método HTTP (GET, POST, PATCH, DELETE)","schema":{"type":"string"}},{"name":"status_min","in":"query","description":"Código de status HTTP mínimo (ex. `400` para erros)","schema":{"type":"integer"}},{"name":"status_max","in":"query","description":"Código de status HTTP máximo","schema":{"type":"integer"}},{"name":"since","in":"query","description":"Filtrar a partir desta data/hora (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","description":"Filtrar até esta data/hora (ISO 8601)","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Lista de logs retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/RequestLog"}}}},"example":{"data":[{"id":12345,"office_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","api_key_id":"d0e1f2a3-b4c5-6789-defa-012345678901","method":"GET","path":"/v1/contacts","status_code":200,"response_time_ms":45,"error_code":null,"created_at":"2026-03-23T14:30:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/scheduled-messages":{"get":{"operationId":"listarMensagensAgendadas","summary":"Listar mensagens agendadas","description":"Retorna uma lista paginada de mensagens agendadas do seu escritório.\nFiltre por status, conversa específica ou intervalo de datas.\n\nCada item diz QUEM agendou em `origin`: `person` (alguém da equipe),\n`automation` (uma automação, com `automation_id` e `automation_batch_id`),\n`integration` (API, MCP ou sistema, sem autor) ou `reminder` (lembrete de\ncobrança).\n\nStatus (texto aberto; trate valores desconhecidos como \"outro\"):\n- agendada por pessoa, API ou lembrete: `pending` → `processing` → `sent` | `failed` | `error` | `canceled`;\n- agendada por automação: `automation_pending` → `automation_sending` → `sent` | `failed` | `canceled`\n  (nunca `pending` nem `processing`);\n- `cancelled` e `canceled` são o mesmo estado (dados históricos têm as duas grafias).\n\n`cancel_reason` (só em linha de automação): `human_reply` (a equipe respondeu antes),\n`manual`, `automation_disabled`, `automation_deleted`, `instance_disconnected`,\n`contact_deleted`, `chat_deleted`, `batch_member_failed` (outra mensagem da mesma\nsequência falhou), `rollback`.\n`failure_reason`: `meta_window_closed`, `gateway_error`, `invalid_payload`, `stale`\n(atrasada mais de 6 h), `automations_disabled`, `instance_deleted`.\nOs dois são texto aberto: novos códigos podem aparecer.\nDefinitivos: `human_reply`, `manual`, `automation_deleted`, `contact_deleted` e\n`chat_deleted` encerram a mensagem (o mesmo evento repetido responde `already_canceled`).\nTemporários: `instance_disconnected`, `batch_member_failed`, `rollback`,\n`automation_disabled` e a falha `stale` deixam a próxima entrega do evento agendar de novo.\nMensagem enviada pela API ou pelo MCP (`source` `open-api`) NÃO conta como resposta da\nequipe e não cancela nada: para parar a sequência, use `cancelarMensagemAgendada` em cada\nlinha `automation_pending` com o mesmo `automation_batch_id`.\n\nPara ver só o que automações deixaram para depois, filtre `status=automation_pending`.\nMensagens de automação na API Oficial (Meta): modelo aprovado sempre espera o horário;\ntexto livre que passaria do fim da janela de 24 h sai na hora do disparo e nem vira linha aqui.\n","tags":["MensagensAgendadas"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"},{"name":"status","in":"query","description":"Filtrar por status da mensagem agendada. `cancelled` e `canceled` são equivalentes e retornam as mensagens canceladas por qualquer origem — as duas grafias existem nos dados históricos. `automation_pending` e `automation_sending` são as linhas de automação esperando o horário e em envio.","schema":{"type":"string","enum":["pending","processing","automation_pending","automation_sending","sent","failed","error","cancelled","canceled"]}},{"name":"conversation_id","in":"query","description":"Filtrar mensagens agendadas de uma conversa específica","schema":{"type":"string","format":"uuid"}},{"name":"after","in":"query","description":"Filtrar mensagens agendadas para depois desta data (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"before","in":"query","description":"Filtrar mensagens agendadas para antes desta data (ISO 8601)","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Lista de mensagens agendadas retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ScheduledMessage"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","message_type":"text","text":"Lembrete: sua consulta é amanhã às 10h.","file_url":null,"template_name":null,"template_language":null,"scheduled_at":"2026-03-25T10:00:00Z","status":"pending","created_at":"2026-03-23T15:00:00Z","origin":"person","automation_id":null,"automation_batch_id":null,"cancel_reason":null,"failure_reason":null,"claimed_at":null,"canceled_at":null,"sent_at":null},{"id":"c3d4e5f6-a7b8-9012-cdef-345678901234","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","message_type":"text","text":"Bom dia! Recebemos sua mensagem e já vamos te atender.","file_url":null,"template_name":null,"template_language":null,"scheduled_at":"2026-03-26T10:00:00Z","status":"automation_pending","created_at":"2026-03-26T06:10:00Z","origin":"automation","automation_id":"d4e5f6a7-b8c9-0123-defa-456789012345","automation_batch_id":"f6a7b8c9-d0e1-2345-fabc-678901234567","cancel_reason":null,"failure_reason":null,"claimed_at":null,"canceled_at":null,"sent_at":null}],"pagination":{"has_more":false,"next_cursor":null,"limit":50}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarMensagemAgendada","summary":"Agendar mensagem","description":"Cria uma nova mensagem agendada para envio futuro em uma conversa existente.\nA mensagem será enviada automaticamente no horário especificado em `scheduled_at`.\nSuporta mensagens de texto, mídia e templates.\nPara instâncias Meta (API Oficial), um template aprovado resolvido para `template_id` sai\nno horário sempre. Texto e mídia também são enviados no horário, mas só se a janela de\natendimento de 24 h ainda estiver aberta nesse momento; com a janela fechada a linha\ntermina em `failed` com o motivo \"Janela de atendimento de 24h fechada\". Para agendar\npara depois da janela, use um template.\nA linha criada aqui tem `origin` `integration` (sem autor) e status `pending`.\n","tags":["MensagensAgendadas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateScheduledMessage"},"examples":{"texto":{"summary":"Mensagem de texto agendada","value":{"conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","scheduled_at":"2026-03-25T10:00:00Z","text":"Lembrete: sua consulta é amanhã às 10h."}},"template":{"summary":"Template agendado","value":{"conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","scheduled_at":"2026-03-25T10:00:00Z","message_type":"template","template_name":"appointment_reminder","template_language":"pt_BR","template_parameters":{"body_1":"João","body_2":"10h"}}}}}}},"responses":{"201":{"description":"Mensagem agendada criada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ScheduledMessage"}}},"example":{"data":{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","message_type":"text","text":"Lembrete: sua consulta é amanhã às 10h.","file_url":null,"template_name":null,"template_language":null,"scheduled_at":"2026-03-25T10:00:00Z","status":"pending","created_at":"2026-03-23T15:00:00Z"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/scheduled-messages/{id}":{"get":{"operationId":"buscarMensagemAgendada","summary":"Buscar mensagem agendada por ID","description":"Retorna os dados completos de uma mensagem agendada específica.","tags":["MensagensAgendadas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados da mensagem agendada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ScheduledMessage"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"cancelarMensagemAgendada","summary":"Cancelar mensagem agendada","description":"Cancela uma mensagem agendada que ainda está esperando o horário: status `pending`\n(agendada por pessoa, API ou lembrete) ou `automation_pending` (deixada para depois por\numa automação). Qualquer outro status devolve 409 com o status atual e o motivo, inclusive\nquando o status muda entre a leitura e a gravação (o enviador pegou a linha, ou alguém\ncancelou antes). Nunca cancela `processing` nem `automation_sending`: a mensagem já está\nsaindo.\nNuma linha de automação o cancelamento grava `cancel_reason` `manual` e `canceled_at`, e o\nregistro de entrega da automação passa a cancelado na mesma transação. Cancela só esta\nlinha: para parar a sequência inteira, cancele também as outras linhas `automation_pending`\ncom o mesmo `automation_batch_id` (veja `listarMensagensAgendadas`).\nA resposta traz o status como gravado (`canceled`).\n","tags":["MensagensAgendadas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Mensagem agendada cancelada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ScheduledMessage"}}},"example":{"data":{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","conversation_id":"e5f6a7b8-c9d0-1234-efab-567890123456","message_type":"text","text":"Lembrete: sua consulta é amanhã às 10h.","file_url":null,"template_name":null,"template_language":null,"scheduled_at":"2026-03-25T10:00:00Z","status":"canceled","created_at":"2026-03-23T15:00:00Z","origin":"automation","automation_id":"d4e5f6a7-b8c9-0123-defa-456789012345","automation_batch_id":"f6a7b8c9-d0e1-2345-fabc-678901234567","cancel_reason":"manual","failure_reason":null,"claimed_at":null,"canceled_at":"2026-03-24T09:12:00Z","sent_at":null}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"A mensagem não está esperando o horário (status diferente de `pending` e `automation_pending`) e não pode ser cancelada. A mensagem nomeia o status atual.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"conflict","message":"Cannot cancel scheduled message with status 'automation_sending': it is being sent right now. Only pending messages (status 'pending' or 'automation_pending') can be canceled.","status":409}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/templates":{"get":{"operationId":"listarTemplates","summary":"Listar templates","description":"Retorna os templates de mensagem WhatsApp disponíveis nos números do seu escritório.\nPor padrão retorna apenas templates com status `APPROVED`. Use o parâmetro `status` para listar outros.\n","tags":["Templates"],"parameters":[{"name":"instance_id","in":"query","description":"Filtrar templates por número de WhatsApp específico","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","description":"Filtrar por status do template (padrão APPROVED)","schema":{"type":"string","enum":["DRAFT","APPROVED","PENDING","REJECTED"],"default":"APPROVED"}},{"name":"search","in":"query","description":"Buscar templates por nome","schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de templates retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppTemplate"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"appointment_reminder","language":"pt_BR","status":"APPROVED","category":"UTILITY","components":[{"type":"BODY","text":"Olá {{1}}! Sua consulta está agendada para {{2}}."}],"instance_id":"f6a7b8c9-d0e1-2345-fabc-678901234567"},{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","name":"welcome_message","language":"pt_BR","status":"APPROVED","category":"MARKETING","components":[{"type":"BODY","text":"Bem-vindo ao nosso escritório, {{1}}!"}],"instance_id":"f6a7b8c9-d0e1-2345-fabc-678901234567"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarTemplate","summary":"Criar template","description":"Cria um novo template de mensagem WhatsApp. O template é criado com status `DRAFT`\ne precisa ser enviado à Meta pelo painel web antes de entrar em aprovação.\nRequer o escopo `messages:send`.\n\n**Cabeçalho com imagem, vídeo ou documento:** envie o componente\n`{\"type\": \"HEADER\", \"format\": \"IMAGE\", \"example\": {\"header_url\": \"<URL pública>\"}}`\n(por exemplo, o link devolvido por `uploadMedia`). O Chat Jurídico baixa o arquivo\n(até 5MB; IMAGE = JPEG/PNG, VIDEO = MP4, DOCUMENT = PDF), envia à Meta e grava o\n`header_handle` exigido na aprovação. Não é preciso (nem possível) obter o handle por fora.\n","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplate"},"example":{"name":"appointment_reminder","language":"pt_BR","category":"UTILITY","components":[{"type":"BODY","text":"Olá {{1}}! Sua consulta está agendada para {{2}}."}],"instance_id":"f6a7b8c9-d0e1-2345-fabc-678901234567"}}}},"responses":{"201":{"description":"Template criado com sucesso (status DRAFT)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppTemplate"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Número WhatsApp não encontrado ou não pertence ao escritório","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/templates/{id}":{"get":{"operationId":"buscarTemplate","summary":"Buscar template por ID","description":"Retorna os dados de um template específico. Requer o escopo `messages:read`.","tags":["Templates"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados do template","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppTemplate"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarTemplate","summary":"Atualizar template","description":"Atualiza os dados de um template. Campos permitidos: `name`, `language`, `category`, `components`.\nO status é redefinido para `DRAFT` após a atualização. Requer o escopo `messages:send`.\nUm cabeçalho de mídia enviado por URL em `components` é tratado como em `criarTemplate`.\n","tags":["Templates"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTemplate"},"example":{"name":"appointment_reminder_v2","components":[{"type":"BODY","text":"Olá {{1}}! Lembrete: sua consulta será em {{2}} às {{3}}."}]}}}},"responses":{"200":{"description":"Template atualizado com sucesso (status redefinido para DRAFT)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppTemplate"}}}}}},"400":{"description":"Corpo inválido ou nenhum campo válido para atualizar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirTemplate","summary":"Excluir template","description":"Remove um template em rascunho (que ainda não foi enviado para a Meta) ou um que a Meta já excluiu. Um template que ainda está na Meta retorna 409 com `error.code` `TEMPLATE_ON_META`: exclua pelo Chat Jurídico em Configurações > WhatsApp, que o remove também da sua conta do WhatsApp. Templates reservados do sistema (como `cj_resumo_qualificacao`) retornam 409 `SYSTEM_TEMPLATE`. Requer o escopo `messages:send`.","tags":["Templates"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Template excluído com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/campaigns":{"get":{"operationId":"listarCampanhas","summary":"Listar campanhas","description":"Retorna uma lista paginada de campanhas de envio em massa do seu escritório.\nFiltre por status e busque por nome da campanha.\n","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"},{"name":"status","in":"query","description":"Filtrar por status da campanha","schema":{"type":"string","enum":["draft","scheduled","running","paused","completed","cancelled","failed"]}},{"name":"search","in":"query","description":"Buscar campanhas por nome","schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de campanhas retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Campaign"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Campanha Black Friday","status":"completed","template_name":"promo_blackfriday","template_language":"pt_BR","total_recipients":150,"sent_count":148,"delivered_count":145,"read_count":98,"failed_count":2,"scheduled_at":"2026-03-20T09:00:00Z","started_at":"2026-03-20T09:00:05Z","completed_at":"2026-03-20T09:15:00Z","created_at":"2026-03-19T18:00:00Z"}],"pagination":{"has_more":false,"next_cursor":null,"limit":50}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarCampanha","summary":"Criar campanha","description":"Cria uma campanha em rascunho (status `draft`), como o rascunho salvo no app.\nInforme o público por `recipient_tag_ids` (contatos com qualquer uma das etiquetas) ou\npor `recipient_contact_ids` (lista de contatos do número), não os dois.\nA data, a mídia do cabeçalho e o envio são definidos ao abrir o rascunho em Campanhas\ne confirmar: é lá que os destinatários são calculados e o template conferido.\nA campanha fica no nome do usuário que criou a chave de API (ele precisa ser usuário\ndo escritório; chaves internas não criam campanhas). A resposta traz `total_recipients`\n(contagem esperada) e `recipients_materialized: false`: os destinatários só passam a\nexistir quando o rascunho é confirmado no app.\nRequer o escopo `campaigns:write`.\n","tags":["Campanhas"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/campaigns\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"Campanha Dezembro\", \"instance_id\": \"uuid-da-instancia\", \"recipient_tag_ids\": [\"uuid-da-etiqueta\"]}'\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.post(\n    \"https://api.jur.chat/v1/campaigns\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\n        \"name\": \"Campanha Dezembro\",\n        \"instance_id\": \"uuid-da-instancia\",\n        \"recipient_tag_ids\": [\"uuid-da-etiqueta\"]\n    }\n)\nprint(response.json())\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCampaignRequest"}}}},"responses":{"201":{"description":"Campanha criada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Campaign"}}}}}},"400":{"description":"`code: \"instance_out_of_scope\"` — o `instance_id` informado não é um número de WhatsApp que esta chave de API pode usar para enviar. Uma campanha dispara em massa a partir de um número; o número deve estar no escopo da chave.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"instance_out_of_scope","message":"instance_id is not a WhatsApp number this API key can send from.","status":400}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Corpo da requisição inválido: sem público, com etiquetas e contatos ao mesmo tempo, ou com `scheduled_at`. `code: \"unknown_tag_ids\"` — etiqueta que não é do escritório; `code: \"contacts_outside_instance\"` — contato que não é do número `instance_id`, é grupo ou está inativo (não receberia a campanha).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Escopo insuficiente, ou `code: \"campaign_creator_unknown\"` — a chave não tem um usuário do escritório em nome de quem criar a campanha (sem criador, ou o criador saiu do escritório).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Não foi possível verificar a chave de API agora; tente de novo.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/campaigns/{id}":{"get":{"operationId":"buscarCampanha","summary":"Buscar campanha por ID","description":"Retorna os dados completos de uma campanha específica, incluindo contadores de envio e entrega.","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Dados da campanha","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Campaign"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarCampanha","summary":"Atualizar campanha","description":"Atualiza os dados de uma campanha existente. Apenas campanhas com status `draft` ou `scheduled` podem ser atualizadas.\nRequer o escopo `campaigns:write`.\n","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCampaignRequest"}}}},"responses":{"200":{"description":"Campanha atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Campaign"}}}}}},"400":{"description":"Nenhum campo para atualizar.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"O status não permite a mudança (só rascunho salvo ou campanha agendada; rascunho não recebe data; agendada não troca o público nem o template), ou a campanha mudou durante a atualização: leia de novo e repita.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Corpo da requisição inválido (lista vazia, etiquetas e contatos juntos, data sem fuso). `code: \"unknown_tag_ids\"` — etiqueta que não é do escritório; `code: \"contacts_outside_instance\"` — contato que não é do número da campanha, é grupo ou está inativo.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirCampanha","summary":"Excluir campanha","description":"Remove uma campanha de forma reversível (soft-delete). Só campanhas que não enviam mais\npodem ser excluídas: `draft`, `cancelled`, `completed` ou `failed`. Uma campanha\n`scheduled`, `paused` ou `running` precisa ser cancelada antes. Um rascunho com data\n(sendo criado no app, ou com a criação interrompida) é excluído pelo app.\nRequer o escopo `campaigns:write`.\n","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Campanha excluída com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Status da campanha não permite exclusão (cancele antes), ou ela mudou durante a exclusão.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/campaigns/{id}/recipients":{"get":{"operationId":"listarDestinatariosCampanha","summary":"Listar destinatários da campanha","description":"Retorna a lista paginada de destinatários de uma campanha, com o status individual de envio de cada um. Um rascunho ainda não tem destinatários (as linhas são criadas quando ele é confirmado no app): a lista vem vazia e `total_recipients` da campanha traz a contagem esperada.\n","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista de destinatários retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CampaignRecipient"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"example":{"data":[{"id":"d4e5f6a7-b8c9-0123-defa-456789012345","campaign_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","contact_id":"e5f6a7b8-c9d0-1234-efab-567890123456","contact_name":"Maria Silva","phone":"5511999998888","status":"delivered","sent_at":"2026-03-20T09:00:10Z","delivered_at":"2026-03-20T09:00:15Z","read_at":"2026-03-20T09:05:00Z"}],"pagination":{"has_more":false,"next_cursor":null,"limit":50}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/campaigns/{id}/analytics":{"get":{"operationId":"analiticoCampanha","summary":"Obter métricas da campanha","description":"Retorna as métricas de desempenho de uma campanha, incluindo taxas de entrega e leitura.","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Métricas da campanha retornadas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CampaignAnalytics"}}},"example":{"data":{"campaign_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","total_recipients":150,"sent_count":148,"delivered_count":145,"read_count":98,"failed_count":2,"delivery_rate":97.97,"read_rate":67.59}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/campaigns/{id}/send":{"post":{"operationId":"enviarCampanha","summary":"Enviar campanha","description":"Não envia pela API: responde 409 `send_from_app`. Uma campanha é enviada ao abrir o\nrascunho em Campanhas, no app, e confirmar; lá os destinatários são calculados, o\ntemplate e a mídia conferidos e o envio agendado.\n","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"`code: \"send_from_app\"` — confirme o rascunho no app para enviar.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/campaigns/{id}/cancel":{"post":{"operationId":"cancelarCampanha","summary":"Cancelar campanha","description":"Cancela uma campanha em andamento. A campanha deve estar com status `scheduled`, `paused` ou `running`.\n","tags":["Campanhas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Campanha cancelada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Campaign"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Status da campanha não permite cancelamento, ou ela mudou (ex. terminou) durante o pedido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"O banco de campanhas não respondeu; tente de novo (não significa que a campanha não existe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/admin/logs/summary":{"get":{"operationId":"resumoUsoAPI","summary":"Resumo de uso da API","description":"Retorna um resumo estatístico do uso da API nos últimos dias.\nInclui total de requisições, taxa de erros e endpoints mais utilizados.\nRequer autenticação via Bearer JWT.\n","tags":["ChavesAPI"],"security":[{"BearerAuth":[]}],"parameters":[{"name":"days","in":"query","description":"Número de dias para o resumo (padrão 30)","schema":{"type":"integer","minimum":1,"maximum":90,"default":30}}],"responses":{"200":{"description":"Resumo de uso retornado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UsageSummary"}}},"example":{"data":{"period_days":30,"total_requests":15230,"total_errors":42,"error_rate":0.28,"avg_response_time_ms":52,"top_endpoints":[{"path":"/v1/contacts","count":8500},{"path":"/v1/conversations","count":4200}],"top_api_keys":[{"api_key_id":"d0e1f2a3-b4c5-6789-defa-012345678901","name":"Integração n8n","count":12000}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/usage":{"get":{"operationId":"consultarUso","summary":"Consultar uso da API","description":"Retorna estatísticas de uso da API para a sua chave nos últimos `days` dias (padrão 7, máximo 30).\nInclui total de requisições, taxa de erros, latência média e detalhamento por endpoint.\n","tags":["Uso"],"parameters":[{"name":"days","in":"query","description":"Número de dias para o período de análise (padrão 7, máximo 30)","schema":{"type":"integer","minimum":1,"maximum":30,"default":7}}],"responses":{"200":{"description":"Estatísticas de uso retornadas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApiUsage"}}},"example":{"data":{"period_days":7,"total_requests":1523,"total_errors":12,"error_rate":0.8,"avg_latency_ms":48,"endpoints":[{"path":"/v1/contacts","requests":850,"errors":3,"avg_latency_ms":42},{"path":"/v1/conversations","requests":420,"errors":1,"avg_latency_ms":55}]}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/usage/rate-limits":{"get":{"operationId":"consultarLimitesRequisicao","summary":"Consultar limites de requisição","description":"Retorna os limites de requisição (rate limits) configurados por tipo de operação.\nCada tier define um limite por minuto e por dia.\n","tags":["Uso"],"responses":{"200":{"description":"Limites retornados com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RateLimitTiers"}}},"example":{"data":{"tiers":{"default":{"per_minute":100,"per_day":null},"message_send":{"per_minute":20,"per_day":500}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/usage/report":{"post":{"operationId":"reportarEventoUso","summary":"Reportar evento de uso","description":"Registra um evento de uso (consumo de tokens de LLM, chamadas de ferramentas MCP,\nenvios de WhatsApp, etc.) para fins de medição, cobrança e visibilidade no\nPainel de Métricas. Categorias aceitas: `llm_tokens`, `mcp_tool_calls`,\n`whatsapp_sends`, `campaign_sends`, `document_gen`, `agent_runs`.\n\nRequer o escopo `contacts:read` (intencional — qualquer chave que possa ler\ncontatos pode reportar uso para o próprio escritório). Aceita também\nautenticação por Bearer JWT do Supabase para chamadas a partir do frontend.\n","tags":["Uso"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/usage/report\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"category\": \"llm_tokens\",\n    \"subcategory\": \"claude-sonnet-4\",\n    \"input_tokens\": 1500,\n    \"output_tokens\": 800,\n    \"client_name\": \"copilot-sidebar\"\n  }'\n"},{"lang":"Python","source":"import requests\n\nrequests.post(\n    \"https://api.jur.chat/v1/usage/report\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\n        \"category\": \"llm_tokens\",\n        \"subcategory\": \"claude-sonnet-4\",\n        \"input_tokens\": 1500,\n        \"output_tokens\": 800,\n        \"client_name\": \"copilot-sidebar\",\n    },\n)\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageReportRequest"},"example":{"category":"llm_tokens","subcategory":"claude-sonnet-4","input_tokens":1500,"output_tokens":800,"client_name":"copilot-sidebar"}}}},"responses":{"200":{"description":"Evento de uso registrado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UsageReportResponse"}}},"example":{"data":{"recorded":true}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/usage/quota":{"get":{"operationId":"consultarQuotaUso","summary":"Consultar quota de uso e projeção","description":"Verificação leve da quota mensal do escritório. Retorna `allowed`\n(true/false), o tier atual, o consumo (`used` / `limit` / `percent` /\n`remaining`), o saldo de créditos avulsos e — quando há histórico\nsuficiente — uma projeção de exaustão para o restante do mês.\n\nUsado pelo Painel de Métricas (Phase 3 MCP App) e pelo Copilot Sidebar\nantes de operações pesadas (ex.: gerar documento, executar rotina).\n\nRequer o escopo `contacts:read`; aceita Bearer JWT do Supabase.\n","tags":["Uso"],"x-codeSamples":[{"lang":"cURL","source":"curl -X GET \"https://api.jur.chat/v1/usage/quota\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\"\n"},{"lang":"Python","source":"import requests\n\nr = requests.get(\n    \"https://api.jur.chat/v1/usage/quota\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n)\nprint(r.json()[\"data\"][\"usage\"][\"percent\"], \"%\")\n"}],"responses":{"200":{"description":"Quota e projeção retornadas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UsageQuota"}}},"example":{"data":{"allowed":true,"warning":"approaching_limit","plan_tier":"pro","usage":{"used":412000,"limit":500000,"percent":82,"remaining":88000},"projection":{"daily_average_tokens":13733,"projected_month_total":425700,"projected_percent":85,"projected_exhaustion_date":null,"days_remaining":5,"on_track":true},"credits":{"balance":0,"effective_limit":500000}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/usage/summary":{"get":{"operationId":"consultarResumoUso","summary":"Consultar resumo detalhado de uso","description":"Resumo de uso por dia e por categoria nos últimos `days` dias\n(padrão 30, máximo 90), com totais agregados (tokens, chamadas de\nferramentas, custo estimado em centavos de USD, requisições) e\ncontexto do plano contratado.\n\nPowered by `usage_events` (migrations 009/010). Usado pelo Painel\nde Métricas para o \"Smart Deal Progression\" ribbon e pelo dashboard\nfinanceiro/admin para reconciliação mensal.\n\nRequer o escopo `contacts:read`; aceita Bearer JWT do Supabase.\n","tags":["Uso"],"x-codeSamples":[{"lang":"cURL","source":"curl -X GET \"https://api.jur.chat/v1/usage/summary?days=30&group_by=day\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\"\n"},{"lang":"Python","source":"import requests\n\nr = requests.get(\n    \"https://api.jur.chat/v1/usage/summary\",\n    params={\"days\": 30, \"group_by\": \"day\"},\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n)\ndata = r.json()[\"data\"]\nprint(data[\"totals\"], data[\"quota\"])\n"}],"parameters":[{"name":"days","in":"query","required":false,"description":"Janela de dias para o resumo (padrão 30, máximo 90)","schema":{"type":"integer","minimum":1,"maximum":90,"default":30}},{"name":"group_by","in":"query","required":false,"description":"Granularidade temporal do resumo. Atualmente apenas `day` é suportado.","schema":{"type":"string","enum":["day"],"default":"day"}}],"responses":{"200":{"description":"Resumo de uso retornado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UsageSummary"}}},"example":{"data":{"period_days":30,"totals":{"tokens":412000,"tool_calls":1853,"cost_cents":1240,"requests":220},"by_day":[{"date":"2026-05-06","tokens":18420,"tool_calls":92,"cost_cents":55},{"date":"2026-05-05","tokens":21000,"tool_calls":110,"cost_cents":63}],"by_category":{"llm_tokens":{"tokens":380000,"calls":0,"cost_cents":1140},"mcp_tool_calls":{"tokens":32000,"calls":1853,"cost_cents":100}},"quota":{"plan_tier":"pro","monthly_token_limit":500000,"is_unlimited":false}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/automations":{"get":{"operationId":"listarAutomacoes","summary":"Listar automações","description":"Retorna uma lista paginada das automações do escritório.\nFiltre por número WhatsApp ou status ativo/inativo.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"},{"name":"instance_id","in":"query","description":"Filtrar automações de um número WhatsApp específico","schema":{"type":"string","format":"uuid"}},{"name":"is_active","in":"query","description":"Filtrar por status ativo/inativo","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Lista de automações retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Automation"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarAutomacao","summary":"Criar automação","description":"Cria uma nova automação para um número WhatsApp.\nO flow_json define o gatilho (trigger) e as ações (actions) no formato de nós e arestas.\nAutomações são criadas desativadas por padrão.\n\nContrato resumido (guia completo e exemplos prontos no recurso `chatjuridico://workflows`, seção Automations):\n- Formato `{version, nodes, edges}`: exatamente um nó `trigger`; cada nó com `id`, `type`, `position {x,y}` e `data`; config em camelCase; ids de etapa, tag e membro reais do escritório.\n- Dias e horários em `trigger.data.config.schedule = {enabled, mode, outside?, windows}` (em `data.schedule` é recusado). `enabled` é booleano JSON (`false` desliga). `mode`: `only_inside` (só dispara dentro das janelas) ou `only_outside` (só fora). `windows` tem exatamente as 7 chaves `segunda terca quarta quinta sexta sabado domingo`, cada uma com uma lista (pode ser vazia) de até 4 `{start, end}` em `HH:MM`; `end` pode ser `24:00`; início antes do fim, sem atravessar a meia-noite e sem sobreposição no dia; início incluso, fim excluso. Vale o instante da mensagem ou do evento no fuso do escritório.\n- `schedule.outside`: `skip` (padrão; fora da janela o evento é descartado, sem linha de execução) ou `defer_messages` (só com `only_inside`): dispara na hora, etiqueta, etapa, responsável, anotação e webhook rodam, e as mensagens esperam o início da próxima janela (03:10 vira 07:00, 07:01, 07:02).\n- `delayMinutes` em `send_message` e `send_template`: inteiro de 0 a 1440 (ou texto só com dígitos), ausente ou `null` = 0, contado a partir do disparo. A chave antiga `delay` é recusada.\n- Recusado com 400: janela ou atraso malformados; atraso ou `defer_messages` com gatilho `sent_message` (a próxima mensagem da equipe cancelaria); atraso num menu interativo; `defer_messages` numa automação com menu interativo. Enquanto o envio adiado não estiver liberado no servidor, `delayMinutes` maior que 0 e `defer_messages` voltam 400 \"Delayed and deferred sends are not enabled yet\".\n- Ativação: janela que nunca dispara (`only_inside` sem janela nenhuma; `only_outside` com 00:00 a 24:00 nos 7 dias) é aceita em rascunho e recusada ao ativar (\"Cannot activate: ...\"). Se a trava do banco recusar, a resposta é 422 com `code` `automation_activation_floor` e a frase do banco dizendo o que corrigir.\n- Mensagem atrasada ou adiada vira linha `automation_pending` em `listarMensagensAgendadas` e é cancelada se alguém da equipe responder antes, se a automação for desligada ou excluída, se o contato ou a conversa forem excluídos, ou se o número estiver desconectado na hora do envio. Mensagem enviada por esta API ou pelo MCP (`source` `open-api`) não conta como resposta da equipe e não cancela nada; para parar, use `cancelarMensagemAgendada`. Uma sequência por conversa: evento repetido enquanto ela espera não agenda de novo (`already_scheduled`, não é falha). API Oficial: modelo aprovado sempre espera; texto livre que passaria do fim da janela de 24 h sai na hora. Pausa da IA (`pauseAi`/`send_and_pause_ai`): vai para a hora do envio só quando a execução foi adiada para a próxima abertura (`outside_window`); com só atraso, pausa na hora do disparo.\n- Ativação com texto de modelo da loja ainda não preenchido: mensagem de `send_message` ou parâmetro de `send_template` com `(coloque aqui` ou `[preencha` é aceito em rascunho e recusado ao ativar com 400 (\"Cannot activate: ... still has a store template placeholder ...\").\n- `[data]`, `[hora]` e `@dd/mm/yyyy`: envio imediato usa o instante do evento em UTC; mensagem atrasada ou adiada usa o instante do envio no fuso do escritório.\n- Webhook Recebido: `createContactIfMissing: true` (booleano JSON, só com `identityMode` `phone` ou ausente) cria contato e conversa neste número quando o telefone ainda não existe, e abre conversa para contato que existe sem nenhuma; só funciona com o interruptor do servidor `AUTOMATION_WEBHOOK_CREATE_CONTACT_ENABLED` ligado (desligado, telefone não achado pula com `identity_not_resolved`); criação recusada responde `{queued: false, reason: \"contact_not_created\"}` e o motivo específico fica na execução; `contactNamePath` (opcional) aponta o nome no corpo; `idempotencyKeyPath` (opcional) faz reentregas com o mesmo valor contarem como o mesmo evento.\n","tags":["Automações"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAutomation"}}}},"responses":{"201":{"description":"Automação criada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AutomationDetail"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/AutomationActivationFloor"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/automations/{id}":{"get":{"operationId":"buscarAutomacao","summary":"Buscar automação por ID","description":"Retorna os detalhes de uma automação específica, incluindo o flow_json completo\ncom todos os nós (trigger e actions) e arestas.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Automação encontrada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AutomationDetail"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarAutomacao","summary":"Atualizar automação","description":"Atualiza uma automação existente. Pode atualizar nome, descrição,\nflow_json ou status ativo/inativo.\n\nO `flow_json` enviado substitui o fluxo inteiro e segue as mesmas regras de `criarAutomacao`\n(dias e horários em `trigger.data.config.schedule`, `delayMinutes`, `schedule.outside`\n`defer_messages`, chaves de criação de contato do Webhook Recebido); o resumo completo está\nna descrição de `criarAutomacao` e o guia com exemplos no recurso `chatjuridico://workflows`.\n- Rascunho pode guardar uma janela que nunca dispara; `is_active: true` com ela volta 400\n  \"Cannot activate: ...\".\n- Editar uma automação ATIVA com um fluxo que não poderia ser ativado volta 400\n  \"Cannot save this flow while the automation is active: ...\".\n- Recusa da trava de ativação do banco: 422 com `code` `automation_activation_floor` e a frase do banco.\n- Desligar a automação cancela as mensagens dela que ainda esperam (`cancel_reason` `automation_disabled`).\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAutomation"}}}},"responses":{"200":{"description":"Automação atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AutomationDetail"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/AutomationActivationFloor"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirAutomacao","summary":"Excluir automação","description":"Remove permanentemente uma automação. As mensagens dela que ainda esperam o horário (`automation_pending`) são canceladas com `cancel_reason` `automation_deleted`.","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Automação excluída com sucesso"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/automations/{id}/toggle":{"post":{"operationId":"alternarStatusAutomacao","summary":"Alternar status da automação","description":"Alterna o status da automação entre ativo e inativo.\nQuando desativada, registra o momento da desativação e cancela as mensagens dela que ainda\nesperam o horário (`cancel_reason` `automation_disabled`).\nAo ATIVAR, o fluxo salvo passa pelas mesmas regras de `criarAutomacao`: uma janela de dias e\nhorários que nunca dispara, atraso ou adiamento não permitidos, ou atraso e adiamento\nenquanto não estão liberados voltam 400 \"Cannot activate: ...\". A recusa da trava do banco\nvolta 422 com `code` `automation_activation_floor` e a frase do banco.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Status alternado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AutomationDetail"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"422":{"$ref":"#/components/responses/AutomationActivationFloor"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/automations/{id}/executions":{"get":{"operationId":"listarExecucoesAutomacao","summary":"Listar execuções de uma automação","description":"Retorna o histórico de execuções de uma automação específica,\nordenado por data de execução (mais recente primeiro).\n\n`status` da execução (texto aberto): `success`, `failed`, `error`, `partial` (parte das\nações falhou), `skipped` (não rodou), `suggested`, e os eventos de configuração\n`toggled_on`/`toggled_off` (alguém ligou ou desligou a automação; não são execuções).\nWebhook Recebido que não rodou vira `skipped` com o motivo em `input_context.skip_reason`:\n`automation_inactive`, `instance_deleted`, `automations_disabled`, `chat_instance_mismatch`,\n`loop_breaker`, `identity_not_resolved`, `identity_ambiguous`, `identity_invalid_phone`\n(telefone de 1 a 9 dígitos é curto demais; nenhum dígito ou mais de 15 é inválido),\n`contact_deleted`, `contact_inactive`, `chat_deleted`, `chat_conflict`,\n`chat_not_found` (contato sem conversa neste número com a criação desligada),\n`phone_not_on_whatsapp`, `phone_is_this_number`, `instance_disconnected`,\n`contact_creation_rate_limited`, `trigger_no_longer_webhook` (o gatilho da automação\ndeixou de ser webhook), `instance_not_found`, `non_python_backend`. Na criação de contato\nrecusada a URL responde só `{queued: false, reason: \"contact_not_created\"}`; o motivo\nespecífico fica aqui. Quando o webhook criou contato ou conversa,\n`input_context.webhook` traz `contact_created` e `conversation_created`.\n\n`actions_taken` e `actions_failed` são listas de `{actionType, result}` (a falha traz também\n`error`). `result.status` de uma ação de envio (texto aberto):\n- `scheduled`: deixada para depois, com `scheduledAt` (UTC), `scheduledAtLocal`, `timezone`,\n  `reason` (`delay` ou `outside_window`), `scheduledMessageId` e `batchId`;\n- `already_scheduled` (com `action` `skipped`): já havia uma sequência desta automação\n  esperando nesta conversa; não é falha;\n- `sent` com `scheduleOverride` `meta_window_ends_before` e `windowExpiresAt`: na API Oficial\n  a janela de 24 h fecharia antes, então o texto saiu na hora;\n- falhas: `invalid_delay`, `meta_new_conversation_template_only`,\n  `interactive_schedule_not_allowed`, `sent_message_schedule_not_allowed`,\n  `schedule_unknown_clock`, `schedule_no_opening`, `schedule_target_unavailable`,\n  `schedule_failed`;\n- `already_canceled` (com `action` `skipped`): o evento repetido encontrou a mensagem já\n  cancelada; não é falha.\nO que acontece depois com a mensagem agendada está em `listarMensagensAgendadas`.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista de execuções retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AutomationExecution"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/automations/{id}/executions/stats":{"get":{"operationId":"estatisticasExecucoesAutomacao","summary":"Estatísticas de execuções de uma automação","description":"Retorna estatísticas agregadas das execuções de uma automação:\ntotal, sucesso, falhas, taxa de sucesso e tempo médio.\n\n`success_rate` (0 a 100) = `successful` / `total_executions` × 100. `total_executions`\nconta toda execução menos os eventos `toggled_on`/`toggled_off`, então inclui `skipped`\n(fora do horário, webhook sem contato) e `suggested`, que não são sucesso nem falha\n(`failed` soma `failed`, `error` e `partial`). Uma automação que mais pula do que roda\nmostra taxa baixa sem ter falhado: para medir saúde, use\n`successful / (successful + failed)`, como `resumo_automacoes` e o inspetor de automações.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Estatísticas retornadas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AutomationExecutionStats"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/automations/executions/{id}/replay":{"post":{"operationId":"replayExecucaoAutomacao","summary":"Reexecutar uma execução de automação","description":"Insere uma nova linha em `automation_executions` marcando um\nreplay manual da execução `{id}`. O `input_context` original\n(chat_id, message_id, payload do trigger) é copiado para que\nqualquer reprocessador downstream (n8n / agent-engine tick)\npossa refazer a automação contra o mesmo contexto. A execução\noriginal não é modificada. Requer o escopo `automations:write`.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"202":{"description":"Replay enfileirado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"unique_id":{"type":"string","description":"ID da nova linha de execução marker"},"original_run_id":{"type":"string"},"automation_id":{"type":"string"},"automation_name":{"type":"string"},"chat_id":{"type":"string"},"triggered_by":{"type":"string"},"executed_at":{"type":"string","format":"date-time"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/automations/{id}/revisions":{"get":{"operationId":"listarRevisoesAutomacao","summary":"Listar revisões de uma automação","description":"Retorna o histórico de revisões da automação `{id}`. Cada revisão\né um snapshot do estado anterior, capturado automaticamente pelo\ntrigger Postgres `automation_capture_revision` a cada UPDATE em\n`flow_json`, `name`, `description` ou `is_active`.\n\nA resposta é paginada da mais recente para a mais antiga;\no cursor é o `revision_number` da última entrada retornada.\nRequer o escopo `automations:read`.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista de revisões retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"automation_id":{"type":"string"},"revision_number":{"type":"integer"},"name":{"type":"string","nullable":true},"is_active":{"type":"boolean","nullable":true},"changed_by":{"type":"string","nullable":true},"change_summary":{"type":"string","nullable":true},"node_count":{"type":"integer"},"action_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/automations/{id}/revisions/{revision}":{"get":{"operationId":"obterRevisaoAutomacao","summary":"Obter o snapshot de uma revisão específica","description":"Retorna o snapshot completo da revisão `{revision}` da automação\n`{id}`, incluindo o `flow_json` integral do estado anterior.\nUsado pelo `scripts/replay_execution.py --revision N` para\nreproduzir uma execução contra uma versão histórica.\n\nRequer o escopo `automations:read`.\n","tags":["Automações"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"name":"revision","in":"path","required":true,"schema":{"type":"integer","minimum":1},"description":"Número da revisão (1-based, monotônico por automation_id)."}],"responses":{"200":{"description":"Snapshot retornado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"automation_id":{"type":"string"},"revision_number":{"type":"integer"},"name":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"is_active":{"type":"boolean","nullable":true},"flow_json":{"type":"object"},"changed_by":{"type":"string","nullable":true},"change_summary":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}}}}}}},"400":{"description":"Número de revisão inválido"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/shortcuts":{"get":{"operationId":"listarAtalhos","summary":"Listar atalhos de mensagem","description":"Retorna todos os atalhos de mensagem do escritório.\nRequer o escopo `shortcuts:read`.\n","tags":["Atalhos"],"responses":{"200":{"description":"Lista de atalhos retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Shortcut"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","trigger":"/saudacao","message":"Olá! Como posso ajudar?","created_at":"2026-01-15T10:00:00Z"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarAtalho","summary":"Criar atalho de mensagem","description":"Cria um novo atalho de mensagem no escritório.\nO `trigger` deve ter entre 1 e 50 caracteres e a `message` entre 1 e 2000.\nRequer o escopo `shortcuts:write`.\n","tags":["Atalhos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateShortcut"},"example":{"trigger":"/saudacao","message":"Olá! Como posso ajudar você hoje?"}}}},"responses":{"201":{"description":"Atalho criado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Shortcut"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/shortcuts/{id}":{"get":{"operationId":"buscarAtalho","summary":"Buscar atalho por ID","description":"Retorna os dados de um atalho específico. Requer o escopo `shortcuts:read`.","tags":["Atalhos"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Atalho retornado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Shortcut"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarAtalho","summary":"Atualizar atalho","description":"Atualiza os dados de um atalho existente. Envie apenas os campos que deseja alterar.\nRequer o escopo `shortcuts:write`.\n","tags":["Atalhos"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateShortcut"},"example":{"message":"Bom dia! Em que posso ajudar?"}}}},"responses":{"200":{"description":"Atalho atualizado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Shortcut"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirAtalho","summary":"Excluir atalho","description":"Remove um atalho de mensagem do escritório. Requer o escopo `shortcuts:write`.","tags":["Atalhos"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Atalho removido com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/members":{"get":{"operationId":"listarMembros","summary":"Listar membros do escritório","description":"Retorna a lista de membros do escritório com informações básicas (ID, nome e cargo).\nÚtil para resolver `accountable_id` de conversas e listar membros disponíveis para atribuição.\nRequer o escopo `conversations:read`.\n","tags":["Membros"],"responses":{"200":{"description":"Lista de membros retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Member"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"João Silva","role":"admin"},{"id":"b2c3d4e5-f6a7-8901-bcde-f23456789012","name":"Maria Santos","role":"member"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/departments":{"get":{"operationId":"listarDepartamentos","summary":"Listar departamentos (filas) do escritório","description":"Retorna os departamentos/filas do escritório (ID e nome). Útil para resolver\n`department_id` ao delegar uma conversa via `POST /v1/conversations/{id}/transfer`\nou `PATCH /v1/conversations/{id}`. Perfis de acesso do RBAC (não delegáveis) são\nomitidos. Requer o escopo `conversations:read`.\n","tags":["Membros"],"responses":{"200":{"description":"Lista de departamentos retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Department"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Trabalhista"},{"id":"b2c3d4e5-f6a7-8901-bcde-f23456789012","name":"Pará"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contact-properties":{"get":{"operationId":"listarPropriedadesContato","summary":"Listar definições de propriedades personalizadas do contato","description":"Retorna as definições de campos personalizados do escritório (as que o\noperador vê no painel do contato). Use as `key` retornadas para gravar\nvalores via `PUT /v1/contacts/{id}/properties`. Requer o escopo `contacts:read`.\n\nNOTA: estes são os campos do CRM que o operador/agente/automação enxergam.\nNão confundir com `/v1/custom-property-*` (tag Documentos), que é o store de\ngeração de documentos e NÃO aparece no painel do contato.\n","tags":["Contatos"],"responses":{"200":{"description":"Lista de definições","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ContactPropertyDefinition"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","key":"numero_atendimento","name":"Número de atendimento","type":"text","required":false}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarPropriedadeContato","summary":"Criar campo personalizado do contato","description":"Cria um campo personalizado no PAINEL DO CONTATO (o mesmo de Configurações →\nPropriedades). Depois de criado, grave os valores por contato com\n`PUT /v1/contacts/{id}/properties` usando a `key`. Requer o escopo `contacts:write`.\n\n- `key` é opcional: sem ela, é gerada a partir do `name` (ex.: \"Tipo de desconto\"\n  → `tipo_de_desconto`). Não pode ser alterada depois.\n- Para `select`/`multi_select`, envie `options` como lista de rótulos\n  (ex.: `[\"Aposentadoria\", \"Auxílio-doença\"]`). O valor de cada opção é gerado a\n  partir do rótulo inteiro (`aposentadoria`, `auxilio_doenca`) e é esse valor que\n  se grava no contato.\n- Se a `key` ou o `name` pertencerem a um campo excluído, ele é REATIVADO como\n  estava (`reactivated: true`) em vez de criar outro.\n\nNÃO confundir com `criarPropriedadeCustomizada` (tag Documentos), que cria campo\ndo gerador de documentos e NÃO aparece no painel do contato.\n","tags":["Contatos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContactProperty"},"example":{"name":"Benefício","type":"select","options":["Aposentadoria por idade","Auxílio-doença","BPC/LOAS"]}}}},"responses":{"201":{"description":"Campo criado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ContactPropertyWriteResult"}}}}}},"200":{"description":"Campo excluído anteriormente foi reativado (`reactivated` = true)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ContactPropertyWriteResult"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Já existe um campo ativo com essa `key` ou esse `name`"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contact-properties/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"ID do campo (o `id` retornado por listarPropriedadesContato)","schema":{"type":"string","format":"uuid"}}],"patch":{"operationId":"atualizarPropriedadeContato","summary":"Editar campo personalizado do contato","description":"Edita a definição de um campo do painel do contato. Envie só o que muda.\n`key` e `type` NÃO podem ser alterados (os valores gravados dependem deles).\nRequer o escopo `contacts:write`.\n\n`options` substitui a lista inteira: opções existentes são mantidas quando o\nrótulo é o mesmo, ou quando você envia `{ \"value\": \"<valor atual>\", \"label\":\n\"<novo rótulo>\" }` para renomear sem mudar o valor. Rótulos novos ganham valor\ngerado do rótulo inteiro. Opções que ficarem fora da lista são removidas.\n","tags":["Contatos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateContactProperty"},"example":{"options":["Aposentadoria por idade","Auxílio-doença","BPC/LOAS","Pensão por morte"]}}}},"responses":{"200":{"description":"Campo atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ContactPropertyDefinition"}}}}}},"400":{"description":"Nenhum campo para atualizar"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Já existe outro campo com esse `name`"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirPropriedadeContato","summary":"Excluir (arquivar) campo personalizado do contato","description":"Arquiva o campo: ele some do painel e das leituras, mas os valores já gravados\nsão preservados. Criar de novo um campo com a mesma `key` ou o mesmo `name` o\nreativa com os valores. Requer o escopo `contacts:write`.\n","tags":["Contatos"],"responses":{"200":{"description":"Campo arquivado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/contacts/{id}/properties":{"parameters":[{"name":"id","in":"path","required":true,"description":"ID do contato","schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"listarPropriedadesDoContato","summary":"Ler valores de propriedades de um contato","description":"Retorna os valores das propriedades personalizadas gravadas no contato,\nendereçados pela `key` da definição. Requer o escopo `contacts:read`.\n","tags":["Contatos"],"responses":{"200":{"description":"Valores do contato","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ContactPropertyValue"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"atualizarPropriedadesDoContato","summary":"Gravar/atualizar propriedades de um contato (por chave)","description":"Grava N propriedades personalizadas do contato numa única chamada,\nendereçadas por `key` (equivalente ao `chat_update_custom_fields`). Envie\num objeto plano `{ \"key\": valor, ... }` (ou `{ \"properties\": { ... } }`).\nUm valor vazio (`null`, `\"\"` ou `[]`) LIMPA a propriedade: a linha é\nremovida (igual ao painel) e a chave volta em `cleared`. Chaves inexistentes\nou valores com tipo inválido são reportados (`unknown_keys` / `invalid`) e\nNÃO gravados — se houver qualquer\num, o status é 422, mas os válidos ainda são gravados. Valores editados\nmanualmente no painel (`source: manual`) NÃO são sobrescritos (voltam em\n`skipped_manual`). Valores gravados por aqui recebem `source: \"api\"`.\nRequer o escopo `contacts:write`.\n","tags":["Contatos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"title":"properties","type":"object","additionalProperties":true,"description":"Objeto plano `{ \"key\": valor, ... }` com as propriedades a gravar, endereçadas pela `key` da definição. `null`/`\"\"`/`[]` limpa o valor."},"example":{"numero_atendimento":"4194731","nome_do_cliente":"Maria Silva"}}}},"responses":{"200":{"description":"Todas as propriedades gravadas","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ContactPropertyUpsertResult"}}}}}},"422":{"description":"Gravou os válidos; há chaves desconhecidas ou valores inválidos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ContactPropertyUpsertResult"}}}}}},"400":{"description":"Corpo vazio"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/annotations":{"get":{"operationId":"listarAnotacoes","summary":"Listar anotações de uma conversa","description":"Retorna todas as anotações internas de uma conversa, ordenadas da mais recente para a mais antiga.\nRequer o escopo `conversations:read`.\n","tags":["Anotações"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"responses":{"200":{"description":"Lista de anotações retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Annotation"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarAnotacao","summary":"Adicionar anotação a uma conversa","description":"Cria uma nova anotação interna em uma conversa.\nRequer o escopo `conversations:write`.\n\nPara mencionar colegas, informe os `mentions` (uuid dos membros) e/ou\nembuta `@[uuid|Nome]` no `text` (até 50 destinatários distintos).\nA entrega depende das preferências e da ativação do produtor de\nnotificações. Menções podem gerar e-mail para os colegas.\n","tags":["Anotações"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAnnotation"},"example":{"text":"Cliente solicitou documentação adicional para o processo"}}}},"responses":{"201":{"description":"Anotação criada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Annotation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/annotations/{annotation_id}":{"get":{"operationId":"buscarAnotacao","summary":"Buscar anotação por ID","description":"Retorna os dados de uma anotação específica. Requer o escopo `conversations:read`.","tags":["Anotações"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"},{"name":"annotation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da anotação"}],"responses":{"200":{"description":"Anotação retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Annotation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarAnotacao","summary":"Atualizar anotação","description":"Atualiza o texto de uma anotação existente. A anotação será marcada como editada.\nEditar não dispara notificações de menção nem altera quem foi mencionado: só a\ncriação da anotação notifica (nesta versão).\nRequer o escopo `conversations:write`.\n","tags":["Anotações"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"},{"name":"annotation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da anotação"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAnnotation"},"example":{"text":"Texto atualizado da anotação"}}}},"responses":{"200":{"description":"Anotação atualizada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Annotation"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirAnotacao","summary":"Excluir anotação","description":"Remove uma anotação de uma conversa. Requer o escopo `conversations:write`.","tags":["Anotações"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"},{"name":"annotation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da anotação"}],"responses":{"200":{"description":"Anotação removida com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/draft":{"get":{"operationId":"buscarRascunho","summary":"Buscar rascunho de uma conversa","description":"Retorna o rascunho de mensagem pendente para a conversa especificada.\nRetorna 404 se não houver rascunho salvo.\nRequer o escopo `conversations:read`.\n","tags":["Rascunhos"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"responses":{"200":{"description":"Rascunho retornado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MessageDraft"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"salvarRascunho","summary":"Salvar rascunho de mensagem","description":"Cria ou atualiza o rascunho de mensagem de uma conversa (upsert).\nSe já existir um rascunho, o texto é substituído.\nRequer o escopo `conversations:write`.\n","tags":["Rascunhos"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveDraftRequest"},"example":{"draft_text":"Olá, gostaria de agendar uma reunião para discutir o processo."}}}},"responses":{"200":{"description":"Rascunho salvo com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MessageDraft"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirRascunho","summary":"Excluir rascunho de mensagem","description":"Remove o rascunho de mensagem de uma conversa.\nRequer o escopo `conversations:write`.\n","tags":["Rascunhos"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"responses":{"200":{"description":"Rascunho removido com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/drafts":{"get":{"operationId":"listarRascunhos","summary":"Listar todos os rascunhos pendentes","description":"Retorna todos os rascunhos de mensagem pendentes do escritório,\nordenados do mais recente para o mais antigo. Inclui nome e telefone\ndo contato associado a cada conversa.\nRequer o escopo `conversations:read`.\n","tags":["Rascunhos"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Número máximo de rascunhos a retornar"}],"responses":{"200":{"description":"Lista de rascunhos retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/MessageDraft"},{"type":"object","properties":{"contact_name":{"type":"string","nullable":true,"description":"Nome do contato da conversa"},"contact_phone":{"type":"string","nullable":true,"description":"Telefone do contato"}}}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions":{"get":{"operationId":"listarSessoesAgente","summary":"Listar sessões do agente IA","description":"Retorna uma lista paginada de sessões do agente IA do escritório.\nFiltre por estado, agente, conversa, contato e intervalo de datas.\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"name":"state","in":"query","schema":{"type":"string","enum":["ongoing","completed"]},"description":"Filtrar por estado da sessão"},{"name":"agent_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar por ID do agente"},{"name":"chat_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar por ID da conversa"},{"name":"contact_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar por ID do contato"},{"name":"date_start","in":"query","schema":{"type":"string","format":"date-time"},"description":"Filtrar sessões criadas a partir desta data"},{"name":"date_end","in":"query","schema":{"type":"string","format":"date-time"},"description":"Filtrar sessões criadas até esta data"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista de sessões retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentSession"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions/count":{"get":{"operationId":"contarSessoesAgente","summary":"Contagem exata de sessões do agente","description":"Retorna a contagem exata de sessões que correspondem aos filtros informados.\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"name":"state","in":"query","schema":{"type":"string","enum":["ongoing","completed"]}},{"name":"agent_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"date_start","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"date_end","in":"query","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Contagem retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"count":{"type":"integer","description":"Número total de sessões"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions/daily-count":{"get":{"operationId":"contagemDiariaSessoesAgente","summary":"Contagem de sessões agrupada por dia","description":"Retorna a contagem de sessões agrupada por dia dentro do intervalo especificado.\nInclui total, em andamento e completadas por dia.\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"name":"date_start","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Data inicial do intervalo (obrigatório)"},{"name":"date_end","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Data final do intervalo (obrigatório)"}],"responses":{"200":{"description":"Contagem diária retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"total":{"type":"integer"},"ongoing":{"type":"integer"},"completed":{"type":"integer"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions/{id}":{"get":{"operationId":"buscarSessaoAgente","summary":"Buscar sessão do agente","description":"Retorna os dados completos de uma sessão do agente, incluindo resumo,\ninformações coletadas, classificação do lead e dados de transferência.\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Sessão retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentSession"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions/{id}/complete":{"post":{"operationId":"completarSessaoAgente","summary":"Forçar conclusão de uma sessão","description":"Marca a sessão como concluída. Útil para encerrar sessões que ficaram\nem andamento por engano ou que precisam ser fechadas manualmente.\nRequer o escopo `agents:write`.\n","tags":["SessoesAgente"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"completed_reason":{"type":"string","maxLength":500,"description":"Motivo da conclusão","example":"Concluída manualmente pelo administrador"}}}}}},"responses":{"200":{"description":"Sessão concluída com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentSession"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions/{id}/feedback":{"post":{"operationId":"enviarFeedbackSessao","summary":"Enviar avaliação de uma sessão","description":"Registra uma nota (1-5) e texto de feedback para a sessão.\nRequer o escopo `agents:write`.\n","tags":["SessoesAgente"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rating"],"properties":{"rating":{"type":"integer","minimum":1,"maximum":5,"description":"Nota de 1 a 5"},"user_feedback":{"type":"string","maxLength":5000,"description":"Texto opcional de feedback"}}}}}},"responses":{"200":{"description":"Feedback registrado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentSession"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions/{id}/evaluations":{"get":{"operationId":"listarAvaliacoesSessao","summary":"Listar avaliações de qualidade da sessão","description":"Retorna as avaliações automáticas de qualidade de uma sessão,\nincluindo scores de satisfação, humanização, triagem e fluxo.\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Avaliações retornadas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SessionEvaluation"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agent-sessions/{id}/logs":{"get":{"operationId":"listarLogsSessao","summary":"Listar logs de debug da sessão","description":"Retorna os logs de execução do agente para a sessão.\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Logs retornados com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentLog"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/session-follow-ups":{"get":{"operationId":"listarFollowUps","summary":"Listar follow-ups do agente","description":"Retorna uma lista paginada de follow-ups agendados pelo agente.\nFiltre por status, sessão e intervalo de datas.\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"name":"session_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar por ID da sessão"},{"name":"status","in":"query","schema":{"type":"string","enum":["scheduled","processing","sent","responded","cancelled","failed"]},"description":"Filtrar por status do follow-up"},{"name":"date_start","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"date_end","in":"query","schema":{"type":"string","format":"date-time"}},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Follow-ups retornados com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SessionFollowUp"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/session-follow-ups/{id}/cancel":{"post":{"operationId":"cancelarFollowUp","summary":"Cancelar follow-up pendente","description":"Cancela um follow-up com status `scheduled` ou `processing`.\nFollow-ups já enviados ou cancelados não podem ser alterados.\nRequer o escopo `agents:write`.\n","tags":["SessoesAgente"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Follow-up cancelado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SessionFollowUp"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/agent-status":{"get":{"operationId":"verificarStatusAgente","summary":"Verificar status do agente na conversa","description":"Retorna se o agente IA está ativo na conversa, se está aguardando humano\ne dados da sessão ativa (se houver).\nRequer o escopo `agents:read`.\n","tags":["SessoesAgente"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"responses":{"200":{"description":"Status retornado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentStatus"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/agent/activate":{"post":{"operationId":"ativarAgenteConversa","summary":"Ativar agente IA na conversa","description":"Ativa o agente de IA na conversa. O agente passará a responder\nautomaticamente às mensagens recebidas.\nRequer o escopo `agents:write`.\n","tags":["SessoesAgente"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"responses":{"200":{"description":"Agente ativado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentStatus"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/conversations/{conversation_id}/agent/deactivate":{"post":{"operationId":"desativarAgenteConversa","summary":"Desativar agente IA na conversa","description":"Desativa o agente de IA na conversa. O agente deixará de responder\nautomaticamente e a conversa voltará ao atendimento humano.\nRequer o escopo `agents:write`.\n","tags":["SessoesAgente"],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da conversa"}],"responses":{"200":{"description":"Agente desativado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AgentStatus"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/simulations":{"get":{"operationId":"listarSimulacoes","summary":"Listar sessões de simulação","description":"Retorna uma lista paginada de sessões de simulação do escritório.\nRequer o escopo `agents:read`.\n","tags":["Simulacoes"],"parameters":[{"name":"state","in":"query","schema":{"type":"string","enum":["ongoing","completed"]}},{"name":"agent_id","in":"query","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Simulações retornadas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SimulationSession"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarSimulacao","summary":"Criar sessão de simulação","description":"Cria uma nova sessão de simulação no sandbox do agente.\nRequer o escopo `agents:write`.\n","tags":["Simulacoes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["instance_id"],"properties":{"instance_id":{"type":"string","format":"uuid","description":"ID do número WhatsApp a ser utilizado"},"agent_id":{"type":"string","format":"uuid","description":"ID do agente a testar (opcional)"},"simulation_purpose":{"type":"string","maxLength":500,"description":"Propósito da simulação"},"contact_name":{"type":"string","maxLength":200,"default":"Simulacao","description":"Nome do contato simulado"}}}}}},"responses":{"201":{"description":"Simulação criada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SimulationSession"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/simulations/{id}":{"get":{"operationId":"buscarSimulacao","summary":"Buscar sessão de simulação","description":"Retorna os dados de uma sessão de simulação específica.\nRequer o escopo `agents:read`.\n","tags":["Simulacoes"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Simulação retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SimulationSession"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/simulations/{id}/messages":{"get":{"operationId":"listarMensagensSimulacao","summary":"Listar mensagens da simulação","description":"Retorna as mensagens de uma sessão de simulação.\nRequer o escopo `agents:read`.\n","tags":["Simulacoes"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Mensagens retornadas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SimulationMessage"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"enviarMensagemSimulacao","summary":"Enviar mensagem na simulação","description":"Envia uma mensagem de teste na sessão de simulação.\nRequer o escopo `agents:write`.\n","tags":["Simulacoes"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["message_text"],"properties":{"message_text":{"type":"string","minLength":1,"maxLength":4096,"description":"Texto da mensagem"},"message_type":{"type":"string","default":"text","description":"Tipo da mensagem"}}}}}},"responses":{"201":{"description":"Mensagem enviada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SimulationMessage"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/simulations/{id}/end":{"post":{"operationId":"encerrarSimulacao","summary":"Encerrar sessão de simulação","description":"Encerra uma sessão de simulação em andamento.\nRequer o escopo `agents:write`.\n","tags":["Simulacoes"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Simulação encerrada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SimulationSession"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/dashboard":{"get":{"operationId":"dashboardAnalitico","summary":"Dashboard analítico do escritório","description":"Retorna métricas agregadas do escritório para o período especificado:\ntotal de interações, usuários ativos, conversas com atividade, etapa mais ativa\ne tipo de interação mais frequente.\n\nUse `compare=true` para incluir variação percentual em relação ao período anterior.\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"$ref":"#/components/parameters/AnalyticsInstanceId"},{"name":"compare","in":"query","required":false,"description":"Incluir comparação com período anterior","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Métricas do dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DashboardMetrics"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/contacts":{"get":{"operationId":"metricasContatos","summary":"Métricas de contatos","description":"Retorna métricas de contatos com múltiplas visões: overview (totais e crescimento),\nsources (por origem), stages (por etapa do funil), tags (por tag),\ngeographic (por região/DDD) e heatmap (padrões de criação por hora/dia).\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"$ref":"#/components/parameters/AnalyticsInstanceId"},{"name":"view","in":"query","required":false,"description":"Visão dos dados de contatos","schema":{"type":"string","enum":["overview","sources","stages","tags","geographic","heatmap"],"default":"overview"}},{"name":"tag_ids","in":"query","required":false,"description":"Filtrar por tags (UUIDs separados por vírgula)","schema":{"type":"string"}}],"responses":{"200":{"description":"Métricas de contatos conforme a visão solicitada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Estrutura varia conforme o parâmetro `view`"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/team":{"get":{"operationId":"metricasEquipe","summary":"Desempenho da equipe","description":"Retorna métricas de desempenho por membro da equipe: total de interações,\ndetalhamento por tipo, conversas modificadas e média de interações por dia.\n\nUse `user_id` para focar em um único membro.\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"$ref":"#/components/parameters/AnalyticsInstanceId"},{"name":"user_id","in":"query","required":false,"description":"Filtrar por membro específico (UUID)","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Métricas de desempenho da equipe","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TeamAnalytics"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/stages":{"get":{"operationId":"metricasEtapas","summary":"Análise do funil","description":"Retorna métricas por etapa do funil: contagem de conversas, total de interações,\ntaxa de conversão e tipos de interação mais frequentes.\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"$ref":"#/components/parameters/AnalyticsInstanceId"}],"responses":{"200":{"description":"Métricas de etapas do funil","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/StageAnalytics"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/response-times":{"get":{"operationId":"temposResposta","summary":"Tempos de resposta (TPR/TMR)","description":"Calcula tempos de primeira resposta (TPR) e tempo médio de resposta (TMR)\npor período, membro e número WhatsApp.\n\n- **TPR**: Tempo entre a primeira mensagem do cliente e a primeira resposta do atendente\n- **TMR**: Tempo médio entre qualquer mensagem do cliente e a resposta seguinte\n\nUse `business_hours_only=true` para considerar apenas horário comercial (Seg-Sex 08:00-18:00 BRT).\n\n**Nota:** Este endpoint processa mensagens em lote e pode demorar mais em períodos longos.\nRecomendamos janelas de até 90 dias.\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"$ref":"#/components/parameters/AnalyticsInstanceId"},{"name":"group_by","in":"query","required":false,"description":"Agrupamento dos períodos","schema":{"type":"string","enum":["day","week","month","quarter"],"default":"day"}},{"name":"business_hours_only","in":"query","required":false,"description":"Considerar apenas horário comercial (Seg-Sex 08:00-18:00 BRT)","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Dados de tempo de resposta","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ResponseTimeData"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/interactions":{"get":{"operationId":"logInteracoes","summary":"Log de interações (audit trail)","description":"Retorna uma lista paginada de interações registradas no escritório,\nordenadas da mais recente para a mais antiga.\n\nFiltre por conversa (`chat_id`), membro (`user_id`) ou tipo de interação.\n\nTipos válidos: `stage_change`, `tag_add`, `tag_remove`, `accountable_change`,\n`archive`, `unarchive`, `agent_activate`, `agent_deactivate`, `department_change`,\n`manual_message`, `note_added`, `status_change`, `contact_update`, `automation_trigger`, `other`.\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"name":"chat_id","in":"query","required":false,"description":"Filtrar por conversa específica","schema":{"type":"string","format":"uuid"}},{"name":"user_id","in":"query","required":false,"description":"Filtrar por membro que realizou a interação","schema":{"type":"string","format":"uuid"}},{"name":"interaction_type","in":"query","required":false,"description":"Filtrar por tipo de interação","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"minimum":1,"maximum":200}},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista paginada de interações","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InteractionLog"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/conversations":{"get":{"operationId":"analiticoConversas","summary":"Análise por conversa","description":"Retorna uma lista paginada de conversas com métricas analíticas:\ncontagem de interações por tipo, usuários únicos envolvidos e timeline.\n\nFiltre por tags e etapa do funil.\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"$ref":"#/components/parameters/AnalyticsInstanceId"},{"name":"tag_ids","in":"query","required":false,"description":"Filtrar por tags (UUIDs separados por vírgula)","schema":{"type":"string"}},{"name":"stage_id","in":"query","required":false,"description":"Filtrar por etapa do funil","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista paginada de conversas com métricas","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ConversationAnalytics"}},"period":{"$ref":"#/components/schemas/AnalyticsPeriod"}}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/analytics/ads":{"get":{"operationId":"metricasAnuncios","summary":"Métricas de anúncios (CTWA)","description":"Retorna métricas de conversas originadas de anúncios Click-to-WhatsApp (CTWA):\ntotal de contatos, distribuição por plataforma (Instagram, Facebook, Google),\ntaxa de ativação e contagem de janelas perdidas.\n\nRequer o escopo `analytics:read`.\n","tags":["Analiticos"],"parameters":[{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"$ref":"#/components/parameters/AnalyticsPeriod"},{"$ref":"#/components/parameters/AnalyticsInstanceId"}],"responses":{"200":{"description":"Métricas de anúncios CTWA","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AdMetrics"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures":{"get":{"operationId":"listarAssinaturas","summary":"Listar documentos para assinatura","description":"Retorna uma lista paginada de documentos para assinatura do escritório.\nCada documento inclui um resumo dos signatários (nome e status).\n\nRequer o escopo `signatures:read`.\n","tags":["Assinaturas"],"parameters":[{"name":"contact_id","in":"query","required":false,"description":"Filtrar documentos por contato","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Filtrar por status do documento","schema":{"type":"string","enum":["draft","pending","partially_signed","signed","rejected","expired","cancelled"]}},{"name":"provider","in":"query","required":false,"description":"Filtrar por provedor de assinatura","schema":{"type":"string","enum":["zapsign","autentique"]}},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista de documentos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DocumentResponse"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarEEnviarDocumento","summary":"Criar e enviar documento para assinatura","description":"Cria um documento e o envia imediatamente para assinatura digital.\nO documento é criado com status `pending` e os signatários recebem o link de assinatura.\n\nUma chamada idêntica em até 24h devolve o documento anterior (`replayed: true`)\nem vez de criar e cobrar outro; altere `name` para enviar um novo.\n\nRequer integração de assinaturas ativa (retorna 402 caso contrário).\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDocumentRequest"}}}},"responses":{"201":{"description":"Documento criado e enviado para assinatura. Quando `partial` está\npresente, um ou mais signatários ficaram sem link (ver `SignaturePartialCreate`).\n","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/DocumentResponse"},{"type":"object","properties":{"partial":{"$ref":"#/components/schemas/SignaturePartialCreate"}}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Sem envio pelo provedor. `code`:\n`no_signature_provider` (nenhuma integração ativa),\n`no_api_plan` (a conta ZapSign não tem o Plano de API; `details.plan_name`\ntraz o plano atual) ou `send_failed` (créditos/limite esgotados).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Múltiplos provedores de assinatura ativos — especifique provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/{id}":{"get":{"operationId":"buscarDocumento","summary":"Buscar documento por ID","description":"Retorna os dados completos de um documento, incluindo todos os signatários com status individual.\n\nRequer o escopo `signatures:read`.\n","tags":["Assinaturas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Documento encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DocumentResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarAssinatura","summary":"Atualizar documento","description":"Atualiza campos do documento. Campos permitidos: `name`, `folder_path`.\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Novo nome do documento"},"folder_path":{"type":"string","description":"Caminho da pasta para organização"}}}}}},"responses":{"200":{"description":"Documento atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DocumentResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirDocumento","summary":"Excluir documento","description":"Exclui um documento e seus signatários. Documentos com status `pending` ou `partially_signed`\nnão podem ser excluídos (retorna 409).\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Documento excluído","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Documento com assinaturas pendentes não pode ser excluído","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/{id}/download":{"get":{"operationId":"baixarDocumento","summary":"Obter URL de download do documento","description":"Retorna a URL de download do documento original ou assinado.\nUse `type=signed` para obter o documento assinado (disponível somente após conclusão).\n\nRequer o escopo `signatures:read`.\n","tags":["Assinaturas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"name":"type","in":"query","required":false,"description":"Tipo de arquivo: `original` (padrão) ou `signed`","schema":{"type":"string","enum":["original","signed"],"default":"original"}}],"responses":{"200":{"description":"URL de download","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"URL do arquivo para download"},"file_name":{"type":"string","description":"Nome do arquivo"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/drafts":{"post":{"operationId":"criarRascunhoDocumento","summary":"Criar rascunho de documento","description":"Cria um documento com status `draft` sem enviar para assinatura.\nUse para montar o documento em etapas (adicionar signatários, upload de arquivo)\nantes de enviá-lo com `POST /v1/signatures/drafts/{id}/send`.\n\nSe `draft_id` for informado, atualiza o rascunho existente (substitui signatários se informados).\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDraftRequest"}}}},"responses":{"201":{"description":"Rascunho criado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DocumentResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Documento existente não é rascunho","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/drafts/{id}/upload":{"post":{"operationId":"uploadArquivoRascunho","summary":"Upload de arquivo para rascunho","description":"Faz upload de um arquivo (PDF ou DOCX) para um documento rascunho.\nO arquivo deve ser acessível via URL pública. Tamanho máximo: 10MB.\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file_url"],"properties":{"file_url":{"type":"string","format":"uri","description":"URL pública do arquivo (PDF ou DOCX). Com Autentique, precisa ser um arquivo em storage.chatjuridico.com obtido via presignedUrlAssinaturas → PUT → processUploadedAssinaturas (outras URLs → 400)."},"file_name":{"type":"string","description":"Nome do arquivo","default":"document.pdf"},"file_type":{"type":"string","enum":["pdf","docx"],"description":"Tipo do arquivo","default":"pdf"}}}}}},"responses":{"200":{"description":"Arquivo associado ao rascunho","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DocumentResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Documento não é rascunho","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/drafts/{id}/send":{"post":{"operationId":"enviarRascunho","summary":"Enviar rascunho para assinatura","description":"Envia um documento rascunho para assinatura digital.\nO rascunho deve ter um arquivo e pelo menos um signatário.\nTransiciona o status de `draft` → `pending`.\n\nRequer integração de assinaturas ativa (retorna 402 caso contrário).\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Rascunho enviado para assinatura. Quando `partial` está presente, um ou\nmais signatários ficaram sem link (ver `SignaturePartialCreate`).\n","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/DocumentResponse"},{"type":"object","properties":{"partial":{"$ref":"#/components/schemas/SignaturePartialCreate"}}}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Sem envio pelo provedor. `code`:\n`no_signature_provider` (nenhuma integração ativa),\n`no_api_plan` (a conta ZapSign não tem o Plano de API; `details.plan_name`\ntraz o plano atual) ou `send_failed` (créditos/limite esgotados).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Documento não é rascunho","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Falha ao comunicar com o provedor de assinaturas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/extract-signer-data":{"post":{"operationId":"extrairDadosSignatario","summary":"Extrair dados do signatário via IA","description":"Usa IA (GPT-4o-mini) para extrair informações do signatário (e-mail, endereço, CPF)\na partir das mensagens de uma conversa. Retorna os dados pré-preenchidos para criar signatários.\n\nFallback: se a IA não estiver disponível, retorna apenas nome e telefone do contato.\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["conversation_id","contact_id"],"properties":{"conversation_id":{"type":"string","format":"uuid","description":"ID da conversa para análise"},"contact_id":{"type":"string","format":"uuid","description":"ID do contato"}}}}}},"responses":{"200":{"description":"Dados extraídos do signatário","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ExtractedSignerData"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/upload/presigned-url":{"post":{"operationId":"presignedUrlAssinaturas","summary":"Gerar URL de upload para assinaturas","description":"Gera uma URL pré-assinada para upload direto de arquivo PDF/DOCX.\nUse PUT nesta URL para enviar o arquivo, depois confirme com\no endpoint process-uploaded.\n\nRoteia automaticamente para o provedor ativo; se houver mais de um,\naceita o parâmetro provider no corpo da requisição.\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file_name"],"properties":{"file_name":{"type":"string","description":"Nome do arquivo com extensão"},"content_type":{"type":"string","default":"application/pdf","description":"MIME type do arquivo"},"provider":{"type":"string","enum":["zapsign","autentique"],"description":"Provedor de assinatura (opcional se apenas um estiver ativo)"}}}}}},"responses":{"200":{"description":"URL de upload gerada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"upload_url":{"type":"string","description":"URL PUT pré-assinada para upload direto"},"file_key":{"type":"string","description":"Chave do arquivo no storage"},"file_url":{"type":"string","description":"URL CDN do arquivo após upload"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Nenhuma integração de assinatura configurada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Múltiplos provedores ativos — especifique provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/upload/process-uploaded":{"post":{"operationId":"processUploadedAssinaturas","summary":"Confirmar upload para assinaturas","description":"Confirma que o arquivo foi carregado com sucesso na URL pré-assinada\ne retorna a URL CDN para uso na criação do documento.\n\nRequer o escopo `signatures:write`.\n","tags":["Assinaturas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file_key"],"properties":{"file_key":{"type":"string","description":"Chave do arquivo retornada por presigned-url"},"provider":{"type":"string","enum":["zapsign","autentique"],"description":"Provedor de assinatura (opcional se apenas um estiver ativo)"}}}}}},"responses":{"200":{"description":"Upload confirmado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"file_url":{"type":"string","description":"URL CDN do arquivo"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Nenhuma integração de assinatura configurada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Múltiplos provedores ativos — especifique provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/zapsign":{"get":{"operationId":"statusIntegracaoZapsign","summary":"Status da integração ZapSign","description":"Retorna o status da integração ZapSign, incluindo se está\nativa, conectada e os créditos disponíveis.\n\nPara status de todos os provedores, use a ferramenta composta\n`status_integracao_assinaturas`.\n\nRequer o escopo `signatures:read`.\n","tags":["Integracoes"],"responses":{"200":{"description":"Status da integração","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/IntegrationStatus"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/zapsign/activate":{"post":{"operationId":"ativarIntegracaoAssinaturas","summary":"Ativar integração de assinaturas","description":"Ativa a integração de assinaturas digitais fornecendo a API key.\nA chave é validada e webhooks são registrados automaticamente.\n\nRequer o escopo `signatures:write`.\n","tags":["Integracoes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["api_key"],"properties":{"api_key":{"type":"string","description":"Chave de API do provedor de assinaturas"}}}}}},"responses":{"200":{"description":"Integração ativada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Signature integration activated successfully"},"is_active":{"type":"boolean","example":true},"credits":{"type":"object","description":"Informações do plano do provedor de assinaturas"},"webhook_count":{"type":"integer","description":"Número de webhooks registrados"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/zapsign/deactivate":{"post":{"operationId":"desativarIntegracaoAssinaturas","summary":"Desativar integração de assinaturas","description":"Desativa a integração de assinaturas digitais. Remove webhooks\nregistrados e limpa as credenciais armazenadas.\n\nRequer o escopo `signatures:write`.\n","tags":["Integracoes"],"responses":{"200":{"description":"Integração desativada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Signature integration deactivated"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/zapsign/sync-credits":{"post":{"operationId":"sincronizarCreditosAssinaturas","summary":"Sincronizar créditos de assinaturas","description":"Consulta o provedor de assinaturas para atualizar o saldo de créditos\narmazenado localmente. Use para obter o saldo mais recente.\n\nRequer o escopo `signatures:write`.\n","tags":["Integracoes"],"responses":{"200":{"description":"Créditos atualizados","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"credits":{"type":"object","description":"Informações atualizadas do plano de assinaturas"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Integração de assinaturas não configurada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Falha ao comunicar com o provedor de assinaturas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/autentique":{"get":{"operationId":"statusIntegracaoAutentique","summary":"Status da integração Autentique","description":"Retorna o status atual da integração Autentique do escritório:\nse está ativa, conectada e quando foi conectada.\n\nRequer o escopo `signatures:read`.\n","tags":["IntegracoesAutentique"],"responses":{"200":{"description":"Status da integração","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/IntegrationStatus"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/autentique/activate":{"post":{"operationId":"ativarIntegracaoAutentique","summary":"Ativar integração Autentique","description":"Conecta o escritório ao Autentique usando uma chave de API.\nValida a chave e registra a integração.\n\nRequer o escopo `signatures:write`.\n","tags":["IntegracoesAutentique"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["api_key"],"properties":{"api_key":{"type":"string","description":"Chave de API do Autentique"},"webhook_secret":{"type":"string","description":"Segredo de webhook exibido no painel do Autentique para este endpoint (opcional). Sem ele, os webhooks não são verificados por assinatura."},"is_sandbox":{"type":"boolean","default":false,"description":"Se true, usa o ambiente sandbox do Autentique"}}}}}},"responses":{"200":{"description":"Integração ativada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string"},"is_active":{"type":"boolean"},"office_integration_id":{"type":"string"}}}}}}}},"400":{"description":"Chave de API inválida ou revogada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/autentique/deactivate":{"post":{"operationId":"desativarIntegracaoAutentique","summary":"Desativar integração Autentique","description":"Desconecta o escritório do Autentique.\n\nRequer o escopo `signatures:write`.\n","tags":["IntegracoesAutentique"],"responses":{"200":{"description":"Integração desativada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Integração Autentique não encontrada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/autentique":{"get":{"operationId":"listarDocumentosAutentique","summary":"Listar documentos Autentique","description":"Lista documentos de assinatura eletrônica do escritório via Autentique,\ncom paginação por cursor e filtros opcionais por contato e status.\n\nRequer o escopo `signatures:read`.\n","tags":["AssinaturasAutentique"],"parameters":[{"name":"contact_id","in":"query","schema":{"type":"string"},"description":"Filtrar por ID do contato"},{"name":"status","in":"query","schema":{"type":"string","enum":["draft","pending","signed","rejected","cancelled","expired"]},"description":"Filtrar por status do documento"},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":100},"description":"Número máximo de registros"},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Cursor para paginação"}],"responses":{"200":{"description":"Lista de documentos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DocumentResponse"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/autentique/documents":{"post":{"operationId":"criarDocumentoAutentique","summary":"Criar documento para assinatura via Autentique","description":"Cria um documento para assinatura eletrônica via Autentique.\nO arquivo deve ter sido previamente carregado via presigned URL.\n\nRequer o escopo `signatures:write`.\n","tags":["AssinaturasAutentique"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","contact_id","instance_id","file_url","signers"],"properties":{"name":{"type":"string","description":"Nome/título do documento"},"contact_id":{"type":"string","description":"ID do contato (signatário principal)"},"instance_id":{"type":"string","description":"ID da instância/número WhatsApp"},"file_url":{"type":"string","description":"URL do arquivo em storage.chatjuridico.com, obtida via presignedUrlAssinaturas → PUT → processUploadedAssinaturas (outras URLs → 400)"},"file_name":{"type":"string","default":"document.pdf"},"file_type":{"type":"string","enum":["pdf","docx"],"default":"pdf"},"signers":{"type":"array","minItems":1,"items":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"phone_number":{"type":"string"},"phone_country":{"type":"string","description":"DDI sem `+` (padrão `55`)"},"cpf":{"type":"string"},"security_verifications":{"type":"array","description":"Verificações de identidade: strings (`[\"LIVE\"]`) ou objetos\n(`[{\"type\": \"SMS\", \"verify_phone\": \"+55...\"}]`).\n","items":{}}}}}}}}}},"responses":{"201":{"description":"Documento criado e enviado para assinatura.\n\nQuando `partial` está presente, o documento EXISTE no Autentique mas\num ou mais signatários não receberam link de assinatura — o documento\nnão poderá se completar sozinho e exige ação manual.\n","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/DocumentResponse"},{"type":"object","properties":{"partial":{"type":"object","description":"Presente apenas em criação parcial","properties":{"signers_requested":{"type":"integer","example":3},"signers_created":{"type":"integer","example":2},"signer_failures":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}]}}}}}},"400":{"description":"Dados inválidos ou conversa não encontrada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Integração Autentique não configurada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"description":"Falha na comunicação com o provedor.\n\n**Verifique o `code` antes de repetir a chamada.**\n`document_persisted_partially` significa que o documento **foi criado\ne cobrado** no Autentique mas não pôde ser salvo por completo. NÃO\nrepita — consulte o documento pelo `id` na mensagem antes de agir.\nOs demais códigos (`document_creation_failed`, `provider_unavailable`)\nsão seguros para retry.\n","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["document_persisted_partially","document_creation_failed","provider_unavailable"]}}}}}]}}}}}}},"/v1/signatures/autentique/upload/presigned-url":{"post":{"operationId":"presignedUrlAutentique","summary":"Gerar URL de upload (Autentique)","description":"Gera uma URL pré-assinada para upload direto de arquivo PDF/DOCX.\nUse PUT nesta URL para enviar o arquivo, depois confirme com\no endpoint process-uploaded.\n\nRequer o escopo `signatures:write`.\n","tags":["AssinaturasAutentique"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file_name"],"properties":{"file_name":{"type":"string","description":"Nome do arquivo com extensão"},"content_type":{"type":"string","default":"application/pdf","description":"MIME type do arquivo (application/pdf ou application/vnd.openxmlformats-officedocument.wordprocessingml.document)"}}}}}},"responses":{"200":{"description":"URL de upload gerada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"upload_url":{"type":"string","description":"URL PUT pré-assinada para upload direto"},"file_key":{"type":"string","description":"Chave do arquivo no storage"},"file_url":{"type":"string","description":"URL CDN do arquivo após upload"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/autentique/upload/process-uploaded":{"post":{"operationId":"processUploadedAutentique","summary":"Confirmar upload (Autentique)","description":"Confirma que o arquivo foi carregado com sucesso na URL pré-assinada\ne retorna a URL CDN para uso na criação do documento.\n\nRequer o escopo `signatures:write`.\n","tags":["AssinaturasAutentique"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["file_key"],"properties":{"file_key":{"type":"string","description":"Chave do arquivo retornada por presigned-url"}}}}}},"responses":{"200":{"description":"Upload confirmado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"file_url":{"type":"string","description":"URL CDN do arquivo"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/autentique/{id}":{"get":{"operationId":"buscarDocumentoAutentique","summary":"Buscar documento Autentique","description":"Retorna os detalhes de um documento de assinatura Autentique,\nincluindo status e signatários.\n\nRequer o escopo `signatures:read`.\n","tags":["AssinaturasAutentique"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"ID do documento"}],"responses":{"200":{"description":"Detalhes do documento","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DocumentResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Documento não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirDocumentoAutentique","summary":"Excluir documento Autentique","description":"Exclui um documento de assinatura Autentique. Não é possível\nexcluir documentos com status `pending` ou `partially_signed`.\n\n**Atenção:** a exclusão é irreversível e remove também os signatários\ne o histórico de eventos (audit trail) do documento via cascade.\n\nRequer o escopo `signatures:write`.\n","tags":["AssinaturasAutentique"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"ID do documento"}],"responses":{"200":{"description":"Documento excluído","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Documento não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Documento em estado que não permite exclusão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarDocumentoAutentique","summary":"Atualizar documento Autentique","description":"Atualiza campos editáveis de um documento Autentique (nome, pasta).\n\nRequer o escopo `signatures:write`.\n","tags":["AssinaturasAutentique"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"ID do documento"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Novo nome do documento"},"folder_path":{"type":"string","description":"Nova pasta do documento"}}}}}},"responses":{"200":{"description":"Documento atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DocumentResponse"}}}}}},"400":{"description":"Nenhum campo válido para atualizar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Documento não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/signatures/autentique/{id}/download":{"get":{"operationId":"baixarDocumentoAutentique","summary":"Baixar documento Autentique","description":"Retorna a URL de download do arquivo original ou assinado.\nUse o parâmetro `type` para escolher: `original` (padrão) ou `signed`.\n\nRequer o escopo `signatures:read`.\n","tags":["AssinaturasAutentique"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"ID do documento"},{"name":"type","in":"query","schema":{"type":"string","enum":["original","signed"],"default":"original"},"description":"Tipo de arquivo (original ou assinado)"}],"responses":{"200":{"description":"URL de download","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"url":{"type":"string"},"file_name":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Documento ou arquivo não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/calculo-juridico/preferences":{"post":{"operationId":"salvarPreferenciasCalculoJuridico","summary":"Salvar preferências de sincronização do Cálculo Jurídico","description":"Grava as preferências de sincronização do escritório. O escritório vem do\ncontexto autenticado e NUNCA do corpo, e só as chaves da lista abaixo são\naceitas: qualquer outra é ignorada em silêncio.\n\nOs `whatsapp_instance_ids` são conferidos contra os números do próprio\nescritório antes de gravar, porque a escrita é feita com papel de serviço\ne passaria por cima da RLS.\n\nA gravação é um merge atômico, então salvar preferência nunca apaga o que\no backend escreveu em paralelo (token em cache, estado de importação).\n","tags":["Integracoes"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Ao menos uma das chaves abaixo. Um corpo sem nenhuma delas é recusado com 400.","properties":{"sync_lawsuits":{"type":"boolean","description":"Se a sincronização de processos está ligada para este escritório"},"auto_sync_enabled":{"type":"boolean","description":"Se a sincronização automática está ligada"},"whatsapp_instance_ids":{"type":"array","description":"Números do escritório que a sincronização alcança","items":{"type":"string","format":"uuid"}}}}}}},"responses":{"200":{"description":"Preferências gravadas","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"saved":{"type":"array","description":"Os NOMES das chaves que foram gravadas, em ordem. A resposta não devolve os valores nem o estado depois do merge.","items":{"type":"string"},"example":["auto_sync_enabled","sync_lawsuits"]}}}}}}}},"400":{"description":"Nenhum campo válido no corpo, ou um campo com o tipo errado (booleano, ou lista de textos em `whatsapp_instance_ids`)"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Um ou mais números informados não pertencem a este escritório"},"404":{"description":"Integração com o Cálculo Jurídico não está ativa"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/calculo-juridico/sync-lawsuits":{"post":{"operationId":"sincronizarProcessosCalculoJuridico","summary":"Disparar a sincronização de processos do Cálculo Jurídico","description":"Enfileira uma sincronização de processos para o escritório autenticado.\nNão tem corpo: o escritório vem do contexto.\n\nUma sincronização já em andamento não é empilhada, e a resposta é 409 até\na anterior terminar ou ser liberada pelo coletor.\n","tags":["Integracoes"],"responses":{"200":{"description":"Sincronização enfileirada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","description":"O que a rota responde. `pending` é o valor gravado no banco, e não o desta resposta.","example":"enqueued"},"message":{"type":"string","example":"Lawsuit sync job enqueued"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"Integração com o Cálculo Jurídico não está ativa"},"409":{"description":"Já existe uma sincronização de processos em andamento"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Não foi possível enfileirar a sincronização"},"503":{"description":"Sincronização de processos indisponível no momento"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/asaas":{"get":{"operationId":"statusIntegracaoAsaas","summary":"Status da integração Asaas","description":"Retorna o status da integração com Asaas, incluindo se está ativa,\nconectada, o ambiente (production/sandbox) e os últimos 4 dígitos da chave.\nConectada não é o mesmo que funcionando: `import_status: failed` ou\n`key_access_state: missing_permissions` indicam que os dados do Asaas não\nchegaram (o motivo está em `import_error`).\n\nRequer o escopo `integrations:read`.\n","tags":["IntegracoesAsaas"],"responses":{"200":{"description":"Status da integração","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AsaasIntegrationStatus"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/asaas/activate":{"post":{"operationId":"ativarIntegracaoAsaas","summary":"Ativar integração Asaas (descontinuado)","deprecated":true,"description":"**Descontinuado — responde sempre `410 Gone`.** A integração com o Asaas é\nconectada somente pela tela Configurações → Integrações → Asaas do Chat\nJurídico, que confere as permissões da chave (clientes, cobranças,\nparcelamentos, assinaturas, webhooks) antes de conectar. Uma chave do\nAsaas não deve trafegar por um cliente de API ou por um agente de IA.\n\nRequer o escopo `integrations:write`.\n","tags":["IntegracoesAsaas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["api_key"],"properties":{"api_key":{"type":"string","description":"Chave de API do Asaas"},"environment":{"type":"string","enum":["production","sandbox"],"default":"production","description":"Ambiente da conta Asaas"}}}}}},"responses":{"410":{"description":"Rota descontinuada — conecte o Asaas pela tela de Integrações","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/asaas/deactivate":{"post":{"operationId":"desativarIntegracaoAsaas","summary":"Desativar integração Asaas","description":"Desativa a integração com Asaas. Remove webhooks registrados\ne limpa as credenciais armazenadas.\n\nRequer o escopo `integrations:write`.\n","tags":["IntegracoesAsaas"],"responses":{"200":{"description":"Integração desativada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Asaas integration deactivated"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/asaas/detect-env":{"post":{"operationId":"detectarAmbienteAsaas","summary":"Detectar ambiente da chave Asaas","description":"Testa a chave de API contra ambos os ambientes (sandbox e production)\npara determinar a qual pertence. Retorna `invalid` se a chave não for aceita.\n\nRequer o escopo `integrations:read`.\n","tags":["IntegracoesAsaas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["api_key"],"properties":{"api_key":{"type":"string","description":"Chave de API do Asaas para testar"}}}}}},"responses":{"200":{"description":"Ambiente detectado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"environment":{"type":"string","enum":["production","sandbox","invalid"],"example":"production"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments":{"get":{"operationId":"listarCobrancas","summary":"Listar cobranças","description":"Retorna uma lista paginada de cobranças do escritório.\nFiltre por contato, status, tipo de cobrança e intervalo de vencimento.\n\nRequer o escopo `payments:read` e permissão `view_payments` (para acesso interno).\n","tags":["Cobrancas"],"x-codeSamples":[{"lang":"cURL","source":"curl -X GET \"https://api.jur.chat/v1/payments\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\"\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.get(\n    \"https://api.jur.chat/v1/payments\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"}\n)\nprint(response.json())\n"}],"parameters":[{"name":"contact_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar cobranças de um contato específico"},{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING","RECEIVED","CONFIRMED","OVERDUE","REFUNDED","CANCELLED","RECEIVED_IN_CASH"]},"description":"Filtrar por status"},{"name":"billing_type","in":"query","schema":{"type":"string","enum":["PIX","BOLETO","CREDIT_CARD"]},"description":"Filtrar por tipo de cobrança"},{"name":"due_date_start","in":"query","schema":{"type":"string","format":"date"},"description":"Vencimento a partir de (YYYY-MM-DD)"},{"name":"due_date_end","in":"query","schema":{"type":"string","format":"date"},"description":"Vencimento até (YYYY-MM-DD)"},{"name":"overdue_acknowledged","in":"query","schema":{"type":"boolean"},"description":"Filtra cobranças vencidas reconhecidas (`true`) ou ainda não reconhecidas (`false`)."},{"name":"sort_by","in":"query","schema":{"type":"string","enum":["due_date","created_at","value"],"default":"due_date"},"description":"Campo de ordenação"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista de cobranças","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Payment"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarCobranca","summary":"Criar cobrança","description":"Cria uma nova cobrança no Asaas para um contato.\nA cobrança é processada via N8N que valida a chave, cria no Asaas e retorna URLs de pagamento.\n\n**Limites CDC:**\n- `interest_percentage`: máximo 1.0% ao mês\n- `fine_percentage`: máximo 2.0% sobre o valor\n\nSe o contato não possuir vínculo com cliente Asaas, um será criado automaticamente.\n\nRequer o escopo `payments:write` e permissão `create_payments` (para acesso interno).\n","tags":["Cobrancas"],"x-codeSamples":[{"lang":"cURL","source":"curl -X POST \"https://api.jur.chat/v1/payments\" \\\n  -H \"X-API-Key: cj_live_sua_chave_aqui\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"contact_id\": \"uuid-do-contato\",\n    \"instance_id\": \"uuid-da-instancia\",\n    \"value\": 150.00,\n    \"due_date\": \"2025-01-15\",\n    \"billing_type\": \"PIX\"\n  }'\n"},{"lang":"Python","source":"import requests\n\nresponse = requests.post(\n    \"https://api.jur.chat/v1/payments\",\n    headers={\"X-API-Key\": \"cj_live_sua_chave_aqui\"},\n    json={\n        \"contact_id\": \"uuid-do-contato\",\n        \"instance_id\": \"uuid-da-instancia\",\n        \"value\": 150.00,\n        \"due_date\": \"2025-01-15\",\n        \"billing_type\": \"PIX\"\n    }\n)\nprint(response.json())\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentRequest"}}}},"responses":{"201":{"description":"Cobrança criada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"Dados da cobrança criada no Asaas"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"502":{"description":"Falha ao comunicar com Asaas via N8N","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/{id}":{"get":{"operationId":"buscarCobranca","summary":"Buscar cobrança","description":"Retorna os dados completos de uma cobrança, incluindo URLs de pagamento\n(PIX QR code, boleto, invoice) e histórico de eventos.\n\nRequer o escopo `payments:read`.\n","tags":["Cobrancas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da cobrança"}],"responses":{"200":{"description":"Dados da cobrança","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Payment"},{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/PaymentEvent"}}}}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/{id}/cancel":{"post":{"operationId":"cancelarCobranca","summary":"Cancelar cobrança","description":"Cancela uma cobrança pendente ou vencida no Asaas.\nCobranças com status `RECEIVED`, `CONFIRMED` ou `REFUNDED` não podem ser canceladas.\n\nRequer o escopo `payments:write` e permissão `cancel_payments` (para acesso interno).\n","tags":["Cobrancas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da cobrança"}],"responses":{"200":{"description":"Cobrança cancelada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Payment cancelled"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/{id}/acknowledge-overdue":{"post":{"operationId":"reconhecerCobrancaVencida","summary":"Reconhecer cobrança vencida","description":"Marca uma cobrança vencida como reconhecida pela equipe.\nÚtil para controle interno — não altera o status no Asaas.\n\nApenas cobranças com status `OVERDUE` podem ser reconhecidas.\n\nRequer o escopo `payments:write`.\n","tags":["Cobrancas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID da cobrança"}],"responses":{"200":{"description":"Vencimento reconhecido","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Overdue payment acknowledged"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/schedule-reminders":{"post":{"operationId":"agendarLembretesPagamento","summary":"Agendar lembretes de pagamento","description":"Agenda mensagens de lembrete via WhatsApp para cobranças pendentes.\nOs lembretes são agendados para 3 dias antes do vencimento às 9h (horário de São Paulo).\nA chamada cria registros reais em `scheduled_messages` e evita duplicados por cobrança/tipo de lembrete.\n\nPara instâncias Meta, informe `template_name` de um template aprovado; texto simples agendado\nsó deve ser usado em provedores com executor próprio de mensagens agendadas.\n\nMáximo de 50 cobranças por requisição.\n\nRequer o escopo `payments:write`.\n","tags":["Cobrancas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["payment_ids"],"properties":{"payment_ids":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":50,"description":"IDs das cobranças para agendar lembretes"},"template_name":{"type":"string","nullable":true,"description":"Nome do template aprovado da Meta para lembretes em instâncias Meta"},"template_language":{"type":"string","default":"pt_BR"},"template_parameters":{"type":"object","additionalProperties":{"type":"string"}},"text":{"type":"string","nullable":true,"description":"Texto usado quando o provedor permite lembrete agendado sem template"}}}}}},"responses":{"200":{"description":"Lembretes agendados","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"scheduled_count":{"type":"integer"},"scheduled":{"type":"array","items":{"type":"object"}},"duplicate_count":{"type":"integer"},"duplicates":{"type":"array","items":{"type":"object"}},"errors":{"type":"array","items":{"type":"object"}}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/fill-data":{"post":{"operationId":"preencherDadosCobranca","summary":"Preencher dados via IA","description":"Usa IA para extrair dados de cobrança a partir das mensagens de uma conversa.\nRetorna valores sugeridos para valor, descrição, vencimento e tipo de cobrança.\n\nRequer o escopo `payments:read`.\n","tags":["Cobrancas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["conversation_id","contact_id"],"properties":{"conversation_id":{"type":"string","format":"uuid","description":"ID da conversa de onde extrair dados"},"contact_id":{"type":"string","format":"uuid","description":"ID do contato"}}}}}},"responses":{"200":{"description":"Dados extraídos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FillPaymentDataResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/dashboard":{"get":{"operationId":"dashboardFinanceiro","summary":"Dashboard financeiro","description":"Retorna métricas agregadas de cobranças: totais, recebidos, pendentes, vencidos,\nagrupamento por tipo e status, e opcionalmente comparação com período anterior.\n\nUse `period` (week/month/quarter/year) para atalhos de data ou `start_date`/`end_date` para período personalizado.\n\nRequer o escopo `payments:read`.\n","tags":["Cobrancas"],"parameters":[{"name":"period","in":"query","schema":{"type":"string","enum":["week","month","quarter","year"]},"description":"Atalho de período"},{"name":"start_date","in":"query","schema":{"type":"string","format":"date-time"},"description":"Data inicial (ISO 8601)"},{"name":"end_date","in":"query","schema":{"type":"string","format":"date-time"},"description":"Data final (ISO 8601)"},{"name":"date_type","in":"query","schema":{"type":"string","enum":["due_date","payment_date","created_at"],"default":"due_date"}},{"name":"statuses","in":"query","schema":{"type":"string"},"description":"Status separados por vírgula"},{"name":"billing_types","in":"query","schema":{"type":"string"},"description":"Tipos separados por vírgula"},{"name":"user_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar por responsável"},{"name":"compare","in":"query","schema":{"type":"boolean","default":false},"description":"Incluir comparação com período anterior"}],"responses":{"200":{"description":"Métricas do dashboard","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PaymentDashboard"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/dashboard/time-series":{"get":{"operationId":"serieTemporal","summary":"Série temporal financeira","description":"Retorna dados de cobrança agregados por período para renderização de gráficos.\n\nRequer o escopo `payments:read`.\n","tags":["Cobrancas"],"parameters":[{"name":"group_by","in":"query","schema":{"type":"string","enum":["day","week","month"],"default":"day"}},{"name":"period","in":"query","schema":{"type":"string","enum":["week","month","quarter","year"]}},{"name":"start_date","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"end_date","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"date_type","in":"query","schema":{"type":"string","enum":["due_date","payment_date","created_at"],"default":"due_date"}},{"name":"statuses","in":"query","schema":{"type":"string"}},{"name":"billing_types","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Dados da série temporal","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TimeSeriesPoint"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/funnel-values":{"get":{"operationId":"valoresPorFunil","summary":"Valores por etapa do funil","description":"Retorna valores financeiros agregados por etapa do funil de vendas.\nUsa a view `asaas_stage_values` do banco de dados.\n\nRequer o escopo `payments:read`.\n","tags":["Cobrancas"],"parameters":[{"name":"start_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"end_date","in":"query","schema":{"type":"string","format":"date"}}],"responses":{"200":{"description":"Valores por etapa","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FunnelStageValues"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/chat-values":{"get":{"operationId":"valoresPorConversa","summary":"Valores por conversa","description":"Retorna valores financeiros agregados por conversa/contato.\nUsa a view `asaas_chat_values` do banco de dados.\n\nRequer o escopo `payments:read`.\n","tags":["Cobrancas"],"parameters":[{"name":"contact_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar por contato"},{"name":"stage_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filtrar por etapa do funil"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Valores por conversa","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChatValues"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/customers":{"get":{"operationId":"listarClientesAsaas","summary":"Listar clientes Asaas","description":"Lista contatos com seu status de vínculo com cliente Asaas.\nFiltre por nome/telefone ou por status do vínculo.\n\nRequer o escopo `payments:read`.\n","tags":["Cobrancas"],"parameters":[{"name":"search","in":"query","schema":{"type":"string"},"description":"Buscar por nome ou telefone"},{"name":"has_asaas_link","in":"query","schema":{"type":"boolean"},"description":"Filtrar por contatos com/sem vínculo Asaas"},{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Lista de clientes","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerAsaasLink"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/customers/sync":{"post":{"operationId":"sincronizarClienteAsaas","summary":"Sincronizar cliente Asaas","description":"Sincroniza os dados de um contato com seu registro no Asaas.\nProcessado via N8N.\n\nRequer o escopo `payments:write`.\n","tags":["Cobrancas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_id"],"properties":{"contact_id":{"type":"string","format":"uuid","description":"ID do contato a sincronizar"}}}}}},"responses":{"200":{"description":"Cliente sincronizado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Customer synced"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"502":{"description":"Falha ao comunicar com Asaas via N8N","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/payments/customers/create":{"post":{"operationId":"criarClienteAsaas","summary":"Criar cliente no Asaas","description":"Cria um novo cliente no Asaas vinculado a um contato do ChatJurídico.\nO CPF/CNPJ é obrigatório (exigência do Asaas).\n\nRequer o escopo `payments:write`.\n","tags":["Cobrancas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_id","cpf_cnpj"],"properties":{"contact_id":{"type":"string","format":"uuid","description":"ID do contato"},"cpf_cnpj":{"type":"string","description":"CPF ou CNPJ do cliente (apenas números)","example":"12345678900"}}}}}},"responses":{"201":{"description":"Cliente criado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Customer created in Asaas"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"502":{"description":"Falha ao comunicar com Asaas via N8N","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/mcp":{"post":{"operationId":"mcpTransport","summary":"Endpoint MCP (Model Context Protocol)","description":"Endpoint de transporte Streamable HTTP do protocolo MCP (Model Context Protocol).\nExpõe a API REST do ChatJuridico como ferramentas MCP para agentes de IA.\n\n**Método:** Apenas POST (GET retorna 405).\n**Transporte:** Streamable HTTP, stateless, JSON responses.\n\n**Autenticação:** Aceita `X-API-Key` no header ou `Authorization: Bearer <token>` (OAuth 2.1).\n\n**Headers opcionais:**\n- `X-MCP-Profile`: Seleciona perfil de ferramentas (`essencial`, `vendas`, `atendimento`, `completo`)\n\n**Descoberta:** Use `GET /.well-known/mcp.json` para obter o MCP Server Card com\ncapacidades, esquemas de autenticação e URL de transporte.\n\nConsulte a seção \"Agentes de IA (MCP)\" na documentação principal para exemplos de configuração.\n","tags":["Saude"],"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"parameters":[{"name":"X-MCP-Profile","in":"header","description":"Perfil de ferramentas MCP a ativar","required":false,"schema":{"type":"string","enum":["essencial","vendas","atendimento","completo"],"default":"completo"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Mensagem JSON-RPC 2.0 do protocolo MCP","properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"method":{"type":"string","description":"Método MCP (tools/list, tools/call, resources/list, etc.)"},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"params":{"type":"object"}},"required":["jsonrpc","method"]},"example":{"jsonrpc":"2.0","method":"tools/list","id":1}}}},"responses":{"200":{"description":"Resposta JSON-RPC 2.0 do protocolo MCP","content":{"application/json":{"schema":{"type":"object","properties":{"jsonrpc":{"type":"string"},"id":{"oneOf":[{"type":"string"},{"type":"integer"}]},"result":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/.well-known/mcp.json":{"get":{"operationId":"obterCartaoMCP","summary":"MCP Server Card","description":"Retorna o MCP Server Card (SEP-1649) descrevendo as capacidades do servidor MCP,\nesquemas de autenticação e endpoints de transporte. Não requer autenticação.\nResposta cacheada por 1 hora.\n","tags":["Saude"],"security":[],"responses":{"200":{"description":"MCP Server Card","content":{"application/json":{"schema":{"type":"object","properties":{"mcp_version":{"type":"string","example":"2025-11-25"},"name":{"type":"string","example":"ChatJuridico"},"description":{"type":"string"},"homepage":{"type":"string","format":"uri","example":"https://chatjuridico.com.br"},"logo_url":{"type":"string","format":"uri"},"transport":{"type":"object","properties":{"streamable_http":{"type":"object","properties":{"url":{"type":"string","format":"uri","example":"https://api.jur.chat/mcp"}}}}},"authentication":{"type":"object","properties":{"schemes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"header":{"type":"string"},"authorization_url":{"type":"string","format":"uri"},"token_url":{"type":"string","format":"uri"}}}}}},"capabilities":{"type":"object","properties":{"tools":{"type":"boolean"},"resources":{"type":"boolean"},"prompts":{"type":"boolean"}}},"categories":{"type":"array","items":{"type":"string"}},"languages":{"type":"array","items":{"type":"string"}}}},"example":{"mcp_version":"2025-11-25","name":"ChatJuridico","description":"WhatsApp CRM for Brazilian law firms — MCP server with 90+ tools","homepage":"https://chatjuridico.com.br","logo_url":"https://chatjuridico.com.br/logo.png","transport":{"streamable_http":{"url":"https://api.jur.chat/mcp"}},"authentication":{"schemes":[{"type":"api_key","header":"X-API-Key"},{"type":"oauth2","authorization_url":"https://api.jur.chat/oauth/authorize","token_url":"https://api.jur.chat/oauth/token"}]},"capabilities":{"tools":true,"resources":true,"prompts":true},"categories":["legal","crm","whatsapp","messaging"],"languages":["pt-BR"]}}},"headers":{"Cache-Control":{"schema":{"type":"string","example":"public, max-age=3600"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"obterMetadadosOAuth","summary":"Metadados do servidor de autorização OAuth","description":"Retorna os metadados do servidor de autorização conforme RFC 8414.\nInclui endpoints, grant types suportados, escopos e métodos de autenticação.\nNão requer autenticação.\n","tags":["Saude"],"security":[],"responses":{"200":{"description":"Metadados do servidor de autorização","content":{"application/json":{"schema":{"type":"object","properties":{"issuer":{"type":"string","format":"uri"},"authorization_endpoint":{"type":"string","format":"uri"},"token_endpoint":{"type":"string","format":"uri"},"registration_endpoint":{"type":"string","format":"uri"},"revocation_endpoint":{"type":"string","format":"uri"},"response_types_supported":{"type":"array","items":{"type":"string"}},"grant_types_supported":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"code_challenge_methods_supported":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"}}}},"example":{"issuer":"https://api.jur.chat","authorization_endpoint":"https://api.jur.chat/oauth/authorize","token_endpoint":"https://api.jur.chat/oauth/token","registration_endpoint":"https://api.jur.chat/oauth/register","revocation_endpoint":"https://api.jur.chat/oauth/revoke","response_types_supported":["code"],"grant_types_supported":["authorization_code","refresh_token"],"token_endpoint_auth_methods_supported":["none"],"code_challenge_methods_supported":["S256"],"scopes_supported":["contacts:read","contacts:write","contacts:delete","conversations:read","conversations:write","messages:read","messages:send","tags:read","tags:write","funnel:read","funnel:write","shortcuts:read","shortcuts:write","campaigns:read","campaigns:write","automations:read","automations:write","webhooks:manage","instances:write","agents:read","agents:write","analytics:read","signatures:read","signatures:write","integrations:read","integrations:write","payments:read","payments:write"]}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/.well-known/oauth-protected-resource":{"get":{"operationId":"obterMetadadosRecursoProtegido","summary":"Metadados do recurso protegido OAuth","description":"Retorna os metadados do recurso protegido conforme RFC 9728.\nIndica qual servidor de autorização protege o endpoint MCP.\nNão requer autenticação.\n","tags":["Saude"],"security":[],"responses":{"200":{"description":"Metadados do recurso protegido","content":{"application/json":{"schema":{"type":"object","properties":{"resource":{"type":"string","format":"uri"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"bearer_methods_supported":{"type":"array","items":{"type":"string"}}}},"example":{"resource":"https://api.jur.chat/mcp","authorization_servers":["https://api.jur.chat"],"bearer_methods_supported":["header"]}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/oauth/register":{"post":{"operationId":"registrarClienteOAuth","summary":"Registrar cliente OAuth (RFC 7591)","description":"Registra dinamicamente um novo cliente OAuth. Retorna um `client_id` para uso\nno fluxo de autorização. Aceita apenas `redirect_uris` com HTTPS (ou HTTP para localhost).\nNão requer autenticação.\n","tags":["Saude"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["redirect_uris"],"properties":{"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"},"description":"URIs de redirecionamento (HTTPS obrigatório, exceto localhost)"},"client_name":{"type":"string","description":"Nome do cliente","default":"MCP Client"}}},"example":{"redirect_uris":["https://app.example.com/callback"],"client_name":"My MCP Client"}}}},"responses":{"201":{"description":"Cliente registrado","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}}}},"example":{"client_id":"cj_client_abc123","client_name":"My MCP Client","redirect_uris":["https://app.example.com/callback"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/oauth/authorize":{"get":{"operationId":"paginaAutorizacaoOAuth","summary":"Página de autorização OAuth","description":"Renderiza a página HTML de autorização onde o usuário insere sua chave de API\npara autorizar o cliente OAuth. Redireciona de volta ao `redirect_uri` com o código de autorização.\n","tags":["Saude"],"security":[],"parameters":[{"name":"response_type","in":"query","required":true,"schema":{"type":"string","enum":["code"]}},{"name":"client_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","format":"uri"}},{"name":"code_challenge","in":"query","required":true,"schema":{"type":"string"},"description":"PKCE code challenge (S256)"},{"name":"code_challenge_method","in":"query","required":true,"schema":{"type":"string","enum":["S256"]}},{"name":"state","in":"query","schema":{"type":"string"},"description":"Valor opaco para proteção CSRF"},{"name":"scope","in":"query","schema":{"type":"string"},"description":"Escopos separados por espaço"}],"responses":{"200":{"description":"Página HTML de autorização","content":{"text/html":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"submeterAutorizacaoOAuth","summary":"Submeter autorização OAuth","description":"Processa a submissão da chave de API na página de autorização.\nSe válida, redireciona ao `redirect_uri` com um código de autorização.\n","tags":["Saude"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["api_key"],"properties":{"api_key":{"type":"string","description":"Chave de API do ChatJurídico"}}}}}},"responses":{"302":{"description":"Redireciona ao redirect_uri com código de autorização"},"400":{"description":"Chave de API inválida"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/oauth/token":{"post":{"operationId":"obterTokenOAuth","summary":"Obter token de acesso OAuth","description":"Troca um código de autorização ou refresh token por um access token JWT.\n\n**Grant types suportados:**\n- `authorization_code`: troca código + PKCE verifier por access + refresh token\n- `refresh_token`: troca refresh token por novo par de tokens (rotação automática)\n\nTokens de acesso expiram em 1 hora. Refresh tokens são de uso único (rotação).\n","tags":["Saude"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"code":{"type":"string","description":"Código de autorização (para grant_type=authorization_code)"},"code_verifier":{"type":"string","description":"PKCE code verifier (para grant_type=authorization_code)"},"redirect_uri":{"type":"string","format":"uri","description":"Deve corresponder ao redirect_uri da autorização"},"client_id":{"type":"string","description":"ID do cliente registrado"},"refresh_token":{"type":"string","description":"Refresh token (para grant_type=refresh_token)"}}}},"application/json":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"code":{"type":"string"},"code_verifier":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"client_id":{"type":"string"},"refresh_token":{"type":"string"}}}}}},"responses":{"200":{"description":"Token emitido com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string","description":"JWT de acesso"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"integer","description":"Tempo de expiração em segundos","example":3600},"refresh_token":{"type":"string","description":"Token para renovação (uso único)"},"scope":{"type":"string","description":"Escopos concedidos separados por espaço"}}},"example":{"access_token":"eyJhbGciOiJIUzI1NiIs...","token_type":"Bearer","expires_in":3600,"refresh_token":"cj_refresh_abc123...","scope":"contacts:read contacts:write messages:send"}}}},"400":{"description":"Erro OAuth","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["invalid_request","invalid_grant","unsupported_grant_type"]},"error_description":{"type":"string"}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/oauth/revoke":{"post":{"operationId":"revogarTokenOAuth","summary":"Revogar refresh token OAuth","description":"Revoga um refresh token, invalidando-o para uso futuro.\nNão requer autenticação.\n","tags":["Saude"],"security":[],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"Refresh token a revogar"}}}},"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string"}}}}}},"responses":{"200":{"description":"Token revogado (ou já inválido)","content":{"application/json":{"schema":{"type":"object","properties":{"revoked":{"type":"boolean","example":true}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/document-templates":{"get":{"operationId":"listarTemplatesDocumento","summary":"Listar templates de documentos","description":"Retorna templates do escritório (`visibility=office`) e templates globais\n(`visibility=global`). Filtre por categoria com `?category=contract`.\n","tags":["Documentos"],"parameters":[{"in":"query","name":"category","schema":{"type":"string","enum":["contract","petition","letter","internal_memo","declaration","power_of_attorney","agreement","opinion"]}},{"in":"query","name":"locale","schema":{"type":"string","default":"pt-BR"}},{"in":"query","name":"include_archived","schema":{"type":"boolean","default":false}},{"in":"query","name":"include_global","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Lista de templates","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DocumentTemplate"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarTemplateDocumento","summary":"Criar template de documento","description":"Cria um template Markdown próprio do escritório.","tags":["Documentos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDocumentTemplateRequest"}}}},"responses":{"201":{"description":"Template criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplate"}}}},"422":{"$ref":"#/components/responses/ValidationError"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/document-templates/{id}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"obterTemplateDocumento","summary":"Obter template de documento","tags":["Documentos"],"responses":{"200":{"description":"Template","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplate"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"atualizarTemplateDocumento","summary":"Atualizar template de documento","tags":["Documentos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDocumentTemplateRequest"}}}},"responses":{"200":{"description":"Template atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplate"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/document-templates/{id}/clone":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"clonarTemplateDocumento","summary":"Clonar template global para o escritório","tags":["Documentos"],"responses":{"201":{"description":"Template clonado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentTemplate"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/documents":{"get":{"operationId":"listarDocumentos","summary":"Listar documentos gerados","tags":["Documentos"],"parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["draft","generating","ready","reviewed","sent","awaiting_signature","signed","archived"]}},{"in":"query","name":"chat_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"contact_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"template_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"include_archived","schema":{"type":"boolean","default":false}},{"in":"query","name":"limit","schema":{"type":"integer","default":50,"minimum":1,"maximum":200}},{"in":"query","name":"offset","schema":{"type":"integer","default":0,"minimum":0}}],"responses":{"200":{"description":"Lista de documentos","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Document"}}}}}}},"post":{"operationId":"criarDocumento","summary":"Criar documento a partir de template","tags":["Documentos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDocumentRequest"}}}},"responses":{"201":{"description":"Documento criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Document"}}}}}}},"/v1/documents/{id}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"obterDocumento","summary":"Obter documento","tags":["Documentos"],"responses":{"200":{"description":"Documento","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Document"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"atualizarDocumento","summary":"Atualizar documento (nova versão se body_markdown mudar)","tags":["Documentos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDocumentRequest"}}}},"responses":{"200":{"description":"Documento atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Document"}}}},"409":{"description":"Bloqueado por gate de revisão antes de exportar/enviar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"apagarDocumentoLgpd","summary":"Excluir documento (LGPD purge com tombstone)","description":"Executa um expurgo LGPD do documento e seu histórico. A operação é\nirreversível: as versões e o histórico AI são removidos, e um\nregistro de tombstone é criado em `lgpd_deletion_tombstones` com o\nSHA-256 da projeção apagada.\n","tags":["Documentos"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["user_request","retention_policy","office_offboarding"],"default":"user_request"},"note":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Tombstone criado","content":{"application/json":{"schema":{"type":"object","properties":{"tombstone_id":{"type":"string","format":"uuid"},"document_id":{"type":"string","format":"uuid"},"reason":{"type":"string"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/documents/{id}/mark-as-reviewed":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"post":{"operationId":"marcarDocumentoComoRevisado","summary":"Marcar documento como revisado pelo advogado","tags":["Documentos"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reviewed":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Documento atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Document"}}}}}}},"/v1/documents/{id}/versions":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"listarVersoesDocumento","summary":"Listar versões do documento","tags":["Documentos"],"responses":{"200":{"description":"Lista de versões","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DocumentVersionSummary"}}}}}}}},"/v1/documents/{id}/versions/{version}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"version","required":true,"schema":{"type":"integer","minimum":1}}],"get":{"operationId":"obterVersaoDocumento","summary":"Obter versão específica do documento","tags":["Documentos"],"responses":{"200":{"description":"Versão","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentVersion"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/documents/{id}/audit":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"listarAuditoriaDocumento","summary":"Listar trilha de auditoria HMAC-encadeada do documento","description":"Retorna todas as ações registradas em `document_audit_log_v2`, em\nordem crescente de `seq`, incluindo `prev_hash` + `row_hmac` para\nverificação externa de integridade da cadeia.\n","tags":["Documentos"],"responses":{"200":{"description":"Lista de eventos de auditoria","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"seq":{"type":"integer"},"action":{"type":"string","enum":["created","updated","ai_block_inserted","ai_block_accepted","ai_block_rejected","marked_as_reviewed","unmarked_as_reviewed","exported_pdf","exported_docx","exported_html","sent_whatsapp","sent_for_signature","signature_completed","signature_cancelled","archived","restored","lgpd_deleted"]},"actor_kind":{"type":"string","enum":["user","ai","workflow","system","webhook"]},"actor_user_id":{"type":"string","format":"uuid","nullable":true},"payload":{"type":"object"},"created_at":{"type":"string","format":"date-time"},"prev_hash":{"type":"string","nullable":true},"row_hmac":{"type":"string"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/documents/{id}/history":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"listarHistoricoIaDocumento","summary":"Listar timeline de eventos AI / revisão / export","tags":["Documentos"],"responses":{"200":{"description":"Timeline desnormalizado consumido pelo painel lateral","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"document_id":{"type":"string","format":"uuid"},"document_version":{"type":"integer"},"event_kind":{"type":"string"},"event_summary":{"type":"string"},"ai_provider":{"type":"string","nullable":true},"ai_model":{"type":"string","nullable":true},"ai_input_tokens":{"type":"integer","nullable":true},"ai_output_tokens":{"type":"integer","nullable":true},"ai_latency_ms":{"type":"integer","nullable":true},"payload":{"type":"object"},"created_at":{"type":"string","format":"date-time"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/v1/custom-property-schemas":{"get":{"operationId":"listarPropriedadesCustomizadas","summary":"Listar definições de propriedades customizadas","tags":["Documentos"],"parameters":[{"in":"query","name":"scope","schema":{"type":"string","enum":["contact","chat"]}},{"in":"query","name":"include_archived","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Lista de propriedades","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomPropertySchema"}}}}}}},"post":{"operationId":"criarPropriedadeCustomizada","summary":"Criar propriedade customizada","description":"Cria um campo do GERADOR DE DOCUMENTOS. NÃO aparece no painel do contato:\npara campos do contato use `criarPropriedadeContato`.\n","tags":["Documentos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomPropertySchemaRequest"}}}},"responses":{"201":{"description":"Propriedade criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomPropertySchema"}}}}}}},"/v1/custom-property-schemas/{id}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"operationId":"obterPropriedadeCustomizada","tags":["Documentos"],"responses":{"200":{"description":"Propriedade","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomPropertySchema"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"atualizarPropriedadeCustomizada","tags":["Documentos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomPropertySchemaRequest"}}}},"responses":{"200":{"description":"Propriedade atualizada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomPropertySchema"}}}}}}},"/v1/custom-property-values":{"get":{"operationId":"listarValoresPropriedadesCustomizadas","summary":"Listar valores de propriedades customizadas para uma entidade","tags":["Documentos"],"parameters":[{"in":"query","name":"scope","required":true,"schema":{"type":"string","enum":["contact","chat"]}},{"in":"query","name":"entity_id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Lista de valores","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomPropertyValue"}}}}}}},"post":{"operationId":"salvarValorPropriedadeCustomizada","summary":"Inserir/atualizar valor de propriedade customizada","tags":["Documentos"],"parameters":[{"in":"query","name":"scope","required":true,"schema":{"type":"string","enum":["contact","chat"]}},{"in":"query","name":"entity_id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["schema_id","value"],"properties":{"schema_id":{"type":"string","format":"uuid"},"value":{"nullable":true,"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object"},{"type":"array"}],"description":"Valor da propriedade. O tipo aceito depende do `type` da definição referenciada por `schema_id`."}}}}}},"responses":{"200":{"description":"Valor salvo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomPropertyValue"}}}}}}},"/v1/custom-property-values/{schema_id}":{"parameters":[{"in":"path","name":"schema_id","required":true,"schema":{"type":"string","format":"uuid"}}],"delete":{"operationId":"removerValorPropriedadeCustomizada","tags":["Documentos"],"parameters":[{"in":"query","name":"entity_id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Valor removido","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}}},"/v1/document-settings":{"get":{"operationId":"obterConfiguracoesDocumento","summary":"Obter configurações de documento do escritório","tags":["Documentos"],"responses":{"200":{"description":"Configurações","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentSettings"}}}}}},"patch":{"operationId":"atualizarConfiguracoesDocumento","summary":"Atualizar configurações de documento (admin)","tags":["Documentos"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDocumentSettingsRequest"}}}},"responses":{"200":{"description":"Configurações atualizadas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentSettings"}}}}}}},"/v1/agents/{agent_id}/faq":{"get":{"operationId":"listarFaq","summary":"Listar perguntas frequentes","description":"Retorna todos os itens de FAQ do agente especificado.\nItens sem ID recebem um UUID automaticamente na primeira leitura.\nRequer o escopo `agents:read`.\n","tags":["FAQ"],"parameters":[{"name":"agent_id","in":"path","required":true,"description":"ID do agente","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Lista de FAQ retornada com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FaqItem"}}}},"example":{"data":[{"id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","question":"Qual o horário de atendimento?","answer":"Atendemos de segunda a sexta, das 9h às 18h."},{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","question":"Quanto custa uma consulta?","answer":"A primeira consulta é gratuita. Entre em contato para agendar."}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarFaq","summary":"Adicionar itens ao FAQ","description":"Adiciona um ou mais itens ao FAQ do agente.\nPara um único item, envie `question` e `answer` no body.\nPara múltiplos itens, envie `items` como array de `{question, answer}`.\nMáximo de 100 itens por agente.\nRequer o escopo `agents:write`.\n","tags":["FAQ"],"parameters":[{"name":"agent_id","in":"path","required":true,"description":"ID do agente","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/CreateFaqItem"},{"$ref":"#/components/schemas/BulkCreateFaq"}]},"examples":{"single":{"summary":"Item único","value":{"question":"Qual o horário de atendimento?","answer":"Atendemos de segunda a sexta, das 9h às 18h."}},"bulk":{"summary":"Múltiplos itens","value":{"items":[{"question":"Quanto custa uma consulta?","answer":"A primeira consulta é gratuita."},{"question":"Quais áreas de atuação?","answer":"Atuamos em direito trabalhista e previdenciário."}]}}}}}},"responses":{"201":{"description":"Itens criados com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FaqItem"}}}}}}},"400":{"description":"Limite de 100 itens excedido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents/{agent_id}/faq/{item_id}":{"put":{"operationId":"atualizarFaq","summary":"Atualizar item do FAQ","description":"Atualiza a pergunta e/ou resposta de um item existente do FAQ.\nEnvie apenas os campos que deseja alterar.\nRequer o escopo `agents:write`.\n","tags":["FAQ"],"parameters":[{"name":"agent_id","in":"path","required":true,"description":"ID do agente","schema":{"type":"string","format":"uuid"}},{"name":"item_id","in":"path","required":true,"description":"ID do item de FAQ","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFaqItem"},"example":{"question":"Qual o novo horário de atendimento?","answer":"Atendemos de segunda a sexta, das 8h às 17h."}}}},"responses":{"200":{"description":"Item atualizado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FaqItem"}}}}}},"400":{"description":"Nenhum campo para atualizar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirFaq","summary":"Excluir item do FAQ","description":"Remove um item do FAQ do agente.\nRequer o escopo `agents:write`.\n","tags":["FAQ"],"parameters":[{"name":"agent_id","in":"path","required":true,"description":"ID do agente","schema":{"type":"string","format":"uuid"}},{"name":"item_id","in":"path","required":true,"description":"ID do item de FAQ","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Item excluído com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true},"question":{"type":"string","example":"Qual o horário de atendimento?"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents/{agent_id}/faq/generate":{"post":{"operationId":"gerarFaq","summary":"Gerar FAQ com IA","description":"Utiliza IA (OpenAI) para gerar perguntas e respostas com base\nem uma descrição do serviço prestado pelo escritório.\nOs itens gerados NÃO são salvos automaticamente — use o endpoint\nde criação para salvar os itens desejados.\nRequer o escopo `agents:write`.\n","tags":["FAQ"],"parameters":[{"name":"agent_id","in":"path","required":true,"description":"ID do agente","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateFaqRequest"},"example":{"prompt":"Escritório de advocacia trabalhista em São Paulo, especializado em reclamações trabalhistas e acordos.","count":10}}}},"responses":{"200":{"description":"FAQ gerado com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"faqs":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedFaqItem"}},"count":{"type":"integer","example":10}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Falha na geração via IA","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"OpenAI não configurado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/agents/{agent_id}/faq/suggest-from-conversations":{"post":{"operationId":"sugerirFaqConversas","summary":"Sugerir FAQ a partir de conversas","description":"Analisa conversas recentes do escritório e utiliza IA para identificar\nperguntas recorrentes e gerar sugestões de FAQ.\nOs itens sugeridos NÃO são salvos automaticamente.\nRequer o escopo `agents:write`.\n","tags":["FAQ"],"parameters":[{"name":"agent_id","in":"path","required":true,"description":"ID do agente","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestFaqRequest"},"example":{"days":7,"limit":10}}}},"responses":{"200":{"description":"Sugestões geradas com sucesso","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"faqs":{"type":"array","items":{"$ref":"#/components/schemas/GeneratedFaqItem"}},"count":{"type":"integer","example":8},"conversations_analyzed":{"type":"integer","example":15}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"Falha na análise via IA","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"OpenAI não configurado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendars":{"get":{"operationId":"listarCalendarios","summary":"Listar calendários do escritório","description":"Retorna os calendários do escritório (nativos e espelhos do Google Calendar).\n\nRequer o escopo `calendar:read`.\n","tags":["Calendario"],"parameters":[{"name":"include_archived","in":"query","required":false,"description":"Incluir calendários arquivados","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"Lista de calendários","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CalendarResponse"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendars/{id}":{"get":{"operationId":"buscarCalendario","summary":"Buscar calendário por ID","description":"Retorna um calendário específico do escritório.\n\nRequer o escopo `calendar:read`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Calendário encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CalendarResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendars/{id}/acl":{"get":{"operationId":"listarPermissoesCalendario","summary":"Listar permissões (ACL) de um calendário","description":"Retorna as permissões de acesso por membro para um calendário.\n\nRequer o escopo `calendar:admin` (gestão de compartilhamento).\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Lista de permissões","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CalendarACLResponse"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendars/{id}/acl/{userId}":{"put":{"operationId":"definirPermissaoCalendario","summary":"Definir permissão de um membro em um calendário","description":"Cria ou atualiza a permissão de um membro para um calendário específico.\nA cadeia de capacidade é enforçada: can_read implica can_view, can_write implica can_read.\n\nRequer o escopo `calendar:admin` (gestão de compartilhamento; somente administradores).\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"name":"userId","in":"path","required":true,"description":"ID do membro","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"can_view":{"type":"boolean"},"can_read":{"type":"boolean"},"can_write":{"type":"boolean"},"is_default_write":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Permissão definida","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CalendarACLResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"removerPermissaoCalendario","summary":"Remover permissão de um membro em um calendário","description":"Remove a entrada de ACL de um membro para um calendário.\n\nRequer o escopo `calendar:admin` (gestão de compartilhamento; somente administradores).\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"},{"name":"userId","in":"path","required":true,"description":"ID do membro","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Permissão removida","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-events":{"get":{"operationId":"listarEventos","summary":"Listar eventos em um intervalo de datas","description":"Retorna eventos do calendário que se sobrepõem ao intervalo informado.\nEventos recorrentes são expandidos automaticamente — cada ocorrência aparece\ncomo um evento individual com ID sintético (`masterId::slotKey`).\n\nOs parâmetros `start` e `end` são obrigatórios (formato ISO 8601).\nA janela máxima é de 366 dias.\n\n**Nota:** chaves de API retornam **todos** os eventos do escritório,\nindependente do ACL de calendário por membro. Eventos com\n`visibility: private` ou `confidential` são omitidos.\n\n**Paginação:** respostas grandes são truncadas cronologicamente.\nSe `pagination.has_more` for `true`, reenvie a requisição com\n`start = pagination.next_start` até que `has_more` seja ausente.\nEventos no instante do corte podem repetir entre páginas —\ndeduplique por `id`.\n\nRequer o escopo `calendar:read`.\n","tags":["Calendario"],"parameters":[{"name":"start","in":"query","required":true,"description":"Início do intervalo (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"end","in":"query","required":true,"description":"Fim do intervalo (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"calendar_id","in":"query","required":false,"description":"Filtrar por calendário","schema":{"type":"string","format":"uuid"}},{"name":"contact_id","in":"query","required":false,"description":"Filtrar por contato vinculado","schema":{"type":"string","format":"uuid"}},{"name":"processo_id","in":"query","required":false,"description":"Filtrar por processo vinculado","schema":{"type":"string","format":"uuid"}},{"name":"source","in":"query","required":false,"description":"Filtrar por origem do evento","schema":{"type":"string","enum":["app","google_synced","google_external","ai_booked"]}},{"name":"status","in":"query","required":false,"description":"Filtrar por status do evento","schema":{"type":"string","enum":["confirmed","tentative","cancelled"]}}],"responses":{"200":{"description":"Lista de eventos (recorrentes expandidos)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CalendarEventResponse"}},"pagination":{"type":"object","description":"Presente quando a resposta é truncada","properties":{"has_more":{"type":"boolean","description":"Há mais eventos após `next_start`"},"next_start":{"type":"string","format":"date-time","description":"Reenvie como `start` para obter a próxima página"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarEvento","summary":"Criar evento no calendário","description":"Cria um novo evento no calendário do escritório (`calendar_id` omitido usa o\ncalendário padrão) ou em um calendário do Google conectado. Para criar uma série\nrecorrente, inclua `recurrence_rule` (formato RRULE RFC 5545, ex:\n`RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR`). Apenas FREQ DAILY, WEEKLY, MONTHLY e\nYEARLY são aceitos.\n\n**Nota:** Em um calendário do Google conectado com permissão de escrita, o\nevento é enviado ao Google Agenda após a criação. Calendários do Google\nconectados só para leitura ou disponibilidade recusam a criação (400).\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEventRequest"}}}},"responses":{"201":{"description":"Evento criado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CalendarEventResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-events/nearest":{"get":{"operationId":"buscarEventoMaisProximo","summary":"Buscar evento mais próximo de um momento","description":"Encontra o evento mais próximo (antes ou depois) de um momento pivot.\nÚtil para encontrar o próximo compromisso, ou a reunião mais recente.\n\nRequer o escopo `calendar:read`.\n","tags":["Calendario"],"parameters":[{"name":"pivot","in":"query","required":true,"description":"Momento pivot (ISO 8601)","schema":{"type":"string","format":"date-time"}},{"name":"calendar_ids","in":"query","required":true,"description":"IDs dos calendários visíveis (separados por vírgula)","schema":{"type":"string"}},{"name":"text","in":"query","required":false,"description":"Filtro por texto (título/descrição)","schema":{"type":"string"}},{"name":"contact_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"processo_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}},{"name":"sources","in":"query","required":false,"description":"Fontes (separadas por vírgula)","schema":{"type":"string"}},{"name":"statuses","in":"query","required":false,"description":"Status (separados por vírgula)","schema":{"type":"string"}},{"name":"meeting_types","in":"query","required":false,"description":"Tipos de reunião (online, in_person — separados por vírgula)","schema":{"type":"string"}}],"responses":{"200":{"description":"Evento mais próximo (ou null)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"nullable":true,"$ref":"#/components/schemas/CalendarEventResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-events/{id}":{"get":{"operationId":"buscarEvento","summary":"Buscar evento por ID","description":"Retorna um evento com seus participantes. Aceita IDs reais (UUID) e\nIDs sintéticos de ocorrências recorrentes (`masterId::slotKey`).\n\nRequer o escopo `calendar:read`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Evento encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CalendarEventResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarEvento","summary":"Atualizar evento","description":"Atualiza um evento existente. Para editar uma ocorrência de série recorrente:\n\n- `scope: only_this` — edita apenas esta ocorrência (cria um override)\n- `scope: all` — edita toda a série\n- `scope: this_and_following` — divide a série neste ponto\n\nQuando `scope` não é informado, o evento é tratado como standalone.\n\nSe a série tiver ocorrências já editadas individualmente (comum em séries\ndo Google), mudar data/horário com `scope: all` retorna 409\n`timing_change_with_children` — use `scope: this_and_following` ou `only_this`.\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEventRequest"}}}},"responses":{"200":{"description":"Evento atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CalendarEventResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"excluirEvento","summary":"Excluir evento","description":"Exclui um evento (soft delete). Para séries recorrentes, use `scope`\nno corpo da requisição:\n\n- `scope: only_this` — cancela apenas esta ocorrência\n- `scope: all` — cancela toda a série\n- `scope: this_and_following` — cancela desta ocorrência em diante\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":"string","enum":["only_this","all","this_and_following"],"description":"Escopo da exclusão para séries recorrentes"},"occurrence_start":{"type":"string","format":"date-time","description":"Início da ocorrência (obrigatório para only_this/this_and_following)"}}}}}},"responses":{"200":{"description":"Evento excluído","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"},"master_id":{"type":"string"},"scope":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-events/{id}/attendees":{"put":{"operationId":"definirParticipantes","summary":"Definir participantes de um evento","description":"Substitui a lista de participantes de um evento. Os participantes anteriores\nsão removidos e os novos são inseridos.\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_ids"],"properties":{"user_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs dos membros participantes"},"organizer_user_id":{"type":"string","format":"uuid","description":"ID do organizador"}}}}}},"responses":{"200":{"description":"Participantes definidos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AttendeeResponse"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-events/{id}/color":{"patch":{"operationId":"definirCorEvento","summary":"Definir cor de um evento","description":"Define ou remove a cor de um evento. Para eventos master (recorrentes),\na cor é propagada para todos os filhos (overrides).\nEnvie `null` para remover a cor.\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"color":{"type":"string","nullable":true,"pattern":"^#[0-9a-fA-F]{6}$","description":"Cor em hexadecimal (#RRGGBB) ou null para remover"}}}}}},"responses":{"200":{"description":"Cor definida","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"updated":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-events/{id}/color-overlays":{"get":{"operationId":"listarSobreposicoesCor","summary":"Listar sobreposições de cor de um evento recorrente","description":"Retorna as sobreposições de cor por ocorrência para um evento master recorrente.\n\nRequer o escopo `calendar:read`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Lista de sobreposições de cor","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ColorOverlayResponse"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"put":{"operationId":"definirSobreposicaoCor","summary":"Definir sobreposição de cor em uma ocorrência","description":"Cria ou atualiza uma sobreposição de cor para uma ocorrência específica\nde um evento recorrente.\n\n- `scope: at` — aplica cor nesta ocorrência exata\n- `scope: from` — aplica cor desta ocorrência em diante\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["scope","slot","color"],"properties":{"scope":{"type":"string","enum":["at","from"]},"slot":{"type":"string","format":"date-time","description":"Slot da ocorrência (ISO 8601)"},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}}}}}},"responses":{"200":{"description":"Sobreposição definida","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ColorOverlayResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"operationId":"limparSobreposicoesCor","summary":"Limpar todas as sobreposições de cor de um evento","description":"Remove todas as sobreposições de cor de um evento master recorrente.\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"description":"Sobreposições removidas","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-visibility":{"get":{"operationId":"listarVisibilidadeCalendarios","summary":"Listar preferências de visibilidade de calendários","description":"Retorna as preferências de visibilidade de calendários para os membros\ndo escritório.\n\nRequer o escopo `calendar:read`.\n","tags":["Calendario"],"responses":{"200":{"description":"Lista de preferências de visibilidade","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CalendarVisibilityResponse"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/calendar-visibility/{calendarId}":{"put":{"operationId":"definirVisibilidadeCalendario","summary":"Definir visibilidade de um calendário para um membro","description":"Define se um calendário é visível na barra lateral para um membro específico.\n\nRequer o escopo `calendar:write`.\n","tags":["Calendario"],"parameters":[{"name":"calendarId","in":"path","required":true,"description":"ID do calendário","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_id","is_visible"],"properties":{"user_id":{"type":"string","format":"uuid"},"is_visible":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Visibilidade definida","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CalendarVisibilityResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/google-calendar":{"get":{"operationId":"statusIntegracaoGoogleCalendar","summary":"Status da integração Google Calendar","description":"Retorna o status da integração com Google Calendar do escritório:\nse está ativa, conectada, e quantas assinaturas de calendário existem.\n\nRequer o escopo `integrations:read`.\n","tags":["IntegracoesGoogleCalendar"],"responses":{"200":{"description":"Status da integração","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GoogleCalendarStatusResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/google-calendar/subscriptions":{"get":{"operationId":"listarAssinaturasGoogleCalendar","summary":"Listar assinaturas de calendários Google","description":"Retorna as assinaturas de calendários do Google configuradas para o escritório.\nCada assinatura representa um calendário Google sincronizado.\n\nRequer o escopo `integrations:read`.\n","tags":["IntegracoesGoogleCalendar"],"responses":{"200":{"description":"Lista de assinaturas","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/GoogleCalendarSubscriptionResponse"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/google/drive/status":{"get":{"operationId":"statusIntegracaoGoogleDrive","summary":"Status da integração Google Drive","description":"Retorna o status da conexão Google Drive do escritório (conectado, ativo, e-mail,\npasta padrão, flags de salvamento automático — reservadas, ainda sem efeito).\nRequer o escopo `integrations:read`.\n","tags":["IntegracoesGoogleDrive"],"responses":{"200":{"description":"Status da integração","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GoogleDriveStatusResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/google/drive/files":{"post":{"operationId":"salvarArquivoGoogleDrive","summary":"Salvar um arquivo interno no Google Drive","description":"Salva um arquivo do ChatJurídico (por `sign_document_id`, `document_id`,\n`media_url`, `r2_key` ou `message_id`) no Google Drive do escritório e registra a\nreferência interna. A origem deve ser um arquivo interno do ChatJurídico — não uma\nURL arbitrária. Contratos e procurações (ids de listarAssinaturas / buscarDocumento)\nvão em `sign_document_id`; salvar de novo a mesma origem devolve a mesma referência.\nLimite de 15 MiB por arquivo (download + upload acontecem na mesma requisição);\nacima disso a chamada retorna 400 `invalid_source`.\nRequer o escopo `integrations:write`.\n","tags":["IntegracoesGoogleDrive"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveGoogleDriveFileRequest"}}}},"responses":{"200":{"description":"Arquivo salvo","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GoogleDriveFileResponse"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"description":"Conexão precisa ser refeita (reauth_required)"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"operationId":"listarArquivosGoogleDrive","summary":"Listar arquivos salvos no Google Drive","description":"Lista as referências de arquivos salvos no Google Drive do escritório, com\npaginação por cursor e filtros opcionais. Requer o escopo `integrations:read`.\n","tags":["IntegracoesGoogleDrive"],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"contact_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"document_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"chat_id","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Lista de arquivos","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/GoogleDriveFileResponse"}},"pagination":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/google/drive/files/{id}":{"get":{"operationId":"obterArquivoGoogleDrive","summary":"Obter uma referência de arquivo do Google Drive","description":"Retorna uma referência de arquivo salva. Requer o escopo `integrations:read`.","tags":["IntegracoesGoogleDrive"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Referência do arquivo","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GoogleDriveFileResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/google/drive/folders":{"post":{"operationId":"criarPastaGoogleDrive","summary":"Criar/garantir uma pasta no Google Drive","description":"Cria (ou retorna, se já existir) uma pasta no Google Drive do escritório — por\nexemplo a pasta de um contato/caso. Idempotente: pedir a mesma pasta de novo\ndevolve a mesma. Requer o escopo `integrations:write`; chaves restritas a números\nde WhatsApp não podem usar o Google Drive.\n- `kind=contact|case`: a pasta é identificada pelo `scope_ref` (id do contato ou do\n  caso), que é obrigatório.\n- `kind=custom` (padrão): a pasta é identificada pela pasta-mãe + nome (sem\n  diferenciar maiúsculas/acentuação composta/espaços extras). \"Contratos\" e\n  \"Procurações\" são duas pastas; `scope_ref` é ignorado e a resposta traz o\n  identificador derivado.\n","tags":["IntegracoesGoogleDrive"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255},"parent_id":{"type":"string","description":"Pasta-mãe: a pasta padrão do escritório ou uma pasta criada pelo ChatJurídico (senão 400 `unknown_folder`). Omitido = pasta padrão."},"kind":{"type":"string","enum":["contact","case","custom"],"default":"custom"},"scope_ref":{"type":"string","description":"Obrigatório para kind=contact|case (id do contato ou do caso). Ignorado para custom."}}}}}},"responses":{"200":{"description":"Pasta","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GoogleDriveFolderResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/integrations/google/drive/settings":{"patch":{"operationId":"atualizarConfigGoogleDrive","summary":"Atualizar configurações do Google Drive","description":"Atualiza a pasta padrão e as flags de salvamento automático do escritório.\n`auto_save_documents` / `auto_save_media` são RESERVADAS: são gravadas, mas\nainda nada salva automaticamente no Drive (sem efeito por enquanto).\nRequer o escopo `integrations:write` e, por definir onde os arquivos do escritório\nvão parar, uma chave criada por um administrador do escritório (403 `not_authorized`\ncaso contrário).\n","tags":["IntegracoesGoogleDrive"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"default_folder_id":{"type":"string"},"default_folder_name":{"type":"string"},"auto_save_documents":{"type":"boolean","description":"Reservada — ainda sem efeito."},"auto_save_media":{"type":"boolean","description":"Reservada — ainda sem efeito."}}}}}},"responses":{"200":{"description":"Configuração atualizada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GoogleDriveStatusResponse"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/partner-invites":{"get":{"operationId":"listarConvitesParceiro","summary":"Listar convites de teste do escritório","description":"Os convites de teste criados por ESTE escritório parceiro, do mais\nrecente para o mais antigo, já com o desfecho de cada um: `status`\n(`active`, `used`, `expired`), quem criou e — quando o convidado já se\ncadastrou — o escritório que nasceu do convite, com quantos membros e\nnúmeros ele tem.\n\nSó enxerga os convites do próprio escritório; os convites feitos pela\nequipe do Chat Jurídico não aparecem aqui. Requer `partner_invites:read`\ne o módulo habilitado para o escritório.\n","tags":["ConvitesParceiro"],"responses":{"200":{"description":"Convites do escritório","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PartnerInvite"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarConviteParceiro","summary":"Criar convite de teste","description":"Cria o convite de teste de 30 dias em uma chamada: cadastro do\nconvidado, assinatura de teste e link de cadastro.\n\nVocê informa apenas QUEM está sendo convidado. Plano, valor, ciclo e\nduração do teste são definidos pelo servidor e não podem ser enviados no\ncorpo. `expires_in_days` controla só a validade do LINK (default 7,\nmáximo 30) — não a duração do teste.\n\nO `signup_url` da resposta é o que você entrega ao convidado. Para mandar\npelo WhatsApp do próprio escritório, use `enviar_para_numero` /\n`enviarMensagem` com esse link.\n\nRecusa e-mail que já tenha cadastro na plataforma (409): o teste não pode\nser pendurado no cadastro de outra pessoa. Requer `partner_invites:write`\ne o módulo habilitado para o escritório.\n","tags":["ConvitesParceiro"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePartnerInviteRequest"}}}},"responses":{"201":{"description":"Convite criado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerInviteCreated"}}}}}},"400":{"description":"E-mail ou WhatsApp do convidado inválido","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Já existe cadastro com este e-mail (`email_already_registered`) ou a\npessoa já tem conta na plataforma (`email_has_account`).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"description":"Plano de teste indisponível no catálogo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/partner-invites/{token}":{"get":{"operationId":"buscarConviteParceiro","summary":"Consultar um convite de teste","description":"Um convite deste escritório, com o mesmo conteúdo da listagem. Convite de\noutro escritório responde 404. Requer `partner_invites:read`.\n","tags":["ConvitesParceiro"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Token do convite (o trecho final do `signup_url`)"}],"responses":{"200":{"description":"Convite encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerInvite"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarConviteParceiro","summary":"Editar ou revogar um convite de teste","description":"Muda a anotação interna (`description`), estende a validade do link\n(`expires_in_days`) ou revoga o convite (`revoke: true`).\n\nRevogar EXPIRA o convite, não o apaga: o histórico de quem convidou quem\ncontinua na listagem, e o link deixa de valer na hora. Um convite já\nusado não volta atrás — a conta do convidado já existe.\n\nOs dados do convidado (nome, e-mail) não são editáveis aqui de propósito:\nrepontar um convite existente para outra pessoa transformaria o link num\ninstrumento de terceiros. Para corrigir o cadastro, crie outro convite.\n\nA chave age como o usuário que a criou (que precisa ser membro do\nescritório) e, como na tela, só edita os convites que ELE criou — admin\ndo escritório edita qualquer um. Convite de colega responde 404.\nRequer `partner_invites:write`.\n","tags":["ConvitesParceiro"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Token do convite"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePartnerInviteRequest"}}}},"responses":{"200":{"description":"Convite atualizado","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PartnerInvite"}}}}}},"400":{"description":"Nada para atualizar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/v1/flows":{"get":{"operationId":"listarFormularios","summary":"Listar formulários do WhatsApp","description":"Lista paginada (cursor) dos formulários do WhatsApp (Meta Flows) do\nescritório, do mais novo para o mais antigo, SEM o JSON do formulário\n(use `obterFormulario` para ele). Chaves restritas a números só veem os\nformulários desses números. Requer `flows:read`.\n","tags":["Formularios"],"parameters":[{"$ref":"#/components/parameters/LimitParam"},{"$ref":"#/components/parameters/CursorParam"},{"name":"instance_id","in":"query","required":false,"description":"Filtrar por número de WhatsApp (UUID da instância Meta)","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Filtrar por status do formulário","schema":{"type":"string","enum":["DRAFT","PUBLISHED","DEPRECATED","BLOCKED","THROTTLED"]}},{"name":"search","in":"query","required":false,"description":"Trecho do nome do formulário (sem diferenciar maiúsculas)","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"Página de formulários","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedFlowList"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"post":{"operationId":"criarFormulario","summary":"Criar formulário do WhatsApp","description":"Cria o formulário na Meta (status `DRAFT`) no número informado e devolve\no formulário completo. Se `flow_json` vier no corpo, ele é enviado à Meta\nna mesma chamada e os erros de validação da Meta voltam em\n`validation_errors` (o formulário é criado mesmo assim).\n\nSó funciona em número da API oficial da Meta (WhatsApp Cloud API) com\nconta comercial (WABA) configurada. O nome é único por número (409\n`name_taken`). Requer `flows:write`. Com o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\n\nCom `X-Idempotency-Key`, um resultado incerto (504 `outcome_unknown`)\nfica preso à chave: repetir com a mesma chave devolve o mesmo 504 em vez\nde criar um segundo formulário — consulte `listarFormularios` antes de\ntentar de novo com uma chave nova.\n","tags":["Formularios"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFlowRequest"}}}},"responses":{"201":{"description":"Formulário criado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/send":{"post":{"operationId":"enviarFormulario","summary":"Enviar formulário para uma conversa","description":"Envia um formulário `PUBLISHED` (ou `THROTTLED`, sujeito ao limite da\nMeta) para o contato de uma conversa. O botão (`flow_cta`) abre o\nformulário no WhatsApp; a resposta chega como mensagem na conversa e\naparece em `listarRespostasFormulario`. Formulário `BLOCKED` responde 409\n`flow_blocked`.\n\nA conversa precisa ser do MESMO número do formulário (409\n`instance_mismatch`) e a janela de atendimento de 24h precisa estar\naberta (409 `service_window_closed` — mensagem livre; reabra com um\ntemplate). Não funciona em grupos. A mensagem é ENTREGUE AO CLIENTE e\nnão pode ser desfeita. Conta no limite de envio de mensagens. Requer\n`flows:write`.\n\n**Idempotência.** Envie sempre `X-Idempotency-Key` (até 256 caracteres;\nacima disso, 422 `validation_error`). No envio de formulários a chave é\nPERMANENTE por escritório, não vale só por 24 h: repetir a mesma chave\ncom o mesmo conteúdo nunca reenvia — devolve o envio original com\n`duplicate: true`, ou 409 `send_in_progress` enquanto o primeiro envio\nainda está em andamento. A mesma chave com outro formulário, botão,\ntexto ou conversa responde 409 `idempotency_conflict` (nas primeiras\n24 h, com outro corpo, 422 `idempotency_key_reused`).\n\nDepois de uma recusa da Meta no envio — 429 `flow_throttled`, 409\n`service_window_closed` vindo da Meta, 422 `recipient_unreachable` ou\n502 `send_failed` — a chave está gasta: a MESMA chave passa a responder\n409 `previous_attempt_failed`. Para tentar de novo é preciso uma chave\nNOVA. Já 429 `meta_rate_limited` (e `rate_limit_exceeded`) NÃO gasta a\nchave: nada foi enviado e a mesma chave pode ser repetida depois do\n`Retry-After`.\n\nQuando não dá para saber se a mensagem saiu, a resposta é 504\n`outcome_unknown` e a mesma chave repete o 504: NÃO repita\nautomaticamente — confira a conversa e, para reenviar, use uma chave\nnova.\n","tags":["Formularios"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendFlowRequest"}}}},"responses":{"202":{"description":"Envio aceito","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendFlowResponse"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"get":{"operationId":"obterFormulario","summary":"Obter formulário do WhatsApp","description":"Um formulário com o JSON completo (`flow_json`). Formulário de outro\nescritório, ou de número fora do alcance da chave, responde 404.\nRequer `flows:read`.\n","tags":["Formularios"],"responses":{"200":{"description":"Formulário","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}},"patch":{"operationId":"atualizarFormulario","summary":"Renomear, recategorizar ou definir onde salvar as respostas","description":"Atualiza `name` (só em `DRAFT`) e/ou `categories` (qualquer status,\nmenos `DEPRECATED`), sincronizando com a Meta, e/ou `contact_mapping`\n(em qualquer status, inclusive `DEPRECATED`: fica só no Chat Jurídico,\nnada vai para a Meta). Requer `flows:write`.\nCom o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\nUm 504 `outcome_unknown` significa que não se sabe se a Meta aplicou a\nmudança: consulte o formulário antes de repetir.\n","tags":["Formularios"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFlowRequest"}}}},"responses":{"200":{"description":"Formulário atualizado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}},"delete":{"operationId":"excluirFormulario","summary":"Excluir formulário em rascunho","description":"Exclui um formulário `DRAFT` na Meta e no Chat Jurídico (permanente).\nFormulário publicado não pode ser excluído — descontinue-o\n(`deprecarFormulario`). Requer `flows:publish`. Com o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\nUm 504 `outcome_unknown` significa que não se sabe se a Meta excluiu o\nformulário: consulte-o antes de repetir.\n","tags":["Formularios"],"responses":{"200":{"description":"Formulário excluído","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true},"id":{"type":"string","format":"uuid"}}}}}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}/json":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"put":{"operationId":"uploadJsonFormulario","summary":"Enviar o JSON do formulário (rascunho)","description":"Substitui o Flow JSON de um formulário `DRAFT` e o envia à Meta.\nResponde 200 MESMO quando a Meta aponta erros: eles voltam em\n`validation_errors`, e o formulário só pode ser publicado com a lista\nvazia. Limite: 256 KB (bytes UTF-8 do JSON compacto), com um array\n`screens` não vazio. Um JSON sem exatamente uma tela de entrada responde\n422 `entry_screen_ambiguous`. Requer `flows:write`. Com o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\nUm 504 `outcome_unknown` significa que não se sabe se a Meta recebeu o\nJSON: consulte o formulário antes de repetir.\n","tags":["Formularios"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadFlowJsonRequest"}}}},"responses":{"200":{"description":"JSON enviado (veja `validation_errors`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}/publish":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"post":{"operationId":"publicarFormulario","summary":"Publicar formulário","description":"Publica na Meta um formulário `DRAFT` (passa a `PUBLISHED`).\nIRREVERSÍVEL: um formulário publicado não volta a rascunho nem pode\nter o JSON editado — para mudar, use `deployFormulario`. Recusa com 422\n(`flow_has_validation_errors` / `flow_json_missing`) se a Meta ainda\naponta erros ou se o JSON não foi enviado. Requer `flows:publish`. Com o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\nAceita `X-Idempotency-Key` (504 `outcome_unknown` fica preso à chave).\n","tags":["Formularios"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Formulário publicado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}/deprecate":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"post":{"operationId":"deprecarFormulario","summary":"Descontinuar formulário","description":"Descontinua na Meta um formulário `PUBLISHED`, `THROTTLED` ou `BLOCKED`\n(passa a `DEPRECATED`): ele deixa de poder ser enviado, e automações e\nenvios que o usam passam a falhar. IRREVERSÍVEL. Requer `flows:publish`.\nCom o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\nCom uma implantação do mesmo formulário em andamento responde 409\n`deploy_in_progress` (aguarde o `Retry-After` e tente de novo).\n","tags":["Formularios"],"responses":{"200":{"description":"Formulário descontinuado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}/deploy":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"post":{"operationId":"deployFormulario","summary":"Implantar JSON do formulário (publicar versão nova)","description":"Publica o `flow_json` informado em uma chamada:\n- `DRAFT`: envia o JSON e publica.\n- `PUBLISHED` com JSON diferente: envia o JSON novo para o mesmo\n  formulário na Meta, valida e publica. A nova versão vale para os\n  próximos envios; formulários já enviados continuam abrindo a versão\n  com que foram enviados. Nem o `id` nem o `meta_flow_id` mudam.\n  Se a Meta não aceitar a edição do formulário publicado, a\n  implantação publica uma cópia e troca o `meta_flow_id` (o `id`\n  continua o mesmo).\n- JSON idêntico ao publicado: nada é feito.\nSe a Meta apontar erros no JSON, nada é publicado (422). Outra\nimplantação do mesmo formulário em andamento responde 409\n`deploy_in_progress`. Requer `flows:publish`. Com o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\nAceita `X-Idempotency-Key` (504 `outcome_unknown` fica preso à chave).\nA resposta traz `deploy: {action, warnings[]}`, com `action` igual a\n`no_change` (JSON idêntico), `published` (rascunho publicado),\n`updated` (versão nova no ar, no mesmo formulário da Meta) ou\n`cloned` (versão nova no ar, em uma cópia).\n","tags":["Formularios"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeployFlowRequest"}}}},"responses":{"200":{"description":"Formulário implantado (ou sem mudança)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}/duplicate":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"post":{"operationId":"duplicarFormulario","summary":"Duplicar formulário","description":"Cria um novo formulário `DRAFT` no mesmo número, com o JSON do\noriginal e o nome `\"<nome> (cópia)\"` (`(cópia 2)`, `(cópia 3)`… se já\nexistir). Funciona com formulário em qualquer status. Requer\n`flows:write`. Com o login de um usuário (token do app) exige administrador do escritório (403 `admin_required`).\nAceita `X-Idempotency-Key` (504 `outcome_unknown` fica preso à chave).\n","tags":["Formularios"],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"201":{"description":"Cópia criada","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlowDetailEnvelope"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}/preview":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"get":{"operationId":"previewFormulario","summary":"Link de pré-visualização do formulário","description":"Link temporário da Meta para ver o formulário como o cliente verá\n(`preview_url`, válido até `expires_at`). Não envia nada a ninguém.\nRequer `flows:read`.\n","tags":["Formularios"],"responses":{"200":{"description":"Link de pré-visualização","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FlowPreview"}}}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"409":{"$ref":"#/components/responses/FlowConflict"},"422":{"$ref":"#/components/responses/FlowUnprocessable"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"502":{"$ref":"#/components/responses/FlowUpstreamError"},"503":{"$ref":"#/components/responses/FlowUpstreamUnavailable"},"504":{"$ref":"#/components/responses/FlowUpstreamTimeout"}}}},"/v1/flows/{flowId}/responses":{"parameters":[{"$ref":"#/components/parameters/FlowIdParam"}],"get":{"operationId":"listarRespostasFormulario","summary":"Listar respostas do formulário","description":"Respostas recebidas para formulários ENVIADOS pelo Chat Jurídico\n(paginação por cursor, das mais novas para as mais antigas). Contém\ndados pessoais preenchidos pelo cliente. Requer `flows:read`.\n\nNo máximo 100 respostas por página (um `limit` maior é reduzido a 100),\ne a página é cortada antes quando passa de ~4 MB: nesse caso vêm menos\nitens que o `limit`, com `has_more: true` e um `next_cursor` válido.\n","tags":["Formularios"],"parameters":[{"name":"limit","in":"query","required":false,"description":"Número máximo de respostas por página (máximo 100)","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"$ref":"#/components/parameters/CursorParam"}],"responses":{"200":{"description":"Página de respostas","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedFlowResponseList"}}}},"400":{"$ref":"#/components/responses/FlowBadRequest"},"401":{"$ref":"#/components/responses/FlowsUnauthorized"},"403":{"$ref":"#/components/responses/FlowsForbidden"},"404":{"$ref":"#/components/responses/FlowNotFound"},"429":{"$ref":"#/components/responses/FlowRateLimited"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Chave de API para endpoints públicos (formato `cj_live_...`)"},"BearerAuth":{"type":"http","scheme":"bearer","description":"Token JWT do Supabase para endpoints administrativos"}},"parameters":{"ContactStatus":{"name":"contact_status","in":"query","description":"Status do contato: lead (is_customer=false), customer (true), unclassified (null). Omitir para todos.","schema":{"type":"string","enum":["lead","customer","unclassified"]}},"ResourceId":{"name":"id","in":"path","required":true,"description":"Identificador único do recurso (UUID)","schema":{"type":"string","format":"uuid"}},"IdempotencyKey":{"name":"X-Idempotency-Key","in":"header","required":false,"description":"Chave de idempotência para requisições de mutação (POST, PUT, PATCH, DELETE).\nSe a mesma chave for enviada novamente dentro de 24 horas, a resposta original é retornada\nsem reprocessar a operação. Recomendado para evitar duplicações em caso de timeout ou retry.\n","schema":{"type":"string","maxLength":256}},"RequestId":{"name":"X-Request-Id","in":"header","required":false,"description":"Identificador de correlação para rastreamento da requisição. Se não fornecido, um UUID é\ngerado automaticamente. Retornado no header `X-Request-Id` da resposta para facilitar debugging.\n","schema":{"type":"string","format":"uuid"}},"AnalyticsStartDate":{"name":"start_date","in":"query","required":false,"description":"Data/hora de início do período (ISO 8601). Padrão: 7 dias atrás.","schema":{"type":"string","format":"date-time"}},"AnalyticsEndDate":{"name":"end_date","in":"query","required":false,"description":"Data/hora de fim do período (ISO 8601). Padrão: agora.","schema":{"type":"string","format":"date-time"}},"AnalyticsPeriod":{"name":"period","in":"query","required":false,"description":"Atalho de período (sobrescreve start_date/end_date)","schema":{"type":"string","enum":["week","month","quarter","year"]}},"AnalyticsInstanceId":{"name":"instance_id","in":"query","required":false,"description":"Filtrar por número de WhatsApp (UUID)","schema":{"type":"string","format":"uuid"}},"LimitParam":{"name":"limit","in":"query","required":false,"description":"Número máximo de resultados por página","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},"CursorParam":{"name":"cursor","in":"query","required":false,"description":"Cursor para buscar a próxima página (valor de `pagination.next_cursor`)","schema":{"type":"string","format":"uuid"}},"FlowIdParam":{"name":"flowId","in":"path","required":true,"description":"Identificador único do formulário (UUID)","schema":{"type":"string","format":"uuid"}}},"responses":{"Unauthorized":{"description":"Autenticação ausente ou inválida","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unauthorized","message":"Chave de API ausente ou inválida","status":401}}}}},"Forbidden":{"description":"Permissão insuficiente — o escopo da chave não permite esta operação","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"forbidden","message":"Esta chave não possui o escopo necessário para esta operação","status":403}}}}},"NotFound":{"description":"Recurso não encontrado","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Recurso não encontrado","status":404}}}}},"ValidationError":{"description":"Erro de validação nos dados enviados","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"validation_error","message":"O campo 'phone' é obrigatório","status":422}}}}},"RateLimited":{"description":"Limite de requisições excedido — aguarde antes de tentar novamente","headers":{"X-RateLimit-Limit":{"description":"Limite de requisições por minuto","schema":{"type":"integer","example":100}},"X-RateLimit-Remaining":{"description":"Requisições restantes na janela atual","schema":{"type":"integer","example":0}},"X-RateLimit-Reset":{"description":"Unix timestamp do fim da janela atual","schema":{"type":"integer","example":1712345700}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limit_exceeded","message":"Limite de 100 requisições por minuto excedido. Tente novamente em alguns segundos.","status":429}}}}},"Conflict":{"description":"Conflito com o estado atual do recurso. O campo `error.code` identifica\no motivo específico:\n  - `contact_bound_to_other_instance`: o `contact_id` informado pertence a\n    **OUTRO número** de WhatsApp do escritório. Um contato está vinculado a\n    um único número; a mesma pessoa tem um contato por número. Use o contato\n    do número desejado — a mensagem de erro traz o `contact_id` irmão quando\n    ele existe. **NÃO** reaproveite a conversa já existente: ela sai pelo\n    outro número.\n  - `duplicate_resource` (contatos): já existe um contato com esse telefone\n    **neste** número — este é o código retornado no caso comum por\n    `POST /v1/contacts`. O mesmo telefone pode existir em um número irmão sem\n    conflito.\n  - `contact_exists_on_instance`: variante do anterior, emitida apenas na\n    corrida em que a verificação prévia passa mas a constraint única dispara.\n    Trate-a como equivalente a `duplicate_resource` (contato já existe neste número).\n  - `conversation_exists_other_instance`: a conversa existe, mas em um número\n    **fora do escopo** da chave de API. Use uma chave com escopo mais amplo.\n  - `instance_mismatch`: no envio, o `instance_id` informado não é o número da\n    conversa. **Nada foi enviado** — a mensagem seria entregue pelo número\n    errado. Use a conversa do número pretendido.\n  - `service_window_closed`: janela de 24h (Meta) fechada; envie um template\n    aprovado.\n  - `duplicate_resource`: já existe um recurso com a mesma identidade.\n  - `TEMPLATE_ON_META` (templates): o template já está na Meta (enviado, aprovado,\n    rejeitado, pausado...). A API só exclui rascunhos e templates que a Meta já\n    excluiu; exclua pelo Chat Jurídico em Configurações > WhatsApp, que o remove\n    também da sua conta do WhatsApp.\n  - `SYSTEM_TEMPLATE` (templates): template reservado do Chat Jurídico (por exemplo\n    `cj_resumo_qualificacao`, usado pelo resumo automático). Não pode ser excluído\n    na Meta.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"contact_bound_to_other_instance","message":"This contact belongs to WhatsApp number <uuid-a>, not the requested <uuid-b>. A contact is bound to a single number; the same person has a separate contact per number.","status":409}}}}},"BadRequest":{"description":"Requisição malformada ou parâmetros inválidos","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"bad_request","message":"Requisição malformada — verifique os parâmetros enviados","status":400}}}}},"AutomationActivationFloor":{"description":"O banco recusou ativar a automação (ou mantê-la ativa) pela trava de ativação. `error.code` é `automation_activation_floor` e `error.message` é a frase do banco (pt-BR) dizendo o que corrigir. Corrija o fluxo ou salve desativada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"automation_activation_floor","message":"Esta automação não pode ser ativada porque os dias e horários do gatilho nunca deixam a automação rodar (só dentro, sem nenhum horário). Adicione um horário, troque o modo ou desligue a janela.","status":422}}}}},"UnprocessableEntity":{"description":"Dados válidos sintaticamente mas que violam regras de negócio","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unprocessable_entity","message":"Não foi possível processar — verifique as regras de negócio","status":422}}}}},"FlowBadRequest":{"description":"Requisição malformada. `error.code`:\n  - `bad_request`: o corpo não é JSON válido, ou não há nada para atualizar.\n  - `invalid_cursor`: use o `pagination.next_cursor` da página anterior.\n  - `invalid_name` / `invalid_categories` / `invalid_flow_json` /\n    `invalid_flow_cta` / `invalid_body_text` /\n    `invalid_prepopulation_data`: recusa do serviço de envio, repassada\n    como veio, para um valor que passou pela validação desta API (raro;\n    a regra é a mesma do 422 `validation_error`).\n  - `endpoint_uri_not_supported`: formulários com endpoint ainda não são suportados.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"bad_request","message":"Invalid JSON in request body","status":400}}}}},"FlowsUnauthorized":{"description":"Autenticação ausente ou inválida (`unauthorized`): chave de API ausente\nou inválida, ou token de usuário inválido. As rotas de formulários só\naceitam o token de um usuário (login do app) quando a chamada vem do\nproxy do próprio Chat Jurídico, que envia o cabeçalho `X-Internal-Key`;\num token de usuário sem esse cabeçalho responde 401. Chaves de API\n(`X-API-Key`) não precisam dele.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unauthorized","message":"Não autorizado.","status":401}}}}},"FlowsForbidden":{"description":"Acesso negado. `error.code`:\n  - `insufficient_scope`: a chave não tem o escopo da rota — `flows:read`\n    (consultas), `flows:write` (criar, editar, enviar JSON, duplicar,\n    enviar) ou `flows:publish` (publicar, implantar, descontinuar, excluir).\n  - `feature_disabled`: os Formulários do WhatsApp não estão habilitados\n    para este escritório (liberação feita pela equipe do Chat Jurídico).\n  - `admin_required`: com o login de um usuário (token do app), toda\n    escrita menos o envio — criar, editar, enviar JSON, duplicar,\n    publicar, implantar, descontinuar e excluir — exige administrador\n    do escritório; membros comuns recebem 403. Chaves de API são\n    julgadas só pelos escopos.\n  - `instance_restricted`: o número está banido ou restrito pela Meta e\n    não pode enviar (nada foi enviado).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"feature_disabled","message":"Formulários do WhatsApp não estão habilitados para este escritório.","status":403}}}}},"FlowNotFound":{"description":"Não encontrado. `error.code`:\n  - `not_found`: formulário inexistente, de outro escritório ou de um\n    número fora do alcance da chave.\n  - `conversation_not_found`: conversa inexistente ou fora do alcance da chave.\n  - `instance_not_found`: número de WhatsApp inexistente, excluído ou\n    fora do alcance da chave.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Formulário não encontrado.","status":404}}}}},"FlowConflict":{"description":"O estado atual não permite a operação. `error.code`:\n  - `invalid_flow_status`: status errado para a ação. Renomear, enviar\n    JSON, publicar e excluir: só `DRAFT`. Categorias: qualquer status\n    menos `DEPRECATED`. Implantar: `DRAFT` ou `PUBLISHED`. Descontinuar:\n    `PUBLISHED`, `THROTTLED` ou `BLOCKED`.\n  - `flow_not_published`: só formulários `PUBLISHED` ou `THROTTLED` podem ser enviados.\n  - `flow_blocked`: o formulário está bloqueado pela Meta. Nada foi enviado.\n  - `flow_not_on_meta`: o formulário ainda não existe na Meta.\n  - `instance_mismatch`: a conversa é de OUTRO número que o formulário.\n    Nada foi enviado.\n  - `service_window_closed`: janela de atendimento de 24h fechada. Nada\n    foi enviado. Se a recusa veio da Meta no envio, a\n    `X-Idempotency-Key` usada está gasta (a mesma chave passa a\n    responder `previous_attempt_failed`): depois que o contato\n    responder, envie com uma chave nova.\n  - `group_unsupported`: formulários não vão para grupos.\n  - `name_taken` (também pode vir como `duplicate_resource`): já existe\n    formulário com esse nome neste número ou na conta da Meta (WABA).\n  - `concurrent_update`: o formulário mudou durante a operação; tente de novo.\n  - `deploy_in_progress`: já há uma implantação deste formulário em\n    andamento; aguarde e consulte o formulário. Vale para implantar e\n    para descontinuar.\n  - `send_in_progress`: o primeiro envio com esta `X-Idempotency-Key`\n    ainda está em andamento (`error.details.message_id`,\n    `error.details.retry_after` em segundos). Aguarde e repita a MESMA chave.\n  - `previous_attempt_failed`: o envio anterior com esta chave falhou de\n    forma definitiva (429 `flow_throttled`, 409 `service_window_closed`\n    vindo da Meta, 422 `recipient_unreachable` ou 502 `send_failed`). A\n    mesma chave nunca reenvia: para tentar de novo, use uma chave NOVA.\n  - `idempotency_conflict`: a `X-Idempotency-Key` já foi usada neste\n    escritório em um envio com outro formulário, botão, texto ou\n    conversa. A chave de envio é permanente (não expira em 24 h): use\n    uma chave nova para um envio diferente.\n  - `idempotency_in_progress`: outra requisição com a mesma\n    `X-Idempotency-Key` ainda está sendo processada.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_flow_status","message":"Não é possível publicar um formulário com status PUBLISHED (permitido: DRAFT).","status":409}}}}},"FlowUnprocessable":{"description":"Dados inválidos ou regra do formulário violada. `error.code`:\n  - `validation_error`: corpo ou parâmetro inválido (limites: `flow_cta`\n    1–30 caracteres sem emoji — símbolos de texto como `°`, `©`, `™`,\n    `✓` e `★` são aceitos; `body_text` ≤ 1024; `prepopulation_data` ≤\n    8 KB; `flow_json` ≤ 256 KB, medido em bytes UTF-8 do JSON compacto,\n    com `screens`; `X-Idempotency-Key` ≤ 256 caracteres no envio e em\n    criar / duplicar / publicar / implantar).\n  - `instance_not_meta` / `instance_missing_waba` / `instance_missing_token`:\n    o número não é da API oficial da Meta, não tem WABA ou está sem token\n    (reconecte o número).\n  - `instance_disconnected`: o número está desconectado; nada foi enviado.\n  - `flow_json_missing`: envie o JSON antes de publicar.\n  - `flow_has_validation_errors`: a Meta aponta erros no JSON\n    (`error.details.validation_errors`).\n  - `entry_screen_ambiguous`: o JSON não tem exatamente uma tela de\n    entrada (uma única tela sem navegação de chegada, da qual todas as\n    outras são alcançáveis, e ao menos uma tela final).\n    `error.details` traz `candidates` (telas que disputam a entrada),\n    `unreachable` (telas que ninguém alcança) e `has_terminal`.\n  - `data_exchange_not_supported`: o JSON usa `data_api_version` ou uma ação\n    `data_exchange`; formulários com endpoint ainda não são suportados.\n  - `recipient_unreachable`: a Meta não consegue entregar a este contato\n    (número sem WhatsApp ou que não aceita a mensagem). Nada foi enviado,\n    e a `X-Idempotency-Key` usada está gasta (a mesma chave passa a\n    responder 409 `previous_attempt_failed`): um novo envio precisa de\n    uma chave nova.\n  - `idempotency_key_reused`: a `X-Idempotency-Key` já foi usada com outro corpo.\n  - `invalid_contact_mapping`: um `property_id` de `contact_mapping` não é uma\n    propriedade de contato ativa deste escritório (arquivada, inexistente ou de\n    outro escritório). Nada foi alterado.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"validation_error","message":"Invalid request: flow_cta: Value error, flow_cta must not contain emoji","status":422}}}}},"FlowRateLimited":{"description":"Limite atingido; nada foi feito. Quando o cabeçalho `Retry-After` vem na\nresposta, aguarde esse número de segundos (o mesmo valor vem em\n`error.details.retry_after`). `error.code`:\n  - `rate_limit_exceeded`: limite de requisições desta API para a chave.\n  - `meta_rate_limited`: a Meta limitou as chamadas da conta. No envio,\n    a `X-Idempotency-Key` NÃO fica gasta: a mesma chave pode ser\n    repetida depois do `Retry-After`.\n  - `flow_throttled`: o formulário está com envio limitado pela Meta\n    (`THROTTLED`) e o limite por hora foi atingido. A\n    `X-Idempotency-Key` usada está gasta (a mesma chave passa a\n    responder 409 `previous_attempt_failed`): para tentar de novo, use\n    uma chave nova.\n  - `upstream_rate_limited`: limite do serviço de envio.\n","headers":{"Retry-After":{"description":"Segundos a aguardar antes de tentar de novo (quando informado)","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"meta_rate_limited","message":"Limite da Meta atingido; tente mais tarde.","status":429}}}}},"FlowUpstreamError":{"description":"Falha ao falar com a Meta / serviço de envio. `error.code`:\n  - `upstream_error`: falha genérica do serviço de envio. No envio, e em\n    criar / duplicar / publicar / implantar COM `X-Idempotency-Key`,\n    nada foi aplicado e a mesma chave pode ser repetida. Em uma\n    operação SEM chave (e em editar, enviar JSON, descontinuar e\n    excluir) este código NÃO garante que nada foi aplicado: consulte o\n    formulário antes de repetir.\n  - `send_failed`: a Meta recusou o envio do formulário; nada foi enviado\n    (a mensagem da Meta não é repassada; `error.details.meta_error_code`\n    traz o código quando houver). A `X-Idempotency-Key` usada está gasta\n    (a mesma chave passa a responder 409 `previous_attempt_failed`):\n    para tentar de novo, use uma chave nova.\n  - `meta_error` / `meta_auth_error` / `meta_unavailable`: a Meta recusou\n    uma operação de ciclo de vida (criar, enviar JSON, publicar…), o\n    token do número foi recusado, ou a Meta está fora do ar; nada foi\n    aplicado. Nesses três `error.message` pode trazer o texto da própria\n    Meta.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"upstream_error","message":"Falha ao falar com o serviço da Meta.","status":502}}}}},"FlowUpstreamUnavailable":{"description":"Formulários temporariamente indisponíveis (`upstream_not_configured`): a\nligação com o serviço de envio não está configurada. Nada foi feito.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"upstream_not_configured","message":"Formulários temporariamente indisponíveis.","status":503}}}}},"FlowUpstreamTimeout":{"description":"A Meta / serviço de envio não respondeu a tempo. `error.code`:\n  - `outcome_unknown`: NÃO se sabe se a operação teve efeito. No envio, e\n    em criar / duplicar / publicar / implantar com `X-Idempotency-Key`,\n    a chave fica presa a este resultado e repete este mesmo 504: não\n    repita automaticamente. Confira a conversa (ou o formulário) e, para\n    refazer, use uma chave nova. Em editar, enviar JSON e excluir o\n    código também aparece (a Meta não confirmou a operação): consulte o\n    formulário antes de repetir; se a requisição levou uma\n    `X-Idempotency-Key`, ela repete este 504 por 24 h.\n  - `meta_timeout`: o tempo acabou SEM nada ter sido aplicado na Meta;\n    pode tentar de novo.\n  - `upstream_timeout`: sem resposta numa operação sem chave de\n    idempotência (ou que pode ser repetida sem risco). A operação PODE\n    ter sido concluída: consulte o formulário antes de repetir.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"outcome_unknown","message":"Não foi possível confirmar se o formulário foi enviado.","status":504}}}}},"InternalError":{"description":"Erro interno do servidor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Ocorreu um erro interno. Tente novamente mais tarde.","status":500}}}}}},"schemas":{"DocumentTemplate":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid","nullable":true},"visibility":{"type":"string","enum":["global","office","draft"]},"name":{"type":"string"},"slug":{"type":"string"},"category":{"type":"string","enum":["contract","petition","letter","internal_memo","declaration","power_of_attorney","agreement","opinion"]},"description":{"type":"string","nullable":true},"locale":{"type":"string","default":"pt-BR"},"body_markdown":{"type":"string"},"content_hash":{"type":"string"},"variables":{"type":"array","items":{"type":"object","additionalProperties":true}},"required_variables":{"type":"array","items":{"type":"string"}},"cloned_from_global":{"type":"boolean"},"archived":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"modified_at":{"type":"string","format":"date-time"}}},"CreateDocumentTemplateRequest":{"type":"object","required":["name","category","body_markdown"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"category":{"type":"string","enum":["contract","petition","letter","internal_memo","declaration","power_of_attorney","agreement","opinion"]},"description":{"type":"string"},"locale":{"type":"string","default":"pt-BR"},"body_markdown":{"type":"string"},"variables":{"type":"array","items":{"type":"object","additionalProperties":true}},"required_variables":{"type":"array","items":{"type":"string"}},"parent_template_id":{"type":"string","format":"uuid"}}},"UpdateDocumentTemplateRequest":{"type":"object","properties":{"name":{"type":"string"},"category":{"type":"string","enum":["contract","petition","letter","internal_memo","declaration","power_of_attorney","agreement","opinion"]},"description":{"type":"string"},"body_markdown":{"type":"string"},"variables":{"type":"array","items":{"type":"object","additionalProperties":true}},"required_variables":{"type":"array","items":{"type":"string"}},"archived":{"type":"boolean"}}},"Document":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"template_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","generating","ready","reviewed","sent","awaiting_signature","signed","archived"]},"source":{"type":"string","enum":["workspace","chat","workflow","api"]},"instance_id":{"type":"string","format":"uuid","nullable":true},"chat_id":{"type":"string","format":"uuid","nullable":true},"contact_id":{"type":"string","format":"uuid","nullable":true},"workflow_run_id":{"type":"string","format":"uuid","nullable":true},"created_by_user_id":{"type":"string","format":"uuid","nullable":true},"latest_version":{"type":"integer"},"latest_body_markdown":{"type":"string"},"latest_content_hash":{"type":"string"},"resolved_variables":{"type":"object","additionalProperties":true},"ai_metadata":{"type":"object","additionalProperties":true},"reviewed_at":{"type":"string","format":"date-time","nullable":true},"reviewed_by_user_id":{"type":"string","format":"uuid","nullable":true},"signature_doc_id":{"type":"string","format":"uuid","nullable":true},"archived":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"modified_at":{"type":"string","format":"date-time"}}},"UpdateDocumentRequest":{"type":"object","properties":{"title":{"type":"string"},"body_markdown":{"type":"string"},"resolved_variables":{"type":"object","additionalProperties":true},"status":{"type":"string","enum":["draft","generating","ready","reviewed","sent","awaiting_signature","signed","archived"]},"archived":{"type":"boolean"}}},"DocumentVersionSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"document_id":{"type":"string","format":"uuid"},"version":{"type":"integer"},"content_hash":{"type":"string"},"author_user_id":{"type":"string","format":"uuid","nullable":true},"author_kind":{"type":"string","enum":["user","ai","workflow","system"]},"created_at":{"type":"string","format":"date-time"}}},"DocumentVersion":{"allOf":[{"$ref":"#/components/schemas/DocumentVersionSummary"},{"type":"object","properties":{"body_markdown":{"type":"string"},"resolved_variables":{"type":"object","additionalProperties":true},"ai_metadata":{"type":"object","additionalProperties":true}}}]},"CustomPropertySchema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"scope":{"type":"string","enum":["contact","chat"]},"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,62}$"},"label":{"type":"string"},"description":{"type":"string","nullable":true},"type":{"type":"string","enum":["text","long_text","number","currency","date","datetime","boolean","enum","document","cpf","cnpj","email","phone","url"]},"required":{"type":"boolean"},"options":{"type":"array","nullable":true,"items":{"type":"object","additionalProperties":true}},"default_value":{"nullable":true,"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object"},{"type":"array"}],"description":"Valor padrão da propriedade. O tipo depende de `type`."},"validation":{"type":"object","nullable":true,"additionalProperties":true},"position":{"type":"integer"},"archived":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"modified_at":{"type":"string","format":"date-time"}}},"CreateCustomPropertySchemaRequest":{"type":"object","required":["scope","key","label","type"],"properties":{"scope":{"type":"string","enum":["contact","chat"]},"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,62}$"},"label":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["text","long_text","number","currency","date","datetime","boolean","enum","document","cpf","cnpj","email","phone","url"]},"required":{"type":"boolean","default":false},"options":{"type":"array","items":{"type":"object","additionalProperties":true}},"default_value":{"nullable":true,"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object"},{"type":"array"}],"description":"Valor padrão da propriedade. O tipo aceito depende de `type` (por ex. texto→string, number/currency→número, boolean→booleano, date/datetime→string ISO-8601)."},"validation":{"type":"object","additionalProperties":true},"position":{"type":"integer","default":0}}},"UpdateCustomPropertySchemaRequest":{"type":"object","properties":{"label":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"},"options":{"type":"array","items":{"type":"object","additionalProperties":true}},"default_value":{"nullable":true,"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object"},{"type":"array"}],"description":"Valor padrão da propriedade. O tipo aceito depende de `type` (por ex. texto→string, number/currency→número, boolean→booleano, date/datetime→string ISO-8601)."},"validation":{"type":"object","additionalProperties":true},"position":{"type":"integer"},"archived":{"type":"boolean"}}},"CustomPropertyValue":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"schema_id":{"type":"string","format":"uuid"},"scope":{"type":"string","enum":["contact","chat"]},"entity_id":{"type":"string","format":"uuid"},"value":{"nullable":true,"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"},{"type":"object"},{"type":"array"}],"description":"Valor da propriedade. O tipo depende da definição referenciada."},"created_at":{"type":"string","format":"date-time"},"modified_at":{"type":"string","format":"date-time"}}},"DocumentSettings":{"type":"object","properties":{"office_id":{"type":"string","format":"uuid"},"documents_v2_enabled":{"type":"boolean"},"ai_disclosure_mode":{"type":"string","enum":["footer","cover_page","none_with_internal_audit"]},"require_review_before_export":{"type":"boolean"},"signature_render_path":{"type":"string","enum":["ours","zapsign","either"]},"ai_preferences":{"type":"object","additionalProperties":true},"document_branding":{"type":"object","additionalProperties":true},"has_zapsign_webhook_secret":{"type":"boolean"},"modified_at":{"type":"string","format":"date-time"}}},"UpdateDocumentSettingsRequest":{"type":"object","properties":{"documents_v2_enabled":{"type":"boolean"},"ai_disclosure_mode":{"type":"string","enum":["footer","cover_page","none_with_internal_audit"]},"require_review_before_export":{"type":"boolean"},"signature_render_path":{"type":"string","enum":["ours","zapsign","either"]},"ai_preferences":{"type":"object","additionalProperties":true},"document_branding":{"type":"object","additionalProperties":true}}},"ConversationSearchRequest":{"type":"object","description":"Corpo da requisição para busca avançada de conversas","properties":{"filters":{"type":"array","description":"Lista de filtros a aplicar (AND entre filtros)","items":{"type":"object","required":["field","operator","value"],"properties":{"field":{"type":"string","description":"Nome do campo a filtrar"},"operator":{"type":"string","description":"Operador de comparação","enum":["eq","neq","gt","gte","lt","lte","in","overlaps","ilike","is_null"]},"value":{"description":"Valor para comparação (tipo depende do operador)"}}}},"sort":{"type":"array","description":"Ordenação dos resultados","items":{"type":"object","properties":{"field":{"type":"string","description":"Campo para ordenação (last_message_at, created_at, unread_messages_count)"},"direction":{"type":"string","enum":["asc","desc"],"default":"desc"}}}},"limit":{"type":"integer","minimum":1,"maximum":200,"default":50},"cursor":{"type":"string","format":"uuid"}}},"Contact":{"type":"object","description":"Representa um contato (cliente ou lead) do escritório","properties":{"is_customer":{"type":"boolean","nullable":true,"description":"Status do contato: true=Cliente, false=Lead, null=não classificado. Independente de is_active, tags e etapa do funil."},"id":{"type":"string","format":"uuid","description":"Identificador único do contato"},"name":{"type":"string","nullable":true,"description":"Nome completo do contato","example":"João Silva"},"phone":{"type":"string","nullable":true,"description":"Telefone no formato internacional sem caracteres especiais","example":"5511999998888"},"instance_id":{"type":"string","format":"uuid","nullable":true,"description":"Número de WhatsApp do escritório ao qual este contato pertence. Um contato está vinculado a UM único número (`contacts` é UNIQUE(phone, instance_id)) e tem no máximo UMA conversa. A mesma PESSOA, portanto, tem um contato — e uma conversa — POR NÚMERO do escritório. Use este campo para distinguir cadastros que compartilham nome e telefone.","example":"9f1c2b3a-4d5e-6f70-8192-a3b4c5d6e7f8"},"tags":{"type":"array","items":{"type":"string"},"nullable":true,"description":"Tags associadas ao contato","example":["cliente-vip","cível"]},"stage_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do estágio no funil de atendimento"},"is_active":{"type":"boolean","description":"Se o contato está ativo","example":true},"is_starred":{"type":"boolean","description":"Se o contato está marcado com estrela","example":false},"source":{"type":"string","nullable":true,"description":"Origem do contato (whatsapp, api, manual, etc.)","example":"whatsapp"},"summary":{"type":"string","nullable":true,"description":"Resumo gerado por IA sobre o contato","example":"Cliente buscando orientação sobre divórcio consensual"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"Data de criação"},"updated_at":{"type":"string","format":"date-time","nullable":true,"description":"Data da última atualização"}}},"CreateContact":{"type":"object","description":"Dados para criação de um novo contato.\n\n**Semântica por instância**: contatos são únicos por `(phone, instance_id)`,\nnão por escritório. O mesmo número pode existir como contatos distintos em\ninstâncias diferentes do mesmo escritório (uma WhatsApp Business + uma\nPessoal, por exemplo). A duplicidade é detectada apenas dentro da mesma\ninstância.\n\n**Resolução de `instance_id`**:\n- Se sua chave de API tem escopo em **uma única instância** e você omitir\n  `instance_id`, o contato é criado nessa instância (default transparente).\n- Se sua chave tem escopo em **múltiplas instâncias** e você omitir\n  `instance_id`, a API responde `400 instance_required` — você precisa\n  informar explicitamente em qual instância o contato deve ser criado.\n- Se você informar um `instance_id` **fora do escopo** da sua chave, a API\n  responde `400 instance_out_of_scope`.\n","required":["name","phone"],"properties":{"name":{"type":"string","description":"Nome completo do contato","example":"Maria Oliveira"},"phone":{"type":"string","description":"Telefone no formato internacional (ex. `5511999998888`)","example":"5521988887777"},"instance_id":{"type":"string","format":"uuid","description":"Instância (canal/conexão) à qual o contato deve ser vinculado.\nObrigatório quando a chave de API abrange múltiplas instâncias;\nquando a chave abrange exatamente uma, o default é transparente.\n","example":"14e78509-f996-4b16-870b-eb8f2a14ee1f"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags para categorizar o contato","example":["lead","trabalhista"]},"stage_id":{"type":"string","format":"uuid","description":"ID do estágio no funil"},"source":{"type":"string","description":"Origem do contato. É um vocabulário controlado — use um dos valores aceitos: `whatsapp`, `whatsapp_business_app`, `app`, `api`, `manual`, `import`, `planilha`, `lp_forms`, `chatguru`, `advbox`, `astrea`, `calculo_juridico`, `lead_opportunity`, `outro_numero`, `meta_coexistence`, `summary_forwarding`. Valores fora dessa lista são rejeitados com `422 invalid_field_value` — para rótulos próprios de campanha/automação (ex. `n8n-leads-wpp-chat-jur`) use uma **tag** ou uma propriedade personalizada, não este campo. Default `api`.","default":"api","example":"api"}}},"UpdateContact":{"type":"object","description":"Dados para atualização parcial de um contato — envie apenas os campos que deseja alterar","properties":{"is_customer":{"type":"boolean","nullable":true,"description":"Status do contato: true=Cliente, false=Lead, null=não classificado. Independente de is_active, tags e etapa do funil."},"name":{"type":"string","description":"Nome completo"},"phone":{"type":"string","description":"Telefone no formato internacional"},"tags":{"type":"array","items":{"type":"string"},"description":"Tags (substitui todas as tags atuais)"},"stage_id":{"type":"string","format":"uuid","description":"ID do estágio no funil"},"is_active":{"type":"boolean","description":"Status ativo/inativo"},"is_starred":{"type":"boolean","description":"Marcar/desmarcar com estrela"}}},"BulkCreateContacts":{"type":"object","description":"Lista de contatos para criação em massa (máximo 100 por requisição)","required":["contacts"],"properties":{"contacts":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/CreateContact"},"description":"Array de contatos a serem criados"}}},"BulkCreateContactsResponse":{"type":"object","description":"Resultado da criação em massa de contatos","properties":{"data":{"type":"object","properties":{"created":{"type":"integer","description":"Número de contatos criados com sucesso"},"failed":{"type":"integer","description":"Número de contatos que falharam"},"results":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","description":"Índice do item na lista original"},"status":{"type":"string","enum":["created","error"],"description":"Status do processamento deste item"},"data":{"$ref":"#/components/schemas/Contact"},"error":{"type":"string","description":"Mensagem de erro (apenas quando status é `error`)"},"code":{"type":"string","description":"Código estável do erro do item (apenas quando status é\n`error`). Valores comuns: `instance_required`,\n`instance_out_of_scope`, `duplicate_resource`.\n"}}}}}}}},"BulkDeleteContacts":{"type":"object","description":"Dados para exclusão em massa de contatos","required":["contact_ids"],"properties":{"contact_ids":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string","format":"uuid"},"description":"Ids dos contatos a excluir. Máximo de 100 por requisição; ids\nduplicados são processados uma única vez.\n"}}},"BulkDeleteContactsResponse":{"type":"object","description":"Resultado da exclusão em massa de contatos","properties":{"data":{"type":"object","properties":{"deleted":{"type":"integer","description":"Número de contatos excluídos (soft-delete)"},"not_found":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Ids que não resolveram para um contato ativo do escritório\n(inexistentes ou já excluídos). Não é um erro.\n"},"conversations_archived":{"type":"integer","description":"Número de conversas arquivadas pela exclusão"},"archive_failed":{"type":"boolean","description":"Presente (`true`) apenas quando o arquivamento das conversas\nfalhou; a exclusão dos contatos já foi aplicada e os demais\nefeitos colaterais seguiram normalmente.\n"},"scheduled_cancel_failed":{"type":"boolean","description":"Presente (`true`) apenas quando o cancelamento das mensagens\nagendadas falhou; a exclusão dos contatos já foi aplicada.\n"},"campaign_cancel_failed":{"type":"boolean","description":"Presente (`true`) apenas quando a remoção dos destinatários de\ncampanha pendentes falhou; a exclusão já foi aplicada.\n"},"audit_failed":{"type":"boolean","description":"Presente (`true`) apenas quando o registro de auditoria falhou;\na exclusão já foi aplicada.\n"}}}}},"Conversation":{"type":"object","description":"Representa uma conversa do WhatsApp vinculada a um contato e número","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único da conversa"},"contact_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do contato vinculado"},"instance_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do número de WhatsApp"},"last_message_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora da última mensagem"},"last_message_text":{"type":"string","nullable":true,"description":"Texto da última mensagem","example":"Bom dia, doutor!"},"last_message_type":{"type":"string","nullable":true,"description":"Tipo da última mensagem (text, image, document, etc.)","example":"text"},"is_archived":{"type":"boolean","description":"Se a conversa está arquivada","example":false},"stage_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do estágio no funil"},"unread_count":{"type":"integer","description":"Número de mensagens não lidas","example":2},"tag_ids":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"description":"IDs das tags associadas à conversa","example":["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]},"contact_phone":{"type":"string","nullable":true,"description":"Telefone do CLIENTE. É idêntico em todas as conversas que essa pessoa tem com o escritório, então NÃO serve para distinguir conversas. Para isso use `instance_id`, que identifica o número DO ESCRITÓRIO que atende esta conversa — e do qual as mensagens saem.","example":"5511999998888"},"phone":{"type":"string","nullable":true,"description":"DEPRECADO — use `contact_phone`. É o telefone do CLIENTE, não o número do escritório que atende a conversa. O número de envio é `instance_id`.","example":"5511999998888"},"accountable_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do membro responsável pela conversa"},"department_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do departamento/fila ao qual a conversa está delegada (null se nenhum)"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"Data de criação da conversa"},"customer_service_window_status":{"type":"string","nullable":true,"enum":["open","expiring","closed"],"description":"Status da janela de atendimento de 24h (Meta apenas). open = janela aberta, expiring = expirando, closed = fechada.\n"},"customer_service_window_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Momento (UTC) em que a janela de atendimento de 24h expira (último inbound + 24h, Meta apenas). Fonte autoritativa para saber se é possível enviar texto livre agora; o campo de status pode ter defasagem de alguns minutos.\n"},"is_waiting_human":{"type":"boolean","description":"`true` quando a conversa está aguardando atendimento humano. É o mesmo campo do filtro `is_waiting_human` em `GET /v1/conversations`.\n","example":false},"agent_is_active":{"type":"boolean","description":"`true` quando o agente de IA está ativo nesta conversa. Consulte antes de pausar ou reativar a IA. É o mesmo campo do filtro `agent_is_active`.\n","example":true},"is_ctwa_origin":{"type":"boolean","description":"`true` se a conversa começou a partir de um anúncio Click-to-WhatsApp (CTWA); `false` para conversas orgânicas.\n","example":true},"tracking":{"type":"object","nullable":true,"description":"Atribuição do anúncio CTWA que originou a conversa — mesmos campos do `data.tracking` do webhook `message.received`, para quem perdeu aquele evento e quer re-obter a atribuição por API. `null` em conversas orgânicas. Campos ausentes são omitidos. (`headline`/`media_type` são nível-mensagem: além do webhook, estão disponíveis por mensagem em `GET /v1/conversations/{id}/messages`, no `tracking` de cada mensagem.) Os valores de exemplo são ilustrativos/sintéticos.\n","properties":{"source_id":{"type":"string","description":"ID da fonte/anúncio (Meta ad id)","example":"000000000000000000"},"source_type":{"type":"string","description":"Tipo da fonte","example":"ad"},"source_url":{"type":"string","description":"URL do criativo/post do anúncio","example":"https://www.instagram.com/p/EXEMPLO0000/"},"ctwa_clid":{"type":"string","description":"Click ID do Click-to-WhatsApp (correlaciona o clique no anúncio)","example":"Af0EXAMPLEctwaClickIdSyntheticValue0000..."}}},"ad_first_contact_at":{"type":"string","format":"date-time","nullable":true,"description":"Momento (UTC) do primeiro contato originado por anúncio."},"free_entry_point_window_open":{"type":"boolean","description":"`true` enquanto a janela gratuita da Meta para anúncios Click-to-WhatsApp (free entry point) está aberta. Ela abre quando o escritório responde em até 24h a uma mensagem vinda do anúncio e dura o que a Meta definir (hoje até 7 dias a partir da resposta; antes eram 72h). Enquanto aberta, TODA mensagem é grátis, inclusive templates de marketing. É só cobrança: não libera texto livre. Texto livre continua exigindo a janela de atendimento de 24h (`customer_service_window_expires_at`); fora dela, envie um template, que sai grátis enquanto este campo for `true`. Meta apenas.\n","example":true},"free_entry_point_window_expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Momento (UTC) em que a janela gratuita do anúncio termina, conforme informado pela própria Meta no webhook de status. Depois dele, cada mensagem volta a ser cobrada. `null` se a janela nunca abriu.\n"},"free_entry_point_reply_deadline":{"type":"string","format":"date-time","nullable":true,"description":"Só para leads de anúncio ainda sem resposta: responda antes deste momento (primeira mensagem do anúncio + 24h) para a Meta abrir a janela gratuita. Se o lead clicou no anúncio pelo app de celular, essa primeira resposta também sai grátis (estimativa: a Meta não abre a janela para cliques pelo WhatsApp Web/computador). `null` quando a janela já abriu, o prazo passou ou a conversa não veio de anúncio.\n"}}},"CreateConversation":{"type":"object","description":"Dados para iniciar uma nova conversa","required":["contact_id","instance_id"],"properties":{"contact_id":{"type":"string","format":"uuid","description":"ID do contato"},"instance_id":{"type":"string","format":"uuid","description":"ID do número de WhatsApp a ser utilizado"}}},"Instance":{"type":"object","description":"Representa um número de WhatsApp conectado ao escritório","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do número — use como `instance_id` nos endpoints de conversas"},"name":{"type":"string","nullable":true,"description":"Nome de exibição do número","example":"Atendimento Principal"},"phone_id":{"type":"string","nullable":true,"description":"Identificador do telefone","example":"5511999998888"},"display_phone_number":{"type":"string","nullable":true,"description":"Número de telefone formatado","example":"+55 11 99999-8888"},"is_connected":{"type":"boolean","description":"Se o número está conectado ao WhatsApp","example":true},"api_provider":{"type":"string","nullable":true,"description":"Provedor da integração (meta, uazapi, waha)","example":"meta"},"created_at":{"type":"string","format":"date-time","description":"Data de criação"}}},"MediaUploadRequest":{"type":"object","required":["file_name","media_type","data"],"description":"Requisição de upload direto de mídia via base64","properties":{"file_name":{"type":"string","description":"Nome do arquivo com extensão (ex. \"foto.jpg\", \"contrato.pdf\")","example":"contrato.pdf"},"media_type":{"type":"string","enum":["image","audio","video","document"],"description":"Tipo de mídia","example":"document"},"data":{"type":"string","format":"byte","description":"Conteúdo do arquivo codificado em base64"}}},"MediaUploadUrlRequest":{"type":"object","required":["file_name","media_type"],"description":"Requisição de URL pré-assinada para upload direto","properties":{"file_name":{"type":"string","description":"Nome do arquivo com extensão","example":"video.mp4"},"media_type":{"type":"string","enum":["image","audio","video","document"],"description":"Tipo de mídia","example":"video"},"content_type":{"type":"string","description":"MIME type do arquivo. Se omitido, é detectado automaticamente pela extensão.","example":"video/mp4"}}},"MediaUploadResponse":{"type":"object","description":"Resposta do upload direto de mídia","properties":{"media_url":{"type":"string","format":"uri","description":"URL pública CDN do arquivo enviado. Use como `file_url` ao enviar mensagem.","example":"https://storage-meta.chatjuridico.com/api-uploads/office123/document/2026/04/07/abc123.pdf"},"media_type":{"type":"string","enum":["image","audio","video","document"],"example":"document"},"content_type":{"type":"string","example":"application/pdf"},"file_size":{"type":"integer","description":"Tamanho do arquivo em bytes","example":234567},"file_name":{"type":"string","example":"contrato.pdf"}}},"MediaUploadUrlResponse":{"type":"object","description":"Resposta com URL pré-assinada para upload direto ao armazenamento","properties":{"upload_url":{"type":"string","format":"uri","description":"URL pré-assinada para upload via PUT. Envie o conteúdo binário do arquivo com o header Content-Type."},"media_url":{"type":"string","format":"uri","description":"URL pública CDN onde o arquivo ficará disponível após o upload. Use como `file_url` ao enviar mensagem."},"content_type":{"type":"string","example":"video/mp4"},"expires_in":{"type":"integer","description":"Tempo de expiração da URL pré-assinada em segundos","example":3600}}},"Message":{"type":"object","description":"Representa uma mensagem em uma conversa do WhatsApp","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único da mensagem"},"conversation_id":{"type":"string","format":"uuid","description":"ID da conversa"},"text":{"type":"string","nullable":true,"description":"Conteúdo textual da mensagem","example":"Bom dia, doutor! Preciso agendar uma consulta."},"type":{"type":"string","enum":["text","image","document","audio","video","sticker","location","contact","template","interactive"],"description":"Tipo do conteúdo da mensagem","example":"text"},"from_me":{"type":"boolean","description":"`true` se enviada pelo escritório, `false` se recebida do contato","example":false},"file_url":{"type":"string","nullable":true,"description":"URL do arquivo de mídia (para tipos image, document, audio, video)"},"file_type":{"type":"string","nullable":true,"description":"MIME type do arquivo (ex. `image/jpeg`, `application/pdf`)"},"file_name":{"type":"string","nullable":true,"description":"Nome original do arquivo"},"file_size":{"type":"integer","nullable":true,"description":"Tamanho do arquivo em bytes"},"mimetype":{"type":"string","nullable":true,"description":"Tipo MIME do arquivo (ex. application/pdf, image/jpeg)"},"audio_transcription":{"type":"string","nullable":true,"description":"Transcrição automática de mensagens de áudio (quando disponível)"},"file_duration_seconds":{"type":"number","format":"float","nullable":true,"description":"Duração em segundos (para áudio e vídeo)"},"status":{"type":"string","enum":["pending","sent","delivered","read","failed","received"],"nullable":true,"description":"Status da mensagem. Mensagens enviadas (`from_me: true`) progridem por `pending` → `sent` → `delivered` → `read` (ou `failed`). Mensagens recebidas do contato (`from_me: false`) vêm sempre com `received`. Pode vir `null` quando o status ainda não foi reportado.","example":"delivered"},"template_name":{"type":"string","nullable":true,"description":"Nome do template utilizado (apenas para mensagens de template)","example":"appointment_reminder"},"template_category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"],"nullable":true,"description":"Categoria do template conforme classificação da Meta (apenas para mensagens de template)","example":"UTILITY"},"interactive":{"type":"object","nullable":true,"description":"Conteúdo interativo (apenas mensagens `interactive` / respostas a menu).\nPara menus enviados: a definição do menu. Para uma resposta recebida a um\nmenu (contato tocou uma opção): a seleção `{ type, id, title }`.\n","example":{"type":"button_reply","id":"sim","title":"Sim"}},"tracking":{"type":"object","nullable":true,"description":"Atribuição do anúncio Click-to-WhatsApp na PRIMEIRA mensagem do lead (mensagem originada do clique). Os mesmos campos do `data.tracking` do webhook `message.received`. `null` para mensagens que não vieram de anúncio. O `referral_data` cru (que também guarda o corpo/mensagem de boas-vindas) NÃO é exposto. Valores de exemplo são ilustrativos.\n","properties":{"source_id":{"type":"string","description":"ID da fonte/anúncio (Meta ad id)","example":"000000000000000000"},"source_type":{"type":"string","description":"Tipo da fonte","example":"ad"},"source_url":{"type":"string","description":"URL do criativo/post do anúncio","example":"https://www.instagram.com/p/EXEMPLO0000/"},"ctwa_clid":{"type":"string","description":"Click ID do Click-to-WhatsApp (correlaciona o clique no anúncio)","example":"Af0EXAMPLEctwaClickIdSyntheticValue0000..."},"headline":{"type":"string","description":"Título/headline do criativo do anúncio (nível-mensagem)","example":"Precisa de ajuda com o seu caso?"},"media_type":{"type":"string","description":"Tipo de mídia do anúncio — ex. image, video (nível-mensagem)","example":"video"}}},"estimated_cost_brl":{"type":"number","format":"float","nullable":true,"description":"Estimativa do custo em BRL desta mensagem pela tabela da Meta para o Brasil (cobrança por mensagem entregue); a fatura da Meta é a referência. Retornado no envio por números Meta: templates pela categoria (marketing R$ 0,3217, utility e authentication R$ 0,035); texto livre (serviço) na tarifa de utility a partir de 01/10/2026. A Meta dá 1.000 mensagens de serviço grátis por número por mês e mede isso do lado dela, então para texto livre o valor é o teto. `0` quando a mensagem cai na janela gratuita de anúncio Click-to-WhatsApp (`free_entry_point: true`). `null` em números não-Meta e quando o envio não saiu (`delivery_status: queue_failed`).\n","example":0.035},"free_entry_point":{"type":"boolean","description":"Só na resposta de envio por números Meta que saiu (não aparece na listagem de mensagens nem quando `delivery_status` é `queue_failed`). Estimativa: `true` quando esta mensagem deve cair na janela gratuita de anúncio Click-to-WhatsApp, já confirmada pela Meta ou aberta por esta primeira resposta ao lead dentro de 24h. A Meta só abre a janela quando o lead clicou no anúncio pelo app de celular, então para a primeira resposta é uma previsão; a fatura da Meta é a referência.\n","example":false},"created_at":{"type":"string","format":"date-time","description":"Data/hora de criação da mensagem"}}},"SendMessage":{"type":"object","description":"Dados para envio de uma nova mensagem via WhatsApp.\n- Para mensagens de texto: `text` é obrigatório.\n- Para mídia: `text`, `type` e `file_url` são obrigatórios.\n- Para templates: `type: \"template\"` e o objeto `template` são obrigatórios; `text` é opcional.\n\nA mensagem sai SEMPRE pelo número da própria conversa. Se o escritório tiver mais de um\nnúmero, informe `instance_id` para afirmar de qual você espera que saia: havendo\ndivergência o envio é recusado (409 `instance_mismatch`) em vez de sair pelo outro número.\n","properties":{"text":{"type":"string","maxLength":4096,"description":"Texto da mensagem (obrigatório para tipos não-template)","example":"Olá! Sua consulta está agendada para amanhã às 10h."},"instance_id":{"type":"string","format":"uuid","nullable":true,"description":"OPCIONAL. Afirma de qual número do escritório esta mensagem DEVE sair. O número de envio é sempre o da própria conversa; este campo não o altera. Se for informado e NÃO corresponder ao da conversa, o envio é RECUSADO (409 `instance_mismatch`) e nada é enviado nem criado. Informe-o sempre que o escritório tiver mais de um número e você pretender um específico — a mesma pessoa costuma ter uma conversa por número, e elas são fáceis de confundir.","example":"9f1c2b3a-4d5e-6f70-8192-a3b4c5d6e7f8"},"type":{"type":"string","enum":["text","image","document","audio","video","template","interactive"],"default":"text","description":"Tipo do conteúdo:\n- `text`: mensagem de texto simples\n- `image`, `document`, `audio`, `video`: mídia (requer `file_url`)\n- `template`: mensagem de template aprovado pela Meta\n- `interactive`: menu interativo (botões/lista/enquete) — **apenas instâncias uazapiGO** (requer o objeto `interactive`)\n","example":"text"},"file_url":{"type":"string","description":"URL pública do arquivo de mídia (obrigatório para tipos de mídia)"},"template":{"$ref":"#/components/schemas/TemplateRequest"},"interactive":{"$ref":"#/components/schemas/InteractiveMenu"},"acting_as_human":{"type":"boolean","default":false,"description":"Quando `false` (padrão), o envio é tratado como automático/API: preserva a contagem de não lidas da conversa, o marcador manual \"marcar como não lida\" e a sinalização `is_waiting_human` (necessita de atendimento humano). Use `true` apenas quando a chamada de API representa um atendente humano que leu a conversa e está assumindo o atendimento (comportamento legado: zera as não lidas e limpa a sinalização de atendimento humano).\n"}}},"SendStatus":{"type":"object","description":"Dados para publicar um status (story) do WhatsApp. Um status é uma **transmissão**\n(não pertence a nenhuma conversa) e só é suportado por instâncias **uazapi**.\n","required":["instance_id"],"properties":{"instance_id":{"type":"string","format":"uuid","description":"Número (instância) uazapi que publicará o status. Obrigatório.","example":"9f1c2b3a-4d5e-6f70-8192-a3b4c5d6e7f8"},"type":{"type":"string","enum":["text","image","video","audio","myaudio","ptt"],"default":"text","description":"Tipo do status:\n- `text`: texto com cor de fundo e fonte\n- `image`/`video`: mídia com legenda opcional (`text`)\n- `audio`/`myaudio`/`ptt`: áudio\n","example":"text"},"text":{"type":"string","maxLength":656,"description":"Corpo do status de texto, ou legenda para status de imagem/vídeo. No status de texto o limite é de 656 bytes em UTF-8, não de caracteres: letra acentuada custa 2 bytes e emoji custa 4.","example":"Novidades chegando!"},"file_url":{"type":"string","maxLength":2048,"description":"URL pública HTTP(S) do arquivo de mídia (obrigatório para tipos não-texto)."},"background_color":{"type":"integer","minimum":1,"maximum":19,"description":"Índice da cor de fundo (1-19), apenas para status de texto.","example":7},"font":{"type":"integer","enum":[0,1,2,6,7,8,9,10],"description":"Fonte do status de texto.","example":1},"recipients":{"type":"array","items":{"type":"string"},"description":"Audiência explícita (números, JIDs `@s.whatsapp.net` ou `@lid`). Omita para transmitir a todos os contatos. Entradas inválidas são descartadas.","example":["5511999999999","5511888888888@s.whatsapp.net"]},"max_recipients":{"type":"integer","minimum":0,"description":"Limita a audiência a um subconjunto determinístico (proteção operacional).","example":100},"mimetype":{"type":"string","maxLength":128,"description":"MIME type do arquivo, quando necessário."},"thumbnail":{"type":"string","maxLength":2048,"description":"URL da miniatura da mídia (opcional; o backend gera quando necessário)."}}},"StatusPost":{"type":"object","description":"Registro de um status (story) publicado (log de status).","properties":{"id":{"type":"string","format":"uuid"},"instance_id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["text","image","video","audio","myaudio","ptt"]},"text":{"type":"string","nullable":true},"file_url":{"type":"string","nullable":true},"background_color":{"type":"integer","nullable":true},"font":{"type":"integer","nullable":true},"audience_mode":{"type":"string","enum":["all","selected"]},"recipients":{"type":"array","nullable":true,"items":{"type":"string"}},"max_recipients":{"type":"integer","nullable":true},"status":{"type":"string","enum":["queued","sent","failed"]},"wpp_status_id":{"type":"string","nullable":true},"error_message":{"type":"string","nullable":true},"source":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":"string","format":"date-time","nullable":true}}},"InteractiveMenu":{"type":"object","description":"Menu interativo (apenas instâncias uazapiGO). Use `type: \"interactive\"` no envio.\n- `button`: requer `buttons[]`\n- `list`: requer `sections[]`\n- `poll`: requer `choices[]`\n","required":["type","text"],"properties":{"type":{"type":"string","enum":["button","list","poll"],"default":"button","description":"Tipo de menu interativo"},"text":{"type":"string","maxLength":4096,"description":"Texto principal (corpo) do menu","example":"Podemos seguir com o atendimento?"},"footer":{"type":"string","maxLength":120,"nullable":true,"description":"Texto de rodapé (botões/lista)"},"list_button":{"type":"string","maxLength":60,"nullable":true,"description":"Rótulo do botão que abre a lista (apenas `list`)","example":"Ver opções"},"selectable_count":{"type":"integer","minimum":1,"maximum":12,"nullable":true,"description":"Máximo de opções selecionáveis (apenas `poll`); limitado ao nº de opções."},"image_url":{"type":"string","maxLength":2048,"nullable":true,"description":"Imagem de cabeçalho (apenas `button`)"},"buttons":{"type":"array","maxItems":3,"description":"Botões (apenas `type=button`)","items":{"$ref":"#/components/schemas/InteractiveMenuButton"}},"sections":{"type":"array","maxItems":10,"description":"Seções da lista (apenas `type=list`)","items":{"$ref":"#/components/schemas/InteractiveMenuSection"}},"choices":{"type":"array","maxItems":12,"description":"Opções da enquete (apenas `type=poll`)","items":{"type":"string"}}}},"InteractiveMenuButton":{"type":"object","description":"Botão de um menu `button`. O texto/identificadores não podem conter `|` ou quebras de linha.\n","required":["title"],"properties":{"title":{"type":"string","minLength":1,"maxLength":60,"description":"Rótulo visível do botão","example":"Sim"},"id":{"type":"string","maxLength":256,"nullable":true,"description":"Id de resposta (padrão = título). Retornado em `interactive.id` quando o contato toca o botão.","example":"sim"},"type":{"type":"string","enum":["reply","url","call","copy"],"default":"reply","description":"Ação do botão"},"url":{"type":"string","maxLength":2048,"nullable":true,"description":"Obrigatório quando `type=url`"},"phone":{"type":"string","maxLength":32,"nullable":true,"description":"Obrigatório quando `type=call`"},"code":{"type":"string","maxLength":256,"nullable":true,"description":"Obrigatório quando `type=copy`"}}},"InteractiveMenuSection":{"type":"object","description":"Seção (grupo de linhas) de um menu `list`","required":["rows"],"properties":{"title":{"type":"string","maxLength":60,"nullable":true,"description":"Título da seção"},"rows":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/InteractiveMenuRow"}}}},"InteractiveMenuRow":{"type":"object","description":"Linha selecionável de uma seção. Texto/id não podem conter `|` ou quebras de linha.","required":["title"],"properties":{"title":{"type":"string","minLength":1,"maxLength":60,"example":"Previdenciário"},"id":{"type":"string","maxLength":256,"nullable":true,"description":"Id de seleção (padrão = título)","example":"previdenciario"},"description":{"type":"string","maxLength":140,"nullable":true}}},"TemplateRequest":{"type":"object","description":"Dados do template a ser enviado. O template deve estar aprovado (status APPROVED) no número WhatsApp.","required":["name"],"properties":{"name":{"type":"string","description":"Nome do template aprovado pela Meta","example":"appointment_reminder"},"language":{"type":"string","default":"pt_BR","description":"Código de idioma do template (ex. pt_BR, en_US)","example":"pt_BR"},"parameters":{"type":"object","additionalProperties":{"type":"string"},"description":"Parâmetros simples no formato chave-valor. Cada variável `{{N}}` no texto\ndo template body corresponde a `body_N` neste objeto. Exemplo: se o template\ndiz \"Olá {{1}}, sua consulta é às {{2}}\", passe `body_1: \"João\"` e\n`body_2: \"10h\"`. As chaves seguem o padrão:\n- `body_1`, `body_2`, ... — variáveis do corpo (`{{1}}`, `{{2}}`, ...)\n- `header_1`, ... — variáveis do cabeçalho de texto\n- `header_image_url` — URL de imagem para cabeçalho de mídia\n- `button_1`, ... — parâmetros de botões dinâmicos\nIMPORTANTE: Os valores devem ser os dados reais (nome do contato, horário, etc.),\nnunca o placeholder como `{1}` ou `{{1}}`.\n","example":{"body_1":"João","body_2":"10h"}},"components":{"type":"array","items":{"type":"object"},"description":"Componentes no formato raw da Meta API (avançado). Quando fornecido, sobrescreve `parameters`.\nConsulte a documentação da Meta: https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates\n"}}},"CollectReplyRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"object","allOf":[{"$ref":"#/components/schemas/SendMessage"}],"description":"A mensagem enviada pedindo a resposta (texto ou menu interativo)."},"token":{"type":"string","maxLength":256,"nullable":true,"description":"ID de correlação escolhido por você, devolvido na captura como `reply_to_token`. Gerado automaticamente se omitido. **O webhook que escuta `message.reply_captured` só entrega a captura se o filtro `reply` casar com este valor** — use o mesmo prefixo/valor em `tokens` ou `token_prefixes` (ex.: token `no-show:538` exige filtro `token_prefixes: [\"no-show:\"]`). Prefixos nos exemplos (`no-show:`, `nao-comparecimento:`) são ilustrativos, não valores fixos."},"ttl_seconds":{"type":"integer","minimum":60,"maximum":86400,"default":1800,"description":"Por quanto tempo aguardar a resposta antes da expectativa expirar (60s–24h)."},"metadata":{"type":"object","nullable":true,"description":"Dados opacos guardados na expectativa (ex. o id do seu registro)."}}},"CollectReplyResponse":{"type":"object","properties":{"expectation_id":{"type":"string","format":"uuid"},"token":{"type":"string"},"expires_at":{"type":"string","format":"date-time","nullable":true},"prompt_message_id":{"type":"string","format":"uuid","nullable":true}}},"MessageSequenceRequest":{"type":"object","required":["idempotency_key","items"],"properties":{"idempotency_key":{"type":"string","minLength":8,"maxLength":160,"description":"Chave estável da sequência para deduplicar retries."},"items":{"type":"array","minItems":1,"maxItems":10,"items":{"$ref":"#/components/schemas/MessageSequenceItem"}}}},"MessageSequenceItem":{"type":"object","required":["client_item_id","message"],"properties":{"client_item_id":{"type":"string","minLength":1,"maxLength":120,"description":"ID estável do item dentro da sequência."},"send_at":{"type":"string","format":"date-time","nullable":true,"description":"Quando ausente ou no passado, envia imediatamente. Futuro cria mensagem agendada."},"message":{"$ref":"#/components/schemas/SendMessage"}}},"MessageSequenceResponse":{"type":"object","properties":{"batch_id":{"type":"string","format":"uuid"},"created_count":{"type":"integer"},"duplicate_count":{"type":"integer"},"items":{"type":"array","items":{"$ref":"#/components/schemas/MessageSequenceItemResult"}},"errors":{"type":"array","items":{"type":"object"}}}},"MessageSequenceItemResult":{"type":"object","properties":{"client_item_id":{"type":"string"},"status":{"type":"string","enum":["sent","pending","failed","scheduled","duplicate"]},"message_id":{"type":"string","format":"uuid","nullable":true},"scheduled_message_id":{"type":"string","format":"uuid","nullable":true},"error":{"type":"string","nullable":true}}},"ApiKey":{"type":"object","description":"Representa uma chave de acesso à API","properties":{"unique_id":{"type":"string","format":"uuid","description":"Identificador único da chave"},"name":{"type":"string","description":"Nome descritivo da chave","example":"Integração n8n"},"key_prefix":{"type":"string","description":"Prefixo visível da chave (para identificação)","example":"cj_live_x8k2"},"scopes":{"type":"array","items":{"type":"string"},"description":"Lista de escopos/permissões da chave","example":["contacts:read","conversations:read","messages:read"]},"instance_ids":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"description":"IDs dos números WhatsApp que esta chave pode acessar.\nSe null, a chave tem acesso a todos os números do escritório.\n","example":null},"is_active":{"type":"boolean","description":"Se a chave está ativa","example":true},"last_used_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora do último uso"},"request_count":{"type":"integer","description":"Total de requisições feitas com esta chave","example":1523},"created_at":{"type":"string","format":"date-time","description":"Data de criação da chave"},"created_by":{"type":"string","format":"uuid","nullable":true,"description":"UUID do usuário que criou a chave"},"created_by_name":{"type":"string","nullable":true,"description":"Nome do usuário que criou a chave","example":"João Silva"},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Data de expiração da chave (null = permanente)"}}},"ApiKeyWithSecret":{"description":"Chave de API incluindo o valor completo (exibido apenas na criação)","allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"raw_key":{"type":"string","description":"Chave completa — exibida **apenas uma vez** no momento da criação. Guarde em local seguro.","example":"cj_live_m4n7p9q2r5s8t1u4v7w0x3y6z9a2b5c8"}}}]},"CreateApiKey":{"type":"object","description":"Dados para criação de uma nova chave de API","required":["name"],"properties":{"name":{"type":"string","maxLength":255,"description":"Nome descritivo para identificar a chave","example":"Integração CRM"},"scopes":{"type":"array","items":{"type":"string","enum":["contacts:read","contacts:write","contacts:delete","conversations:read","conversations:write","messages:read","messages:send","tags:read","tags:write","funnel:read","funnel:write","webhooks:manage","campaigns:read","campaigns:write","automations:read","automations:write","shortcuts:read","shortcuts:write","instances:write"]},"description":"Escopos de acesso. Se não informado, serão aplicados apenas os escopos de leitura.\nO escopo `messages:send` permite envio de mensagens de texto, mídia e templates.\nOs escopos `automations:*` controlam acesso a automações (fluxos de gatilho/ação).\nOs escopos `shortcuts:*` controlam acesso a atalhos de mensagem.\nO escopo `campaigns:write` permite criar, editar, excluir e enviar campanhas.\nO escopo `instances:write` permite reconectar instâncias e gerar QR code.\nO escopo `contacts:delete` permite exclusão de contatos em massa e não é\nconcedido junto com `contacts:write` — precisa ser atribuído explicitamente.\n","example":["contacts:read","conversations:read","messages:read","messages:send"]},"instance_ids":{"type":"array","items":{"type":"string","format":"uuid"},"nullable":true,"description":"Restringe a chave a números WhatsApp específicos do escritório.\nSe não informado ou null, a chave terá acesso a todos os números.\nCada UUID deve pertencer a um número válido do escritório.\n","example":null}}},"Pagination":{"type":"object","description":"Informações de paginação baseada em cursor","properties":{"has_more":{"type":"boolean","description":"Se existem mais resultados além desta página","example":true},"next_cursor":{"type":"string","format":"uuid","nullable":true,"description":"Cursor para buscar a próxima página. Passe como parâmetro `cursor` na próxima requisição.","example":"b2c3d4e5-f6a7-8901-bcde-f12345678901"},"limit":{"type":"integer","description":"Limite utilizado nesta requisição","example":50}}},"CreateLawsuitRequest":{"type":"object","required":["cnj"],"properties":{"cnj":{"type":"string","description":"CNJ formatado (NNNNNNN-DD.AAAA.J.TR.OOOO) ou 20 dígitos"},"conversation_id":{"type":"string","format":"uuid","description":"Vincular o processo a esta conversa"},"contact_id":{"type":"string","format":"uuid","description":"Vincular o processo a todas as conversas deste contato"},"client_polo":{"type":"string","enum":["ATIVO","PASSIVO"],"description":"Polo do cliente do escritório"},"monitoring_frequency":{"type":"string","enum":["ALTA","MEDIA","BAIXA"],"description":"Frequência de monitoramento no Escavador. O monitoramento ainda não\nestá disponível para todos os escritórios: veja o campo `monitoring`\nda resposta para saber se ele pode ser ativado.\n"},"set_as_active":{"type":"boolean","description":"Definir como processo ativo da conversa vinculada"}}},"UpdateLawsuitRequest":{"type":"object","description":"Ao menos um campo deve ser informado.","properties":{"client_polo":{"type":"string","enum":["ATIVO","PASSIVO"]},"status_operacional":{"type":"string","enum":["CONHECIMENTO","RECURSAL","EXECUCAO","ENCERRADO","AGUARDANDO_BAIXA"]},"advogado_user_id":{"type":"string","nullable":true,"description":"Advogado responsável (UUID). Envie null para limpar (REST) ou \"__LIMPAR__\" (MCP)."}}},"LawsuitCreateResult":{"type":"object","properties":{"status":{"type":"string","enum":["found","draft","existing"],"description":"found = obtido do Escavador; draft = criado offline; existing = já cadastrado"},"numero_cnj":{"type":"string"},"office_lawsuit_id":{"type":"string","format":"uuid"},"titulo_polo_ativo":{"type":"string","nullable":true},"titulo_polo_passivo":{"type":"string","nullable":true},"classe_processual":{"type":"string","nullable":true},"area":{"type":"string","nullable":true},"status_operacional":{"type":"string","nullable":true},"bound_conversations":{"type":"array","description":"Conversas efetivamente vinculadas","items":{"type":"string","format":"uuid"}},"monitoring":{"type":"object","description":"Presente só quando `monitoring_frequency` foi enviado. Diz se o\nmonitoramento pode ser ativado; a ativação em si é confirmada em\nsegundo plano (consulte `status_monitoramento_processo`).\n","properties":{"enabled":{"type":"boolean"},"reason":{"type":"string","enum":["not_available","no_monitoring_quota","lawsuit_not_active"],"description":"Ausente quando `enabled` é true.\nnot_available = monitoramento não disponível para o escritório;\nno_monitoring_quota = escritório sem cota de monitoramento;\nlawsuit_not_active = o processo não ficou ativo na conversa\n"},"message":{"type":"string"}}},"active_skipped_reason":{"type":"string","description":"Presente quando a ativação pedida (`set_as_active`) não foi feita:\nACTIVE_LAWSUITS_LIMIT_REACHED = a conversa já está no limite de\nprocessos ativos; ACTIVATION_FAILED = erro ao ativar (o processo foi\ncadastrado; tente definirProcessoAtivo).\n"}}},"LawsuitMovement":{"type":"object","description":"Movimentação (andamento) de um processo","properties":{"unique_id":{"type":"string","format":"uuid"},"data":{"type":"string","format":"date","description":"Data da movimentação"},"tipo":{"type":"string","nullable":true},"conteudo":{"type":"string","nullable":true,"description":"Texto da movimentação"},"classificacao_nome":{"type":"string","nullable":true},"fonte_nome":{"type":"string","nullable":true},"fonte_grau":{"type":"string","nullable":true}}},"LawsuitLink":{"type":"object","description":"Vínculo do processo com o escritório (origem em office_lawsuits)","properties":{"office_lawsuit_id":{"type":"string","format":"uuid"},"status_operacional":{"type":"string","nullable":true,"description":"Status operacional no escritório (CONHECIMENTO, RECURSAL, EXECUCAO, ENCERRADO, RASCUNHO)"},"client_polo":{"type":"string","nullable":true,"enum":["ATIVO","PASSIVO"],"description":"Polo do cliente do escritório no processo"},"source":{"type":"string","nullable":true,"description":"Origem do cadastro (ex. advbox, planilha)"},"advogado_user_id":{"type":"string","format":"uuid","nullable":true,"description":"Advogado responsável (UUID do usuário)"}}},"Lawsuit":{"type":"object","description":"Processo jurídico cadastrado no escritório, vinculado a um ou mais\ncontatos. Processos em segredo de justiça têm os detalhes omitidos\n(apenas `unique_id`, `numero_cnj`, `situacao`, `tribunal_origem_sigla`,\n`data_ultima_movimentacao`, `segredo_de_justica` e `_aviso`).\n","properties":{"unique_id":{"type":"string","format":"uuid"},"numero_cnj":{"type":"string","description":"Número CNJ formatado"},"numero_cnj_limpo":{"type":"string","description":"Número CNJ com 20 dígitos, sem formatação"},"titulo_polo_ativo":{"type":"string","nullable":true},"titulo_polo_passivo":{"type":"string","nullable":true},"ano_inicio":{"type":"integer","nullable":true},"data_inicio":{"type":"string","format":"date","nullable":true},"data_ultima_movimentacao":{"type":"string","format":"date","nullable":true},"movimentacoes_totais":{"type":"integer","nullable":true},"tribunal_origem_sigla":{"type":"string","nullable":true},"classe_processual":{"type":"string","nullable":true},"assunto_principal":{"type":"string","nullable":true},"area":{"type":"string","nullable":true},"orgao_julgador":{"type":"string","nullable":true},"situacao":{"type":"string","nullable":true},"valor_causa":{"type":"number","nullable":true},"moeda_valor_causa":{"type":"string","nullable":true},"data_distribuicao":{"type":"string","format":"date","nullable":true},"data_arquivamento":{"type":"string","format":"date","nullable":true},"segredo_de_justica":{"type":"boolean","nullable":true},"status_predito":{"type":"string","nullable":true,"description":"Status previsto a partir das movimentações (ex. ATIVO, INATIVO)"},"vinculo":{"$ref":"#/components/schemas/LawsuitLink"},"ultimas_movimentacoes":{"type":"array","description":"Movimentações mais recentes (omitidas em segredo de justiça)","items":{"$ref":"#/components/schemas/LawsuitMovement"}},"_aviso":{"type":"string","description":"Presente apenas em processos sob segredo de justiça"}}},"PagePagination":{"type":"object","description":"Paginação por página (offset)","properties":{"page":{"type":"integer","example":1},"limit":{"type":"integer","example":20},"total":{"type":"integer","description":"Total de itens que casam com os filtros","example":42},"has_more":{"type":"boolean","example":true}}},"CaseParty":{"type":"object","description":"Parte de um caso (cliente, autor, réu, envolvido…)","properties":{"id":{"type":"string","format":"uuid"},"case_id":{"type":"string","format":"uuid"},"contact_id":{"type":"string","format":"uuid","nullable":true,"description":"Contato do escritório, quando a parte é um contato"},"name":{"type":"string"},"role":{"type":"string","description":"Papel no caso (cliente, autor, réu, envolvido, terceiro, testemunha…)"},"qualification":{"type":"string","nullable":true,"description":"Qualificação (ex. \"parte contrária\", \"advogado da parte\")"},"document":{"type":"string","nullable":true,"description":"CPF/CNPJ"},"created_at":{"type":"string","format":"date-time"}}},"CasePartyRequest":{"type":"object","description":"Parte a adicionar. `name` é obrigatório salvo quando `contact_id` é informado (preenchido a partir do contato). `role` assume `cliente` quando omitido.","properties":{"name":{"type":"string","description":"Nome da parte (obrigatório sem `contact_id`)"},"role":{"type":"string","default":"cliente","description":"Papel no caso (cliente, autor, réu, envolvido, terceiro, testemunha…)"},"contact_id":{"type":"string","format":"uuid","description":"Contato do escritório a vincular como parte"},"qualification":{"type":"string","description":"Qualificação (ex. \"parte contrária\")"},"document":{"type":"string","description":"CPF/CNPJ"}}},"PartnerInvite":{"type":"object","properties":{"token":{"type":"string","format":"uuid","description":"Identificador do convite; é o trecho final do `signup_url`."},"signup_url":{"type":"string","description":"Link de cadastro a entregar ao convidado."},"status":{"type":"string","enum":["active","used","expired"],"description":"`used` = o convidado se cadastrou (conversão). `expired` = passou da\nvalidade OU foi revogado — revogar é expirar.\n"},"description":{"type":"string","nullable":true,"description":"Anotação interna do escritório. O convidado não vê."},"created_at":{"type":"string","format":"date-time","nullable":true},"expires_at":{"type":"string","format":"date-time","description":"Até quando o LINK vale (não confundir com os 30 dias de teste)."},"used_at":{"type":"string","format":"date-time","nullable":true},"email_sent_count":{"type":"integer","description":"Quantas vezes o convite foi enviado por e-mail pela tela."},"guest":{"type":"object","nullable":true,"description":"Quem foi convidado.","properties":{"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true}}},"created_by":{"type":"object","nullable":true,"description":"Quem criou o convite. Para convites criados por API, é o usuário dono\nda chave; `id` vem nulo quando não há usuário atrás dela.\n","properties":{"id":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"email":{"type":"string","nullable":true}}},"office":{"type":"object","nullable":true,"description":"O escritório que NASCEU deste convite, quando o convidado já se\ncadastrou. Traz apenas sinal de uso — nada de dentro do escritório\nconvidado (contatos, conversas e mensagens não são do parceiro).\n","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"created_at":{"type":"string","format":"date-time","nullable":true},"last_access":{"type":"string","format":"date-time","nullable":true,"description":"Acesso mais recente de qualquer membro."},"members":{"type":"integer"},"instances":{"type":"integer","description":"Quantos números de WhatsApp o escritório já conectou."}}}},"example":{"token":"c3d4e5f6-a7b8-9012-cdef-123456789012","signup_url":"https://web.chatjuridico.com.br/signup/c3d4e5f6-a7b8-9012-cdef-123456789012","status":"used","description":"Banca do Dr. Silva — indicação do evento de outubro","created_at":"2026-09-10T14:00:00Z","expires_at":"2026-09-17T14:00:00Z","used_at":"2026-09-11T09:12:00Z","email_sent_count":1,"guest":{"name":"Dr. Silva","email":"silva@banca.com.br","phone":"5511999887766"},"created_by":{"id":"b2c3d4e5-f6a7-8901-bcde-f12345678901","name":"Ana Vendedora","email":"ana@parceiro.com.br"},"office":{"id":"d4e5f6a7-b8c9-0123-def1-234567890123","name":"Silva Advogados","created_at":"2026-09-11T09:12:00Z","last_access":"2026-09-16T18:40:00Z","members":3,"instances":1}}},"PartnerInviteCreated":{"type":"object","properties":{"token":{"type":"string","format":"uuid"},"signup_url":{"type":"string","description":"Entregue este link ao convidado."},"expires_at":{"type":"string","format":"date-time","description":"Validade do LINK."},"trial_days":{"type":"integer","description":"Duração do teste que o convidado recebe, definida pelo servidor."},"plan":{"type":"string","description":"Plano do teste, definido pelo servidor."},"subscription_id":{"type":"string","format":"uuid"},"guest":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string","description":"Normalizado para E.164 sem \"+\"."}}}},"example":{"token":"c3d4e5f6-a7b8-9012-cdef-123456789012","signup_url":"https://web.chatjuridico.com.br/signup/c3d4e5f6-a7b8-9012-cdef-123456789012","expires_at":"2026-09-24T14:00:00Z","trial_days":30,"plan":"Ouro","subscription_id":"a7b8c9d0-e1f2-3456-7890-abcdef123456","guest":{"name":"Dr. Silva","email":"silva@banca.com.br","phone":"5511999887766"}}},"CreatePartnerInviteRequest":{"type":"object","required":["name","email","phone"],"properties":{"name":{"type":"string","minLength":2,"maxLength":100,"description":"Nome do convidado."},"email":{"type":"string","maxLength":255,"description":"E-mail do convidado. Precisa ser inédito na plataforma — endereço com\ncadastro responde 409.\n"},"phone":{"type":"string","maxLength":32,"description":"WhatsApp do convidado, com DDD. Aceita máscara\n(`(11) 99988-7766`) e é normalizado para E.164. Obrigatório: é por\nonde o convite costuma ser entregue.\n"},"description":{"type":"string","maxLength":500,"description":"Anotação interna do escritório. O convidado não vê."},"expires_in_days":{"type":"integer","minimum":1,"maximum":30,"default":7,"description":"Validade do LINK em dias. NÃO é a duração do teste, que é sempre a\ndefinida pelo servidor.\n"}},"example":{"name":"Dr. Silva","email":"silva@banca.com.br","phone":"(11) 99988-7766","description":"Indicação do evento de outubro","expires_in_days":7}},"UpdatePartnerInviteRequest":{"type":"object","description":"Pelo menos um campo. Os dados do convidado não são editáveis: para\ncorrigi-los, crie outro convite.\n","properties":{"description":{"type":"string","nullable":true,"maxLength":500,"description":"Anotação interna. `null` limpa o campo."},"expires_in_days":{"type":"integer","minimum":1,"maximum":30,"description":"Nova validade do link, contada a partir de agora."},"revoke":{"type":"boolean","description":"`true` expira o convite imediatamente. O registro continua na\nlistagem (como `expired`) — o histórico de quem convidou quem é o\nponto do módulo.\n"}},"example":{"revoke":true}},"Case":{"type":"object","description":"Caso do escritório — processo administrativo, extrajudicial, consultivo\nou de prospecção, sem número CNJ. `status` é a coluna do kanban.\n","properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true,"description":"Observações internas"},"status":{"type":"string","enum":["novo","em_andamento","aguardando","concluido","arquivado"]},"case_type":{"type":"string","nullable":true,"description":"Tipo (administrativo, extrajudicial, consultivo, prospecção…)"},"folder_number":{"type":"string","nullable":true,"description":"Pasta"},"action_type":{"type":"string","nullable":true,"description":"Tipo de ação"},"legal_area":{"type":"string","nullable":true,"description":"Área jurídica"},"phase":{"type":"string","nullable":true},"stage":{"type":"string","nullable":true},"case_value":{"type":"number","nullable":true,"description":"Valor da causa"},"fee_value":{"type":"number","nullable":true,"description":"Honorários"},"fee_percentage":{"type":"number","nullable":true},"filed_at":{"type":"string","format":"date-time","nullable":true},"archived_at":{"type":"string","format":"date-time","nullable":true},"responsible_user_id":{"type":"string","format":"uuid","nullable":true,"description":"Responsável (UUID do membro)"},"tags":{"type":"array","items":{"type":"string"},"description":"Etiquetas"},"chats_id":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Conversas vinculadas"},"source":{"type":"string","enum":["manual","advbox","astrea","calculo_juridico"]},"external_id":{"type":"string","nullable":true,"description":"ID no sistema de origem (quando sincronizado)"},"parties":{"type":"array","description":"Partes do caso (presente em `consultarCaso` e `criarCaso`)","items":{"$ref":"#/components/schemas/CaseParty"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CaseCounts":{"type":"object","description":"Casos por coluna do kanban","properties":{"novo":{"type":"integer"},"em_andamento":{"type":"integer"},"aguardando":{"type":"integer"},"concluido":{"type":"integer"},"arquivado":{"type":"integer"}}},"CreateCaseRequest":{"type":"object","required":["title"],"properties":{"title":{"type":"string","description":"Título (boa prática do escritório — nome do cliente e a tese)"},"status":{"type":"string","enum":["novo","em_andamento","aguardando","concluido","arquivado"],"default":"novo"},"contact_id":{"type":"string","format":"uuid","description":"Contato cliente do caso — adiciona como parte `cliente` e vincula todas as conversas dele"},"conversation_id":{"type":"string","format":"uuid","description":"Vincular esta conversa"},"parties":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/CasePartyRequest"},"description":"Demais partes (máx. 50)"},"description":{"type":"string"},"notes":{"type":"string"},"folder_number":{"type":"string","description":"Pasta"},"case_type":{"type":"string"},"legal_area":{"type":"string"},"action_type":{"type":"string"},"phase":{"type":"string"},"stage":{"type":"string"},"case_value":{"type":"number"},"fee_value":{"type":"number"},"fee_percentage":{"type":"number"},"filed_at":{"type":"string","format":"date-time"},"responsible_user_id":{"type":"string","format":"uuid","description":"Responsável — membro do escritório (`listarMembros`)"},"tags":{"type":"array","items":{"type":"string"},"description":"Etiquetas (ex. tese, \"contrato fechado por ia\")"}}},"UpdateCaseRequest":{"type":"object","description":"Ao menos um campo deve ser informado. Campos omitidos não mudam.","properties":{"title":{"type":"string"},"status":{"type":"string","enum":["novo","em_andamento","aguardando","concluido","arquivado"],"description":"Mover no kanban"},"description":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"folder_number":{"type":"string","nullable":true},"case_type":{"type":"string","nullable":true},"legal_area":{"type":"string","nullable":true},"action_type":{"type":"string","nullable":true},"phase":{"type":"string","nullable":true},"stage":{"type":"string","nullable":true},"case_value":{"type":"number","nullable":true},"fee_value":{"type":"number","nullable":true},"fee_percentage":{"type":"number","nullable":true},"filed_at":{"type":"string","format":"date-time","nullable":true},"archived_at":{"type":"string","format":"date-time","nullable":true},"responsible_user_id":{"type":"string","nullable":true,"description":"Responsável (UUID). Envie null para limpar (REST) ou \"__LIMPAR__\" (MCP)."},"tags":{"type":"array","items":{"type":"string"},"description":"Substitui a lista de etiquetas"}}},"Tag":{"type":"object","description":"Representa uma tag para classificação de conversas","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único da tag"},"name":{"type":"string","description":"Nome da tag","example":"Cliente VIP"},"color_hex":{"type":"string","nullable":true,"description":"Cor da tag em formato hexadecimal","example":"#FF5733"},"color_name":{"type":"string","nullable":true,"description":"Nome da cor da tag","example":"vermelho"},"created_at":{"type":"string","format":"date-time","description":"Data de criação da tag"}}},"CreateTag":{"type":"object","description":"Dados para criação de uma nova tag","required":["name"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Nome da tag (entre 1 e 100 caracteres)","example":"Urgente"},"color_id":{"type":"integer","description":"ID da cor predefinida (opcional)","example":3}}},"UpdateTag":{"type":"object","description":"Dados para atualização parcial de uma tag — envie apenas os campos que deseja alterar","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Novo nome da tag"},"color_id":{"type":"integer","description":"ID da cor predefinida"}}},"Funnel":{"type":"object","description":"Representa um funil de vendas do escritório","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do funil"},"name":{"type":"string","description":"Nome do funil","example":"Funil Principal"},"description":{"type":"string","nullable":true,"description":"Descrição do funil"},"color_hex":{"type":"string","nullable":true,"description":"Cor do funil em formato hexadecimal","example":"#3B82F6"},"is_default":{"type":"boolean","description":"Se este é o funil padrão do escritório","example":true},"is_active":{"type":"boolean","description":"Se o funil está ativo","example":true},"sort_order":{"type":"integer","description":"Ordem de exibição do funil","example":0},"stage_count":{"type":"integer","nullable":true,"description":"Número de etapas no funil (quando disponível)"},"created_at":{"type":"string","format":"date-time","description":"Data de criação do funil"}}},"FunnelStage":{"type":"object","description":"Representa uma etapa do funil de vendas do escritório","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único da etapa"},"funnel_id":{"type":"string","format":"uuid","nullable":true,"description":"Identificador do funil ao qual esta etapa pertence"},"name":{"type":"string","description":"Nome da etapa","example":"Novo Lead"},"color_hex":{"type":"string","nullable":true,"description":"Cor da etapa em formato hexadecimal","example":"#3498DB"},"color_name":{"type":"string","nullable":true,"description":"Nome da cor da etapa","example":"azul"},"stage_order":{"type":"integer","description":"Posição da etapa no funil (ordem de exibição)","example":1},"is_active":{"type":"boolean","description":"Se a etapa está ativa","example":true},"created_at":{"type":"string","format":"date-time","description":"Data de criação da etapa"}}},"CreateFunnelStageRequest":{"type":"object","description":"Dados para criar uma nova etapa no funil de vendas","required":["name"],"properties":{"name":{"type":"string","description":"Nome da etapa","minLength":1,"maxLength":255,"example":"Qualificação"},"color_hex":{"type":"string","nullable":true,"description":"Cor da etapa em formato hexadecimal","maxLength":7,"example":"#3B82F6"},"color_name":{"type":"string","nullable":true,"description":"Nome da cor da etapa","maxLength":50,"example":"azul"},"stage_order":{"type":"integer","nullable":true,"description":"Posição da etapa no funil"},"funnel_id":{"type":"string","format":"uuid","nullable":true,"description":"Identificador do funil ao qual esta etapa pertence"}}},"UpdateFunnelStageRequest":{"type":"object","description":"Dados para atualizar uma etapa existente do funil de vendas","properties":{"name":{"type":"string","description":"Nome da etapa","minLength":1,"maxLength":255},"color_hex":{"type":"string","nullable":true,"description":"Cor da etapa em formato hexadecimal","maxLength":7},"color_name":{"type":"string","nullable":true,"description":"Nome da cor da etapa","maxLength":50},"stage_order":{"type":"integer","nullable":true,"description":"Posição da etapa no funil"},"is_active":{"type":"boolean","description":"Se a etapa está ativa"}}},"Webhook":{"type":"object","description":"Representa um webhook registrado para receber notificações de eventos","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do webhook"},"url":{"type":"string","description":"URL do endpoint que receberá os eventos","example":"https://meu-servidor.com/webhooks/chatjuridico"},"events":{"type":"array","items":{"type":"string"},"description":"Eventos assinados por este webhook","example":["message.received","conversation.created"]},"description":{"type":"string","nullable":true,"description":"Descrição do webhook","example":"Webhook principal para CRM"},"is_active":{"type":"boolean","description":"Se o webhook está ativo","example":true},"failure_count":{"type":"integer","description":"Número de falhas consecutivas de entrega","example":0},"last_delivery_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora da última entrega bem-sucedida"},"last_failure_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora da última falha de entrega"},"created_at":{"type":"string","format":"date-time","description":"Data de criação do webhook"},"filters":{"$ref":"#/components/schemas/WebhookFilter"}}},"WebhookWithSecret":{"description":"Webhook incluindo o segredo HMAC (exibido apenas na criação)","allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string","description":"Segredo para validação HMAC-SHA256 — exibido **apenas uma vez** no momento da criação. Guarde em local seguro.","example":"whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"}}}]},"CreateWebhook":{"type":"object","description":"Dados para registro de um novo webhook","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","description":"URL do endpoint (deve utilizar HTTPS)","example":"https://meu-servidor.com/webhooks/chatjuridico"},"events":{"type":"array","items":{"type":"string","enum":["message.received","message.sent","message.status_changed","conversation.created","conversation.updated","contact.created","contact.updated","automation.executed","automation.activated","automation.deactivated"]},"description":"Eventos que o webhook deve receber. Valores válidos:\n`message.received`, `message.sent`, `message.status_changed`,\n`conversation.created`, `conversation.updated`,\n`contact.created`, `contact.updated`,\n`automation.executed`, `automation.activated`, `automation.deactivated`\n","example":["message.received","conversation.created"]},"description":{"type":"string","description":"Descrição opcional do webhook","example":"Webhook principal para CRM"},"filters":{"$ref":"#/components/schemas/WebhookFilter"}}},"UpdateWebhook":{"type":"object","description":"Dados para atualização parcial de um webhook — envie apenas os campos que deseja alterar","properties":{"url":{"type":"string","format":"uri","description":"Nova URL do endpoint (deve utilizar HTTPS)"},"events":{"type":"array","items":{"type":"string"},"description":"Novos eventos a serem assinados (substitui os eventos atuais)"},"description":{"type":"string","description":"Descrição do webhook"},"is_active":{"type":"boolean","description":"Ativar ou desativar o webhook"},"filters":{"$ref":"#/components/schemas/WebhookFilter"}}},"WebhookFilter":{"type":"object","description":"Filtros opcionais aplicados aos eventos `message.*` antes da entrega. Quando\ndefinidos, somente as mensagens que satisfazem TODOS os filtros são entregues.\nEventos que não sejam de mensagem (ex. `conversation.*`, `contact.*`) não são afetados.\n","properties":{"message_types":{"type":"array","maxItems":20,"description":"Entregar apenas mensagens destes tipos.","items":{"type":"string","enum":["text","image","document","audio","video","sticker","location","contact","template","interactive"]},"example":["interactive","text"]},"direction":{"type":"string","enum":["inbound","outbound","any"],"description":"Direção da mensagem — `inbound` (recebida do contato), `outbound` (enviada pelo escritório) ou `any` (padrão)."},"text":{"$ref":"#/components/schemas/WebhookTextFilter"},"interactive":{"$ref":"#/components/schemas/WebhookInteractiveFilter"},"reply":{"$ref":"#/components/schemas/WebhookReplyFilter"}}},"WebhookReplyFilter":{"type":"object","description":"Filtra capturas de `collect-reply` (evento `message.reply_captured`) pelo token de\ncorrelação registrado em `POST /conversations/{id}/collect-reply`. Informe ao menos um\nentre `has_token`, `tokens` e `token_prefixes` (OR entre eles).\n**O valor aqui precisa casar com o `token` enviado no `collect-reply`**: `tokens`\nexige igualdade exata; `token_prefixes` casa por prefixo (`token.startswith`). Ex.:\n`token_prefixes: [\"nao-comparecimento:\"]` NÃO captura `no-show:538`, mas `[\"no-show:\"]`\ncaptura. (`has_token: true` sozinho entrega qualquer captura com token, ignorando\nprefixo/valor.) Se o webhook nunca dispara mas a resposta aparece na UI e você usou\n`tokens`/`token_prefixes`, o prefixo/valor provavelmente diverge do token enviado.\n","properties":{"has_token":{"type":"boolean","description":"`true` = somente respostas capturadas (com token); `false` = somente mensagens sem token."},"tokens":{"type":"array","maxItems":50,"description":"Entregar apenas capturas cujo token esteja exatamente nesta lista.","items":{"type":"string"},"example":["no-show:REC-8821"]},"token_prefixes":{"type":"array","maxItems":50,"description":"Entregar apenas capturas cujo token comece com um destes prefixos (roteia um fluxo inteiro para um endpoint).","items":{"type":"string"},"example":["no-show:"]}}},"WebhookTextFilter":{"type":"object","description":"Filtra pelo conteúdo textual da mensagem.","required":["values"],"properties":{"mode":{"type":"string","enum":["equals","contains","regex"],"default":"contains","description":"Como comparar — igualdade exata, substring (`contains`) ou expressão regular."},"values":{"type":"array","minItems":1,"maxItems":50,"description":"Valores a comparar (OR entre os valores). Para `regex`, cada valor é um padrão.","items":{"type":"string"},"example":["orçamento","contrato"]}}},"WebhookInteractiveFilter":{"type":"object","description":"Filtra por respostas a menu interativo (o contato tocou um botão / selecionou uma\nopção). Informe `button_ids` e/ou `titles` (ao menos um é obrigatório).\n","properties":{"button_ids":{"type":"array","maxItems":50,"description":"Entregar apenas respostas cujo id selecionado esteja na lista (ex. `sim`).","items":{"type":"string"},"example":["sim","nao"]},"titles":{"type":"array","maxItems":50,"description":"Entregar apenas respostas cujo rótulo selecionado esteja na lista (ex. `Sim`).","items":{"type":"string"},"example":["Sim","Não"]}}},"UpdateConversation":{"type":"object","description":"Dados para atualização de uma conversa. Envie apenas os campos que deseja alterar.","properties":{"tag_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs das tags a associar à conversa (substitui as tags atuais)","example":["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]},"stage_id":{"type":"string","format":"uuid","description":"ID da etapa do funil para mover a conversa","example":"c3d4e5f6-a7b8-9012-cdef-123456789012"},"is_archived":{"type":"boolean","description":"Arquivar (true) ou desarquivar (false) a conversa","example":false},"accountable_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do membro responsável pela conversa (null para remover atribuição)","example":"d4e5f6a7-b8c9-0123-def0-456789abcdef"},"department_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do departamento/fila ao qual delegar a conversa (null para remover). Resolva via `GET /v1/departments`.","example":"b2c3d4e5-f6a7-8901-bcde-f23456789012"}}},"RequestLog":{"type":"object","description":"Registro de uma requisição feita à API","properties":{"id":{"type":"integer","format":"int64","description":"Identificador sequencial do log"},"office_id":{"type":"string","format":"uuid","description":"ID do escritório"},"api_key_id":{"type":"string","format":"uuid","description":"ID da chave de API utilizada"},"method":{"type":"string","description":"Método HTTP da requisição","example":"GET"},"path":{"type":"string","description":"Caminho do endpoint acessado","example":"/v1/contacts"},"status_code":{"type":"integer","description":"Código de status HTTP da resposta","example":200},"response_time_ms":{"type":"integer","description":"Tempo de resposta em milissegundos","example":45},"error_code":{"type":"string","nullable":true,"description":"Código de erro (quando aplicável)"},"created_at":{"type":"string","format":"date-time","description":"Data/hora da requisição"}}},"ScheduledMessage":{"type":"object","description":"Representa uma mensagem agendada para envio futuro. A associação com o número de WhatsApp é derivada da conversa (conversation_id → chat → número).","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único da mensagem agendada"},"conversation_id":{"type":"string","format":"uuid","nullable":true,"description":"ID da conversa onde a mensagem será enviada"},"message_type":{"type":"string","nullable":true,"description":"Tipo do conteúdo da mensagem: `text`, `image`, `document`, `audio`, `video`, `sticker`, `location`, `contact` ou `template`. Texto aberto: uma linha antiga pode trazer o tipo cru gravado no banco.","example":"text"},"text":{"type":"string","nullable":true,"description":"Texto da mensagem","example":"Lembrete: sua consulta é amanhã às 10h."},"file_url":{"type":"string","nullable":true,"description":"URL do arquivo de mídia (para tipos de mídia)"},"template_name":{"type":"string","nullable":true,"description":"Nome do template (derivado do snapshot do template aprovado)"},"template_language":{"type":"string","nullable":true,"description":"Código de idioma do template (derivado do snapshot)","example":"pt_BR"},"scheduled_at":{"type":"string","format":"date-time","description":"Data/hora programada para o envio"},"status":{"type":"string","description":"Status da mensagem agendada (texto aberto). Pessoa, API ou lembrete: `pending`, `processing`, `sent`, `failed`, `error`, `canceled`. Automação: `automation_pending` (esperando o horário), `automation_sending` (saindo agora), depois `sent`, `failed` ou `canceled`. Mensagens canceladas podem vir como `cancelled` (dados históricos) ou `canceled`; trate as duas como o mesmo estado.","example":"pending"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"Data de criação"},"origin":{"type":"string","description":"Quem agendou: `person` (alguém da equipe), `automation` (tem `automation_id`), `integration` (API, MCP ou sistema, sem autor) ou `reminder` (lembrete de cobrança).","example":"person"},"automation_id":{"type":"string","format":"uuid","nullable":true,"description":"Automação que deixou a mensagem para depois (só em linha de automação)"},"automation_batch_id":{"type":"string","format":"uuid","nullable":true,"description":"Sequência de uma execução da automação. Linhas com o mesmo valor são as mensagens da mesma execução, em ordem de `scheduled_at`."},"cancel_reason":{"type":"string","nullable":true,"description":"Por que uma linha de automação foi cancelada (texto aberto): `human_reply`, `manual`, `automation_disabled`, `automation_deleted`, `instance_disconnected`, `contact_deleted`, `chat_deleted`, `batch_member_failed`, `rollback`."},"failure_reason":{"type":"string","nullable":true,"description":"Por que uma linha de automação falhou (texto aberto): `meta_window_closed`, `gateway_error`, `invalid_payload`, `stale`, `automations_disabled`, `instance_deleted`."},"claimed_at":{"type":"string","format":"date-time","nullable":true,"description":"Quando o enviador de automações pegou a linha para enviar"},"canceled_at":{"type":"string","format":"date-time","nullable":true,"description":"Quando a mensagem foi cancelada"},"sent_at":{"type":"string","format":"date-time","nullable":true,"description":"Quando a mensagem foi enviada"}}},"CreateScheduledMessage":{"type":"object","description":"Dados para criação de uma nova mensagem agendada","required":["conversation_id","scheduled_at"],"properties":{"conversation_id":{"type":"string","format":"uuid","description":"ID da conversa onde a mensagem será enviada"},"message_type":{"type":"string","enum":["text","image","document","audio","video","template"],"default":"text","description":"Tipo do conteúdo da mensagem"},"text":{"type":"string","description":"Texto da mensagem (obrigatório para tipo `text`)","example":"Lembrete: sua consulta é amanhã às 10h."},"file_url":{"type":"string","description":"URL do arquivo de mídia (obrigatório para tipos de mídia)"},"template_name":{"type":"string","description":"Nome do template aprovado pela Meta (obrigatório para tipo `template`)"},"template_language":{"type":"string","default":"pt_BR","description":"Código de idioma do template"},"template_parameters":{"type":"object","additionalProperties":{"type":"string"},"description":"Parâmetros do template no formato chave-valor.\nAs chaves seguem o padrão `body_1`, `body_2`, etc.\n"},"scheduled_at":{"type":"string","format":"date-time","description":"Data/hora para o envio (deve ser no futuro)"}}},"WhatsAppTemplate":{"type":"object","description":"Representa um template de mensagem WhatsApp aprovado pela Meta","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do template"},"name":{"type":"string","description":"Nome do template","example":"appointment_reminder"},"language":{"type":"string","description":"Código de idioma do template","example":"pt_BR"},"status":{"type":"string","description":"Status de aprovação do template","example":"APPROVED"},"category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"],"nullable":true,"description":"Categoria do template conforme classificação da Meta","example":"UTILITY"},"components":{"type":"array","items":{"type":"object"},"nullable":true,"description":"Componentes do template (cabeçalho, corpo, botões) no formato Meta"},"instance_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do número de WhatsApp ao qual o template pertence"}}},"CreateTemplate":{"type":"object","description":"Dados para criação de um novo template WhatsApp","required":["name","language","category","components","instance_id"],"properties":{"name":{"type":"string","description":"Nome do template","example":"appointment_reminder"},"language":{"type":"string","description":"Código de idioma do template","example":"pt_BR"},"category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"],"description":"Categoria do template conforme classificação da Meta","example":"UTILITY"},"components":{"type":"array","items":{"type":"object"},"description":"Componentes do template (cabeçalho, corpo, botões) no formato Meta"},"instance_id":{"type":"string","format":"uuid","description":"ID do número de WhatsApp ao qual o template pertence"}}},"UpdateTemplate":{"type":"object","description":"Dados para atualização parcial de um template — envie apenas os campos que deseja alterar. O status é redefinido para `DRAFT`.","properties":{"name":{"type":"string","description":"Nome do template"},"language":{"type":"string","description":"Código de idioma do template"},"category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"],"description":"Categoria do template"},"components":{"type":"array","items":{"type":"object"},"description":"Componentes do template no formato Meta"}}},"Campaign":{"type":"object","description":"Representa uma campanha de envio em massa de mensagens","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único da campanha"},"name":{"type":"string","description":"Nome da campanha","example":"Campanha Black Friday"},"status":{"type":"string","enum":["draft","scheduled","running","paused","completed","cancelled","failed"],"description":"Status atual da campanha","example":"completed"},"template_name":{"type":"string","nullable":true,"description":"Nome do template utilizado na campanha","example":"promo_blackfriday"},"template_language":{"type":"string","nullable":true,"description":"Código de idioma do template","example":"pt_BR"},"total_recipients":{"type":"integer","description":"Número de destinatários. Num rascunho, é a contagem esperada (contatos que receberiam a campanha se fosse confirmada agora), como a tela de Campanhas mostra.\n","example":150},"audience":{"type":"object","description":"Público da campanha, como está gravado.","properties":{"type":{"type":"string","enum":["all","filtered","manual","csv_import"],"description":"`filtered` = por etiquetas/filtros; `manual`/`csv_import` = lista de contatos."},"tag_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Etiquetas do público (quando filtrado por etiquetas)."},"contact_count":{"type":"integer","nullable":true,"description":"Tamanho da lista de contatos gravada (público `manual`/`csv_import`). `null` em `listarCampanhas`, que não lê a lista; use `buscarCampanha`.\n"}}},"recipients_materialized":{"type":"boolean","description":"`false` enquanto a campanha é rascunho: as linhas de destinatários só existem depois que o rascunho é confirmado no app, então `listarDestinatariosCampanha` devolve lista vazia. Use `total_recipients` para a contagem esperada.\n"},"sent_count":{"type":"integer","description":"Quantidade de mensagens enviadas","example":148},"delivered_count":{"type":"integer","description":"Quantidade de mensagens entregues","example":145},"read_count":{"type":"integer","description":"Quantidade de mensagens lidas","example":98},"failed_count":{"type":"integer","description":"Quantidade de mensagens com falha","example":2},"scheduled_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora agendada para início do envio"},"started_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora em que o envio foi iniciado"},"completed_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora em que o envio foi concluído"},"created_at":{"type":"string","format":"date-time","nullable":true,"description":"Data de criação da campanha"}}},"CampaignRecipient":{"type":"object","description":"Representa um destinatário individual de uma campanha","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do registro de destinatário"},"campaign_id":{"type":"string","format":"uuid","description":"ID da campanha"},"contact_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do contato destinatário"},"contact_name":{"type":"string","nullable":true,"description":"Nome do contato destinatário","example":"Maria Silva"},"phone":{"type":"string","nullable":true,"description":"Telefone do destinatário","example":"5511999998888"},"status":{"type":"string","enum":["pending","sent","delivered","read","failed","skipped"],"description":"Status de envio para este destinatário (`skipped` = o disparo pulou este destinatário sem enviar)","example":"delivered"},"sent_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora do envio"},"delivered_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora da entrega"},"read_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora da leitura"}}},"CampaignAnalytics":{"type":"object","description":"Métricas de desempenho de uma campanha","properties":{"campaign_id":{"type":"string","format":"uuid","description":"ID da campanha"},"total_recipients":{"type":"integer","description":"Número total de destinatários","example":150},"sent_count":{"type":"integer","description":"Quantidade de mensagens enviadas","example":148},"delivered_count":{"type":"integer","description":"Quantidade de mensagens entregues","example":145},"read_count":{"type":"integer","description":"Quantidade de mensagens lidas","example":98},"failed_count":{"type":"integer","description":"Quantidade de mensagens com falha","example":2},"delivery_rate":{"type":"number","format":"float","description":"Taxa de entrega em porcentagem","example":97.97},"read_rate":{"type":"number","format":"float","description":"Taxa de leitura em porcentagem","example":67.59}}},"ApiUsage":{"type":"object","description":"Estatísticas de uso da API para a chave autenticada","properties":{"period_days":{"type":"integer","description":"Número de dias do período analisado","example":7},"total_requests":{"type":"integer","description":"Total de requisições no período"},"total_errors":{"type":"integer","description":"Total de requisições com erro (status >= 400)"},"error_rate":{"type":"number","format":"float","description":"Taxa de erros em percentual","example":0.8},"avg_latency_ms":{"type":"integer","description":"Latência média em milissegundos","example":48},"endpoints":{"type":"array","description":"Detalhamento por endpoint (top 20)","items":{"type":"object","properties":{"path":{"type":"string","description":"Caminho do endpoint"},"requests":{"type":"integer","description":"Total de requisições"},"errors":{"type":"integer","description":"Total de erros"},"avg_latency_ms":{"type":"integer","description":"Latência média em milissegundos"}}}}}},"RateLimitTiers":{"type":"object","description":"Limites de requisição configurados por tipo de operação","properties":{"tiers":{"type":"object","additionalProperties":{"type":"object","properties":{"per_minute":{"type":"integer","nullable":true,"description":"Limite de requisições por minuto"},"per_day":{"type":"integer","nullable":true,"description":"Limite de requisições por dia"}}}}}},"UsageReportRequest":{"type":"object","description":"Corpo da requisição para reportar um evento de uso.","required":["category"],"properties":{"category":{"type":"string","description":"Categoria do evento de uso.","enum":["llm_tokens","mcp_tool_calls","whatsapp_sends","campaign_sends","document_gen","agent_runs"]},"subcategory":{"type":"string","nullable":true,"description":"Subcategoria opcional. Para `llm_tokens`, use o identificador do\nmodelo (ex.: `claude-sonnet-4`, `gpt-4.1-mini`) — usado para\ncalcular `cost_cents` automaticamente quando não enviado.\n","example":"claude-sonnet-4"},"input_tokens":{"type":"integer","minimum":0,"default":0,"description":"Tokens de entrada consumidos pelo evento (apenas `llm_tokens`)."},"output_tokens":{"type":"integer","minimum":0,"default":0,"description":"Tokens de saída produzidos pelo evento (apenas `llm_tokens`)."},"call_count":{"type":"integer","minimum":0,"default":0,"description":"Número de chamadas representadas por este evento."},"cost_cents":{"type":"integer","minimum":0,"nullable":true,"description":"Custo em centavos de USD. Quando omitido para `llm_tokens`, o servidor\nestima o custo a partir de `subcategory` (preço por modelo conhecido).\n"},"client_name":{"type":"string","nullable":true,"description":"Identificador opcional do cliente (ex.: `copilot-sidebar`, `mcp/claude`).","example":"copilot-sidebar"},"user_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do usuário que originou o evento (opcional)."}}},"UsageReportResponse":{"type":"object","description":"Confirmação de registro de evento de uso.","properties":{"recorded":{"type":"boolean","example":true}}},"UsageQuotaProjection":{"type":"object","nullable":true,"description":"Projeção mensal calculada quando há pelo menos 3 dias de histórico no mês.\nQuando há histórico insuficiente, retorna `{status: \"learning\", message: ...}`.\n","properties":{"status":{"type":"string","nullable":true,"example":"learning"},"message":{"type":"string","nullable":true},"daily_average_tokens":{"type":"integer","nullable":true},"projected_month_total":{"type":"integer","nullable":true},"projected_percent":{"type":"integer","nullable":true},"projected_exhaustion_date":{"type":"string","format":"date","nullable":true,"description":"Data ISO 8601 prevista para exaustão da quota, ou `null` se não houver exaustão antes do fim do mês."},"days_remaining":{"type":"integer","nullable":true},"on_track":{"type":"boolean","nullable":true}}},"UsageQuota":{"type":"object","description":"Status atual da quota mensal do escritório.","required":["allowed","plan_tier","usage","credits"],"properties":{"allowed":{"type":"boolean","description":"`true` quando novas operações que consomem quota podem prosseguir.\n`false` quando a quota foi excedida além do `grace_percent`.\n"},"warning":{"type":"string","nullable":true,"description":"Aviso opcional. Possíveis valores:\n- `approaching_limit` (≥ 80% da quota efetiva)\n- `quota_exhausted` (limite atingido, mas dentro do grace)\n- `quota_exceeded` (grace excedido)\n","enum":["approaching_limit","quota_exhausted","quota_exceeded"]},"plan_tier":{"type":"string","description":"Tier do plano contratado (ex.: `free`, `starter`, `pro`, `enterprise`).","example":"pro"},"usage":{"type":"object","required":["used","limit","percent","remaining"],"properties":{"used":{"type":"integer","description":"Tokens consumidos no mês corrente."},"limit":{"type":"integer","description":"Limite efetivo (quota mensal + saldo de créditos avulsos)."},"percent":{"type":"integer","description":"Percentual de uso em relação ao `limit`."},"remaining":{"type":"integer","description":"Tokens restantes."}}},"projection":{"$ref":"#/components/schemas/UsageQuotaProjection"},"credits":{"type":"object","required":["balance","effective_limit"],"properties":{"balance":{"type":"integer","description":"Saldo de créditos avulsos disponíveis."},"effective_limit":{"type":"integer","description":"Limite efetivo (quota + créditos)."}}}}},"UsageSummaryDayBucket":{"type":"object","properties":{"date":{"type":"string","format":"date"},"tokens":{"type":"integer"},"tool_calls":{"type":"integer"},"cost_cents":{"type":"integer"}}},"UsageSummaryCategoryBucket":{"type":"object","properties":{"tokens":{"type":"integer"},"calls":{"type":"integer"},"cost_cents":{"type":"integer"}}},"UsageSummary":{"type":"object","description":"Resumo agregado de uso para um período.","required":["period_days","totals","by_day","by_category","quota"],"properties":{"period_days":{"type":"integer","example":30},"totals":{"type":"object","required":["tokens","tool_calls","cost_cents","requests"],"properties":{"tokens":{"type":"integer"},"tool_calls":{"type":"integer"},"cost_cents":{"type":"integer"},"requests":{"type":"integer"}}},"by_day":{"type":"array","description":"Buckets diários ordenados por data decrescente.","items":{"$ref":"#/components/schemas/UsageSummaryDayBucket"}},"by_category":{"type":"object","description":"Mapa categoria → totais agregados no período.","additionalProperties":{"$ref":"#/components/schemas/UsageSummaryCategoryBucket"}},"quota":{"type":"object","required":["plan_tier","monthly_token_limit","is_unlimited"],"properties":{"plan_tier":{"type":"string"},"monthly_token_limit":{"type":"integer"},"is_unlimited":{"type":"boolean"}}}}},"JurisprudenciaSourceStatus":{"type":"object","properties":{"status":{"type":"string","description":"Estado da fonte. Valores possíveis: `configured`, `degraded`,\n`circuit_open`, `fallback_public_key`.\n","enum":["configured","degraded","circuit_open","fallback_public_key"]},"fetcher_mode":{"type":"string","nullable":true,"description":"Modo do fetcher (apenas `stj_scon`). Valores: `direct`, `chain:direct`,\n`flaresolverr`, `zenrows`.\n"}}},"JurisprudenciaTpuSnapshot":{"type":"object","description":"Metadados do snapshot da Tabela de Padronização de Assuntos (TPU).","properties":{"version":{"type":"string","nullable":true,"description":"Versão (data) do snapshot."},"total":{"type":"integer","nullable":true,"description":"Total de registros carregados."}}},"JurisprudenciaHealth":{"type":"object","description":"Status agregado das fontes externas usadas pela pesquisa de jurisprudência.\n","required":["status","service","stage","timestamp","sources"],"properties":{"status":{"type":"string","enum":["ok","degraded"],"description":"Estado agregado (`degraded` quando qualquer fonte está degradada ou tem circuit aberto)."},"service":{"type":"string","example":"cj-open-api"},"stage":{"type":"string","example":"production"},"timestamp":{"type":"string","format":"date-time"},"sources":{"type":"object","required":["stj_scon","lexml","datajud","tjpr"],"properties":{"stj_scon":{"$ref":"#/components/schemas/JurisprudenciaSourceStatus"},"lexml":{"$ref":"#/components/schemas/JurisprudenciaSourceStatus"},"datajud":{"$ref":"#/components/schemas/JurisprudenciaSourceStatus"},"tjpr":{"$ref":"#/components/schemas/JurisprudenciaSourceStatus"}}},"tpu_snapshot":{"type":"object","properties":{"assuntos":{"$ref":"#/components/schemas/JurisprudenciaTpuSnapshot"},"classes":{"$ref":"#/components/schemas/JurisprudenciaTpuSnapshot"}}},"circuit_breakers_open":{"type":"array","description":"Lista de chaves de circuit breaker atualmente abertas.","items":{"type":"string"}}}},"Automation":{"type":"object","description":"Resumo de uma automação","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"is_active":{"type":"boolean"},"instance_id":{"type":"string","format":"uuid"},"trigger_type":{"type":"string","nullable":true,"description":"Tipo do gatilho (chat_message, crm_stage, crm_tag, etc.)"},"action_count":{"type":"integer","description":"Número de ações na automação"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time","nullable":true}}},"AutomationDetail":{"type":"object","description":"Detalhes completos de uma automação incluindo o flow_json","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"is_active":{"type":"boolean"},"instance_id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"flow_json":{"type":"object","additionalProperties":true,"description":"Fluxo salvo, no formato de `FlowJson` (o mesmo de `criarAutomacao`). Na resposta é um objeto aberto: um fluxo gravado pelo painel pode trazer tipos e chaves que o esquema de entrada não lista."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time","nullable":true},"created_by":{"type":"string","format":"uuid","nullable":true},"updated_by":{"type":"string","format":"uuid","nullable":true}}},"FlowJson":{"type":"object","description":"Fluxo da automação no formato React Flow (grafo de nós + arestas).\nContrato canônico: services/open-api/src/models/flow_json.schema.json.\nUSE EXATAMENTE esta estrutura — NÃO invente chaves de topo\n(ex.: `event`, `filters`, `apply_tag`): o editor visual as rejeita e\na tela de automações quebra ao abrir.\nRegras: pode ser `{}` (rascunho vazio); se tiver nós, deve ter\nEXATAMENTE um nó `trigger`; cada nó precisa de `id`, `type`,\n`position {x,y}` e `data`; `trigger.data.triggerType` e\n`action.data.actionType` são obrigatórios; o conteúdo de `data.config`\ndepende do tipo (ver exemplos abaixo). Para ramificar o fluxo, use nós\n`condition` (Se/Senão) e `switch` (Escolha) com `version: 1` — o shape de\n`data.condition`/`data.switch` está na descrição de `data`, abaixo.\n","properties":{"version":{"type":"integer","enum":[0,1],"description":"0 = fluxo linear (padrão); 1 = DAG com nós condition/switch."},"nodes":{"type":"array","description":"Nós do grafo. Exatamente um deve ser do tipo `trigger`.","items":{"type":"object","required":["id","type","position","data"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["trigger","action","condition","switch"]},"position":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}}},"data":{"type":"object","description":"Conteúdo do nó. Para `trigger`: `{ triggerType, config }`.\nPara `action`: `{ actionType, config }`.\nconfig de AÇÕES (camelCase):\n- send_message     → { template }  (Meta, fora da janela de 24h) ou { message } (texto livre dentro da janela). Opcionais: delayMinutes, pauseAi\n  `delayMinutes` (também em send_template): inteiro JSON de 0 a 1440 ou texto só com dígitos\n  (\"15\"); ausente ou null = 0 (envia na hora). Conta a partir do disparo: 0, 1 e 2 mandam uma\n  mensagem por minuto. A chave antiga `delay` é recusada, e fração, sinal, texto vazio, booleano,\n  lista ou objeto também. Não vale em menu interativo nem em gatilho sent_message. Enquanto o\n  envio adiado não estiver liberado no servidor, maior que 0 volta 400 \"Delayed and deferred\n  sends are not enabled yet\". A mensagem atrasada vira linha `automation_pending` em\n  GET /v1/scheduled-messages e é cancelada se a equipe responder antes.\n- send_template    → { templateName, templateLanguage, templateParameters? {body_1, header_1, button_1, ...}, delayMinutes? }\n  (só API Oficial/Meta, modelo APROVADO; recusado em uazapi ao salvar). Modelo agendado sempre espera o horário.\n  Para responder com um arquivo da biblioteca de mídias do escritório:\n  `{ responseType: \"image\"|\"video\"|\"document\"|\"audio\", mediaId }`, onde `mediaId` é o\n  `unique_id` do arquivo. A categoria do arquivo tem que casar com o tipo (image→image,\n  video→video, document→pdf/document/planilha/arquivo, audio→audio). Opcional `caption`\n  (até 1024 caracteres, aceita variáveis; áudio não tem legenda) e, em documento,\n  `filename` (nome que o contato vê; default = nome do arquivo na biblioteca).\n  No Meta valem os limites do WhatsApp Cloud API (imagem JPEG/PNG até 5MB, vídeo MP4\n  H.264/AAC até 16MB, documento até 100MB) — acima disso o envio é recusado com motivo.\n  Para responder com um menu: `{ responseType: \"interactive\", interactive: { type:\n  \"button\"|\"list\"|\"poll\", text, buttons?|sections?|choices?, footer?, list_button? } }`.\n  Menu só funciona em instâncias uazapiGO. A resposta do contato chega como uma mensagem\n  normal carregando o título escolhido em `message_text`, então dá para ramificar com um\n  gatilho de palavra-chave (o tipo da mensagem varia por provedor).\n- create_tag       → { tagName, colorId? }   (cria a tag se não existir)\n- update_crm       → { field, value }   (field: stage|tag|responsible|accountable_id|contact_status;\n  com field=tag, `{ field: \"tag\", tagIds: [..] }` adiciona várias tags de uma vez)\n- stage_change     → { value }   (atalho de update_crm com field=stage)\n- add_annotation   → { annotationText, mentionedUserIds?: uuid[], sendMentionEmail?: boolean }\n  (`value` é aceito como alias; send_mention_email é alias de sendMentionEmail).\n  sendMentionEmail omitido vale true; false desliga somente o e-mail de menção,\n  sem alterar a notificação in-app nem as preferências do destinatário.\n- remove_tag       → { tagId }   (`value` é aceito como alias, é o que o motor lê primeiro;\n  `tagIds: [..]` remove várias tags de uma vez e tem precedência sobre os dois)\n- archive_conversation → { action: \"archive\" | \"unarchive\" }   (default: archive; QUALQUER outro\n  valor é tratado como DESARQUIVAR pelo motor, então um valor fora do par é recusado na escrita)\n- http_request / outgoing_webhook → { webhookUrl, webhookMethod, webhookHeaders?, webhookBody? }\n  `webhookBody` e `webhookHeaders` aceitam variáveis de automação (inclusive\n  `[prop.<chave>]`). A substituição acontece DENTRO do JSON já parseado, então\n  um valor com aspas ou quebra de linha não invalida o corpo. A URL NÃO aceita\n  variável. Em outgoing_webhook, `includeCustomProperties: true` acrescenta\n  `contact.properties` ao payload padrão (propriedades marcadas como sensíveis\n  ficam de fora; para enviá-las, cite `[prop.<chave>]` no body).\n- transfer_responsible → { transferMode (fixed|round_robin|sender|quota), targetUserId | targetUserIds | quotaParticipants, onlyIfUnassigned? }\n  `fixed` (o padrão, inclusive quando transferMode é omitido) EXIGE targetUserId; `round_robin`\n  exige targetUserIds com pelo menos DOIS membros, senão o motor recusa a transferência em\n  toda execução. `quota` (roleta por cota) exige quotaParticipants com ao menos um participante\n  `paid` (userId + dailyQuota inteiro ≥ 1); parceiros (`partner`) recebem o excedente.\n  `onlyIfUnassigned: true` só atribui se a conversa NÃO tiver responsável — nunca toma\n  um atendimento que já é de outro membro. Use SEMPRE que a automação distribuir leads\n  novos, senão ela rouba a conversa do closer quando o cliente responde o template dele.\n  (`transferMode: sender` atribui a quem enviou a mensagem do gatilho e já é sempre\n  \"só se não tiver responsável\".)\n- meta_conversions_api → { eventName }   (requer integração Meta CAPI conectada no escritório)\n  A Meta tem DOIS catálogos e eles quase não se sobrepõem. Escolha pelo tipo de conversa:\n  • Anúncio de clique para WhatsApp (única lista que credita a conversão ao anúncio):\n    Purchase|LeadSubmitted|QualifiedLead|InitiateCheckout|AddToCart|ViewContent|OrderCreated|\n    OrderShipped|OrderDelivered|OrderCanceled|OrderReturned|CartAbandoned|RatingProvided|ReviewProvided\n  • Site / CRM (conversas que não vieram de anúncio):\n    Lead|CompleteRegistration|Schedule|Subscribe|Purchase|ViewContent|Contact|CustomizeProduct|InitiateCheckout\n  Repare que `Lead` e `Schedule` NÃO existem no catálogo de anúncio: numa conversa vinda de\n  anúncio a conversão é entregue mesmo assim, porém sem vínculo com o anúncio.\n- set_contact_property → { propertyKey, value, overwriteManual? }\n  Grava uma propriedade personalizada no contato. `propertyKey` é a chave de uma\n  propriedade que JÁ existe no escritório (`GET /v1/contact-properties`) — a ação\n  não cria definição. `value` aceita variáveis e é validado/convertido conforme o\n  tipo da propriedade. Por padrão NÃO sobrescreve valor digitado à mão no painel\n  (`source='manual'`); use `overwriteManual: true` para a automação mandar.\n- advbox_create_customer → { advboxUserId, advboxOriginId }\n- advbox_create_task → { advboxResponsibleId, advboxTaskTypeId, advboxStartDate?, advboxDateDeadline?, advboxComments? }\n- send_flow → { flowId, flowCta?, flowBodyText? }\n  Envia um formulário do WhatsApp (Flows). `flowId` é o id de um formulário\n  do MESMO número da automação (`GET /v1/flows`); id inexistente ou de outro\n  número: 400 `invalid_flow_reference`. Para ATIVAR a automação (ou para\n  trocar o `flowId` de uma automação já ativa) o formulário precisa estar\n  `PUBLISHED` ou `THROTTLED` (400 `flow_not_published`); uma automação ativa\n  cujo formulário foi bloqueado ou descontinuado continua editável. Se o\n  formulário não puder ser consultado nessa hora: 503\n  `flow_lookup_unavailable` (tente de novo). `flowCta` é\n  o texto do botão (1–30 caracteres, sem emoji; padrão \"Abrir\"); `flowBodyText`\n  ≤ 1024 e aceita variáveis. Só números da API oficial da Meta (recusado na\n  escrita em uazapi) e só com a flag `whatsapp_flows_enabled` (sem ela: 403\n  `feature_disabled` ao salvar ou ativar); janela de 24h precisa estar aberta.\n  Um formulário NÃO pode ser atrasado nem adiado: `delayMinutes` acima de 0\n  neste nó, ou `schedule.outside: defer_messages` no gatilho, respondem 422\n  `flow_schedule_not_allowed`.\nIMPORTANTE — IDs reais: para ações que referenciam entidades\nexistentes, `value`/`tagId(s)`/`targetUserId(s)` devem ser IDs\nREAIS do escritório, NÃO nomes nem placeholders. Resolva antes\nde montar o config: update_crm(field=stage)/stage_change →\n`listarEtapasFunil`; update_crm(field=tag) e remove_tag →\n`listarTags`; update_crm(field=responsible)/transfer_responsible\n→ `listarMembros`. Um ID inventado é REJEITADO na criação\n(validado contra o escritório). (create_tag é exceção: aceita\num nome, pois cria a tag.)\nconfig de GATILHOS (camelCase):\n- custom_property_changed → { propertyKey, changeMode?, propertyValue? }\n  Dispara quando uma propriedade personalizada do contato muda — inclusive por\n  `PUT /v1/contacts/{id}/properties`. É o caminho de entrada mais barato para\n  quem já fala esta API e conhece o contato: não cria URL nem credencial nova.\n  `propertyKey` é OBRIGATÓRIO. A propriedade também precisa estar marcada como\n  gatilho de automação nas Configurações, senão a mudança nunca chega no motor.\n  `changeMode` ∈ any|filled|changed_to (default any); `changed_to` exige\n  `propertyValue`.\n- webhook_received → { identityPath, identityMode?, identityPropertyKey? }\n  Para quem NÃO fala esta API: Make, Zapier, o webhook de saída de um ERP. A\n  automação ganha uma URL própria (`POST /automations/webhook/{token}`), gerada\n  no editor; o token da URL é a credencial e gerar uma nova revoga a anterior na\n  hora. Como o corpo recebido não tem contato nenhum dentro, `identityPath` é\n  OBRIGATÓRIO: caminho pontilhado até o valor que identifica a pessoa (só nomes\n  de campo, sem curinga e sem índice de lista). `identityMode` ∈\n  phone|contact_id|custom_property (default phone); `custom_property` exige\n  `identityPropertyKey`. Se o valor não resolver para EXATAMENTE um contato\n  vivo desse número, a automação não roda (nunca escolhe uma conversa no chute) e\n  o recebimento vira execução `skipped` com `input_context.skip_reason`\n  (identity_not_resolved, identity_ambiguous, identity_invalid_phone (1 a 9\n  dígitos é curto demais; nenhum ou mais de 15 é inválido), contact_deleted,\n  contact_inactive, chat_deleted, chat_conflict, chat_not_found (contato sem\n  conversa com a criação desligada), phone_not_on_whatsapp, phone_is_this_number,\n  instance_disconnected, contact_creation_rate_limited, automation_inactive,\n  instance_deleted, instance_not_found, automations_disabled, non_python_backend,\n  chat_instance_mismatch, loop_breaker, trigger_no_longer_webhook). A URL responde\n  `{queued: false, reason}` nesses casos; na criação de contato recusada o\n  `reason` é o genérico `contact_not_created` e o motivo específico\n  (phone_not_on_whatsapp, phone_is_this_number, instance_disconnected,\n  contact_creation_rate_limited) fica só na execução.\n  Opcionais: `createContactIfMissing` (booleano JSON true/false; true só com\n  identityMode phone ou ausente): telefone sem contato neste número cria contato\n  (origem api) e conversa na etapa de entrada, e contato existente sem conversa\n  ganha uma, com teto por automação por hora;\n  na API Oficial a conversa nova só aceita modelo (texto livre falha com\n  `meta_new_conversation_template_only`). Só cria com o interruptor do servidor\n  `AUTOMATION_WEBHOOK_CREATE_CONTACT_ENABLED` ligado. Sem a opção, ou com o\n  interruptor desligado, telefone sem contato é\n  `identity_not_resolved`. `contactNamePath`: caminho (mesma linguagem do\n  identityPath) até o nome do contato criado. `idempotencyKeyPath`: caminho até um\n  id do evento no corpo; reentregas com o mesmo valor contam como o mesmo evento.\n  Nos dois, ausente, null ou texto em branco = não definido. O painel tem um\n  \"Testar com um exemplo\" que mostra se o contato seria achado ou criado, sem gravar.\n  O corpo recebido fica legível como `[webhook.<caminho>]` em mensagens e como\n  campo de condição `webhook.<caminho>`; só folhas escalares e listas de\n  escalares, no máximo 200 caminhos.\n- contract_signed → { } (sem configuração; dispara quando a ÚLTIMA assinatura de um documento entra)\n- flow_response → { flowId? }\n  Dispara quando o cliente responde um formulário do WhatsApp (Flows) ENVIADO pelo\n  Chat Jurídico neste número. `flowId` (opcional) restringe a um formulário; sem ele,\n  qualquer formulário dispara; um `flowId` inexistente ou de outro número responde\n  400 `invalid_flow_reference`, e sem a flag `whatsapp_flows_enabled` o gatilho\n  responde 403 `feature_disabled`. Respostas a formulários criados fora do Chat Jurídico\n  não disparam. As respostas ficam em `[flow_response.response.<campo>]`. A\n  resposta NÃO dispara os gatilhos de mensagem (chat_message, new_message, keyword…).\n- chat_message/new_message/sent_message/keyword_match → { keywords, matchingType }\n  `keywords` é uma STRING JSON (ex.: \"[\\\"imobiliário\\\"]\"); array nativo também é aceito.\n  matchingType ∈ any|contains|exact|starts_with|ends_with|regex (default contains).\n  `exact` compara a mensagem INTEIRA com o texto informado — use UMA chave só,\n  com o corpo completo (quebras de linha e emojis são preservados e CONTAM).\n  Ignora: CRLF vs LF, e espaços/linhas em branco no começo e no fim do texto todo.\n  NÃO ignora: espaço sobrando no fim de uma linha interna, espaço duplo interno.\n  Teto de 2000 caracteres — acima disso o gatilho nunca casa.\n  Cuidado: o corpo comparado é o RENDERIZADO, então texto com variável\n  (ex.: template Meta com parâmetro) nunca é idêntico — use `contains`.\n  TETO: no máximo 10 palavras-chave por gatilho (contagem BRUTA, igual à do\n  editor). Passar disso é 400. Em contains/exact/starts_with/ends_with a\n  comparação dobra caixa e acento por padrão, então \"Integração\" e\n  \"integracao\" casam a mesma mensagem: variação assim ocupa vaga do teto\n  sem comprar nada. Em matchingType any ou regex as keywords são IGNORADAS\n  pelo motor e não contam.\n  QUANDO trocar por regex: a partir de ~8 variações da MESMA intenção, ou\n  quando as variações são morfológicas (integrar/integração/integro/integram).\n  Para 3 ou 4 palavras distintas, keywords é melhor — e cuidado, porque\n  regex PERDE a normalização de acento que keywords tem: uma lista\n  [\"divórcio\",\"pensão\"] convertida ingenuamente para regex deixa de casar\n  \"divorcio\" e \"pensao\", que é como a maioria escreve no WhatsApp.\n  Ex. bom: em vez de [\"integração com advbox\", \"integrar advbox\",\n  \"como integro o advbox\", \"conectar advbox\", ...] use regexPattern\n  \"(integr|conect|vincul)\\w*[\\s\\S]{0,60}adv[ .-]?box\" — o \\w* cobre as\n  conjugações e o [\\s\\S] atravessa quebra de linha.\n  REGEX, o que o motor realmente faz (é o re do PYTHON, não JavaScript):\n  - Teto de 200 caracteres, e o padrão precisa COMPILAR. Os dois são 400 na\n    criação. Se o que você tem é uma frase e não um padrão, use contains.\n  - Sem quantificador aninhado: (a+)+, (a*)*, (a|a)+ são recusados como risco\n    de ReDoS EM TEMPO DE EXECUÇÃO, sem erro na criação: o gatilho salva e\n    nunca casa. Este é o único dos limites que ainda falha calado.\n  - Case-insensitive por padrão (caseSensitive true para diferenciar).\n  - Acento NAO é normalizado em regex, ao contrário do modo palavra-chave:\n    escreva as duas formas, ex. \"divorcio|divórcio\".\n  - \".\" NÃO atravessa quebra de linha, e mensagem de WhatsApp costuma ter\n    várias linhas: use [\\s\\S] no lugar de \".\" para ligar trechos.\n  - Sintaxe é Python: grupo nomeado é (?P<n>...), e não (?<n>...).\n  O mesmo vale para o operador regex dos blocos condicionais.\n  Opcionais: caseSensitive, accentSensitive (só vale fora de regex), firstMessageOnly,\n  minSilenceSeconds (só dispara em conversa nova ou após N s de silêncio, também serve de anti-spam;\n  vale só em chat_message, new_message, keyword_match e missed_call; sent_message e os demais ignoram),\n  firstMessageInChatOnly.\n  ATENÇÃO, são coisas DIFERENTES:\n  • `firstMessageInChatOnly: true` → dispara SÓ na PRIMEIRA mensagem da conversa, ou seja,\n    apenas quando o CONTATO abriu o atendimento (lead receptivo). Se o cliente está\n    respondendo a um template que o escritório enviou, a conversa não é nova e NÃO dispara.\n    É este o campo para \"só chats novos/receptivos\".\n  • `firstMessageOnly` NÃO é isso: é memória de execução (\"esta automação nunca rodou\n    neste chat\"), equivalente a sendOnce. Não use para detectar conversa nova.\n- crm_stage → { stage (ID via listarEtapasFunil) };  crm_tag → { tag (ID via listarTags), tagAction? (added|removed|both) }\n- missed_call → { callTypeFilter? (any|audio|video), minRingDurationSeconds?, includeBusy?, minSilenceSeconds? }\n- advbox_task_created/updated/completed → { advboxResponsibleId? }   (filtro opcional por responsável; alimentado pelo poller ADVBox, sem webhook)\nModificadores comuns a QUALQUER gatilho (todos opcionais):\n- sendOnce { enabled, scope: chat|contact }   (memória de envio único)\n- aiPolicy { mode: ignore|only_when_ai_would_not_answer|only_when_ai_inactive|pre_agent_greeting|send_and_pause_ai }\n- contactCriteria { profile: any|new_lead|imported_contact|has_lawsuits|no_lawsuits|advbox_client|not_advbox_client }\n- schedule { enabled, mode, outside?, windows }   (dias e horários; mora em data.config.schedule, NUNCA em data.schedule)\n  `enabled` booleano JSON (false = sem janela; outro tipo = malformado). `mode` only_inside (só\n  dispara dentro das janelas) | only_outside (só fora). `windows` com EXATAMENTE as 7 chaves\n  segunda, terca, quarta, quinta, sexta, sabado, domingo (sem acento); cada dia é uma lista (pode\n  ser vazia) de até 4 { start, end } em HH:MM; end pode ser \"24:00\"; start < end, sem atravessar a\n  meia-noite, sem sobreposição no mesmo dia (janelas encostadas valem). Início incluso, fim excluso.\n  Relógio: instante da mensagem ou do evento, no fuso do escritório; relógio ou fuso ilegível, ou\n  janela malformada, não dispara. `outside` skip (padrão: fora da janela o evento é descartado,\n  sem linha de execução) | defer_messages (só com only_inside e só com o envio adiado liberado no\n  servidor): dispara na hora, ações que não enviam rodam, e as mensagens esperam o início da\n  próxima janela (lead às 03:10 recebe às 07:00, 07:01, 07:02). Não vale com gatilho\n  sent_message nem com menu interativo. Janela que nunca dispara (only_inside sem horário\n  nenhum; only_outside com 00:00-24:00 nos 7 dias) salva em rascunho e é recusada ao ativar.\n  Exemplo (fora do horário comercial, seg a sex 08:00-12:00 e 14:00-18:00):\n  { \"enabled\": true, \"mode\": \"only_outside\", \"outside\": \"skip\", \"windows\": {\n    \"segunda\": [{\"start\":\"08:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\n    \"terca\": [{\"start\":\"08:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\n    \"quarta\": [{\"start\":\"08:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\n    \"quinta\": [{\"start\":\"08:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\n    \"sexta\": [{\"start\":\"08:00\",\"end\":\"12:00\"},{\"start\":\"14:00\",\"end\":\"18:00\"}],\n    \"sabado\": [], \"domingo\": [] } }\nVARIÁVEIS de texto: [nome_contato], [palavra_chave], [data], [hora], @dd/mm/yyyy, [prop.<chave>],\n[webhook.<caminho>]. [data]/[hora]/@dd/mm/yyyy usam o instante do evento em UTC no envio imediato\ne o instante do envio no fuso do escritório em mensagem atrasada ou adiada.\nconfig de CONDICIONAIS (blocos \"Se / Senão\" e \"Escolha\" — exigem version: 1):\n- condition (nó \"Se / Senão\") → data.condition = { version?: 1, root }.\n  `root` é uma árvore booleana; CADA nó tem EXATAMENTE um de:\n    all: [ ...subnós ]  (E)  |  any: [ ...subnós ]  (OU)  |\n    not: { ...subnó }   (negação)  |  rule: { field, op }\n  Campos válidos (rule.field / switch.subject.field): message_text,\n  message_type, from_me, contact_name, contact_phone, contact_id,\n  tags_ids, contact_tags_ids, matched_keywords, office_stage_id,\n  current_funnel_id, accountable_id, is_archived, event_type,\n  contact_has_name, is_customer, seconds_since_last_message,\n  local_hour (0 a 23, no fuso do escritório), local_weekday (segunda..domingo, sem acento),\n  scenario_context.agentEligibility.wouldAnswer,\n  scenario_context.message.source,\n  scenario_context.agentEligibility.details.contactIsAdvboxClient,\n  e as famílias dinâmicas custom_property.<chave> (propriedade do contato)\n  e webhook.<caminho> (corpo do Webhook Recebido), ambas texto.\n  Operadores (rule.op) por tipo de campo:\n    texto (message_text, contact_name, contact_phone, contact_id, custom_property.*, webhook.*): equals, not_equals, contains,\n      not_contains, starts_with, ends_with, regex, is_empty,\n      is_not_empty, length_gt, length_lt, in, not_in\n    enum (message_type, office_stage_id, current_funnel_id,\n      accountable_id, event_type, local_weekday, scenario_context.message.source):\n      equals, not_equals, in, not_in, is_empty, is_not_empty\n    número (local_hour, seconds_since_last_message): equals, not_equals, gt, gte,\n      lt, lte, between (value = [mínimo, máximo] com mínimo <= máximo; para \"fora do\n      expediente\" use any com dois between, nunca between 18 e 8)\n    lista (tags_ids, contact_tags_ids, matched_keywords): includes,\n      not_includes, includes_any, includes_all, size_eq, size_gt,\n      size_lt, is_empty, is_not_empty\n    booleano (from_me, is_archived, contact_has_name, is_customer,\n      scenario_context.agentEligibility.wouldAnswer,\n      scenario_context.agentEligibility.details.contactIsAdvboxClient): is_true, is_false\n  Saídas: o nó condition tem DUAS saídas — as arestas devem usar\n  sourceHandle \"true\" ou \"false\" (uma aresta por handle).\n  Limites: profundidade da árvore ≤ 16, ≤ 64 regras.\n- switch (nó \"Escolha\") → data.switch = { subject: { field },\n  cases: [ { id, values: [...] } ], default?: {} }. Cada aresta de\n  saída usa sourceHandle = o `id` de um case declarado, ou \"default\".\n  Até 32 cases.\nIMPORTANTE — IDs reais também nas condicionais: valores para\noffice_stage_id/current_funnel_id/accountable_id/tags_ids devem ser\nIDs REAIS do escritório (resolva via listarEtapasFunil/listarTags/\nlistarMembros), nunca nomes.\nLimites do grafo com condicionais: ≤ 30 nós, ≤ 60 arestas, sem ciclos,\nUMA aresta de entrada por nó (ramos se juntam encadeando ações\ndownstream, não reentrando num nó compartilhado).\n","properties":{"triggerType":{"type":"string","enum":["chat_message","new_message","keyword_match","crm_stage","crm_tag","sent_message","missed_call","advbox_task_created","advbox_task_updated","advbox_task_completed","custom_property_changed","webhook_received","contract_signed","flow_response"]},"actionType":{"type":"string","enum":["send_message","send_template","update_crm","add_annotation","create_tag","remove_tag","archive_conversation","stage_change","outgoing_webhook","http_request","transfer_responsible","meta_conversions_api","advbox_create_customer","advbox_create_task","set_contact_property","send_flow"]},"config":{"type":"object"},"condition":{"type":"object","description":"Presente em nós `condition` (Se/Senão). Ver \"config de CONDICIONAIS\" na descrição de `data` acima."},"switch":{"type":"object","description":"Presente em nós `switch` (Escolha). Ver \"config de CONDICIONAIS\" na descrição de `data` acima."}}}}}},"edges":{"type":"array","description":"Conexões entre nós. Conectam o trigger às ações.","items":{"type":"object","required":["id","source","target"],"properties":{"id":{"type":"string"},"source":{"type":"string"},"target":{"type":"string"},"sourceHandle":{"type":"string","nullable":true},"targetHandle":{"type":"string","nullable":true},"type":{"type":"string"}}}}},"example":{"version":0,"nodes":[{"id":"trigger-1","type":"trigger","position":{"x":80,"y":80},"data":{"triggerType":"chat_message","config":{"keywords":"[\"imobiliário\"]","matchingType":"contains"}}},{"id":"action-1","type":"action","position":{"x":476,"y":80},"data":{"actionType":"create_tag","config":{"tagName":"Imobiliário","colorId":"1"}}}],"edges":[{"id":"edge-trigger-1-action-1","source":"trigger-1","target":"action-1","type":"smoothstep"}]}},"CreateAutomation":{"type":"object","description":"Dados para criar uma nova automação com gatilhos e ações","required":["name","instance_id"],"properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Nome da automação"},"description":{"type":"string","maxLength":1000,"nullable":true,"description":"Descrição opcional"},"instance_id":{"type":"string","format":"uuid","description":"ID do número WhatsApp associado"},"flow_json":{"$ref":"#/components/schemas/FlowJson"},"is_active":{"type":"boolean","default":false,"description":"Se true, ativa a automação imediatamente"}}},"UpdateAutomation":{"type":"object","description":"Dados para atualizar uma automação existente (gatilhos, ações, condições)","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"description":{"type":"string","maxLength":1000,"nullable":true},"flow_json":{"$ref":"#/components/schemas/FlowJson"},"is_active":{"type":"boolean"}}},"AutomationExecution":{"type":"object","description":"Registro de uma execução de automação","properties":{"id":{"type":"string","format":"uuid"},"automation_id":{"type":"string","format":"uuid"},"chat_id":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","description":"Texto aberto: `success`, `failed`, `error`, `partial`, `skipped`, `suggested`, `toggled_on`, `toggled_off` (os dois últimos são eventos de ligar/desligar)."},"execution_time_ms":{"type":"integer","nullable":true},"actions_taken":{"type":"array","description":"Ações que deram certo, cada uma `{actionType, result}`.","items":{"type":"object","additionalProperties":true,"properties":{"actionType":{"type":"string","nullable":true},"result":{"type":"object","nullable":true,"additionalProperties":true,"description":"Resultado da ação. Em envio, `status` pode ser `sent`, `scheduled`, `already_scheduled` e outros (texto aberto)."}}}},"actions_failed":{"type":"array","description":"Ações que falharam, cada uma `{actionType, error, result?}`.","items":{"type":"object","additionalProperties":true,"properties":{"actionType":{"type":"string","nullable":true},"error":{"type":"string","nullable":true},"result":{"type":"object","nullable":true,"additionalProperties":true}}}},"triggered_by":{"type":"string","nullable":true},"error_message":{"type":"string","nullable":true},"executed_at":{"type":"string","format":"date-time","nullable":true},"input_context":{"type":"object","nullable":true,"additionalProperties":true,"description":"Contexto do evento que disparou. Em Webhook Recebido ignorado traz `skip_reason`; `webhook.contact_created`/`webhook.conversation_created` dizem se o webhook criou algo."}}},"AutomationExecutionStats":{"type":"object","description":"Estatísticas agregadas de execuções","properties":{"total_executions":{"type":"integer"},"successful":{"type":"integer"},"failed":{"type":"integer"},"success_rate":{"type":"number","description":"Taxa de sucesso em porcentagem (0-100): successful / total_executions × 100. total_executions inclui skipped e suggested (não são sucesso nem falha); para saúde, use successful / (successful + failed)."},"avg_execution_time_ms":{"type":"number","nullable":true}}},"Member":{"type":"object","description":"Membro do escritório (informações básicas)","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do membro"},"name":{"type":"string","description":"Nome completo do membro","example":"João Silva"},"role":{"type":"string","enum":["admin","member"],"description":"Papel do membro no escritório","example":"admin"}}},"Department":{"type":"object","description":"Departamento/fila do escritório (destino de delegação de conversas)","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do departamento"},"name":{"type":"string","description":"Nome do departamento","example":"Trabalhista"}}},"ContactPropertyDefinition":{"type":"object","description":"Definição de um campo personalizado do contato (visível no painel)","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string","description":"Chave usada para gravar o valor (PUT /v1/contacts/{id}/properties)","example":"numero_atendimento"},"name":{"type":"string","example":"Número de atendimento"},"type":{"type":"string","enum":["text","number","email","phone","url","date","datetime","boolean","select","multi_select","file","rich_text"]},"required":{"type":"boolean"},"options":{"type":"array","nullable":true,"description":"Opções válidas (para type select/multi_select)","items":{"type":"object"}},"category":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"sensitive":{"type":"boolean","description":"Campo sensível (mascarado no painel até ser revelado)"},"prefix":{"type":"string","nullable":true,"description":"Prefixo de exibição (type number)"},"suffix":{"type":"string","nullable":true,"description":"Sufixo de exibição (type number)"}}},"ContactPropertyWriteResult":{"allOf":[{"$ref":"#/components/schemas/ContactPropertyDefinition"},{"type":"object","properties":{"reactivated":{"type":"boolean","description":"true quando um campo excluído com a mesma key/name foi reativado"}}}]},"ContactPropertyOptionInput":{"description":"Rótulo da opção (string) ou objeto. Em edição, `value` identifica uma opção existente para renomeá-la mantendo o valor; opções novas nunca recebem `value`.","oneOf":[{"type":"string","minLength":1,"maxLength":100},{"type":"object","required":["label"],"additionalProperties":false,"properties":{"label":{"type":"string","minLength":1,"maxLength":100},"value":{"type":"string"},"color":{"type":"string","maxLength":32}}}]},"CreateContactProperty":{"type":"object","required":["name","type"],"additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Nome exibido no painel (único no escritório)"},"type":{"type":"string","enum":["text","rich_text","number","email","phone","url","date","boolean","select","multi_select"]},"key":{"type":"string","pattern":"^[a-z0-9_]{1,50}$","description":"Opcional; gerada a partir do name. Imutável depois de criada."},"description":{"type":"string","maxLength":500},"category":{"type":"string","maxLength":50,"description":"Padrão \"general\""},"required":{"type":"boolean","default":false},"sensitive":{"type":"boolean","default":false,"description":"Mascara o valor no painel até ser revelado"},"options":{"type":"array","maxItems":200,"description":"Obrigatório para select/multi_select; recusado nos outros tipos","items":{"$ref":"#/components/schemas/ContactPropertyOptionInput"}},"prefix":{"type":"string","maxLength":20,"description":"Só para type number"},"suffix":{"type":"string","maxLength":20,"description":"Só para type number"}}},"UpdateContactProperty":{"type":"object","additionalProperties":false,"properties":{"key":{"type":"string","description":"NÃO pode ser alterada (os valores gravados dependem dela); enviar retorna 422."},"type":{"type":"string","description":"NÃO pode ser alterado (reclassificaria os valores gravados); enviar retorna 422."},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"category":{"type":"string","maxLength":50},"required":{"type":"boolean"},"sensitive":{"type":"boolean"},"options":{"type":"array","maxItems":200,"description":"Lista completa de opções (substitui a atual; ver descrição)","items":{"$ref":"#/components/schemas/ContactPropertyOptionInput"}},"prefix":{"type":"string","maxLength":20},"suffix":{"type":"string","maxLength":20}}},"ContactPropertyValue":{"type":"object","description":"Valor de uma propriedade personalizada de um contato","properties":{"key":{"type":"string","example":"numero_atendimento"},"value":{"description":"Valor (string, número, booleano ou array, conforme o tipo)"},"source":{"type":"string","nullable":true,"description":"Origem do valor (api, manual, agent, automation, import, ...)","example":"api"},"updated_at":{"type":"string","format":"date-time","nullable":true}}},"ContactPropertyUpsertResult":{"type":"object","properties":{"updated":{"type":"array","description":"Chaves gravadas com sucesso","items":{"type":"string"}},"cleared":{"type":"array","description":"Chaves LIMPAS (valor removido). Um valor vazio (null, \"\" ou []) apaga a propriedade do contato — a linha é excluída, igual ao painel — em vez de gravar um valor vazio.","items":{"type":"string"}},"unknown_keys":{"type":"array","description":"Chaves sem definição no escritório (não gravadas)","items":{"type":"string"}},"invalid":{"type":"array","description":"Valores rejeitados por tipo (não gravados)","items":{"type":"object","properties":{"key":{"type":"string"},"error":{"type":"string"}}}},"skipped_manual":{"type":"array","description":"Chaves NÃO gravadas por já terem um valor editado manualmente no painel (override humano que a API não sobrescreve). Não é erro.","items":{"type":"string"}}}},"Annotation":{"type":"object","description":"Anotação interna em uma conversa","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único da anotação"},"chat_id":{"type":"string","format":"uuid","description":"ID da conversa"},"text":{"type":"string","description":"Texto da anotação","example":"Cliente solicitou documentação adicional"},"created_by":{"type":"string","format":"uuid","nullable":true,"description":"ID do usuário que criou a anotação"},"is_edited":{"type":"boolean","description":"Se a anotação foi editada"},"created_at":{"type":"string","format":"date-time","description":"Data de criação"},"send_email":{"type":"boolean","default":false,"description":"Intenção de envio por e-mail persistida nesta anotação; anotações históricas sem intenção explícita retornam false."}}},"CreateAnnotation":{"type":"object","description":"Dados para criação de uma anotação","required":["text"],"properties":{"send_email":{"type":"boolean","default":true,"description":"Intenção do autor para e-mails desta anotação. false desliga somente o e-mail; a notificação in-app é independente. Não altera as preferências do destinatário. Aceita apenas boolean JSON, não strings nem null.","example":false},"text":{"type":"string","minLength":1,"maxLength":5000,"description":"Texto da anotação (1-5000 caracteres)","example":"Cliente solicitou documentação adicional para o processo"},"mentions":{"type":"array","maxItems":50,"items":{"type":"string","format":"uuid"},"description":"IDs (uuid) de membros do escritório a mencionar. Também é possível embutir menções no texto no formato `@[uuid|Nome]`; as duas fontes são combinadas e deduplicadas, com limite de 50 destinatários distintos. A entrega (in-app e e-mail) depende das preferências e da ativação do produtor de notificações. IDs fora do escritório ou de membros stealth são ignorados; falha na consulta de membros salva a anotação sem menções.","example":["3f9a1b2c-0000-0000-0000-000000000001"]}},"example":{"text":"Revisar documentos @[3f9a1b2c-0000-0000-0000-000000000001|Ana]","mentions":["3f9a1b2c-0000-0000-0000-000000000001"],"send_email":false}},"UpdateAnnotation":{"type":"object","description":"Dados para atualização de uma anotação","required":["text"],"properties":{"text":{"type":"string","minLength":1,"maxLength":5000,"description":"Novo texto da anotação","example":"Texto atualizado da anotação"}}},"MessageDraft":{"type":"object","description":"Rascunho de mensagem pendente em uma conversa","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do rascunho"},"conversation_id":{"type":"string","format":"uuid","description":"ID da conversa associada"},"draft_text":{"type":"string","description":"Texto do rascunho","example":"Olá, gostaria de agendar uma reunião para discutir o processo."},"created_at":{"type":"string","format":"date-time","description":"Data de criação"},"modified_at":{"type":"string","format":"date-time","description":"Data da última modificação"}}},"SaveDraftRequest":{"type":"object","description":"Dados para criar ou atualizar um rascunho","required":["draft_text"],"properties":{"draft_text":{"type":"string","minLength":1,"maxLength":10000,"description":"Texto do rascunho (1-10000 caracteres)","example":"Olá, gostaria de agendar uma reunião para discutir o processo."}}},"Shortcut":{"type":"object","description":"Atalho de mensagem para respostas rápidas","properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do atalho"},"trigger":{"type":"string","description":"Gatilho do atalho (ex. \"/saudacao\")","example":"/saudacao"},"message":{"type":"string","description":"Texto da mensagem associada ao atalho","example":"Olá! Como posso ajudar?"},"created_at":{"type":"string","format":"date-time","description":"Data de criação"},"allowed_group_ids":{"type":"array","nullable":true,"readOnly":true,"items":{"type":"string","format":"uuid"},"description":"Grupos (departamentos) que enxergam este atalho. null ou vazio = visível para todos. Filtro de conveniência de UI, não fronteira de segurança."},"allowed_user_ids":{"type":"array","nullable":true,"readOnly":true,"items":{"type":"string","format":"uuid"},"description":"Usuários que enxergam este atalho. null ou vazio = visível para todos. CAMPO RESERVADO — ainda NÃO é aplicado por nenhum consumidor: a filtragem em produção usa exclusivamente allowed_group_ids. Não use este campo para decidir visibilidade até que a regra de composição seja implementada e documentada. Filtro de conveniência de UI, não fronteira de segurança."}}},"CreateShortcut":{"type":"object","description":"Dados para criação de um atalho de mensagem","required":["trigger","message"],"properties":{"trigger":{"type":"string","minLength":1,"maxLength":50,"description":"Gatilho do atalho (1-50 caracteres)","example":"/saudacao"},"message":{"type":"string","minLength":1,"maxLength":2000,"description":"Texto da mensagem (1-2000 caracteres)","example":"Olá! Como posso ajudar você hoje?"}}},"UpdateShortcut":{"type":"object","description":"Dados para atualização de um atalho","properties":{"trigger":{"type":"string","minLength":1,"maxLength":50,"description":"Novo gatilho do atalho"},"message":{"type":"string","minLength":1,"maxLength":2000,"description":"Novo texto da mensagem"}}},"CreateCampaignRequest":{"type":"object","description":"Dados para criar uma nova campanha de envio em massa via WhatsApp","required":["name","instance_id"],"properties":{"name":{"type":"string","minLength":1,"maxLength":255,"description":"Nome da campanha"},"template_name":{"type":"string","description":"Nome do template WhatsApp aprovado"},"template_language":{"type":"string","default":"pt_BR","description":"Código de idioma do template"},"instance_id":{"type":"string","format":"uuid","description":"ID do número WhatsApp para envio"},"recipient_tag_ids":{"type":"array","maxItems":500,"items":{"type":"string","format":"uuid"},"description":"IDs das etiquetas; o público são os contatos com qualquer uma delas. Não use junto com recipient_contact_ids."},"recipient_contact_ids":{"type":"array","maxItems":10000,"items":{"type":"string","format":"uuid"},"description":"IDs dos contatos do número instance_id. Não use junto com recipient_tag_ids."}}},"UpdateCampaignRequest":{"type":"object","description":"Dados para atualizar uma campanha existente (nome, template, destinatários, data). O público e o template só mudam em rascunho salvo; a data só muda em campanha já agendada (um rascunho é agendado no app). Uma campanha de um número fora do escopo da chave responde 404.\n","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"template_name":{"type":"string"},"template_language":{"type":"string"},"scheduled_at":{"type":"string","format":"date-time","description":"Nova data de uma campanha com status `scheduled`, com fuso (ex. `2026-10-20T10:00:00-03:00`)."},"recipient_tag_ids":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"string","format":"uuid"},"description":"Só em rascunho. Não use junto com recipient_contact_ids."},"recipient_contact_ids":{"type":"array","minItems":1,"maxItems":10000,"items":{"type":"string","format":"uuid"},"description":"Só em rascunho; contatos do número da campanha. Não use junto com recipient_tag_ids."}}},"AgentSession":{"type":"object","description":"Sessão de interação do agente IA com um contato","properties":{"id":{"type":"string","format":"uuid"},"state":{"type":"string","enum":["ongoing","completed"],"nullable":true},"contact_id":{"type":"string","format":"uuid","nullable":true},"chat_id":{"type":"string","format":"uuid","nullable":true},"instance_id":{"type":"string","format":"uuid","nullable":true},"agent_id":{"type":"string","format":"uuid","nullable":true},"agent_type":{"type":"string","nullable":true,"description":"Tipo do agente (ex. sdr)"},"summary":{"type":"string","nullable":true,"description":"Resumo gerado pelo agente sobre a conversa"},"lead_classification":{"type":"string","nullable":true,"description":"Classificação do lead (ex. QUALIFICADO)"},"current_stage":{"type":"string","nullable":true},"model":{"type":"string","nullable":true,"description":"Modelo de IA utilizado"},"prompt_version":{"type":"string","nullable":true},"rating":{"type":"integer","nullable":true,"description":"Avaliação do usuário (0-5)"},"user_feedback":{"type":"string","nullable":true},"collected_info":{"type":"object","nullable":true,"description":"Dados coletados pelo agente (nome, motivação, resumo do caso)"},"collected_data":{"type":"object","nullable":true},"qualification_data":{"type":"object","nullable":true},"transfer_summary":{"type":"object","nullable":true,"description":"Resumo da transferência para humano"},"existing_client":{"type":"boolean","nullable":true},"completed_at":{"type":"string","format":"date-time","nullable":true},"completed_reason":{"type":"string","nullable":true,"description":"Motivo da conclusão (ex. Assumed by user, Inactivity)"},"transfered_at":{"type":"string","format":"date-time","nullable":true},"greeted_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"},"modified_at":{"type":"string","format":"date-time","nullable":true}}},"SessionEvaluation":{"type":"object","description":"Avaliação automática de qualidade de uma sessão","properties":{"id":{"type":"string","format":"uuid"},"session_id":{"type":"string","format":"uuid"},"evaluated_at":{"type":"string","format":"date-time"},"evaluated_by":{"type":"string","format":"uuid","nullable":true},"overall_score":{"type":"integer","nullable":true,"description":"Pontuação geral (1-10)"},"customer_satisfaction_score":{"type":"integer","description":"Satisfação do cliente (1-10)"},"customer_satisfaction_comment":{"type":"string","nullable":true},"response_humanization_score":{"type":"integer","description":"Humanização da resposta (1-10)"},"response_humanization_comment":{"type":"string","nullable":true},"triage_effectiveness_score":{"type":"integer","description":"Eficácia da triagem (1-10)"},"triage_effectiveness_comment":{"type":"string","nullable":true},"conversation_flow_score":{"type":"integer","description":"Fluxo da conversa (1-10)"},"conversation_flow_comment":{"type":"string","nullable":true},"limits_and_escalation_score":{"type":"integer","nullable":true,"description":"Limites e escalonamento (1-10)"},"limits_and_escalation_comment":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"SessionFollowUp":{"type":"object","description":"Follow-up agendado pelo agente para uma sessão","properties":{"id":{"type":"string","format":"uuid"},"session_id":{"type":"string","format":"uuid"},"chat_id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","format":"uuid"},"follow_up_number":{"type":"integer","description":"Número sequencial do follow-up"},"status":{"type":"string","enum":["scheduled","processing","sent","responded","cancelled","failed"]},"send_at":{"type":"string","format":"date-time","description":"Data/hora agendada para envio"},"sent_at":{"type":"string","format":"date-time","nullable":true},"responded_at":{"type":"string","format":"date-time","nullable":true},"cancelled_reason":{"type":"string","nullable":true},"generated_message":{"type":"string","nullable":true,"description":"Texto da mensagem gerada pelo agente"},"last_agent_message_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"}}},"AgentLog":{"type":"object","description":"Log de execução do agente","properties":{"id":{"type":"string","format":"uuid"},"chat_id":{"type":"string","format":"uuid"},"event_type":{"type":"integer","nullable":true},"comment":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"AgentStatus":{"type":"object","description":"Status do agente IA em uma conversa","properties":{"conversation_id":{"type":"string","format":"uuid"},"agent_is_active":{"type":"boolean","description":"Se o agente IA está ativo na conversa"},"is_waiting_human":{"type":"boolean","description":"Se a conversa está aguardando atendimento humano"},"active_session":{"type":"object","nullable":true,"description":"Sessão ativa do agente (se houver)","properties":{"unique_id":{"type":"string","format":"uuid"},"state":{"type":"string"},"agent_type":{"type":"string"},"current_stage":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}}}},"SimulationSession":{"type":"object","description":"Sessão de simulação do agente (sandbox)","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid","nullable":true},"instance_id":{"type":"string","format":"uuid","nullable":true},"started_by":{"type":"string","format":"uuid","nullable":true},"agent_id":{"type":"string","format":"uuid","nullable":true},"agent_type":{"type":"string","nullable":true},"state":{"type":"string","enum":["ongoing","completed"],"nullable":true},"simulation_purpose":{"type":"string","nullable":true},"contact_name":{"type":"string","nullable":true},"summary":{"type":"string","nullable":true},"lead_classification":{"type":"string","nullable":true},"current_stage":{"type":"string","nullable":true},"collected_info":{"type":"object","nullable":true},"metadata":{"type":"object","nullable":true},"greeted_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"SimulationMessage":{"type":"object","description":"Mensagem em uma sessão de simulação","properties":{"id":{"type":"string","format":"uuid"},"simulation_id":{"type":"string","format":"uuid"},"agent_id":{"type":"string","format":"uuid","nullable":true},"sender_type":{"type":"string","description":"Remetente (user ou agent)"},"message_text":{"type":"string"},"message_type":{"type":"string","default":"text"},"audio_transcription":{"type":"string","nullable":true},"sent_at":{"type":"string","format":"date-time"}}},"AnalyticsPeriod":{"type":"object","description":"Período da consulta analítica","properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}},"DashboardMetrics":{"type":"object","description":"Métricas do dashboard do escritório","properties":{"total_interactions":{"type":"integer","description":"Total de interações no período","example":1234},"total_interactions_change":{"type":"number","nullable":true,"description":"Variação percentual em relação ao período anterior (requer `compare=true`)","example":12.5},"active_users":{"type":"integer","description":"Quantidade de membros com interações no período","example":8},"active_users_change":{"type":"number","nullable":true,"description":"Variação percentual de usuários ativos","example":-5.0},"chats_with_activity":{"type":"integer","description":"Conversas que tiveram interações no período","example":342},"interactions_today":{"type":"integer","description":"Interações registradas hoje","example":89},"most_active_stage":{"type":"string","nullable":true,"description":"Nome da etapa do funil com mais interações","example":"Qualificação"},"top_interaction_type":{"type":"string","nullable":true,"description":"Tipo de interação mais frequente","example":"stage_change"},"average_interactions_per_chat":{"type":"number","nullable":true,"description":"Média de interações por conversa","example":3.6},"period":{"$ref":"#/components/schemas/AnalyticsPeriod"}}},"TeamAnalytics":{"type":"object","description":"Métricas de desempenho da equipe","properties":{"members":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"user_name":{"type":"string","nullable":true},"total_interactions":{"type":"integer"},"interaction_breakdown":{"type":"object","additionalProperties":{"type":"integer"},"description":"Contagem por tipo de interação"},"chats_modified":{"type":"integer","description":"Conversas em que o membro atuou"},"avg_interactions_per_day":{"type":"number","nullable":true},"top_interaction_type":{"type":"string","nullable":true}}}},"total_interactions":{"type":"integer"},"period":{"$ref":"#/components/schemas/AnalyticsPeriod"}}},"StageAnalytics":{"type":"object","description":"Métricas de etapas do funil","properties":{"stages":{"type":"array","items":{"type":"object","properties":{"stage_id":{"type":"string","format":"uuid"},"stage_name":{"type":"string"},"stage_order":{"type":"integer","nullable":true},"chat_count":{"type":"integer","description":"Conversas atualmente nesta etapa"},"total_interactions":{"type":"integer","description":"Interações nas conversas desta etapa no período"},"avg_time_in_stage_days":{"type":"number","nullable":true,"description":"Tempo médio de permanência na etapa (dias)"},"conversion_rate":{"type":"number","nullable":true,"description":"Taxa de conversão para próxima etapa (%)"},"top_interaction_types":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"count":{"type":"integer"}}}}}}},"total_chats":{"type":"integer"},"period":{"$ref":"#/components/schemas/AnalyticsPeriod"}}},"ResponseTimeData":{"type":"object","description":"Dados de tempo de resposta (TPR/TMR)","properties":{"summary":{"type":"object","properties":{"tpr_avg_seconds":{"type":"number","nullable":true,"description":"Tempo médio de primeira resposta (segundos)"},"tpr_median_seconds":{"type":"number","nullable":true,"description":"Mediana do tempo de primeira resposta"},"tmr_avg_seconds":{"type":"number","nullable":true,"description":"Tempo médio de resposta (segundos)"},"tmr_median_seconds":{"type":"number","nullable":true,"description":"Mediana do tempo de resposta"},"total_chats":{"type":"integer"},"total_response_pairs":{"type":"integer","description":"Pares mensagem-resposta analisados"}}},"by_period":{"type":"array","items":{"type":"object","properties":{"period":{"type":"string"},"avg_seconds":{"type":"number","nullable":true},"median_seconds":{"type":"number","nullable":true},"response_count":{"type":"integer"}}}},"by_user":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"user_name":{"type":"string","nullable":true},"avg_seconds":{"type":"number","nullable":true},"median_seconds":{"type":"number","nullable":true},"response_count":{"type":"integer"}}}},"by_instance":{"type":"array","items":{"type":"object","properties":{"instance_id":{"type":"string","format":"uuid"},"avg_seconds":{"type":"number","nullable":true},"median_seconds":{"type":"number","nullable":true},"response_count":{"type":"integer"}}}},"period":{"$ref":"#/components/schemas/AnalyticsPeriod"}}},"InteractionLog":{"type":"object","description":"Registro de interação no log de auditoria","properties":{"id":{"type":"string","format":"uuid"},"chat_id":{"type":"string","format":"uuid"},"interaction_type":{"type":"string","description":"Tipo da interação (stage_change, tag_add, tag_remove, etc.)","example":"stage_change"},"actor_user_id":{"type":"string","format":"uuid","nullable":true},"actor_user_name":{"type":"string","nullable":true},"actor_type":{"type":"string","nullable":true,"description":"Tipo do ator (user, automation, agent)"},"field_name":{"type":"string","nullable":true},"old_value":{"nullable":true,"description":"Valor anterior (JSON)"},"new_value":{"nullable":true,"description":"Novo valor (JSON)"},"context":{"type":"object","nullable":true},"automation_id":{"type":"string","format":"uuid","nullable":true},"agent_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"ConversationAnalytics":{"type":"object","description":"Métricas analíticas de uma conversa","properties":{"id":{"type":"string","format":"uuid"},"contact_name":{"type":"string","nullable":true},"contact_phone":{"type":"string","nullable":true},"stage_name":{"type":"string","nullable":true},"total_interactions":{"type":"integer"},"interaction_breakdown":{"type":"object","additionalProperties":{"type":"integer"}},"unique_users":{"type":"integer","description":"Membros da equipe que interagiram na conversa"},"first_interaction_at":{"type":"string","format":"date-time","nullable":true},"last_interaction_at":{"type":"string","format":"date-time","nullable":true}}},"AdMetrics":{"type":"object","description":"Métricas de anúncios Click-to-WhatsApp (CTWA)","properties":{"total_ad_contacts":{"type":"integer","description":"Total de contatos originados de anúncios","example":25},"platform_distribution":{"type":"object","additionalProperties":{"type":"integer"},"description":"Contagem por plataforma (Instagram, Facebook, Google, Unknown)","example":{"Instagram":15,"Facebook":10}},"activation_rate":{"type":"number","nullable":true,"description":"Percentual de janelas CTWA ativadas","example":60.0},"missed_window_count":{"type":"integer","description":"Contatos que não tiveram a janela CTWA ativada","example":10},"period":{"$ref":"#/components/schemas/AnalyticsPeriod"}}},"SignerRequest":{"type":"object","description":"Dados de um signatário para criação","required":["name"],"properties":{"name":{"type":"string","description":"Nome completo do signatário","example":"Maria Silva"},"email":{"type":"string","format":"email","nullable":true,"description":"E-mail do signatário (para notificações)","example":"maria@email.com"},"phone_number":{"type":"string","nullable":true,"description":"Telefone do signatário","example":"11999998888"},"cpf":{"type":"string","nullable":true,"description":"CPF do signatário","example":"12345678900"},"auth_mode":{"type":"string","description":"Modo de autenticação para assinatura","default":"assinaturaTela","example":"assinaturaTela"},"require_selfie_photo":{"type":"boolean","description":"Exigir selfie do signatário","default":false},"require_document_photo":{"type":"boolean","description":"Exigir foto de documento do signatário","default":false},"qualification":{"type":"string","nullable":true,"description":"Qualificação do signatário (ex: Contratante, Testemunha)"},"phone_country":{"type":"string","nullable":true,"description":"DDI do telefone, sem `+` (padrão `55`). Pode ser omitido quando\n`phone_number` vem em E.164 (`+5511999998888`).\n","example":"55"},"address":{"type":"string","nullable":true,"description":"Endereço do signatário (ZapSign)"},"selfie_validation_type":{"type":"string","nullable":true,"description":"Método de validação biométrica da ZapSign (cobrado por assinatura).\nSó é enviado quando `require_selfie_photo` ou `require_document_photo`\né `true`; caso contrário é ignorado.\n"},"security_verifications":{"type":"array","nullable":true,"description":"Verificações de identidade do Autentique (ignorado na ZapSign). Aceita\nstrings (`[\"LIVE\"]`) ou objetos (`[{\"type\": \"SMS\", \"verify_phone\": \"+55...\"}]`).\nTipos: `SMS`, `MANUAL`, `UPLOAD`, `LIVE`, `PF_FACIAL`,\n`BIOMETRIC_AND_TEXT_EXTRACTION`, `LIVENESS_AND_TEXT_EXTRACTION`.\nApenas em `criarEEnviarDocumento` — rascunhos recusam este campo (400).\n","items":{}}}},"SignaturePartialCreate":{"type":"object","description":"Presente apenas em criação parcial: o documento EXISTE no provedor, mas um\nou mais signatários não receberam link — ele não poderá se completar\nsozinho e exige ação manual.\n","properties":{"signers_requested":{"type":"integer","example":3},"signers_created":{"type":"integer","example":2},"signer_failures":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"SignerResponse":{"type":"object","description":"Dados de um signatário retornado pela API","properties":{"id":{"type":"string","format":"uuid","description":"ID único do signatário"},"name":{"type":"string","description":"Nome do signatário"},"email":{"type":"string","nullable":true,"description":"E-mail do signatário"},"phone_number":{"type":"string","nullable":true,"description":"Telefone do signatário"},"cpf":{"type":"string","nullable":true,"description":"CPF do signatário"},"auth_mode":{"type":"string","nullable":true,"description":"Modo de autenticação"},"status":{"type":"string","description":"Status do signatário","enum":["new","sent","viewed","signed","rejected"],"example":"new"},"sign_url":{"type":"string","format":"uri","nullable":true,"description":"URL para o signatário assinar o documento"},"times_viewed":{"type":"integer","description":"Quantas vezes o signatário visualizou o documento","example":0},"last_viewed_at":{"type":"string","format":"date-time","nullable":true,"description":"Última vez que o signatário visualizou o documento"},"signed_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora da assinatura"},"rejected_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora da rejeição"},"require_selfie_photo":{"type":"boolean","description":"Se selfie é obrigatória"},"require_document_photo":{"type":"boolean","description":"Se foto de documento é obrigatória"}}},"DocumentResponse":{"type":"object","description":"Documento para assinatura eletrônica","properties":{"id":{"type":"string","format":"uuid","description":"ID único do documento"},"office_id":{"type":"string","format":"uuid","description":"ID do escritório"},"contact_id":{"type":"string","format":"uuid","description":"ID do contato associado"},"chat_id":{"type":"string","format":"uuid","description":"ID da conversa associada"},"name":{"type":"string","description":"Nome do documento","example":"Contrato de Prestação de Serviços"},"provider":{"type":"string","enum":["zapsign","autentique"],"default":"zapsign","description":"Provedor de assinatura do documento"},"status":{"type":"string","description":"Status do documento","enum":["draft","pending","partially_signed","signed","rejected","expired","cancelled"],"example":"draft"},"uploaded_file_url":{"type":"string","format":"uri","nullable":true,"description":"URL do arquivo original enviado"},"uploaded_file_name":{"type":"string","nullable":true,"description":"Nome do arquivo original"},"uploaded_file_type":{"type":"string","nullable":true,"description":"Tipo do arquivo (pdf, docx)"},"uploaded_file_size":{"type":"integer","nullable":true,"description":"Tamanho do arquivo em bytes"},"original_file_url":{"type":"string","format":"uri","nullable":true,"description":"URL do arquivo original (neutro por provedor)"},"signed_file_url":{"type":"string","format":"uri","nullable":true,"description":"URL do arquivo assinado (neutro por provedor)"},"provider_doc_ref":{"type":"string","nullable":true,"description":"Identificador do documento no provedor"},"zapsign_original_file_url":{"type":"string","format":"uri","nullable":true,"description":"URL do arquivo original no provedor de assinaturas"},"zapsign_signed_file_url":{"type":"string","format":"uri","nullable":true,"description":"URL do arquivo assinado no provedor de assinaturas"},"signatures_completed":{"type":"integer","description":"Número de assinaturas completadas","example":0},"signatures_total":{"type":"integer","description":"Número total de signatários","example":1},"source_type":{"type":"string","description":"Origem do documento (upload, template)","example":"upload"},"folder_path":{"type":"string","nullable":true,"description":"Caminho de organização do documento"},"expires_at":{"type":"string","format":"date-time","nullable":true,"description":"Data de expiração do documento"},"completed_at":{"type":"string","format":"date-time","nullable":true,"description":"Data/hora de conclusão de todas as assinaturas"},"created_at":{"type":"string","format":"date-time","description":"Data de criação"},"updated_at":{"type":"string","format":"date-time","description":"Data da última atualização"},"signers":{"type":"array","description":"Lista de signatários do documento","items":{"$ref":"#/components/schemas/SignerResponse"}}}},"CreateDocumentRequest":{"type":"object","description":"Dados para criar e enviar um documento para assinatura","required":["name","contact_id","instance_id","file_url","signers"],"properties":{"name":{"type":"string","description":"Nome do documento","example":"Contrato de Prestação de Serviços"},"contact_id":{"type":"string","format":"uuid","description":"ID do contato (cliente)"},"instance_id":{"type":"string","format":"uuid","description":"ID do número WhatsApp para resolver a conversa"},"file_url":{"type":"string","format":"uri","description":"URL pública do arquivo (PDF ou DOCX). Com Autentique, precisa ser um arquivo em storage.chatjuridico.com obtido via presignedUrlAssinaturas → PUT → processUploadedAssinaturas (outras URLs → 400)."},"file_name":{"type":"string","description":"Nome do arquivo","default":"document.pdf"},"file_type":{"type":"string","enum":["pdf","docx"],"description":"Tipo do arquivo","default":"pdf"},"provider":{"type":"string","enum":["zapsign","autentique"],"description":"Provedor de assinatura. Opcional quando o escritório tem apenas um provedor ativo. Obrigatório quando há mais de um.\n"},"signers":{"type":"array","description":"Lista de signatários (mínimo 1)","minItems":1,"items":{"$ref":"#/components/schemas/SignerRequest"}}}},"CreateDraftRequest":{"type":"object","description":"Dados para criar um rascunho de documento","required":["name","contact_id","chat_id"],"properties":{"name":{"type":"string","description":"Nome do documento","example":"Contrato Rascunho"},"contact_id":{"type":"string","format":"uuid","description":"ID do contato"},"chat_id":{"type":"string","format":"uuid","description":"ID da conversa"},"draft_id":{"type":"string","format":"uuid","nullable":true,"description":"ID de rascunho existente para atualizar"},"file_url":{"type":"string","format":"uri","nullable":true,"description":"URL do arquivo (opcional, pode ser enviado depois via upload). Com Autentique, precisa ser um arquivo em storage.chatjuridico.com obtido via presignedUrlAssinaturas → PUT → processUploadedAssinaturas (outras URLs → 400)."},"file_name":{"type":"string","nullable":true,"description":"Nome do arquivo"},"file_type":{"type":"string","enum":["pdf","docx"],"description":"Tipo do arquivo","default":"pdf"},"provider":{"type":"string","enum":["zapsign","autentique"],"description":"Provedor de assinatura. Opcional quando o escritório tem apenas um provedor ativo; quando há mais de um, o padrão é ZapSign.\n"},"signers":{"type":"array","description":"Lista de signatários (opcional, pode ser adicionado depois)","items":{"$ref":"#/components/schemas/SignerRequest"}}}},"ExtractedSignerData":{"type":"object","description":"Dados do signatário extraídos via IA a partir de uma conversa","properties":{"name":{"type":"string","nullable":true,"description":"Nome extraído","example":"Maria Silva"},"email":{"type":"string","nullable":true,"description":"E-mail extraído","example":"maria@email.com"},"phone_number":{"type":"string","nullable":true,"description":"Telefone extraído","example":"11999998888"},"address":{"type":"string","nullable":true,"description":"Endereço extraído"},"cpf":{"type":"string","nullable":true,"description":"CPF extraído","example":"12345678900"}}},"IntegrationStatus":{"type":"object","description":"Status da integração de assinaturas digitais","properties":{"is_active":{"type":"boolean","description":"Se a integração está ativa","example":true},"is_connected":{"type":"boolean","description":"Se a conexão está estabelecida","example":true},"connected_at":{"type":"string","format":"date-time","nullable":true,"description":"Data da conexão"},"credits":{"type":"object","nullable":true,"description":"Informações do plano e créditos de assinaturas","properties":{"plan_name":{"type":"string","description":"Nome do plano","example":"Pro"},"remaining":{"type":"integer","description":"Créditos restantes","example":50}}}}},"AsaasIntegrationStatus":{"type":"object","description":"Status da integração com Asaas","properties":{"is_active":{"type":"boolean","example":true},"is_connected":{"type":"boolean","example":true},"environment":{"type":"string","enum":["production","sandbox"],"nullable":true,"example":"production"},"connected_at":{"type":"string","format":"date-time","nullable":true},"api_key_last4":{"type":"string","nullable":true,"example":"cdef","description":"Últimos 4 caracteres da chave de API"},"import_status":{"type":"string","nullable":true,"example":"completed","description":"Estado da importação do histórico do Asaas: `importing`, `completed`\nou `failed` (outros valores podem aparecer em operações de suporte).\n`failed` significa que clientes e cobranças do Asaas não chegaram.\n"},"import_error":{"type":"string","nullable":true,"description":"Motivo da falha da importação, em português, para o escritório"},"last_sync_at":{"type":"string","format":"date-time","nullable":true,"description":"Quando a última importação terminou. Nulo = o histórico nunca chegou."},"key_access_state":{"type":"string","nullable":true,"example":"ok","description":"Verificação diária da chave: `ok`, `missing_permissions` (a chave não\nlê clientes, cobranças, parcelamentos ou assinaturas), `invalid` ou\n`unreachable` (não foi possível verificar).\n"}}},"Payment":{"type":"object","description":"Cobrança do Asaas","properties":{"id":{"type":"string","format":"uuid"},"asaas_id":{"type":"string","nullable":true,"description":"ID externo no Asaas (ex. pay_abc123)"},"asaas_payment_id":{"type":"string","nullable":true,"description":"Alias retornado pela API para o ID externo no Asaas."},"contact_id":{"type":"string","format":"uuid","nullable":true},"contact_name":{"type":"string","nullable":true},"chat_id":{"type":"string","format":"uuid","nullable":true},"status":{"type":"string","enum":["PENDING","RECEIVED","CONFIRMED","OVERDUE","REFUNDED","CANCELLED","RECEIVED_IN_CASH"],"example":"PENDING"},"billing_type":{"type":"string","enum":["PIX","BOLETO","CREDIT_CARD"],"nullable":true},"value":{"type":"number","format":"float","example":150.0},"net_value":{"type":"number","format":"float","nullable":true,"description":"Valor líquido (após taxas Asaas)"},"description":{"type":"string","nullable":true},"due_date":{"type":"string","format":"date","nullable":true},"payment_date":{"type":"string","format":"date","nullable":true},"invoice_url":{"type":"string","format":"uri","nullable":true,"description":"URL da fatura no Asaas"},"bankslip_url":{"type":"string","format":"uri","nullable":true,"description":"URL do boleto bancário"},"pix_qr_code":{"type":"string","nullable":true,"description":"Payload do QR code PIX"},"pix_copy_paste":{"type":"string","nullable":true,"description":"Código PIX copia e cola"},"installment_count":{"type":"integer","nullable":true},"installment_number":{"type":"integer","nullable":true},"installment_value":{"type":"number","format":"float","nullable":true},"interest_percentage":{"type":"number","format":"float","nullable":true,"description":"Taxa de juros mensal (max 1.0% CDC)"},"fine_percentage":{"type":"number","format":"float","nullable":true,"description":"Percentual de multa (max 2.0% CDC)"},"discount_value":{"type":"number","format":"float","nullable":true},"discount_due_date":{"type":"string","format":"date","nullable":true},"overdue_acknowledged_at":{"type":"string","format":"date-time","nullable":true},"overdue_acknowledged_by":{"type":"string","nullable":true},"created_by":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"PaymentEvent":{"type":"object","description":"Evento no histórico de uma cobrança","properties":{"unique_id":{"type":"string","format":"uuid"},"event_type":{"type":"string","example":"PAYMENT_RECEIVED"},"event_data":{"type":"object"},"created_at":{"type":"string","format":"date-time"}}},"CreatePaymentRequest":{"type":"object","description":"Dados para criar uma nova cobrança via Asaas (PIX, boleto ou cartão de crédito)","required":["contact_id","instance_id","value","due_date","billing_type"],"properties":{"contact_id":{"type":"string","format":"uuid","description":"ID do contato a cobrar"},"instance_id":{"type":"string","format":"uuid","description":"ID do número WhatsApp"},"value":{"type":"number","format":"float","minimum":0.01,"description":"Valor da cobrança em BRL","example":150.0},"due_date":{"type":"string","format":"date","description":"Data de vencimento (YYYY-MM-DD)","example":"2025-06-15"},"billing_type":{"type":"string","enum":["PIX","BOLETO","CREDIT_CARD"],"description":"Tipo de cobrança"},"description":{"type":"string","maxLength":500,"nullable":true,"description":"Descrição da cobrança","example":"Honorários advocatícios"},"installment_count":{"type":"integer","minimum":1,"maximum":12,"default":1,"description":"Número de parcelas"},"interest_percentage":{"type":"number","format":"float","minimum":0,"maximum":1.0,"nullable":true,"description":"Juros mensal (max 1.0% — limite CDC)"},"fine_percentage":{"type":"number","format":"float","minimum":0,"maximum":2.0,"nullable":true,"description":"Multa por atraso (max 2.0% — limite CDC)"},"discount_value":{"type":"number","format":"float","minimum":0,"nullable":true,"description":"Valor de desconto em BRL"},"discount_due_date":{"type":"string","format":"date","nullable":true,"description":"Desconto válido até (YYYY-MM-DD)"}}},"PaymentDashboard":{"type":"object","description":"Métricas agregadas do dashboard financeiro","properties":{"total_value":{"type":"number","format":"float","example":45000.0},"total_count":{"type":"integer","example":89},"received_value":{"type":"number","format":"float"},"pending_value":{"type":"number","format":"float"},"overdue_value":{"type":"number","format":"float"},"overdue_count":{"type":"integer"},"avg_payment_value":{"type":"number","format":"float"},"by_billing_type":{"type":"object","description":"Totais agrupados por tipo (PIX, BOLETO, CREDIT_CARD)"},"by_status":{"type":"object","description":"Totais agrupados por status"},"comparison":{"type":"object","nullable":true,"description":"Comparação com período anterior (quando compare=true)"},"period":{"type":"object","properties":{"start":{"type":"string","format":"date-time"},"end":{"type":"string","format":"date-time"}}}}},"TimeSeriesPoint":{"type":"object","description":"Ponto de dados na série temporal","properties":{"period":{"type":"string","description":"Rótulo do período (ex. 2025-01-15, 2025-01, 2025-W03)"},"total_value":{"type":"number","format":"float"},"total_count":{"type":"integer"},"received_value":{"type":"number","format":"float"},"pending_value":{"type":"number","format":"float"}}},"FunnelStageValues":{"type":"object","description":"Valores financeiros por etapa do funil","properties":{"stage_id":{"type":"string","format":"uuid","nullable":true},"stage_name":{"type":"string","example":"Qualificação"},"stage_order":{"type":"integer"},"total_value":{"type":"number","format":"float"},"received_value":{"type":"number","format":"float"},"pending_value":{"type":"number","format":"float"},"overdue_value":{"type":"number","format":"float"},"payment_count":{"type":"integer"},"contact_count":{"type":"integer"}}},"ChatValues":{"type":"object","description":"Valores financeiros por conversa","properties":{"chat_id":{"type":"string","format":"uuid","nullable":true},"contact_id":{"type":"string","format":"uuid","nullable":true},"contact_name":{"type":"string","nullable":true},"total_value":{"type":"number","format":"float"},"received_value":{"type":"number","format":"float"},"pending_value":{"type":"number","format":"float"},"overdue_value":{"type":"number","format":"float"},"payment_count":{"type":"integer"}}},"CustomerAsaasLink":{"type":"object","description":"Contato com status de vínculo Asaas","properties":{"contact_id":{"type":"string","format":"uuid"},"contact_name":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"asaas_customer_id":{"type":"string","nullable":true,"description":"ID do cliente no Asaas"},"has_asaas_link":{"type":"boolean","description":"Se o contato possui vínculo com cliente Asaas"},"linked_at":{"type":"string","format":"date-time","nullable":true}}},"FillPaymentDataResponse":{"type":"object","description":"Dados de cobrança extraídos por IA","properties":{"value":{"type":"number","format":"float","nullable":true},"description":{"type":"string","nullable":true},"due_date":{"type":"string","format":"date","nullable":true},"billing_type":{"type":"string","nullable":true},"contact_name":{"type":"string","nullable":true},"contact_phone":{"type":"string","nullable":true}}},"Error":{"type":"object","description":"Formato padrão de erro da API","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Código do erro. Valores possíveis:\n`bad_request`, `unauthorized`, `forbidden`, `not_found`, `conflict`,\n`validation_error`, `rate_limit_exceeded`, `internal_error`, `service_unavailable`,\n`duplicate_resource`, `check_violation`, `insufficient_scope`, `quota_exceeded`,\n`service_window_closed` (janela de atendimento de 24h fechada — envie um template)\n","example":"not_found"},"message":{"type":"string","description":"Mensagem descritiva do erro em português","example":"Contato não encontrado"},"status":{"type":"integer","description":"Código HTTP do erro","example":404},"details":{"type":"object","additionalProperties":true,"description":"Dados estruturados opcionais (ex. `plan_name` em `no_api_plan`)"}}}}},"FlowSummary":{"type":"object","description":"Formulário do WhatsApp (sem o JSON). Usado nas listagens.","required":["id","name","status","instance_id"],"properties":{"id":{"type":"string","format":"uuid","description":"ID do formulário no Chat Jurídico (estável, inclusive após `deployFormulario`)"},"name":{"type":"string","example":"Cadastro de novo cliente"},"status":{"type":"string","enum":["DRAFT","PUBLISHED","DEPRECATED","BLOCKED","THROTTLED"],"description":"`DRAFT` (editável) → `PUBLISHED` (enviável, irreversível) → `DEPRECATED`.\n`BLOCKED`/`THROTTLED` são definidos pela Meta.\n"},"categories":{"type":"array","items":{"type":"string","enum":["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]}},"instance_id":{"type":"string","format":"uuid","description":"Número de WhatsApp (instância Meta) dono do formulário"},"waba_id":{"type":"string","nullable":true,"description":"ID da conta comercial do WhatsApp (WABA) na Meta"},"meta_flow_id":{"type":"string","nullable":true,"description":"ID do formulário na Meta"},"validation_errors":{"type":"array","description":"Erros de validação apontados pela Meta no último JSON enviado (vazio = ok)","items":{"type":"object","additionalProperties":true}},"flow_json_version":{"type":"string","nullable":true,"example":"7.0"},"data_api_version":{"type":"string","nullable":true,"example":"3.0"},"endpoint_uri":{"type":"string","nullable":true,"description":"Endpoint de troca de dados (formulários dinâmicos). Somente leitura."},"preview_url":{"type":"string","nullable":true},"last_published_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"FlowDetail":{"description":"Formulário do WhatsApp com o Flow JSON completo.","allOf":[{"$ref":"#/components/schemas/FlowSummary"},{"type":"object","properties":{"flow_json":{"type":"object","nullable":true,"additionalProperties":true,"description":"Flow JSON da Meta (`version`, `screens`, …). Nulo até o primeiro envio."},"contact_mapping":{"type":"array","description":"Onde as respostas são salvas no contato (`[]` = em nenhum lugar). Ver `UpdateFlowRequest.contact_mapping`.","items":{"$ref":"#/components/schemas/FlowContactMappingEntry"}},"upload_error":{"type":"object","nullable":true,"description":"Só em `POST /v1/flows` com `flow_json` (e em duplicar): o formulário FOI\ncriado (201), mas o JSON não subiu para a Meta. Reenvie com\n`PUT /v1/flows/{flowId}/json`.\n","properties":{"error_code":{"type":"string","nullable":true},"error":{"type":"string","nullable":true},"details":{"type":"object","description":"Só quando `error_code` é `entry_screen_ambiguous`: o mesmo detalhe do\n422 de `PUT /v1/flows/{flowId}/json`.\n","properties":{"candidates":{"type":"array","items":{"type":"string"},"description":"Telas que disputam a posição de tela de entrada"},"unreachable":{"type":"array","items":{"type":"string"},"description":"Telas que nenhuma navegação alcança"},"has_terminal":{"type":"boolean","description":"Se o JSON tem ao menos uma tela final"}}}}},"deploy":{"type":"object","nullable":true,"description":"Só na resposta de `POST /v1/flows/{flowId}/deploy`.","properties":{"action":{"type":"string","nullable":true,"description":"O que a implantação fez: `no_change` (o JSON é idêntico ao publicado,\nnada foi feito), `published` (o rascunho recebeu o JSON e foi\npublicado), `updated` (o JSON novo foi publicado no mesmo formulário\nda Meta; quem já recebeu o formulário continua com a versão que\nrecebeu) ou `cloned` (uma cópia com o JSON novo foi publicada e\npassou a valer; a versão anterior é aposentada depois, quando\nnenhum formulário já enviado depender dela).\n"},"warnings":{"type":"array","items":{"type":"string"},"description":"Avisos não fatais (hoje sempre vazio)."}}}}}]},"FlowDetailEnvelope":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FlowDetail"}}},"CreateFlowRequest":{"type":"object","required":["name","instance_id"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Nome do formulário (único por número)","example":"Cadastro de novo cliente"},"instance_id":{"type":"string","format":"uuid","description":"Número de WhatsApp da API oficial da Meta que vai enviar o formulário"},"categories":{"type":"array","description":"Categorias da Meta (padrão `[\"OTHER\"]`)","items":{"type":"string","enum":["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]}},"flow_json":{"type":"object","additionalProperties":true,"description":"Flow JSON opcional (≤ 256 KB, com um array `screens` não vazio)"}}},"UpdateFlowRequest":{"type":"object","description":"Informe ao menos um campo.","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Novo nome (só em `DRAFT`)"},"categories":{"type":"array","items":{"type":"string","enum":["SIGN_UP","SIGN_IN","APPOINTMENT_BOOKING","LEAD_GENERATION","CONTACT_US","CUSTOMER_SUPPORT","SURVEY","OTHER"]}},"contact_mapping":{"type":"array","maxItems":30,"description":"Salvar respostas no contato: a lista INTEIRA (substitui a anterior; `[]` limpa).\nQuando o formulário é respondido pela primeira vez, cada item grava a resposta do\ncampo `field` no nome do contato (`contact_name`) ou numa propriedade do contato\n(`property` + `property_id`), com as mesmas regras de conversão da automação\n\"Definir propriedade do contato\". Resposta vazia ou inválida para o tipo da\npropriedade é ignorada (as outras são gravadas). Um valor digitado pela equipe\n(origem `manual`) e um nome editado no painel NÃO são substituídos. Uma escolha\n(lista, botões, caixas) é gravada com o título da opção. Cada `field` aparece uma vez.\n`property_id` precisa ser uma propriedade ativa do escritório\n(422 `invalid_contact_mapping`); formato inválido é 422 `validation_error`.\n","items":{"$ref":"#/components/schemas/FlowContactMappingEntry"}}}},"FlowContactMappingEntry":{"type":"object","additionalProperties":false,"required":["field","target"],"properties":{"field":{"type":"string","pattern":"^[A-Za-z0-9_]{1,80}$","description":"`name` de um campo de entrada do formulário (Flow JSON)","example":"nome_completo"},"target":{"type":"string","enum":["contact_name","property"],"description":"`contact_name` = nome do contato; `property` = propriedade do contato"},"property_id":{"type":"string","format":"uuid","description":"Só (e obrigatório) com `target` = `property`; id de uma propriedade ativa do escritório"}}},"UploadFlowJsonRequest":{"type":"object","required":["flow_json"],"properties":{"flow_json":{"type":"object","additionalProperties":true,"description":"Flow JSON da Meta (≤ 256 KB, com um array `screens` não vazio)"}}},"DeployFlowRequest":{"type":"object","required":["flow_json"],"properties":{"flow_json":{"type":"object","additionalProperties":true,"description":"Flow JSON a publicar (≤ 256 KB, com um array `screens` não vazio)"}}},"SendFlowRequest":{"type":"object","required":["flow_id","chat_id","flow_cta"],"properties":{"flow_id":{"type":"string","format":"uuid","description":"ID do formulário (`PUBLISHED` ou `THROTTLED`)"},"chat_id":{"type":"string","format":"uuid","description":"ID da conversa (do mesmo número do formulário)"},"flow_cta":{"type":"string","minLength":1,"maxLength":30,"description":"Texto do botão que abre o formulário: até 30 caracteres, sem emoji\n(símbolos de texto como `°`, `©`, `™`, `✓` e `★` são aceitos).\n","example":"Preencher"},"body_text":{"type":"string","maxLength":1024,"description":"Texto da mensagem acima do botão","example":"Para darmos andamento, preencha seus dados."},"prepopulation_data":{"type":"object","additionalProperties":true,"description":"Dados para pré-preencher a primeira tela (≤ 8 KB)"}}},"SendFlowResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"message_id":{"type":"string","format":"uuid","description":"ID da mensagem criada na conversa"},"status":{"type":"string","enum":["sent"],"description":"Sempre `sent` — também na repetição de uma chave já usada."},"duplicate":{"type":"boolean","description":"`true` quando a `X-Idempotency-Key` já tinha sido usada e esta\nresposta devolve o envio original (nada foi enviado de novo).\n"}}}}},"FlowPreview":{"type":"object","properties":{"preview_url":{"type":"string","nullable":true,"description":"Link temporário de pré-visualização da Meta"},"expires_at":{"type":"string","format":"date-time","nullable":true}}},"FlowResponseEntry":{"type":"object","description":"Uma resposta de cliente a um formulário enviado pelo Chat Jurídico","properties":{"id":{"type":"string","format":"uuid"},"flow_id":{"type":"string","format":"uuid"},"chat_id":{"type":"string","format":"uuid","nullable":true},"contact_id":{"type":"string","format":"uuid","nullable":true},"message_id":{"type":"string","format":"uuid","nullable":true,"description":"Mensagem de resposta na conversa"},"response_json":{"type":"object","additionalProperties":true,"description":"Campos preenchidos pelo cliente"},"screen_id":{"type":"string","nullable":true},"status":{"type":"string","enum":["completed","partial","error","late"],"description":"`late` = respondido depois que o token expirou"},"created_at":{"type":"string","format":"date-time"}}},"PaginatedFlowList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FlowSummary"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"PaginatedFlowResponseList":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FlowResponseEntry"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"WebhookDeliveryPayload":{"type":"object","description":"Payload enviado para a URL do webhook quando um evento ocorre","required":["event","data","timestamp"],"properties":{"event":{"type":"string","description":"Tipo do evento","enum":["message.sent","message.received","message.status_changed","conversation.created","conversation.updated","contact.created","contact.updated","scheduled_message.sent","scheduled_message.failed","campaign.completed","api_key.created","api_key.revoked","automation.executed","automation.activated","automation.deactivated"]},"data":{"type":"object","description":"Entidade completa do evento. A estrutura depende do tipo de evento.\n\nPara eventos de mensagem (`message.*`), o `data` traz **as duas formas\ndo mesmo dado**, para compatibilidade:\n\n- **Campos da API** (recomendado): `id`, `conversation_id`, `type`,\n  `content`, `direction`.\n- **Colunas internas** (legado, continuam sendo enviadas): `unique_id`,\n  `chat_id`, `message_type`, `message_text`, `from_me`, e demais colunas\n  da mensagem.\n\n`type` usa o tipo da API (`text`, `image`, `interactive`, …) enquanto\n`message_type` traz o tipo bruto do WhatsApp (ex.: `buttonsResponseMessage`).\nPara subtipos sem equivalente na API (ex.: `reactionMessage`, `call_log`,\n`revoke`), `type` repassa o tipo bruto — trate valores desconhecidos como\n\"ignorar\", não como erro.\n\n`direction` é `inbound`/`outbound`.\n\n`status`: em `message.received` / `message.reply_captured` (mensagem que\nacabou de chegar) vem sempre `received`. Em `message.sent` e\n`message.status_changed` reflete o estado real do ciclo de vida\n(`pending`, `sent`, `delivered`, `read`, `failed`) e **pode vir `null`** —\nnesses eventos nunca inventamos um valor, pois é justamente o `status`\nque está sendo reportado.\n\nEm respostas de menu interativo, a opção escolhida vem em `interactive`\n(`{type, id, title}`). `contact` traz o contato do lead (`{id, phone, name}`).\n\nCampos internos são removidos antes do envio (file_thumb_base64,\ntemplate_components, template_parameters, template_metadata,\ninteractive_data, referral_data, call_log).\n"},"timestamp":{"type":"integer","description":"Unix epoch em segundos","example":1712345678}},"example":{"event":"message.received","data":{"id":"msg-uuid-123","conversation_id":"conv-uuid-456","type":"interactive","content":"Sim","direction":"inbound","status":"received","created_at":"2026-04-12T10:30:00Z","unique_id":"msg-uuid-123","chat_id":"conv-uuid-456","message_type":"templateButtonReplyMessage","message_text":"Sim","from_me":false,"interactive":{"type":"button_reply","id":"sim","title":"Sim"},"contact":{"id":"contact-uuid-789","phone":"5511999998888","name":"Maria Silva"}},"timestamp":1712345678}},"FaqItem":{"type":"object","description":"Item de FAQ do agente IA","required":["id","question","answer"],"properties":{"id":{"type":"string","format":"uuid","description":"Identificador único do item","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"question":{"type":"string","description":"Pergunta frequente","example":"Qual o horário de atendimento?"},"answer":{"type":"string","description":"Resposta da pergunta frequente","example":"Atendemos de segunda a sexta, das 9h às 18h."}}},"CreateFaqItem":{"type":"object","description":"Dados para criar um item de FAQ","required":["question","answer"],"properties":{"question":{"type":"string","minLength":1,"maxLength":1000,"description":"Pergunta frequente"},"answer":{"type":"string","minLength":1,"maxLength":5000,"description":"Resposta da pergunta frequente"}}},"BulkCreateFaq":{"type":"object","description":"Criação em lote de itens de FAQ","required":["items"],"properties":{"items":{"type":"array","minItems":1,"maxItems":50,"items":{"$ref":"#/components/schemas/CreateFaqItem"}}}},"UpdateFaqItem":{"type":"object","description":"Campos para atualizar em um item de FAQ","properties":{"question":{"type":"string","minLength":1,"maxLength":1000,"description":"Nova pergunta"},"answer":{"type":"string","minLength":1,"maxLength":5000,"description":"Nova resposta"}}},"GenerateFaqRequest":{"type":"object","description":"Requisição para gerar FAQ com IA","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":10,"maxLength":5000,"description":"Descrição do serviço ou contexto para geração","example":"Escritório de advocacia trabalhista em São Paulo"},"count":{"type":"integer","minimum":1,"maximum":30,"default":10,"description":"Quantidade de perguntas a gerar"}}},"SuggestFaqRequest":{"type":"object","description":"Requisição para sugerir FAQ a partir de conversas","properties":{"conversation_ids":{"type":"array","maxItems":20,"items":{"type":"string","format":"uuid"},"description":"IDs de conversas específicas (opcional)"},"days":{"type":"integer","minimum":1,"maximum":30,"default":7,"description":"Período em dias para buscar conversas recentes"},"limit":{"type":"integer","minimum":1,"maximum":30,"default":10,"description":"Quantidade máxima de sugestões"}}},"GeneratedFaqItem":{"type":"object","description":"Item de FAQ gerado por IA (não salvo automaticamente)","required":["question","answer"],"properties":{"question":{"type":"string","description":"Pergunta gerada","example":"Quanto custa uma consulta trabalhista?"},"answer":{"type":"string","description":"Resposta gerada","example":"A primeira consulta é gratuita. Entre em contato para agendar."}}},"CalendarResponse":{"type":"object","description":"Calendário do escritório","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["office","personal","google_mirror"],"description":"Tipo do calendário"},"name":{"type":"string"},"color":{"type":"string","description":"Cor em hexadecimal (#RRGGBB)"},"is_default":{"type":"boolean"},"is_archived":{"type":"boolean"},"is_office_central":{"type":"boolean"},"google_calendar_id":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CalendarEventResponse":{"type":"object","description":"Evento do calendário (standalone ou ocorrência expandida de série recorrente)","properties":{"id":{"type":"string","description":"UUID para standalones, `masterId::slotKey` para ocorrências recorrentes"},"calendar_id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"created_by":{"type":"string","format":"uuid","nullable":true},"title":{"type":"string"},"description":{"type":"string","nullable":true},"location":{"type":"string","nullable":true},"video_url":{"type":"string","nullable":true},"start_at":{"type":"string","format":"date-time"},"end_at":{"type":"string","format":"date-time"},"all_day":{"type":"boolean"},"timezone":{"type":"string","description":"Fuso horário IANA (ex. America/Sao_Paulo)"},"reminder_minutes_before":{"type":"integer","nullable":true},"status":{"type":"string","enum":["confirmed","tentative","cancelled"]},"contact_id":{"type":"string","format":"uuid","nullable":true},"chat_id":{"type":"string","format":"uuid","nullable":true},"processo_id":{"type":"string","format":"uuid","nullable":true},"source":{"type":"string","enum":["app","google_synced","google_external","ai_booked"]},"visibility":{"type":"string","enum":["default","private","public","confidential"]},"recurrence_role":{"type":"string","enum":["standalone","master","override","cancellation","occurrence"]},"recurrence_rule":{"type":"string","nullable":true,"description":"RRULE RFC 5545 (ex. RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR)"},"recurrence_master_id":{"type":"string","format":"uuid","nullable":true},"original_start_at":{"type":"string","format":"date-time","nullable":true},"recurrence_end_at":{"type":"string","format":"date-time","nullable":true},"color":{"type":"string","nullable":true,"description":"Cor em hexadecimal (#RRGGBB)"},"google_meet_requested":{"type":"boolean"},"office_lawsuit_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do processo jurídico vinculado ao evento"},"attendees":{"type":"array","items":{"$ref":"#/components/schemas/AttendeeResponse"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"AttendeeResponse":{"type":"object","description":"Participante de um evento","properties":{"id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"response_status":{"type":"string","enum":["needs_action","accepted","declined","tentative"]},"is_organizer":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CreateEventRequest":{"type":"object","required":["calendar_id","title","start_at","end_at"],"properties":{"calendar_id":{"type":"string","format":"uuid"},"title":{"type":"string","example":"Reunião com cliente João Silva"},"description":{"type":"string"},"location":{"type":"string"},"video_url":{"type":"string"},"start_at":{"type":"string","format":"date-time"},"end_at":{"type":"string","format":"date-time"},"all_day":{"type":"boolean","default":false},"timezone":{"type":"string","default":"America/Sao_Paulo"},"reminder_minutes_before":{"type":"integer"},"status":{"type":"string","enum":["confirmed","tentative"],"default":"confirmed"},"visibility":{"type":"string","enum":["default","private","public","confidential"],"default":"default"},"contact_id":{"type":"string","format":"uuid","description":"Contato vinculado ao evento"},"chat_id":{"type":"string","format":"uuid"},"processo_id":{"type":"string","format":"uuid","description":"Processo jurídico vinculado"},"recurrence_rule":{"type":"string","description":"RRULE RFC 5545 para série recorrente","example":"RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"},"recurrence_end_at":{"type":"string","format":"date-time"},"google_meet_requested":{"type":"boolean","default":false},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"office_lawsuit_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do processo jurídico vinculado"},"attendee_user_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs dos membros participantes"},"organizer_user_id":{"type":"string","format":"uuid"}}},"UpdateEventRequest":{"type":"object","description":"Campos a atualizar no evento. Para séries recorrentes, inclua `scope`.","properties":{"title":{"type":"string"},"description":{"type":"string"},"location":{"type":"string"},"video_url":{"type":"string"},"start_at":{"type":"string","format":"date-time"},"end_at":{"type":"string","format":"date-time"},"all_day":{"type":"boolean"},"timezone":{"type":"string"},"reminder_minutes_before":{"type":"integer"},"status":{"type":"string","enum":["confirmed","tentative"]},"visibility":{"type":"string","enum":["default","private","public","confidential"]},"contact_id":{"type":"string","format":"uuid"},"chat_id":{"type":"string","format":"uuid"},"processo_id":{"type":"string","format":"uuid"},"calendar_id":{"type":"string","format":"uuid","description":"Mover para outro calendário"},"color":{"type":"string","nullable":true},"office_lawsuit_id":{"type":"string","format":"uuid","nullable":true,"description":"ID do processo jurídico vinculado"},"google_meet_requested":{"type":"boolean"},"scope":{"type":"string","enum":["only_this","all","this_and_following"],"description":"Escopo de edição para séries recorrentes"},"occurrence_start":{"type":"string","format":"date-time","description":"Início da ocorrência (obrigatório para only_this/this_and_following)"}}},"ColorOverlayResponse":{"type":"object","description":"Sobreposição de cor por ocorrência","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"calendar_id":{"type":"string","format":"uuid"},"master_id":{"type":"string","format":"uuid"},"scope":{"type":"string","enum":["at","from"]},"slot":{"type":"string","format":"date-time"},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"created_by":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CalendarACLResponse":{"type":"object","description":"Permissão de acesso a um calendário","properties":{"id":{"type":"string","format":"uuid"},"calendar_id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"can_view":{"type":"boolean"},"can_read":{"type":"boolean"},"can_write":{"type":"boolean"},"is_default_write":{"type":"boolean"},"ceiling_access_role":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CalendarVisibilityResponse":{"type":"object","description":"Preferência de visibilidade de calendário","properties":{"user_id":{"type":"string","format":"uuid"},"calendar_id":{"type":"string","format":"uuid"},"is_visible":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"}}},"GoogleCalendarStatusResponse":{"type":"object","description":"Status da integração Google Calendar","properties":{"is_active":{"type":"boolean"},"is_connected":{"type":"boolean"},"connected_at":{"type":"string","format":"date-time","nullable":true},"subscriptions_count":{"type":"integer"}}},"GoogleCalendarSubscriptionResponse":{"type":"object","description":"Assinatura de um calendário Google","properties":{"id":{"type":"string","format":"uuid"},"office_id":{"type":"string","format":"uuid"},"google_calendar_id":{"type":"string","description":"ID do calendário no Google"},"google_calendar_summary":{"type":"string","nullable":true,"description":"Nome do calendário no Google"},"google_calendar_color":{"type":"string","nullable":true},"google_access_role":{"type":"string","nullable":true,"enum":["owner","writer","reader","freeBusyReader"]},"app_capability":{"type":"string","enum":["ignore","freebusy","read","write"]},"is_active":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"GoogleDriveStatusResponse":{"type":"object","description":"Status da integração Google Drive do escritório","properties":{"is_connected":{"type":"boolean"},"is_active":{"type":"boolean"},"google_email":{"type":"string","nullable":true},"status":{"type":"string","nullable":true,"enum":["ok","reauth_required","revoked","revoked_by_provider","revoked_offboarding"]},"default_folder_id":{"type":"string","nullable":true},"default_folder_name":{"type":"string","nullable":true},"auto_save_documents":{"type":"boolean","description":"Reservada — ainda sem efeito."},"auto_save_media":{"type":"boolean","description":"Reservada — ainda sem efeito."},"connected_at":{"type":"string","format":"date-time","nullable":true},"scopes":{"type":"array","items":{"type":"string"}}}},"SaveGoogleDriveFileRequest":{"type":"object","description":"Origem interna do arquivo a salvar — exatamente UM de sign_document_id |\ndocument_id | media_url | r2_key | message_id. Prefira `sign_document_id`\n(documento de assinatura: contrato, procuração), `document_id` (documento do\neditor) ou `message_id` (mídia de uma mensagem): todos são resolvidos pelo banco,\nsempre dentro do escritório. `media_url`/`r2_key` só aceitam arquivos enviados pela API do\nescritório (`api-uploads/{office_id}/...` no armazenamento do ChatJurídico);\nqualquer outro caminho é recusado com 400 `invalid_source`.\n","properties":{"source":{"type":"object","properties":{"sign_document_id":{"type":"string","format":"uuid","description":"Documento de assinatura (contrato, procuração) — o id de listarAssinaturas / buscarDocumento / visao_assinaturas."},"variant":{"type":"string","enum":["auto","signed","original"],"default":"auto","description":"Só com sign_document_id. auto = PDF assinado quando totalmente assinado, senão o original; signed = só o assinado (400 se ainda não assinado por todos); original = o arquivo enviado para assinatura."},"document_id":{"type":"string","format":"uuid","description":"Documento do editor (listarDocumentos) já renderizado em PDF. Para contratos/procurações use sign_document_id."},"media_url":{"type":"string","description":"Somente URLs api-uploads/{office_id}/... do armazenamento do ChatJurídico."},"r2_key":{"type":"string","description":"Somente chaves api-uploads/{office_id}/..."},"message_id":{"type":"string","format":"uuid","description":"Preferencial — mídia de uma mensagem da conversa."}}},"file_name":{"type":"string"},"mime_type":{"type":"string"},"folder_id":{"type":"string","description":"Pasta de destino: a pasta padrão do escritório ou uma pasta criada pelo ChatJurídico (criarPastaGoogleDrive); outra pasta = 400 `unknown_folder`. Omitido = pasta padrão."},"context":{"type":"object","properties":{"contact_id":{"type":"string"},"document_id":{"type":"string"},"chat_id":{"type":"string"},"message_id":{"type":"string"},"instance_id":{"type":"string"}}}},"required":["source"]},"GoogleDriveFileResponse":{"type":"object","description":"Referência interna a um arquivo salvo no Google Drive","properties":{"id":{"type":"string","format":"uuid"},"drive_file_id":{"type":"string"},"file_name":{"type":"string"},"mime_type":{"type":"string","nullable":true},"file_size":{"type":"integer","nullable":true},"web_view_link":{"type":"string","nullable":true},"folder_id":{"type":"string","nullable":true},"status":{"type":"string"}}},"GoogleDriveFolderResponse":{"type":"object","description":"Pasta do Google Drive (criada/garantida pelo app)","properties":{"drive_folder_id":{"type":"string"},"name":{"type":"string"},"parent_id":{"type":"string","nullable":true},"kind":{"type":"string","nullable":true},"scope_ref":{"type":"string","nullable":true}}}}},"x-webhooks":{"messageReceived":{"post":{"operationId":"webhookMessageReceived","summary":"Mensagem recebida","description":"Disparado quando uma nova mensagem é recebida de um contato via WhatsApp.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"},"example":{"event":"message.received","data":{"id":"msg-uuid-123","conversation_id":"conv-uuid-456","type":"text","content":"Olá, preciso de ajuda","direction":"inbound","status":"received"},"timestamp":1712345678}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"messageSent":{"post":{"operationId":"webhookMessageSent","summary":"Mensagem enviada","description":"Disparado quando uma mensagem é enviada pelo escritório.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"},"example":{"event":"message.sent","data":{"id":"msg-uuid-456","conversation_id":"conv-uuid-789","type":"text","content":"Bom dia! Em que posso ajudar?","direction":"outbound","status":"sent"},"timestamp":1712345679}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"messageStatusChanged":{"post":{"operationId":"webhookMessageStatusChanged","summary":"Status da mensagem alterado","description":"Disparado quando o status de uma mensagem muda (delivered, read, failed).","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"conversationCreated":{"post":{"operationId":"webhookConversationCreated","summary":"Conversa criada","description":"Disparado quando uma nova conversa é criada.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"conversationUpdated":{"post":{"operationId":"webhookConversationUpdated","summary":"Conversa atualizada","description":"Disparado quando uma conversa é atualizada (etapa, responsável, status).","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"contactCreated":{"post":{"operationId":"webhookContactCreated","summary":"Contato criado","description":"Disparado quando um novo contato é criado.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"contactUpdated":{"post":{"operationId":"webhookContactUpdated","summary":"Contato atualizado","description":"Disparado quando um contato é atualizado.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"scheduledMessageSent":{"post":{"operationId":"webhookScheduledMessageSent","summary":"Mensagem agendada enviada","description":"Disparado quando uma mensagem agendada é enviada com sucesso.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"scheduledMessageFailed":{"post":{"operationId":"webhookScheduledMessageFailed","summary":"Mensagem agendada falhou","description":"Disparado quando uma mensagem agendada falha no envio.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"campaignCompleted":{"post":{"operationId":"webhookCampaignCompleted","summary":"Campanha finalizada","description":"Disparado quando uma campanha finaliza todos os envios.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"apiKeyCreated":{"post":{"operationId":"webhookApiKeyCreated","summary":"Chave de API criada","description":"Disparado quando uma nova chave de API é criada.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"apiKeyRevoked":{"post":{"operationId":"webhookApiKeyRevoked","summary":"Chave de API revogada","description":"Disparado quando uma chave de API é revogada.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"automationExecuted":{"post":{"operationId":"webhookAutomationExecuted","summary":"Automação executada","description":"Disparado quando uma automação executa uma ação.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"automationActivated":{"post":{"operationId":"webhookAutomationActivated","summary":"Automação ativada","description":"Disparado quando uma automação é ativada.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}},"automationDeactivated":{"post":{"operationId":"webhookAutomationDeactivated","summary":"Automação desativada","description":"Disparado quando uma automação é desativada.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPayload"}}}},"responses":{"200":{"description":"Webhook processado com sucesso"}}}}}}