openapi: 3.0.3
info:
  title: API da Magnet Customer
  version: 2026-11
  description: "A API REST da Magnet Customer permite ler e gravar os registros do CRM a partir dos seus sistemas.\n\n# Comece aqui\n\n## 1. Crie\
    \ um Service Account\n\nNo CRM, abra **Configurações › Service Accounts** (apenas administradores), clique em **Novo service account**, escolha\
    \ a validade e as permissões, e copie o **Client ID** e o **Client Secret**. O secret aparece uma única vez.\n\n## 2. Solicite um token\n\n\
    ```bash\ncurl -X POST https://magnetcustomer.net/realms/platform/protocol/openid-connect/token \\\n  -H \"Content-Type: application/x-www-form-urlencoded\"\
    \ \\\n  -d \"grant_type=client_credentials\" \\\n  -d \"client_id=SEU_CLIENT_ID\" \\\n  -d \"client_secret=SEU_CLIENT_SECRET\"\n```\n\n```json\n\
    { \"access_token\": \"eyJhbGciOiJSUzI1NiIs...\", \"expires_in\": 900, \"token_type\": \"Bearer\" }\n```\n\nO token vale 15 minutos e este\
    \ fluxo não tem refresh token: quando vencer, ou ao receber `401`, repita o pedido.\n\n## 3. Chame a API\n\nCada conta tem o seu endereço.\
    \ Troque `sua-conta` pelo subdomínio que você usa para abrir o CRM:\n\n```bash\ncurl \"https://sua-conta.platform-api.magnetcustomer.com/api/deals?page=1&limit=15\"\
    \ \\\n  -H \"Authorization: Bearer SEU_TOKEN\"\n```\n\nRequisições com corpo levam também `Content-Type: application/json`.\n\n# Convenções\n\
    \n## Paginação\n\nEnvie sempre `page`. Com ele, toda listagem devolve o mesmo envelope:\n\n```json\n{ \"docs\": [], \"total\": 132, \"pages\"\
    : 9, \"page\": 1, \"limit\": 15 }\n```\n\n| Parâmetro | Padrão | Descrição |\n|---|---|---|\n| `page` | — | Página, a partir de 1. **Obrigatório\
    \ na prática** |\n| `limit` | 15 | Registros por página. Máximo 100: acima disso a API usa 100 |\n| `sortBy` | varia por recurso | Campo de\
    \ ordenação |\n| `sortType` | `desc` | `asc` ou `desc` |\n| `search` | — | Busca textual, quando o recurso aceita |\n\nSem `page`, algumas\
    \ listagens (Contatos, Pipelines, Campos personalizados e os cadastros de apoio, como tipos e situações) devolvem uma lista simples (`[...]`),\
    \ sem o envelope, e ignoram `limit`. Para percorrer tudo, peça `page=1` e avance até `page` ser igual a `pages`. Uma página além do fim devolve\
    \ `docs` vazio.\n\nO tamanho de página padrão é 15, com duas exceções: 25 em Leads, Prospects e Clientes, e 100 em Usuários. Um `limit` inválido\
    \ cai no padrão.\n\n## Identificadores e datas\n\nTodo ID é um texto de 24 caracteres hexadecimais, como `507f1f77bcf86cd799439011`. Ao gravar,\
    \ envie a referência como **ID em texto** — nunca o objeto.\n\nNo corpo e nas respostas, datas são ISO 8601 em UTC, como `2026-05-15T10:00:00Z`.\
    \ Nos filtros de data da URL (`minDate`, `createdAt_minDate`, `dateOfExpires_minDate`...), envie um **timestamp em milissegundos**, como `1798761600000`.\n\
    \n## Listas na URL\n\nFiltros que aceitam vários valores levam colchetes no nome:\n\n```\n/api/contacts?page=1&staffs[]=507f191e810c19729de860eb&staffs[]=507f191e810c19729de860ec\n\
    ```\n\nUm valor único sem colchetes (`staffs=id`) ou separado por vírgula (`staffs=id1,id2`) também é aceito.\n\n## Campos ausentes\n\nCampo\
    \ sem valor costuma vir **ausente** da resposta, não como `null`. Trate a falta da chave como vazio.\n\n## Campos personalizados\n\nOs campos\
    \ criados pelo administrador da conta vão na lista `customFields`:\n\n```json\n{ \"customFields\": [ { \"customField\": \"507f1f77bcf86cd799439014\"\
    , \"k\": \"segmento\", \"v\": \"Tecnologia\" } ] }\n```\n\nO valor vai em **`v`**. Um item com `value` é aceito sem erro, mas o campo fica\
    \ vazio. Descubra os campos e os IDs com `GET /api/customfields?page=1&feature=deal` (troque `deal` por `meeting`, `task`, `contact`...).\
    \ Com `&required=true` vêm só os obrigatórios da conta.\n\n## Erros\n\n```json\n{\n  \"reason\": \"Missing required parameters\",\n  \"message\"\
    : \"Missing required parameters\",\n  \"code\": \"VALIDATION_ERROR\",\n  \"errors\": [ { \"field\": \"title\", \"message\": \"must NOT have\
    \ fewer than 1 characters\", \"keyword\": \"minLength\" } ]\n}\n```\n\n| Status | Quando |\n|---|---|\n| `400` | Corpo ou parâmetro inválido:\
    \ campo obrigatório ausente, tipo errado, valor fora da lista, ID fora do formato |\n| `401` | Token ausente, inválido ou vencido. Corpo:\
    \ `{\"reason\": \"Access Denied\"}` |\n| `403` | O Service Account não tem a permissão da rota (`type: SA_PERMISSION_DENIED`, com `requiredPermission`)\
    \ |\n| `404` | Registro não encontrado |\n| `409` | O registro não está no estado que a operação exige |\n| `429` | Limite de requisições\
    \ excedido. Espere o tempo de `Retry-After` |\n\n## Permissões\n\nUm Service Account só chama as rotas que as permissões dele liberam. Cada\
    \ operação desta referência informa a permissão exigida, quando há uma: marque-a ao criar o Service Account. Uma chamada sem a permissão devolve\
    \ `403` com o nome dela em `requiredPermission`.\n\nRotas de administração da conta (usuários, perfis, criação de webhooks, configurações)\
    \ exigem um usuário administrador e não estão disponíveis para Service Accounts; por isso não aparecem aqui.\n\n## Limite de requisições\n\
    \nOs limites são aplicados por conta. Ao receber `429`, espere os segundos indicados em `Retry-After` e tente de novo com intervalo crescente.\n\
    \n## Área de trabalho\n\nEm contas com áreas de trabalho, a API define a área do registro pela credencial ou pelo registro pai. Não envie\
    \ `workspace`.\n\n# Regras de filtro\n\nDashboards, widgets, predefinições de filtro e automações usam o mesmo formato para dizer quais registros\
    \ entram: o **conjunto de regras** (`ruleset`).\n\n```json\n{\n  \"condition\": \"AND\",\n  \"rules\": [\n    { \"fieldId\": \"deal.amount\"\
    , \"operator\": \"greater\", \"value\": 1000 },\n    {\n      \"condition\": \"OR\",\n      \"rules\": [\n        { \"fieldId\": \"deal.won\"\
    , \"operator\": \"false_value\" },\n        { \"fieldId\": \"deal.stage_name\", \"operator\": \"contains\", \"value\": \"Proposta\" }\n  \
    \    ]\n    }\n  ]\n}\n```\n\n- **`condition`**: `AND` exige todas as regras; `OR`, pelo menos uma.\n- **`rules`**: cada item é uma condição\
    \ (`fieldId`, `operator`, `value`) ou um grupo aninhado (`condition`, `rules`).\n- **`fieldId`**: o `slug` de um campo. A lista de campos\
    \ da conta, com os personalizados, vem de `GET /api/resource-keywords`; filtre por recurso com `?modelRef=deal`.\n- **`operator`**: escolha\
    \ pelo tipo do campo (`type`, na lista de campos).\n\n| Tipo do campo | Operadores |\n|---|---|\n| Texto (`string`) | `equals`, `not_equal`,\
    \ `contains`, `not_contains`, `starts_with`, `ends_with`, `exists`, `not_exists` |\n| Número e valor (`number`, `monetary`, `days`) | `equals`,\
    \ `not_equal`, `greater`, `greater_or_equals`, `less`, `less_or_equals`, `between` (com `valueMin` e `valueMax`) |\n| Sim ou não (`boolean`)\
    \ | `true_value`, `false_value` |\n| Lista (`select`) | `in`, `not_in`, com `value` em lista |\n| Data (`date`) | `current_day`, `current_week`,\
    \ `current_month`, `current_year`, `before_day`, `before_week`, `before_month`, `before_year`, `last_n_days`, `next_n_days`, `before_date`,\
    \ `after_date`, `date_between` |\n\nAs regras de um widget não são validadas na gravação: um campo ou operador que não existe é aceito, e\
    \ o resultado vem vazio. Confira os nomes antes de enviar.\n\n# Webhooks\n\nPara ser avisado quando algo muda no CRM, em vez de consultar\
    \ a API de tempos em tempos, use webhooks. Um administrador cria o webhook no CRM, em **Integrações › Webhooks**, informando a URL do seu\
    \ sistema e os eventos. A lista de eventos está em `GET /api/webhooks/triggers`; o formato da entrega, a assinatura e os reenvios estão na\
    \ seção Webhooks do portal.\n\n# Cobertura\n\nEsta referência cobre Contatos (leads, prospects e clientes), Empresas, Oportunidades, Pipelines,\
    \ etapas e motivos de perda, Produtos, tabelas de preço e itens de produto, Jornadas, Reuniões, Tarefas, Observações, Tickets, Atendimentos,\
    \ Usuários, Equipes, Áreas de trabalho, Campos personalizados, Dashboards (com widgets, filtros e resultados), Automações de processo e os\
    \ eventos de webhook.\n"
  contact:
    name: Suporte Magnet Customer
    email: suporte@magnetcustomer.com
servers:
- url: https://{conta}.platform-api.magnetcustomer.com
  description: API da sua conta
  variables:
    conta:
      default: sua-conta
      description: Subdomínio da sua conta — o mesmo que você usa para abrir o CRM.
security:
- ServiceAccount: []
tags:
- name: Contatos
  description: 'Pessoas em qualquer fase: lead, prospect ou cliente.'
- name: Leads
  description: Contatos na fase `lead`. Mesmos campos de Contatos.
- name: Prospects
  description: Contatos na fase `prospect`. Mesmos campos de Contatos.
- name: Clientes
  description: Contatos na fase `customer`. Mesmos campos de Contatos.
- name: Empresas
  description: Organizações às quais os contatos pertencem.
- name: Oportunidades
  description: 'Negócios em andamento: pipeline, etapa, responsável, valor e fechamento.'
- name: Pipelines e etapas
  description: Consulta dos pipelines, das etapas e dos motivos de perda, para criar, mover e fechar oportunidades.
- name: Produtos
  description: Catálogo de produtos e serviços da conta.
- name: Tabelas de preço
  description: 'Preços por tabela: a padrão e as específicas, como parceiros ou regiões.'
- name: Itens de produto
  description: Produtos vinculados a oportunidades, contatos e empresas, com quantidade, preço e desconto.
- name: Jornadas
  description: 'Acompanhamento do cliente depois da venda: fase, contrato, renovação e health score. Somente leitura.'
- name: Reuniões
  description: Compromissos com contato, empresa ou oportunidade.
- name: Tarefas
  description: Atividades com responsável, tipo e vencimento.
- name: Tipos de tarefa
  description: Cadastro dos tipos usados no campo `type` das tarefas.
- name: Observações
  description: Anotações em contatos, empresas, oportunidades e produtos, com menção a usuários.
- name: Tickets
  description: 'Pedidos de suporte: abertura, situação, responsável e mensagens.'
- name: Atendimentos
  description: 'Registro de cada contato feito com o cliente: ligação, visita, e-mail.'
- name: Usuários
  description: Consulta dos usuários da conta, para preencher responsáveis e participantes.
- name: Campos personalizados
  description: 'Consulta dos campos da conta: IDs, chaves e quais são obrigatórios.'
- name: Equipes e áreas de trabalho
  description: Consulta das equipes e das áreas de trabalho da conta.
- name: Automações
  description: 'Automações de processo: o que dispara, quais registros entram e quais ações são executadas.'
- name: Dashboards
  description: 'Painéis de indicadores: dashboards, widgets, filtros e resultados.'
- name: Campos para filtros
  description: Catálogo dos campos que podem entrar em regras de filtro, eixos e colunas.
- name: Webhooks
  description: Eventos que o CRM envia para o seu sistema. A assinatura é configurada no CRM; aqui você consulta os eventos disponíveis.
x-tagGroups:
- name: Cadastros
  tags:
  - Contatos
  - Leads
  - Prospects
  - Clientes
  - Empresas
- name: Vendas
  tags:
  - Oportunidades
  - Pipelines e etapas
  - Jornadas
- name: Catálogo
  tags:
  - Produtos
  - Tabelas de preço
  - Itens de produto
- name: Atividades
  tags:
  - Reuniões
  - Tarefas
  - Tipos de tarefa
  - Observações
- name: Suporte
  tags:
  - Tickets
  - Atendimentos
- name: Configuração da conta
  tags:
  - Usuários
  - Campos personalizados
  - Equipes e áreas de trabalho
- name: Automação
  tags:
  - Automações
- name: Indicadores
  tags:
  - Dashboards
  - Campos para filtros
- name: Eventos
  tags:
  - Webhooks
paths:
  /api/deals:
    get:
      tags:
      - Oportunidades
      operationId: listDeals
      summary: Listar oportunidades
      description: 'Ordenação padrão: `createdAt` decrescente.


        **Permissão do Service Account:** `deals.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: pipeline
        in: query
        description: ID do pipeline.
        schema:
          type: string
      - name: stage
        in: query
        description: ID da etapa.
        schema:
          type: string
      - name: contact
        in: query
        description: ID do contato.
        schema:
          type: string
      - name: organization
        in: query
        description: ID da empresa.
        schema:
          type: string
      - name: staffs[]
        in: query
        description: 'Usuários: busca em responsável, participantes e observadores. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).'
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: contacts[]
        in: query
        description: Vários contatos. Lista de IDs (`?contacts[]=id1&contacts[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: minDate
        in: query
        description: Fechamento previsto (`expectedCloseDate`) a partir de. Timestamp em milissegundos, não data ISO.
        schema:
          type: integer
          example: 1798761600000
      - name: maxDate
        in: query
        description: Fechamento previsto até. Timestamp em milissegundos.
        schema:
          type: integer
      - name: search
        in: query
        description: Busca em título, nome e e-mail do contato.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Deal'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439015
                  code: 39
                  title: Implantação CRM Enterprise
                  amount: 75000
                  expectedCloseDate: '2026-06-30T23:59:59Z'
                  won: false
                  lost: false
                  closed: false
                  closedDate: null
                  enteredStageDate: '2026-04-28T10:30:00Z'
                  source: platform
                  active: true
                  pipeline: 507f1f77bcf86cd799439011
                  stage:
                    _id: 507f191e810c19729de860ea
                    name: Prospecção
                    position: 0
                    won: false
                    lost: false
                  staff:
                    _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    email: joao@acme.com
                  contact:
                    _id: 507f1f77bcf86cd799439012
                    fullname: Maria Santos
                    lifeCycle: customer
                    owners:
                    - 507f191e810c19729de860eb
                  participants: []
                  observers: []
                  labels: []
                  customFields:
                  - customField: 507f1f77bcf86cd799439014
                    k: segmento
                    v: Tecnologia
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 132
                pages: 9
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Oportunidades
      operationId: createDeal
      summary: Criar oportunidade
      description: 'Se `staff` não for enviado, a API usa o usuário da credencial.


        A criação é idempotente por 5 minutos: reenviar o mesmo corpo não duplica o registro. Para controlar a chave, envie o cabeçalho `Idempotency-Key`.


        **Permissão do Service Account:** `deals.create`'
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DealCreate'
            example:
              title: Implantação CRM Enterprise
              pipeline: 507f1f77bcf86cd799439011
              stage: 507f191e810c19729de860ea
              staff: 507f191e810c19729de860eb
              amount: 75000
              contact: 507f1f77bcf86cd799439012
              expectedCloseDate: '2026-06-30T23:59:59Z'
              customFields:
              - customField: 507f1f77bcf86cd799439014
                k: segmento
                v: Tecnologia
      responses:
        '201':
          description: Oportunidade criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
              example:
                _id: 507f1f77bcf86cd799439015
                code: 39
                title: Implantação CRM Enterprise
                amount: 75000
                expectedCloseDate: '2026-06-30T23:59:59Z'
                won: false
                lost: false
                closed: false
                closedDate: null
                enteredStageDate: '2026-04-28T10:30:00Z'
                source: platform
                active: true
                pipeline: 507f1f77bcf86cd799439011
                stage:
                  _id: 507f191e810c19729de860ea
                  name: Prospecção
                  position: 0
                  won: false
                  lost: false
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                  owners:
                  - 507f191e810c19729de860eb
                participants: []
                observers: []
                labels: []
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/deals/{id}:
    get:
      tags:
      - Oportunidades
      operationId: getDeal
      summary: Obter oportunidade
      description: 'No detalhe, `pipeline` vem como objeto (`_id`, `title`) e `contact` vem com o cadastro completo.


        **Permissão do Service Account:** `deals.read`'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
              example:
                _id: 507f1f77bcf86cd799439015
                code: 39
                title: Implantação CRM Enterprise
                amount: 75000
                expectedCloseDate: '2026-06-30T23:59:59Z'
                won: false
                lost: false
                closed: false
                closedDate: null
                enteredStageDate: '2026-04-28T10:30:00Z'
                source: platform
                active: true
                pipeline:
                  _id: 507f1f77bcf86cd799439011
                  title: Comercial
                stage:
                  _id: 507f191e810c19729de860ea
                  name: Prospecção
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                  owners:
                  - 507f191e810c19729de860eb
                participants: []
                observers: []
                labels: []
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Oportunidades
      operationId: updateDeal
      summary: Atualizar oportunidade
      description: 'Envie só os campos que mudam.


        **Permissão do Service Account:** `deals.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DealInput'
            example:
              amount: 90000
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
              example:
                _id: 507f1f77bcf86cd799439015
                code: 39
                title: Implantação CRM Enterprise
                amount: 90000
                expectedCloseDate: '2026-06-30T23:59:59Z'
                won: false
                lost: false
                closed: false
                closedDate: null
                enteredStageDate: '2026-04-28T10:30:00Z'
                source: platform
                active: true
                pipeline: 507f1f77bcf86cd799439011
                stage:
                  _id: 507f191e810c19729de860ea
                  name: Prospecção
                  position: 0
                  won: false
                  lost: false
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                  owners:
                  - 507f191e810c19729de860eb
                participants: []
                observers: []
                labels: []
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Oportunidades
      operationId: deleteDeal
      summary: Excluir oportunidade
      description: 'Exclusão lógica (`active: false`). Responde `400` com `code: DEAL_HAS_LINKED_JOURNEYS` se houver jornadas ativas vinculadas.


        **Permissão do Service Account:** `deals.delete`'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Oportunidade excluída.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
              example:
                _id: 507f1f77bcf86cd799439015
                code: 39
                title: Implantação CRM Enterprise
                amount: 75000
                expectedCloseDate: '2026-06-30T23:59:59Z'
                won: false
                lost: false
                closed: false
                closedDate: null
                enteredStageDate: '2026-04-28T10:30:00Z'
                source: platform
                active: false
                pipeline: 507f1f77bcf86cd799439011
                stage:
                  _id: 507f191e810c19729de860ea
                  name: Prospecção
                  position: 0
                  won: false
                  lost: false
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                  owners:
                  - 507f191e810c19729de860eb
                participants: []
                observers: []
                labels: []
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '204':
          description: A oportunidade não existe ou já estava excluída.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/deals/{id}/stage:
    patch:
      tags:
      - Oportunidades
      operationId: moveDealStage
      summary: Mover de etapa
      description: Devolve a oportunidade já na nova etapa.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - stageId
              properties:
                stageId:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  example: 507f1f77bcf86cd799439011
                  description: ID da nova etapa.
                enteredStageDate:
                  type: string
                  format: date-time
                  description: 'Quando entrou na etapa. Padrão: agora.'
                  example: '2026-05-15T14:00:00Z'
            example:
              stageId: 507f191e810c19729de860ef
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
              example:
                _id: 507f1f77bcf86cd799439015
                code: 39
                title: Implantação CRM Enterprise
                amount: 75000
                expectedCloseDate: '2026-06-30T23:59:59Z'
                won: false
                lost: false
                closed: false
                closedDate: null
                enteredStageDate: '2026-04-28T10:30:00Z'
                source: platform
                active: true
                pipeline: 507f1f77bcf86cd799439011
                stage:
                  _id: 507f191e810c19729de860ef
                  name: Proposta
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                  owners:
                  - 507f191e810c19729de860eb
                participants: []
                observers: []
                labels: []
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/deals/{id}/close:
    put:
      tags:
      - Oportunidades
      operationId: closeDeal
      summary: Fechar como ganha ou perdida
      description: 'Envie `won: true` para ganha, ou `lost: true` com `lostReason` para perdida.'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                won:
                  type: boolean
                  description: Marca como ganha.
                lost:
                  type: boolean
                  description: Marca como perdida.
                lostReason:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  example: 507f1f77bcf86cd799439011
                  description: ID do motivo da perda (de `GET /api/pipelines/settings/lostreasons`).
            example:
              lost: true
              lostReason: 507f1f77bcf86cd799439016
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/deals/{id}/reopen:
    put:
      tags:
      - Oportunidades
      operationId: reopenDeal
      summary: Reabrir oportunidade
      description: Reabre uma oportunidade ganha ou perdida. Se ela já estiver aberta, a resposta é `200` com a oportunidade sem alteração.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                pipeline:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  example: 507f1f77bcf86cd799439011
                  description: 'Opcional: move para outro pipeline ao reabrir.'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Deal'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/deals/{id}/validate:
    get:
      tags:
      - Oportunidades
      operationId: validateDeal
      summary: Validar pendências antes de fechar
      description: Diz se a oportunidade tem ações ou campos obrigatórios pendentes. Cada lista traz o que falta; vazia quando não falta nada.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  validation:
                    type: boolean
                    description: '`true` quando não há pendência.'
                  requiredActionsPending:
                    type: object
                    properties:
                      validation:
                        type: boolean
                      task:
                        type: array
                        items:
                          type: object
                        description: Tarefas exigidas pela etapa.
                      meeting:
                        type: array
                        items:
                          type: object
                        description: Reuniões exigidas pela etapa.
                      ticket:
                        type: array
                        items:
                          type: object
                        description: Tickets exigidos pela etapa.
                  requiredFieldPending:
                    type: object
                    properties:
                      validation:
                        type: boolean
                      contact:
                        type: array
                        items:
                          type: object
                        description: Campos do contato que faltam.
                      deal:
                        type: array
                        items:
                          type: object
                        description: Campos da oportunidade que faltam.
              example:
                requiredActionsPending:
                  task: []
                  meeting: []
                  ticket: []
                  validation: true
                requiredFieldPending:
                  contact: []
                  deal: []
                  validation: true
                validation: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/deals/{id}/stage-history:
    get:
      tags:
      - Oportunidades
      operationId: getDealStageHistory
      summary: Histórico de etapas
      description: Uma entrada por passagem em etapa, com a duração de cada uma e os totais.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  dealId:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  history:
                    type: array
                    items:
                      type: object
                      properties:
                        _id:
                          type: string
                          pattern: ^[a-fA-F0-9]{24}$
                          example: 507f1f77bcf86cd799439011
                        stage:
                          type: object
                          properties:
                            _id:
                              type: string
                              pattern: ^[a-fA-F0-9]{24}$
                              example: 507f1f77bcf86cd799439011
                            name:
                              type: string
                            position:
                              type: integer
                            probability:
                              type: number
                        staff:
                          type: string
                          pattern: ^[a-fA-F0-9]{24}$
                          example: 507f1f77bcf86cd799439011
                          description: Quem moveu.
                        startDate:
                          type: string
                          format: date-time
                          description: Entrada na etapa.
                          example: '2026-05-15T14:00:00Z'
                        closingDate:
                          type: string
                          format: date-time
                          description: Saída da etapa. Ausente na etapa atual.
                          example: '2026-05-15T14:00:00Z'
                        open:
                          type: boolean
                          description: Etapa atual.
                        durationMs:
                          type: integer
                          description: Tempo na etapa, em milissegundos.
                  totals:
                    type: object
                    properties:
                      totalDurationMs:
                        type: integer
                      openStages:
                        type: integer
              example:
                dealId: 507f1f77bcf86cd799439015
                history:
                - _id: 507f1f77bcf86cd799439070
                  stage:
                    _id: 507f191e810c19729de860ea
                    name: Prospecção
                    position: 0
                    probability: 10
                  staff: 507f191e810c19729de860eb
                  startDate: '2026-04-28T10:00:00Z'
                  closingDate: '2026-04-29T15:10:00Z'
                  open: false
                  durationMs: 105000000
                totals:
                  totalDurationMs: 105000000
                  openStages: 0
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/meetings:
    get:
      tags:
      - Reuniões
      operationId: listMeetings
      summary: Listar reuniões
      description: 'Ordenação padrão: `start` decrescente.


        **Permissão do Service Account:** `meetings.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: staff
        in: query
        description: ID do organizador.
        schema:
          type: string
      - name: staffs[]
        in: query
        description: 'Usuários: busca em organizador, participantes e observadores. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).'
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: type
        in: query
        description: ID do tipo de reunião.
        schema:
          type: string
      - name: room
        in: query
        description: ID da sala.
        schema:
          type: string
      - name: contact
        in: query
        description: ID do contato.
        schema:
          type: string
      - name: deal
        in: query
        description: ID da oportunidade.
        schema:
          type: string
      - name: organization
        in: query
        description: ID da empresa.
        schema:
          type: string
      - name: priority
        in: query
        description: Prioridade.
        schema:
          type: string
          enum:
          - p1
          - p2
          - p3
          - p4
          description: Prioridade.
      - name: status
        in: query
        description: Situação.
        schema:
          type: string
          enum:
          - pending
          - cancelled
          - finished
          - no-show
          description: 'Padrão: `pending`. Com `finished`, `cancelled` ou `no-show`, a API preenche `dateFinished`.'
      - name: labels[]
        in: query
        description: Etiquetas. Lista de IDs (`?labels[]=id1&labels[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: start_minDate
        in: query
        description: Início a partir de (timestamp em ms ou data ISO).
        schema:
          type: string
      - name: start_maxDate
        in: query
        description: Início até (timestamp em ms ou data ISO).
        schema:
          type: string
      - name: createdAt_minDate
        in: query
        description: Criada a partir de (timestamp em milissegundos).
        schema:
          type: integer
      - name: createdAt_maxDate
        in: query
        description: Criada até (timestamp em milissegundos).
        schema:
          type: integer
      - name: visualization
        in: query
        description: Atalho de período.
        schema:
          type: string
          enum:
          - all
          - dueDateToday
          - dueDateWeek
          - nextWeek
          - finished
      - name: search
        in: query
        description: Busca em título, descrição, contato, empresa, oportunidade e organizador.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Meeting'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439030
                  title: Apresentação da proposta — Acme
                  description: Demonstração técnica e comercial
                  start: '2026-05-15T14:00:00Z'
                  end: '2026-05-15T15:30:00Z'
                  status: pending
                  priority: p2
                  active: true
                  staff:
                    _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                    email: joao@acme.com
                  contact:
                    _id: 507f1f77bcf86cd799439012
                    fullname: Maria Santos
                    lifeCycle: customer
                  deal:
                    _id: 507f1f77bcf86cd799439015
                    title: Implantação CRM
                    amount: 75000
                  participants: []
                  observers: []
                  labels: []
                  customFields: []
                  createdAt: '2026-04-28T10:00:00Z'
                  updatedAt: '2026-04-28T10:00:00Z'
                total: 47
                pages: 4
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Reuniões
      operationId: createMeeting
      summary: Criar reunião
      description: 'Obrigatórios por padrão: `title` e `start`. `staff` e `calendar` são preenchidos pela API quando omitidos.


        Os campos obrigatórios podem variar por conta: o administrador define quais campos de sistema são obrigatórios. `GET /api/customfields?feature=meeting&required=true`
        devolve os campos obrigatórios da conta. Quando falta um deles, a API responde `400` com `code: MISSING_REQUIRED` e a lista em `fields`.


        Referências (`staff`, `contact`, `deal`...) aceitam só o ID em texto. Enviar o objeto (`{"_id": "..."}`) devolve `400 VALIDATION_ERROR`.


        **Permissão do Service Account:** `meetings.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MeetingCreate'
            example:
              title: Apresentação da proposta — Acme
              description: Demonstração técnica e comercial
              start: '2026-05-15T14:00:00Z'
              end: '2026-05-15T15:30:00Z'
              staff: 507f191e810c19729de860eb
              priority: p2
              contact: 507f1f77bcf86cd799439012
              deal: 507f1f77bcf86cd799439015
              participants:
              - 507f191e810c19729de860ec
      responses:
        '201':
          description: Reunião criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Meeting'
              example:
                _id: 507f1f77bcf86cd799439030
                title: Apresentação da proposta — Acme
                description: Demonstração técnica e comercial
                start: '2026-05-15T14:00:00Z'
                end: '2026-05-15T15:30:00Z'
                status: pending
                priority: p2
                active: true
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                deal:
                  _id: 507f1f77bcf86cd799439015
                  title: Implantação CRM
                  amount: 75000
                participants: []
                observers: []
                labels: []
                customFields: []
                createdAt: '2026-04-28T10:00:00Z'
                updatedAt: '2026-04-28T10:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/meetings/{id}:
    get:
      tags:
      - Reuniões
      operationId: getMeeting
      summary: Obter reunião
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Meeting'
              example:
                _id: 507f1f77bcf86cd799439030
                title: Apresentação da proposta — Acme
                description: Demonstração técnica e comercial
                start: '2026-05-15T14:00:00Z'
                end: '2026-05-15T15:30:00Z'
                status: pending
                priority: p2
                active: true
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                deal:
                  _id: 507f1f77bcf86cd799439015
                  title: Implantação CRM
                  amount: 75000
                participants: []
                observers: []
                labels: []
                customFields: []
                createdAt: '2026-04-28T10:00:00Z'
                updatedAt: '2026-04-28T10:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `meetings.read`'
    put:
      tags:
      - Reuniões
      operationId: replaceMeeting
      summary: Substituir reunião
      description: 'Substitui o registro: reenvie o objeto completo. Para alterar só alguns campos, use `PATCH`.


        **Permissão do Service Account:** `meetings.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MeetingCreate'
            example:
              title: Apresentação da proposta — Acme
              description: Demonstração técnica e comercial
              start: '2026-05-15T14:00:00Z'
              end: '2026-05-15T15:30:00Z'
              staff: 507f191e810c19729de860eb
              priority: p2
              contact: 507f1f77bcf86cd799439012
              deal: 507f1f77bcf86cd799439015
              participants:
              - 507f191e810c19729de860ec
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Meeting'
              example:
                _id: 507f1f77bcf86cd799439030
                title: Apresentação da proposta — Acme
                description: Demonstração técnica e comercial
                start: '2026-05-15T14:00:00Z'
                end: '2026-05-15T15:30:00Z'
                status: pending
                priority: p2
                active: true
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                deal:
                  _id: 507f1f77bcf86cd799439015
                  title: Implantação CRM
                  amount: 75000
                participants: []
                observers: []
                labels: []
                customFields: []
                createdAt: '2026-04-28T10:00:00Z'
                updatedAt: '2026-04-28T10:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
      - Reuniões
      operationId: updateMeeting
      summary: Atualizar reunião
      description: 'Atualização parcial: envie só os campos que mudam.'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MeetingInput'
            example:
              start: '2026-05-16T14:00:00Z'
              end: '2026-05-16T15:30:00Z'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Meeting'
              example:
                _id: 507f1f77bcf86cd799439030
                title: Apresentação da proposta — Acme
                description: Demonstração técnica e comercial
                start: '2026-05-15T14:00:00Z'
                end: '2026-05-15T15:30:00Z'
                status: pending
                priority: p2
                active: true
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                deal:
                  _id: 507f1f77bcf86cd799439015
                  title: Implantação CRM
                  amount: 75000
                participants: []
                observers: []
                labels: []
                customFields: []
                createdAt: '2026-04-28T10:00:00Z'
                updatedAt: '2026-04-28T10:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Reuniões
      operationId: deleteMeeting
      summary: Excluir reunião
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Reunião excluída.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `meetings.delete`'
  /api/meetings/{id}/finish:
    put:
      tags:
      - Reuniões
      operationId: finishMeeting
      summary: Concluir reunião
      description: Marca como `finished` e registra `dateFinished`.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                dateFinished:
                  type: string
                  format: date-time
                  description: 'Quando terminou. Padrão: agora.'
                  example: '2026-05-15T14:00:00Z'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Meeting'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/meetings/selected/remove:
    put:
      tags:
      - Reuniões
      operationId: deleteMeetings
      summary: Excluir várias reuniões
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - meetingList
              properties:
                meetingList:
                  type: array
                  items:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  description: IDs das reuniões.
            example:
              meetingList:
              - 507f1f77bcf86cd799439030
              - 507f1f77bcf86cd799439031
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  removed:
                    type: integer
                    description: Quantas foram excluídas.
                  blocked:
                    type: integer
                    description: Quantas não puderam ser excluídas.
              example:
                removed: 2
                blocked: 0
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/tasks:
    get:
      tags:
      - Tarefas
      operationId: listTasks
      summary: Listar tarefas
      description: 'Ordenação padrão: `status` decrescente. Campos aceitos em `sortBy`: `title`, `dateOfExpires`, `createdAt`, `updatedAt`, `status`,
        `priority`, `type`, `owner`, `dueDate`.


        **Permissão do Service Account:** `tasks.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: owner
        in: query
        description: ID do responsável.
        schema:
          type: string
      - name: staffs[]
        in: query
        description: 'Usuários: busca em responsável, participantes e observadores. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).'
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: type
        in: query
        description: ID do tipo.
        schema:
          type: string
      - name: status
        in: query
        description: Situação.
        schema:
          type: string
          enum:
          - pending
          - finished
          description: 'Padrão: `pending`.'
      - name: priority
        in: query
        description: Prioridade.
        schema:
          type: string
          enum:
          - p1
          - p2
          - p3
          - p4
          description: Prioridade.
      - name: source
        in: query
        description: Origem.
        schema:
          type: string
      - name: contact
        in: query
        description: ID do contato.
        schema:
          type: string
      - name: deal
        in: query
        description: ID da oportunidade.
        schema:
          type: string
      - name: organization
        in: query
        description: ID da empresa.
        schema:
          type: string
      - name: dateOfExpires_minDate
        in: query
        description: Vencimento a partir de. Timestamp em milissegundos, não data ISO.
        schema:
          type: integer
          example: 1798761600000
      - name: dateOfExpires_maxDate
        in: query
        description: Vencimento até. Timestamp em milissegundos.
        schema:
          type: integer
      - name: visualization
        in: query
        description: 'Atalho: `dueDate` traz as atrasadas.'
        schema:
          type: string
          enum:
          - all
          - dueDateToday
          - dueDate
          - pending
          - finished
      - name: search
        in: query
        description: Busca em título, detalhes, responsável, contato e oportunidade.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Task'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439050
                  code: 126
                  title: Enviar proposta comercial
                  dateOfExpires: '2026-05-15T17:00:00Z'
                  status: pending
                  priority: p2
                  source: deal
                  active: true
                  type:
                    _id: 507f1f77bcf86cd799439040
                    name: Ligação
                  owner:
                    _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                    email: joao@acme.com
                  deal:
                    _id: 507f1f77bcf86cd799439015
                    title: Implantação CRM
                    amount: 75000
                  participants: []
                  observers: []
                  customFields: []
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 89
                pages: 6
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Tarefas
      operationId: createTask
      summary: Criar tarefa
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskCreate'
            example:
              title: Enviar proposta comercial
              dateOfExpires: '2026-05-15T17:00:00Z'
              type: 507f1f77bcf86cd799439040
              owner: 507f191e810c19729de860eb
              priority: p2
              source: deal
              deal: 507f1f77bcf86cd799439015
              estimatedMinutes: 60
      responses:
        '201':
          description: Tarefa criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
              example:
                _id: 507f1f77bcf86cd799439050
                code: 126
                title: Enviar proposta comercial
                dateOfExpires: '2026-05-15T17:00:00Z'
                status: pending
                priority: p2
                source: deal
                active: true
                type:
                  _id: 507f1f77bcf86cd799439040
                  name: Ligação
                owner:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                deal:
                  _id: 507f1f77bcf86cd799439015
                  title: Implantação CRM
                  amount: 75000
                participants: []
                observers: []
                customFields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      description: '**Permissão do Service Account:** `tasks.create`'
  /api/tasks/{id}:
    get:
      tags:
      - Tarefas
      operationId: getTask
      summary: Obter tarefa
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
              example:
                _id: 507f1f77bcf86cd799439050
                code: 126
                title: Enviar proposta comercial
                dateOfExpires: '2026-05-15T17:00:00Z'
                status: pending
                priority: p2
                source: deal
                active: true
                type:
                  _id: 507f1f77bcf86cd799439040
                  name: Ligação
                owner:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                deal:
                  _id: 507f1f77bcf86cd799439015
                  title: Implantação CRM
                  amount: 75000
                participants: []
                observers: []
                customFields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `tasks.read`'
    put:
      tags:
      - Tarefas
      operationId: updateTask
      summary: Atualizar tarefa
      description: 'Envie só os campos que mudam.


        **Permissão do Service Account:** `tasks.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TaskInput'
            example:
              priority: p1
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
              example:
                _id: 507f1f77bcf86cd799439050
                code: 126
                title: Enviar proposta comercial
                dateOfExpires: '2026-05-15T17:00:00Z'
                status: pending
                priority: p2
                source: deal
                active: true
                type:
                  _id: 507f1f77bcf86cd799439040
                  name: Ligação
                owner:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                deal:
                  _id: 507f1f77bcf86cd799439015
                  title: Implantação CRM
                  amount: 75000
                participants: []
                observers: []
                customFields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Tarefas
      operationId: deleteTask
      summary: Excluir tarefa
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '204':
          description: Tarefa excluída.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `tasks.delete`'
  /api/tasks/{id}/finish:
    put:
      tags:
      - Tarefas
      operationId: finishTask
      summary: Concluir tarefa
      description: 'Marca como `finished`. Sem corpo.


        **Permissão do Service Account:** `tasks.finish`'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/tasks/selected/finish:
    put:
      tags:
      - Tarefas
      operationId: finishTasks
      summary: Concluir várias tarefas
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - taskList
              properties:
                taskList:
                  type: array
                  items:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  description: IDs das tarefas. Não pode ser vazio.
            example:
              taskList:
              - 507f1f77bcf86cd799439050
      responses:
        '200':
          description: Tarefas concluídas.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      description: '**Permissão do Service Account:** `tasks.finish`'
  /api/tasks/selected/remove:
    put:
      tags:
      - Tarefas
      operationId: deleteTasks
      summary: Excluir várias tarefas
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - taskList
              properties:
                taskList:
                  type: array
                  items:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  description: IDs das tarefas. Não pode ser vazio.
            example:
              taskList:
              - 507f1f77bcf86cd799439050
      responses:
        '200':
          description: Tarefas excluídas.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/tasks/types:
    get:
      tags:
      - Tipos de tarefa
      operationId: listTaskTypes
      summary: Listar tipos de tarefa
      description: 'Sem `page`, devolve uma lista simples com todos os tipos. Com `page`, devolve o envelope paginado.


        **Permissão do Service Account:** `tasks.read`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - name: search
        in: query
        description: Busca no nome.
        schema:
          type: string
      - name: active
        in: query
        description: Filtra por ativos.
        schema:
          type: boolean
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                oneOf:
                - type: array
                  items:
                    $ref: '#/components/schemas/TaskType'
                - allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      docs:
                        type: array
                        items:
                          $ref: '#/components/schemas/TaskType'
              example:
              - _id: 507f1f77bcf86cd799439040
                name: Ligação
                slug: ligacao
                active: true
                default: false
                defaultForNewTask: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Tipos de tarefa
      operationId: createTaskType
      summary: Criar tipo de tarefa
      description: 'Não envie `slug`: ele é gerado a partir do nome.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - type: object
                properties:
                  name:
                    type: string
                    description: Nome do tipo.
                  active:
                    type: boolean
                  default:
                    type: boolean
                    description: Tipo padrão da conta.
                  defaultForNewTask:
                    type: boolean
                    description: Vem selecionado ao criar tarefa.
              - type: object
                required:
                - name
            example:
              name: Ligação
              defaultForNewTask: true
      responses:
        '201':
          description: Tipo criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskType'
              example:
                _id: 507f1f77bcf86cd799439040
                name: Ligação
                slug: ligacao
                active: true
                default: false
                defaultForNewTask: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/tasks/types/{id}:
    get:
      tags:
      - Tipos de tarefa
      operationId: getTaskType
      summary: Obter tipo de tarefa
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskType'
              example:
                _id: 507f1f77bcf86cd799439040
                name: Ligação
                slug: ligacao
                active: true
                default: false
                defaultForNewTask: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Tipos de tarefa
      operationId: updateTaskType
      summary: Atualizar tipo de tarefa
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Nome do tipo.
                active:
                  type: boolean
                default:
                  type: boolean
                  description: Tipo padrão da conta.
                defaultForNewTask:
                  type: boolean
                  description: Vem selecionado ao criar tarefa.
            example:
              name: Ligação telefônica
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskType'
              example:
                _id: 507f1f77bcf86cd799439040
                name: Ligação
                slug: ligacao
                active: true
                default: false
                defaultForNewTask: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Tipos de tarefa
      operationId: deleteTaskType
      summary: Excluir tipo de tarefa
      description: Exclusão definitiva. As tarefas que usam o tipo continuam existindo, apontando para um tipo que não existe mais.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '204':
          description: Tipo excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/observations:
    get:
      tags:
      - Observações
      operationId: listObservations
      summary: Listar observações
      description: 'Devolve só as ativas. Ordenação padrão: `createdAt` decrescente.


        **Permissão do Service Account:** `observations.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: deal
        in: query
        description: ID da oportunidade.
        schema:
          type: string
      - name: contact
        in: query
        description: ID do contato.
        schema:
          type: string
      - name: contacts[]
        in: query
        description: Vários contatos. Lista de IDs (`?contacts[]=id1&contacts[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: organization
        in: query
        description: ID da empresa.
        schema:
          type: string
      - name: search
        in: query
        description: Busca no texto, contato, empresa, oportunidade e produto.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Observation'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439060
                  observation: Cliente pediu desconto de 15%. Aguardando aprovação.
                  active: true
                  staff:
                    _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    email: joao@acme.com
                  contact:
                    _id: 507f1f77bcf86cd799439012
                    fullname: Maria Santos
                    email: maria@cliente.com
                  organization: null
                  deal:
                    _id: 507f1f77bcf86cd799439015
                    title: Implantação CRM
                  createdAt: '2026-04-28T14:30:00Z'
                  updatedAt: '2026-04-28T14:30:00Z'
                total: 5
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Observações
      operationId: createObservation
      summary: Criar observação
      description: Vincule a pelo menos um registro (contato, empresa, oportunidade ou produto). As referências aceitam só o ID em texto.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/ObservationInput'
              - type: object
                required:
                - observation
                - staff
            example:
              observation: Cliente pediu desconto de 15%. Aguardando aprovação.
              staff: 507f191e810c19729de860eb
              contact: 507f1f77bcf86cd799439012
              deal: 507f1f77bcf86cd799439015
      responses:
        '201':
          description: Observação criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Observation'
              example:
                _id: 507f1f77bcf86cd799439060
                observation: Cliente pediu desconto de 15%. Aguardando aprovação.
                staff: 507f191e810c19729de860eb
                contact: 507f1f77bcf86cd799439012
                deal: 507f1f77bcf86cd799439015
                active: true
                createdAt: '2026-04-28T14:30:00Z'
                updatedAt: '2026-04-28T14:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/observations/{id}:
    get:
      tags:
      - Observações
      operationId: getObservation
      summary: Obter observação
      description: Aqui as referências vêm como ID, não resumidas.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Observation'
              example:
                _id: 507f1f77bcf86cd799439060
                observation: Cliente pediu desconto de 15%. Aguardando aprovação.
                staff: 507f191e810c19729de860eb
                contact: 507f1f77bcf86cd799439012
                deal: 507f1f77bcf86cd799439015
                active: true
                createdAt: '2026-04-28T14:30:00Z'
                updatedAt: '2026-04-28T14:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Observações
      operationId: updateObservation
      summary: Atualizar observação
      description: Envie só os campos que mudam.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ObservationInput'
            example:
              observation: Desconto aprovado.
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Observation'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Observações
      operationId: deleteObservation
      summary: Excluir observação
      description: 'Exclusão lógica (`active: false`).'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '204':
          description: Observação excluída.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/observations/selected/remove:
    put:
      tags:
      - Observações
      operationId: deleteObservations
      summary: Excluir várias observações
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - observationList
              properties:
                observationList:
                  type: array
                  items:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  description: IDs das observações. Não pode ser vazio.
            example:
              observationList:
              - 507f1f77bcf86cd799439060
      responses:
        '200':
          description: Observações excluídas.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/contacts:
    get:
      tags:
      - Contatos
      operationId: listContacts
      summary: Listar contatos
      description: 'Ordenação padrão: `fullname` crescente. Devolve só os registros ativos.


        **Permissão do Service Account:** `contacts.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: staffs[]
        in: query
        description: Usuários responsáveis. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: organization
        in: query
        description: ID da empresa vinculada.
        schema:
          type: string
      - name: search
        in: query
        description: Busca em nome, e-mail, telefone e documento.
        schema:
          type: string
      - name: emailsEmpty
        in: query
        description: Com `true`, só os sem e-mail.
        schema:
          type: boolean
      - name: phonesEmpty
        in: query
        description: Com `true`, só os sem telefone.
        schema:
          type: boolean
      - name: lifeCycle
        in: query
        description: Fase do contato.
        schema:
          type: string
          enum:
          - lead
          - prospect
          - customer
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Contact'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439012
                  lifeCycle: customer
                  fullname: Maria Santos
                  name: Maria
                  lastname: Santos
                  email: maria@cliente.com
                  type: physical
                  active: true
                  owners:
                  - _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                  phones:
                  - number: '11987654321'
                    ddi: '+55'
                    typePhone: cellphone
                  source: site
                  customFields:
                  - customField: 507f1f77bcf86cd799439014
                    k: segmento
                    v: Tecnologia
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 250
                pages: 17
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Contatos
      operationId: createContact
      summary: Criar contato
      description: 'O corpo não tem campo obrigatório fixo: o administrador da conta define quais são. `GET /api/customfields?feature=contact&required=true`
        devolve os campos obrigatórios da conta. Quando falta um deles, a API responde `400` com `code: MISSING_REQUIRED` e a lista em `fields`.


        **Permissão do Service Account:** `contacts.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              fullname: Maria Santos
              email: maria@cliente.com
              phones:
              - number: '11987654321'
                ddi: '+55'
                typePhone: cellphone
              source: site
              customFields:
              - customField: 507f1f77bcf86cd799439014
                k: segmento
                v: Tecnologia
      responses:
        '201':
          description: Registro criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: customer
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/contacts/{id}:
    get:
      tags:
      - Contatos
      operationId: getContact
      summary: Obter contato
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: customer
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `contacts.read`'
    put:
      tags:
      - Contatos
      operationId: updateContact
      summary: Atualizar contato
      description: 'Envie só os campos que mudam.


        **Permissão do Service Account:** `contacts.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              email: maria.santos@cliente.com
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: customer
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Contatos
      operationId: deleteContact
      summary: Excluir contato
      description: 'Exige a permissão de excluir.


        **Permissão do Service Account:** `contacts.remove`. Ela não aparece na lista de permissões da tela: vem no modelo **Acesso CRM Completo**'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Registro excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/leads:
    get:
      tags:
      - Leads
      operationId: listLeads
      summary: Listar leads
      description: 'Ordenação padrão: `createdAt` decrescente. Tamanho de página padrão: 25. Devolve só os registros ativos, como `GET /api/contacts?lifeCycle=lead`.'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: staffs[]
        in: query
        description: Usuários responsáveis. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: organization
        in: query
        description: ID da empresa vinculada.
        schema:
          type: string
      - name: search
        in: query
        description: Busca em nome, e-mail, telefone e documento.
        schema:
          type: string
      - name: emailsEmpty
        in: query
        description: Com `true`, só os sem e-mail.
        schema:
          type: boolean
      - name: phonesEmpty
        in: query
        description: Com `true`, só os sem telefone.
        schema:
          type: boolean
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Contact'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439012
                  lifeCycle: lead
                  fullname: Maria Santos
                  name: Maria
                  lastname: Santos
                  email: maria@cliente.com
                  type: physical
                  active: true
                  owners:
                  - _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                  phones:
                  - number: '11987654321'
                    ddi: '+55'
                    typePhone: cellphone
                  source: site
                  customFields:
                  - customField: 507f1f77bcf86cd799439014
                    k: segmento
                    v: Tecnologia
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 250
                pages: 17
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Leads
      operationId: createLead
      summary: Criar lead
      description: 'O corpo não tem campo obrigatório fixo: o administrador da conta define quais são. `GET /api/customfields?feature=contact&required=true`
        devolve os campos obrigatórios da conta. Quando falta um deles, a API responde `400` com `code: MISSING_REQUIRED` e a lista em `fields`.
        Os registros criados por aqui entram como `lead`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              fullname: Maria Santos
              email: maria@cliente.com
              phones:
              - number: '11987654321'
                ddi: '+55'
                typePhone: cellphone
              source: site
              customFields:
              - customField: 507f1f77bcf86cd799439014
                k: segmento
                v: Tecnologia
      responses:
        '201':
          description: Registro criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: lead
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/leads/{id}:
    get:
      tags:
      - Leads
      operationId: getLead
      summary: Obter lead
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: lead
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Leads
      operationId: updateLead
      summary: Atualizar lead
      description: Envie só os campos que mudam.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              email: maria.santos@cliente.com
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: lead
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Leads
      operationId: deleteLead
      summary: Excluir lead
      description: Exige a permissão de excluir.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Registro excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/prospects:
    get:
      tags:
      - Prospects
      operationId: listProspects
      summary: Listar prospects
      description: 'Ordenação padrão: `fullname` crescente. Tamanho de página padrão: 25. Devolve só os registros ativos, como `GET /api/contacts?lifeCycle=prospect`.'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: staffs[]
        in: query
        description: Usuários responsáveis. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: organization
        in: query
        description: ID da empresa vinculada.
        schema:
          type: string
      - name: search
        in: query
        description: Busca em nome, e-mail, telefone e documento.
        schema:
          type: string
      - name: emailsEmpty
        in: query
        description: Com `true`, só os sem e-mail.
        schema:
          type: boolean
      - name: phonesEmpty
        in: query
        description: Com `true`, só os sem telefone.
        schema:
          type: boolean
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Contact'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439012
                  lifeCycle: prospect
                  fullname: Maria Santos
                  name: Maria
                  lastname: Santos
                  email: maria@cliente.com
                  type: physical
                  active: true
                  owners:
                  - _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                  phones:
                  - number: '11987654321'
                    ddi: '+55'
                    typePhone: cellphone
                  source: site
                  customFields:
                  - customField: 507f1f77bcf86cd799439014
                    k: segmento
                    v: Tecnologia
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 250
                pages: 17
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Prospects
      operationId: createProspect
      summary: Criar prospect
      description: 'O corpo não tem campo obrigatório fixo: o administrador da conta define quais são. `GET /api/customfields?feature=contact&required=true`
        devolve os campos obrigatórios da conta. Quando falta um deles, a API responde `400` com `code: MISSING_REQUIRED` e a lista em `fields`.
        Os registros criados por aqui entram como `prospect`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              fullname: Maria Santos
              email: maria@cliente.com
              phones:
              - number: '11987654321'
                ddi: '+55'
                typePhone: cellphone
              source: site
              customFields:
              - customField: 507f1f77bcf86cd799439014
                k: segmento
                v: Tecnologia
      responses:
        '201':
          description: Registro criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: prospect
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/prospects/{id}:
    get:
      tags:
      - Prospects
      operationId: getProspect
      summary: Obter prospect
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: prospect
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Prospects
      operationId: updateProspect
      summary: Atualizar prospect
      description: Envie só os campos que mudam.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              email: maria.santos@cliente.com
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: prospect
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Prospects
      operationId: deleteProspect
      summary: Excluir prospect
      description: Exige a permissão de excluir.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Registro excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/customers:
    get:
      tags:
      - Clientes
      operationId: listCustomers
      summary: Listar clientes
      description: 'Ordenação padrão: `createdAt` decrescente. Tamanho de página padrão: 25. Devolve só os registros ativos, como `GET /api/contacts?lifeCycle=customer`.'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: staffs[]
        in: query
        description: Usuários responsáveis. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: organization
        in: query
        description: ID da empresa vinculada.
        schema:
          type: string
      - name: search
        in: query
        description: Busca em nome, e-mail, telefone e documento.
        schema:
          type: string
      - name: emailsEmpty
        in: query
        description: Com `true`, só os sem e-mail.
        schema:
          type: boolean
      - name: phonesEmpty
        in: query
        description: Com `true`, só os sem telefone.
        schema:
          type: boolean
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Contact'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439012
                  lifeCycle: customer
                  fullname: Maria Santos
                  name: Maria
                  lastname: Santos
                  email: maria@cliente.com
                  type: physical
                  active: true
                  owners:
                  - _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                  phones:
                  - number: '11987654321'
                    ddi: '+55'
                    typePhone: cellphone
                  source: site
                  customFields:
                  - customField: 507f1f77bcf86cd799439014
                    k: segmento
                    v: Tecnologia
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 250
                pages: 17
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Clientes
      operationId: createCustomer
      summary: Criar cliente
      description: 'O corpo não tem campo obrigatório fixo: o administrador da conta define quais são. `GET /api/customfields?feature=contact&required=true`
        devolve os campos obrigatórios da conta. Quando falta um deles, a API responde `400` com `code: MISSING_REQUIRED` e a lista em `fields`.
        Os registros criados por aqui entram como `customer`.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              fullname: Maria Santos
              email: maria@cliente.com
              phones:
              - number: '11987654321'
                ddi: '+55'
                typePhone: cellphone
              source: site
              customFields:
              - customField: 507f1f77bcf86cd799439014
                k: segmento
                v: Tecnologia
      responses:
        '201':
          description: Registro criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: customer
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/customers/{id}:
    get:
      tags:
      - Clientes
      operationId: getCustomer
      summary: Obter cliente
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: customer
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Clientes
      operationId: updateCustomer
      summary: Atualizar cliente
      description: Envie só os campos que mudam.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContactInput'
            example:
              email: maria.santos@cliente.com
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: 507f1f77bcf86cd799439012
                lifeCycle: customer
                fullname: Maria Santos
                name: Maria
                lastname: Santos
                email: maria@cliente.com
                type: physical
                active: true
                owners:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                phones:
                - number: '11987654321'
                  ddi: '+55'
                  typePhone: cellphone
                source: site
                customFields:
                - customField: 507f1f77bcf86cd799439014
                  k: segmento
                  v: Tecnologia
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Clientes
      operationId: deleteCustomer
      summary: Excluir cliente
      description: Exige a permissão de excluir.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Registro excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/organizations:
    get:
      tags:
      - Empresas
      operationId: listOrganizations
      summary: Listar empresas
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: staffs[]
        in: query
        description: Usuários responsáveis. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: search
        in: query
        description: Busca em nome, e-mail e documento.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Organization'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439013
                  fullname: Cliente XYZ Ltda
                  doc: '12345678000190'
                  email: contato@clientexyz.com.br
                  phones: []
                  customFields: []
                  active: true
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 80
                pages: 6
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      description: '**Permissão do Service Account:** `organizations.list`'
    post:
      tags:
      - Empresas
      operationId: createOrganization
      summary: Criar empresa
      description: 'Os campos obrigatórios são definidos pelo administrador da conta: consulte `GET /api/customfields?feature=organization&required=true`.


        **Permissão do Service Account:** `organizations.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrganizationInput'
            example:
              fullname: Cliente XYZ Ltda
              doc: '12345678000190'
              email: contato@clientexyz.com.br
      responses:
        '201':
          description: Empresa criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Organization'
              example:
                _id: 507f1f77bcf86cd799439013
                fullname: Cliente XYZ Ltda
                doc: '12345678000190'
                email: contato@clientexyz.com.br
                phones: []
                customFields: []
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/organizations/{id}:
    get:
      tags:
      - Empresas
      operationId: getOrganization
      summary: Obter empresa
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Organization'
              example:
                _id: 507f1f77bcf86cd799439013
                fullname: Cliente XYZ Ltda
                doc: '12345678000190'
                email: contato@clientexyz.com.br
                phones: []
                customFields: []
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `organizations.read`'
    put:
      tags:
      - Empresas
      operationId: updateOrganization
      summary: Atualizar empresa
      description: 'Envie só os campos que mudam.


        **Permissão do Service Account:** `organizations.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrganizationInput'
            example:
              email: financeiro@clientexyz.com.br
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Organization'
              example:
                _id: 507f1f77bcf86cd799439013
                fullname: Cliente XYZ Ltda
                doc: '12345678000190'
                email: contato@clientexyz.com.br
                phones: []
                customFields: []
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Empresas
      operationId: deleteOrganization
      summary: Excluir empresa
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Empresa excluída.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `organizations.remove`. Ela não aparece na lista de permissões da tela: vem no modelo **Acesso
        CRM Completo**'
  /api/pipelines:
    get:
      tags:
      - Pipelines e etapas
      operationId: listPipelines
      summary: Listar pipelines
      description: Use para descobrir o `pipeline` de uma oportunidade.
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Pipeline'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439011
                  title: Comercial
                  active: true
                  defaultView: true
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 3
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/pipelines/{id}:
    get:
      tags:
      - Pipelines e etapas
      operationId: getPipeline
      summary: Obter pipeline
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pipeline'
              example:
                _id: 507f1f77bcf86cd799439011
                title: Comercial
                active: true
                defaultView: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
                stages:
                - _id: 507f191e810c19729de860ea
                  name: Prospecção
                  position: 0
                  pipeline: 507f1f77bcf86cd799439011
                  probability: 10
                  active: true
                  rottingEnabled: false
                  rottingDays: null
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/pipelines/{id}/stages:
    get:
      tags:
      - Pipelines e etapas
      operationId: listPipelineStages
      summary: Listar etapas do pipeline
      description: Use para descobrir o `stage` de uma oportunidade. Devolve uma lista simples, sem envelope de paginação.
      parameters:
      - $ref: '#/components/parameters/Id'
      - name: search
        in: query
        description: Busca no nome da etapa.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Stage'
              example:
              - _id: 507f191e810c19729de860ea
                name: Prospecção
                position: 0
                pipeline: 507f1f77bcf86cd799439011
                probability: 10
                active: true
                rottingEnabled: false
                rottingDays: null
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/staffs:
    get:
      tags:
      - Usuários
      operationId: listStaffs
      summary: Listar usuários
      description: Use para descobrir o ID de responsáveis, participantes e autores. Nesta listagem o tamanho de página padrão é 100.
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: search
        in: query
        description: Busca em nome e e-mail.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Staff'
              example:
                docs:
                - _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                  active: true
                  type: user
                  roleName: Vendas
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 12
                pages: 1
                page: 1
                limit: 100
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/staffs/{id}:
    get:
      tags:
      - Usuários
      operationId: getStaff
      summary: Obter usuário
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Staff'
              example:
                _id: 507f191e810c19729de860eb
                fullname: João Silva
                name: João
                lastname: Silva
                email: joao@acme.com
                active: true
                type: user
                roleName: Vendas
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/customfields:
    get:
      tags:
      - Campos personalizados
      operationId: listCustomFields
      summary: Listar campos personalizados
      description: 'Devolve os campos de um recurso: os criados pelo administrador e os de sistema. É aqui que você descobre os IDs para `customFields`
        e quais campos a conta exige (`settings.required: true`).'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - name: feature
        in: query
        description: Recurso.
        schema:
          type: string
          enum:
          - contact
          - organization
          - deal
          - task
          - meeting
          - ticket
          - treatment
          - staff
          - product
      - name: system
        in: query
        description: Com `true`, só os campos de sistema; com `false`, só os criados pelo administrador.
        schema:
          type: boolean
      - name: required
        in: query
        description: Com `true`, só os obrigatórios.
        schema:
          type: boolean
      - name: search
        in: query
        description: Busca no nome.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/CustomField'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439014
                  name: Segmento
                  slug: segmento
                  feature: deal
                  system: false
                  active: true
                  values: []
                  settings:
                    required: false
                    unique: false
                total: 20
                pages: 2
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/tickets:
    get:
      tags:
      - Tickets
      operationId: listTickets
      summary: Listar tickets
      description: 'A credencial vê os tickets das áreas de trabalho e funções a que o usuário dela pertence.


        **Permissão do Service Account:** `tickets.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: status
        in: query
        description: ID da situação.
        schema:
          type: string
      - name: statuses[]
        in: query
        description: Várias situações. Lista de IDs (`?statuses[]=id1&statuses[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: category
        in: query
        description: Categoria da situação, como `waiting` ou `done`.
        schema:
          type: string
      - name: priority
        in: query
        description: Prioridade.
        schema:
          type: string
          enum:
          - low
          - medium
          - high
          - urgent
          description: Prioridade.
      - name: receiver
        in: query
        description: ID do responsável.
        schema:
          type: string
      - name: receivers[]
        in: query
        description: Vários responsáveis. Lista de IDs (`?receivers[]=id1&receivers[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: sender
        in: query
        description: ID de quem abriu.
        schema:
          type: string
      - name: senders[]
        in: query
        description: Vários solicitantes. Lista de IDs (`?senders[]=id1&senders[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: participant
        in: query
        description: ID de um participante.
        schema:
          type: string
      - name: observer
        in: query
        description: ID de um observador.
        schema:
          type: string
      - name: contact
        in: query
        description: ID do contato.
        schema:
          type: string
      - name: contacts[]
        in: query
        description: Vários contatos. Lista de IDs (`?contacts[]=id1&contacts[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: organization
        in: query
        description: ID da empresa.
        schema:
          type: string
      - name: deal
        in: query
        description: ID da oportunidade.
        schema:
          type: string
      - name: workspace
        in: query
        description: ID da área de trabalho.
        schema:
          type: string
      - name: createdAt_minDate
        in: query
        description: Abertos a partir de. Timestamp em milissegundos.
        schema:
          type: integer
          example: 1798761600000
      - name: createdAt_maxDate
        in: query
        description: Abertos até. Timestamp em milissegundos.
        schema:
          type: integer
      - name: search
        in: query
        description: Busca textual.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Ticket'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439080
                  code: 1042
                  subject: Erro ao emitir boleto
                  description: Cliente relata erro 500 ao gerar a segunda via.
                  priority: high
                  status:
                    _id: 507f1f77bcf86cd799439081
                    name: Aguardando responsável
                    slug: aguardando-responsavel
                    category: waiting
                  sender:
                    _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                    email: joao@acme.com
                  workspaceReceiver:
                    _id: 507f1f77bcf86cd799439100
                    name: Geral
                  contact:
                    _id: 507f1f77bcf86cd799439012
                    fullname: Maria Santos
                    lifeCycle: customer
                  participants: []
                  observers: []
                  labels: []
                  customFields: []
                  active: true
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 38
                pages: 3
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Tickets
      operationId: createTicket
      summary: Criar ticket
      description: 'Obrigatórios: `subject` e `workspaceReceiver`. O ticket é aberto em nome do usuário da credencial e nasce na situação padrão
        da área. A `description` vira a primeira mensagem do ticket. Referências aceitam só o ID em texto.


        **Permissão do Service Account:** `tickets.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TicketCreate'
            example:
              subject: Erro ao emitir boleto
              description: Cliente relata erro 500 ao gerar a segunda via.
              priority: high
              workspaceReceiver: 507f1f77bcf86cd799439100
              contact: 507f1f77bcf86cd799439012
      responses:
        '201':
          description: Ticket criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ticket'
              example:
                _id: 507f1f77bcf86cd799439080
                code: 1042
                subject: Erro ao emitir boleto
                description: Cliente relata erro 500 ao gerar a segunda via.
                priority: high
                status:
                  _id: 507f1f77bcf86cd799439081
                  name: Aguardando responsável
                  slug: aguardando-responsavel
                  category: waiting
                sender:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                workspaceReceiver:
                  _id: 507f1f77bcf86cd799439100
                  name: Geral
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                participants: []
                observers: []
                labels: []
                customFields: []
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/tickets/{id}:
    get:
      tags:
      - Tickets
      operationId: getTicket
      summary: Obter ticket
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ticket'
              example:
                _id: 507f1f77bcf86cd799439080
                code: 1042
                subject: Erro ao emitir boleto
                description: Cliente relata erro 500 ao gerar a segunda via.
                priority: high
                status:
                  _id: 507f1f77bcf86cd799439081
                  name: Aguardando responsável
                  slug: aguardando-responsavel
                  category: waiting
                sender:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                workspaceReceiver:
                  _id: 507f1f77bcf86cd799439100
                  name: Geral
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                participants: []
                observers: []
                labels: []
                customFields: []
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `tickets.read`'
    put:
      tags:
      - Tickets
      operationId: updateTicket
      summary: Atualizar ticket
      description: 'Envie só os campos que mudam. Para trocar a situação, envie `status` com o ID da nova situação.


        **Permissão do Service Account:** `tickets.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TicketInput'
            example:
              priority: urgent
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ticket'
              example:
                _id: 507f1f77bcf86cd799439080
                code: 1042
                subject: Erro ao emitir boleto
                description: Cliente relata erro 500 ao gerar a segunda via.
                priority: urgent
                status:
                  _id: 507f1f77bcf86cd799439081
                  name: Aguardando responsável
                  slug: aguardando-responsavel
                  category: waiting
                sender:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                workspaceReceiver:
                  _id: 507f1f77bcf86cd799439100
                  name: Geral
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                  lifeCycle: customer
                participants: []
                observers: []
                labels: []
                customFields: []
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Tickets
      operationId: deleteTicket
      summary: Excluir ticket
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '204':
          description: Ticket excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `tickets.delete`'
  /api/tickets/{id}/start:
    put:
      tags:
      - Tickets
      operationId: startTicket
      summary: Iniciar atendimento do ticket
      description: 'O usuário da credencial assume o ticket: a resposta traz `receiver` e `startAt`. Sem corpo.'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ticket'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/tickets/{id}/finish:
    post:
      tags:
      - Tickets
      operationId: finishTicket
      summary: Finalizar ticket
      description: Move o ticket para a situação de conclusão e registra `finishedAt`. Sem corpo.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Ticket'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/tickets/{id}/messages:
    get:
      tags:
      - Tickets
      operationId: listTicketMessages
      summary: Listar mensagens do ticket
      description: 'Devolve as mensagens mais recentes. Não usa `page`: para ler as anteriores, repita a chamada com `beforeTimestamp` igual a
        `pagination.oldestMessageTimestamp`, enquanto `pagination.hasOlderMessages` for `true`.'
      parameters:
      - $ref: '#/components/parameters/Id'
      - name: limit
        in: query
        description: Quantidade de mensagens.
        schema:
          type: integer
      - name: beforeTimestamp
        in: query
        description: Só mensagens anteriores a este momento (milissegundos).
        schema:
          type: integer
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  messages:
                    type: array
                    items:
                      $ref: '#/components/schemas/TicketMessage'
                    description: Mensagens.
                  pagination:
                    type: object
                    properties:
                      oldestMessageTimestamp:
                        type: integer
                        description: Momento da mensagem mais antiga devolvida.
                      hasOlderMessages:
                        type: boolean
                        description: Há mensagens anteriores.
              example:
                messages:
                - _id: 507f1f77bcf86cd799439090
                  ticket: 507f1f77bcf86cd799439080
                  type: text
                  content: Segunda via reenviada por e-mail.
                  internal: false
                  source: staff
                  timestamp: 1777374000000
                  createdAt: '2026-04-28T11:00:00Z'
                pagination:
                  oldestMessageTimestamp: 1777374000000
                  hasOlderMessages: false
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
      - Tickets
      operationId: createTicketMessage
      summary: Enviar mensagem no ticket
      description: Registra uma resposta ou uma nota interna. `type` é obrigatório.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - type
              - content
              properties:
                type:
                  type: string
                  enum:
                  - text
                  description: Use `text`.
                content:
                  type: string
                  description: Texto da mensagem.
                internal:
                  type: boolean
                  description: 'Com `true`, grava como nota interna. Padrão: `false`.'
            example:
              type: text
              content: Segunda via reenviada por e-mail.
              internal: false
      responses:
        '201':
          description: Mensagem registrada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TicketMessage'
              example:
                _id: 507f1f77bcf86cd799439090
                ticket: 507f1f77bcf86cd799439080
                type: text
                content: Segunda via reenviada por e-mail.
                internal: false
                source: staff
                timestamp: 1777374000000
                createdAt: '2026-04-28T11:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/tickets/status:
    get:
      tags:
      - Tickets
      operationId: listTicketStatuses
      summary: Listar situações de ticket
      description: 'Use para descobrir o `status` de um ticket. As situações são configuradas por área de trabalho.


        **Permissão do Service Account:** `tickets.read`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/TicketStatus'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439081
                  name: Aguardando responsável
                  slug: aguardando-responsavel
                  category: waiting
                  default: true
                  active: true
                  nextStatusAllowed:
                  - 507f1f77bcf86cd799439082
                  workspace:
                    _id: 507f1f77bcf86cd799439100
                    name: Geral
                total: 4
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/treatments:
    get:
      tags:
      - Atendimentos
      operationId: listTreatments
      summary: Listar atendimentos
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: type
        in: query
        description: ID do tipo de atendimento.
        schema:
          type: string
      - name: types[]
        in: query
        description: Vários tipos. Lista de IDs (`?types[]=id1&types[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: staff
        in: query
        description: ID do usuário que registrou.
        schema:
          type: string
      - name: staffs[]
        in: query
        description: Vários usuários. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: contact
        in: query
        description: ID do contato.
        schema:
          type: string
      - name: contacts[]
        in: query
        description: Vários contatos. Lista de IDs (`?contacts[]=id1&contacts[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: deal
        in: query
        description: ID da oportunidade.
        schema:
          type: string
      - name: deals[]
        in: query
        description: Várias oportunidades. Lista de IDs (`?deals[]=id1&deals[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: createdAt_minDate
        in: query
        description: Registrados a partir de. Timestamp em milissegundos.
        schema:
          type: integer
          example: 1798761600000
      - name: createdAt_maxDate
        in: query
        description: Registrados até. Timestamp em milissegundos.
        schema:
          type: integer
      - name: search
        in: query
        description: Busca textual.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Treatment'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439111
                  code: 771
                  subject: Ligação de acompanhamento
                  description: Cliente confirmou o recebimento da proposta.
                  type:
                    _id: 507f1f77bcf86cd799439110
                    name: Ligação
                    slug: ligacao
                  contact:
                    _id: 507f1f77bcf86cd799439012
                    fullname: Maria Santos
                  staff:
                    _id: 507f191e810c19729de860eb
                    fullname: João Silva
                    name: João
                    lastname: Silva
                    email: joao@acme.com
                  done: true
                  active: true
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 12
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      description: '**Permissão do Service Account:** `treatments.list`'
    post:
      tags:
      - Atendimentos
      operationId: createTreatment
      summary: Registrar atendimento
      description: 'O corpo não tem campo obrigatório fixo: `GET /api/customfields?page=1&feature=treatment&required=true` devolve os obrigatórios
        da conta. O atendimento é registrado em nome do usuário da credencial. Referências aceitam só o ID em texto.


        **Permissão do Service Account:** `treatments.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TreatmentInput'
            example:
              subject: Ligação de acompanhamento
              description: Cliente confirmou o recebimento da proposta.
              type: 507f1f77bcf86cd799439110
              contact: 507f1f77bcf86cd799439012
      responses:
        '201':
          description: Atendimento registrado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Treatment'
              example:
                _id: 507f1f77bcf86cd799439111
                code: 771
                subject: Ligação de acompanhamento
                description: Cliente confirmou o recebimento da proposta.
                type:
                  _id: 507f1f77bcf86cd799439110
                  name: Ligação
                  slug: ligacao
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                done: true
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/treatments/{id}:
    get:
      tags:
      - Atendimentos
      operationId: getTreatment
      summary: Obter atendimento
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Treatment'
              example:
                _id: 507f1f77bcf86cd799439111
                code: 771
                subject: Ligação de acompanhamento
                description: Cliente confirmou o recebimento da proposta.
                type:
                  _id: 507f1f77bcf86cd799439110
                  name: Ligação
                  slug: ligacao
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                done: true
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `treatments.read`'
    put:
      tags:
      - Atendimentos
      operationId: updateTreatment
      summary: Atualizar atendimento
      description: 'Envie só os campos que mudam.


        **Permissão do Service Account:** `treatments.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TreatmentInput'
            example:
              description: Cliente pediu novo contato na sexta.
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Treatment'
              example:
                _id: 507f1f77bcf86cd799439111
                code: 771
                subject: Ligação de acompanhamento
                description: Cliente confirmou o recebimento da proposta.
                type:
                  _id: 507f1f77bcf86cd799439110
                  name: Ligação
                  slug: ligacao
                contact:
                  _id: 507f1f77bcf86cd799439012
                  fullname: Maria Santos
                staff:
                  _id: 507f191e810c19729de860eb
                  fullname: João Silva
                  name: João
                  lastname: Silva
                  email: joao@acme.com
                done: true
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Atendimentos
      operationId: deleteTreatment
      summary: Excluir atendimento
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Atendimento excluído.
        '204':
          description: O atendimento não existe ou já estava excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `treatments.remove`. Ela não aparece na lista de permissões da tela: vem no modelo **Acesso
        CRM Completo**'
  /api/treatments/types:
    get:
      tags:
      - Atendimentos
      operationId: listTreatmentTypes
      summary: Listar tipos de atendimento
      description: 'Use para descobrir o `type` de um atendimento.


        **Permissão do Service Account:** `treatments.read`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/TreatmentType'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439110
                  name: Ligação
                  slug: ligacao
                  color: '#8955ef'
                  icon: fa-tag
                  active: true
                  default: false
                total: 11
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/journeys:
    get:
      tags:
      - Jornadas
      operationId: listJourneys
      summary: Listar jornadas
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - name: status
        in: query
        description: Situação.
        schema:
          type: string
          enum:
          - active
          - success
          - churn
          description: Situação da jornada.
      - name: journeyFlow
        in: query
        description: ID do fluxo de jornada.
        schema:
          type: string
      - name: phase
        in: query
        description: ID da fase.
        schema:
          type: string
      - name: staffs[]
        in: query
        description: Responsáveis. Lista de IDs (`?staffs[]=id1&staffs[]=id2`).
        schema:
          type: array
          items:
            type: string
        style: form
        explode: true
      - name: contact
        in: query
        description: ID do contato.
        schema:
          type: string
      - name: organization
        in: query
        description: ID da empresa.
        schema:
          type: string
      - name: sourceDeal
        in: query
        description: ID da oportunidade de origem.
        schema:
          type: string
      - name: healthScoreMin
        in: query
        description: Health score mínimo.
        schema:
          type: number
      - name: healthScoreMax
        in: query
        description: Health score máximo.
        schema:
          type: number
      - name: search
        in: query
        description: Busca no título.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Journey'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439120
                  title: Acme — Implantação
                  status: active
                  healthScore: 72
                  healthScoreMode: auto
                  mrr: 4500
                  renewalStatus: active
                  renewalDate: '2027-04-28T00:00:00Z'
                  active: true
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-05-10T09:00:00Z'
                total: 54
                pages: 4
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      description: '**Permissão do Service Account:** `journeys.list`'
  /api/journeys/{id}:
    get:
      tags:
      - Jornadas
      operationId: getJourney
      summary: Obter jornada
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Journey'
              example:
                _id: 507f1f77bcf86cd799439120
                title: Acme — Implantação
                status: active
                healthScore: 72
                healthScoreMode: auto
                mrr: 4500
                renewalStatus: active
                renewalDate: '2027-04-28T00:00:00Z'
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-05-10T09:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `journeys.read`'
  /api/journeys/{id}/health-score:
    get:
      tags:
      - Jornadas
      operationId: getJourneyHealthScore
      summary: Obter health score da jornada
      description: Devolve o valor em vigor, o valor calculado e os componentes do cálculo.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  healthScore:
                    type: number
                    description: Valor em vigor.
                  healthScoreAuto:
                    type: number
                    description: Valor calculado pelo CRM.
                  mode:
                    type: string
                    enum:
                    - manual
                    - auto
                    description: '`manual`: vale o valor informado por um usuário. `auto`: vale o calculado.'
                  components:
                    type: object
                    description: Componentes do cálculo e o peso de cada um.
              example:
                healthScore: 72
                healthScoreAuto: 72
                mode: auto
                components: {}
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/pipelines/settings/lostreasons:
    get:
      tags:
      - Pipelines e etapas
      operationId: listLostReasons
      summary: Listar motivos de perda
      description: Cada motivo pertence a um pipeline. Use o `_id` em `lostReason` ao fechar uma oportunidade como perdida.
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/LostReason'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439016
                  reason: Preço
                  pipeline:
                    _id: 507f1f77bcf86cd799439011
                    title: Comercial
                  active: true
                total: 12
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/meetingtypes:
    get:
      tags:
      - Reuniões
      operationId: listMeetingTypes
      summary: Listar tipos de reunião
      description: Use para descobrir o `type` de uma reunião.
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/MeetingType'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439130
                  name: Apresentação
                  slug: apresentacao
                  default: false
                  active: true
                total: 3
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/meetingrooms:
    get:
      tags:
      - Reuniões
      operationId: listMeetingRooms
      summary: Listar salas de reunião
      description: Use para descobrir o `room` de uma reunião.
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/MeetingRoom'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439131
                  name: Presencial
                  slug: presencial
                  provider: personal
                  default: false
                  active: true
                total: 2
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/teams:
    get:
      tags:
      - Equipes e áreas de trabalho
      operationId: listTeams
      summary: Listar equipes
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Team'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439140
                  name: Comercial SP
                  slug: comercial-sp
                  members:
                  - 507f191e810c19729de860eb
                  active: true
                total: 2
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/workspaces:
    get:
      tags:
      - Equipes e áreas de trabalho
      operationId: listWorkspaces
      summary: Listar áreas de trabalho
      description: Use para descobrir o `workspaceReceiver` de um ticket.
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Workspace'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439100
                  name: Geral
                  slug: geral
                  active: true
                total: 1
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/webhooks/triggers:
    get:
      tags:
      - Webhooks
      operationId: listWebhookTriggers
      summary: Listar eventos de webhook
      description: 'Devolve os eventos que a conta pode assinar, agrupados por recurso. Devolve uma lista simples, sem envelope de paginação.


        Os webhooks são criados e editados por um administrador no CRM, em **Integrações › Webhooks**: a criação por API exige um usuário administrador
        e não está disponível para Service Accounts.'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WebhookTriggerGroup'
              example:
              - _id: Oportunidades
                triggers:
                - key: deal.won
                  label: Oportunidade ganha
                  group: deal
                  groupName: Oportunidades
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/products:
    get:
      tags:
      - Produtos
      operationId: listProducts
      summary: Listar produtos
      description: 'Ordenação padrão: `name` crescente. Devolve o envelope de paginação mesmo sem `page`.


        **Permissão do Service Account:** `products.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - name: search
        in: query
        description: Busca no nome e no código.
        schema:
          type: string
      - name: category
        in: query
        description: Categoria.
        schema:
          type: string
          enum:
          - CATALOG
          - PORTFOLIO
          description: '`CATALOG`: produto com preço, vendido em oportunidades. `PORTFOLIO`: produto de carteira, sem preço. Padrão: `CATALOG`.'
      - name: type
        in: query
        description: Tipo.
        schema:
          type: string
          enum:
          - ONE_TIME
          - RECURRING
          description: '`ONE_TIME`: venda única. `RECURRING`: assinatura. Padrão: `ONE_TIME`.'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Product'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439150
                  name: Plano Enterprise
                  code: ENT-001
                  description: Licença anual.
                  type: ONE_TIME
                  category: CATALOG
                  price: 1200
                  recurring:
                    interval: 1
                  inventoryControl:
                    enabled: false
                    allowNegative: false
                    lowStockThreshold: 0
                    trackQuantity: true
                  stockQuantity: 0
                  active: true
                  customFields: []
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 8
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Produtos
      operationId: createProduct
      summary: Criar produto
      description: 'O único campo obrigatório é `name`.


        **Permissão do Service Account:** `products.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/ProductInput'
              - type: object
                required:
                - name
            example:
              name: Plano Enterprise
              code: ENT-001
              description: Licença anual.
              type: ONE_TIME
              category: CATALOG
              price: 1200
      responses:
        '201':
          description: Produto criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
              example:
                _id: 507f1f77bcf86cd799439150
                name: Plano Enterprise
                code: ENT-001
                description: Licença anual.
                type: ONE_TIME
                category: CATALOG
                price: 1200
                recurring:
                  interval: 1
                inventoryControl:
                  enabled: false
                  allowNegative: false
                  lowStockThreshold: 0
                  trackQuantity: true
                stockQuantity: 0
                active: true
                customFields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/products/{id}:
    get:
      tags:
      - Produtos
      operationId: getProduct
      summary: Obter produto
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
              example:
                _id: 507f1f77bcf86cd799439150
                name: Plano Enterprise
                code: ENT-001
                description: Licença anual.
                type: ONE_TIME
                category: CATALOG
                price: 1200
                recurring:
                  interval: 1
                inventoryControl:
                  enabled: false
                  allowNegative: false
                  lowStockThreshold: 0
                  trackQuantity: true
                stockQuantity: 0
                active: true
                customFields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `products.read`'
    patch:
      tags:
      - Produtos
      operationId: updateProduct
      summary: Atualizar produto
      description: 'Envie só os campos que mudam. `PUT` no mesmo endereço tem o mesmo efeito.


        **Permissão do Service Account:** `products.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProductInput'
            example:
              price: 1500
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
              example:
                _id: 507f1f77bcf86cd799439150
                name: Plano Enterprise
                code: ENT-001
                description: Licença anual.
                type: ONE_TIME
                category: CATALOG
                price: 1500
                recurring:
                  interval: 1
                inventoryControl:
                  enabled: false
                  allowNegative: false
                  lowStockThreshold: 0
                  trackQuantity: true
                stockQuantity: 0
                active: true
                customFields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Produtos
      operationId: deleteProduct
      summary: Excluir produto
      description: 'Exclusão lógica: o produto fica com `active: false` e o `code` ganha um sufixo, liberando o código para um novo produto.


        **Permissão do Service Account:** `products.delete`'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Produto excluído.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
              example:
                _id: 507f1f77bcf86cd799439150
                name: Plano Enterprise
                code: ENT-001
                description: Licença anual.
                type: ONE_TIME
                category: CATALOG
                price: 1200
                recurring:
                  interval: 1
                inventoryControl:
                  enabled: false
                  allowNegative: false
                  lowStockThreshold: 0
                  trackQuantity: true
                stockQuantity: 0
                active: false
                customFields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/products/{id}/pricebooks:
    get:
      tags:
      - Produtos
      operationId: listProductPricebooks
      summary: Listar preços do produto por tabela
      parameters:
      - $ref: '#/components/parameters/Id'
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/PricebookEntry'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439160
                  pricebook:
                    _id: 507f1f77bcf86cd799439161
                    name: Standard Pricebook
                    isStandard: true
                  price: 1100
                  currency: BRL
                  createdAt: '2026-04-28T10:30:00Z'
                total: 1
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `products.read`'
  /api/pricebooks:
    get:
      tags:
      - Tabelas de preço
      operationId: listPricebooks
      summary: Listar tabelas de preço
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Pricebook'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439161
                  name: Standard Pricebook
                  description: Tabela padrão.
                  isStandard: true
                  active: true
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 1
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      description: '**Permissão do Service Account:** `pricebooks.list`'
    post:
      tags:
      - Tabelas de preço
      operationId: createPricebook
      summary: Criar tabela de preço
      description: 'Só os campos abaixo são aceitos; um campo a mais devolve `400`.


        **Permissão do Service Account:** `pricebooks.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/PricebookInput'
              - type: object
                required:
                - name
            example:
              name: Parceiros
              description: Preços para parceiros.
      responses:
        '201':
          description: Tabela criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pricebook'
              example:
                _id: 507f1f77bcf86cd799439161
                name: Parceiros
                description: Tabela padrão.
                isStandard: false
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/pricebooks/{id}:
    get:
      tags:
      - Tabelas de preço
      operationId: getPricebook
      summary: Obter tabela de preço
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pricebook'
              example:
                _id: 507f1f77bcf86cd799439161
                name: Standard Pricebook
                description: Tabela padrão.
                isStandard: true
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `pricebooks.read`'
    put:
      tags:
      - Tabelas de preço
      operationId: updatePricebook
      summary: Atualizar tabela de preço
      description: 'Envie só os campos que mudam.


        **Permissão do Service Account:** `pricebooks.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PricebookInput'
            example:
              description: Vigente em 2027.
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pricebook'
              example:
                _id: 507f1f77bcf86cd799439161
                name: Standard Pricebook
                description: Tabela padrão.
                isStandard: true
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Tabelas de preço
      operationId: deletePricebook
      summary: Excluir tabela de preço
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Tabela excluída.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `pricebooks.delete`'
  /api/pricebooks/{id}/products:
    get:
      tags:
      - Tabelas de preço
      operationId: listPricebookProducts
      summary: Listar produtos da tabela
      description: 'Devolve uma lista simples, sem envelope de paginação. Cada item traz o produto completo em `product`.


        **Permissão do Service Account:** `pricebooks.read`'
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/PricebookEntry'
              example:
              - _id: 507f1f77bcf86cd799439160
                product:
                  _id: 507f1f77bcf86cd799439150
                  name: Plano Enterprise
                  code: ENT-001
                  description: Licença anual.
                  type: ONE_TIME
                  category: CATALOG
                  price: 1200
                  recurring:
                    interval: 1
                  inventoryControl:
                    enabled: false
                    allowNegative: false
                    lowStockThreshold: 0
                    trackQuantity: true
                  stockQuantity: 0
                  active: true
                  customFields: []
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                pricebook: 507f1f77bcf86cd799439161
                price: 1100
                currency: BRL
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
      - Tabelas de preço
      operationId: addPricebookProduct
      summary: Incluir produto na tabela
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required:
              - product
              - price
              properties:
                product:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  example: 507f1f77bcf86cd799439011
                  description: ID do produto.
                price:
                  type: number
                  description: Preço nesta tabela.
                  example: 1100
                currency:
                  type: string
                  description: 'Moeda. Padrão: `BRL`.'
            example:
              product: 507f1f77bcf86cd799439150
              price: 1100
      responses:
        '201':
          description: Produto incluído.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricebookEntry'
              example:
                _id: 507f1f77bcf86cd799439160
                product: 507f1f77bcf86cd799439150
                pricebook: 507f1f77bcf86cd799439161
                price: 1100
                currency: BRL
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `pricebooks.update`'
  /api/pricebooks/{id}/products/{entryId}:
    put:
      tags:
      - Tabelas de preço
      operationId: updatePricebookProduct
      summary: Alterar preço do produto na tabela
      parameters:
      - $ref: '#/components/parameters/Id'
      - name: entryId
        in: path
        required: true
        description: ID do preço na tabela (`_id` devolvido ao incluir o produto).
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                price:
                  type: number
                  description: Novo preço.
                currency:
                  type: string
                  description: Moeda.
            example:
              price: 1150
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricebookEntry'
              example:
                _id: 507f1f77bcf86cd799439160
                product: 507f1f77bcf86cd799439150
                pricebook: 507f1f77bcf86cd799439161
                price: 1150
                currency: BRL
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `pricebooks.update`'
    delete:
      tags:
      - Tabelas de preço
      operationId: removePricebookProduct
      summary: Retirar produto da tabela
      parameters:
      - $ref: '#/components/parameters/Id'
      - name: entryId
        in: path
        required: true
        description: ID do preço na tabela (`_id` devolvido ao incluir o produto).
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Produto retirado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PricebookEntry'
              example:
                _id: 507f1f77bcf86cd799439160
                product: 507f1f77bcf86cd799439150
                pricebook: 507f1f77bcf86cd799439161
                price: 1100
                currency: BRL
                active: false
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `pricebooks.update`'
  /api/{entityType}/{entityId}/products:
    get:
      tags:
      - Itens de produto
      operationId: listEntityProducts
      summary: Listar produtos de um registro
      description: 'Produtos vinculados a uma oportunidade, um contato ou uma empresa.


        **Permissão do Service Account:** `products.read`'
      parameters:
      - name: entityType
        in: path
        required: true
        description: Tipo do registro.
        schema:
          type: string
          enum:
          - deals
          - contacts
          - organizations
          - customers
          - leads
          - prospects
      - name: entityId
        in: path
        required: true
        description: ID do registro.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/ProductItem'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439170
                  product:
                    _id: 507f1f77bcf86cd799439150
                    name: Plano Enterprise
                    code: ENT-001
                  pricebookEntry: 507f1f77bcf86cd799439160
                  category: CATALOG
                  quantity: 2
                  unitPrice: 1200
                  discount: 100
                  totalPrice: 2300
                  deal: 507f1f77bcf86cd799439015
                  active: true
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 1
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
      - Itens de produto
      operationId: addEntityProduct
      summary: Vincular produto a um registro
      description: 'Sem `unitPrice`, vale o preço do produto na tabela (`pricebookId`, ou a tabela padrão). Sem `quantity`, vale 1. Produtos `PORTFOLIO`
        entram sem preço.


        **Permissão do Service Account:** `products.create`'
      parameters:
      - name: entityType
        in: path
        required: true
        description: Tipo do registro.
        schema:
          type: string
          enum:
          - deals
          - contacts
          - organizations
          - customers
          - leads
          - prospects
      - name: entityId
        in: path
        required: true
        description: ID do registro.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - productId
              properties:
                productId:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  example: 507f1f77bcf86cd799439011
                  description: ID do produto.
                pricebookId:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  example: 507f1f77bcf86cd799439011
                  description: 'Tabela de preço. Padrão: a tabela padrão da conta.'
                quantity:
                  type: number
                  description: 'Quantidade. Padrão: 1.'
                unitPrice:
                  type: number
                  description: 'Preço unitário. Padrão: o da tabela.'
                discount:
                  type: number
                  description: 'Desconto em valor. Padrão: 0.'
            example:
              productId: 507f1f77bcf86cd799439150
              quantity: 2
              discount: 100
      responses:
        '201':
          description: Produto vinculado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductItem'
              example:
                _id: 507f1f77bcf86cd799439170
                product: 507f1f77bcf86cd799439150
                pricebookEntry: 507f1f77bcf86cd799439160
                category: CATALOG
                quantity: 2
                unitPrice: 1200
                discount: 100
                totalPrice: 2300
                deal: 507f1f77bcf86cd799439015
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/product-items/{itemId}:
    put:
      tags:
      - Itens de produto
      operationId: updateProductItem
      summary: Atualizar item
      description: 'O `totalPrice` é recalculado.


        **Permissão do Service Account:** `products.update`'
      parameters:
      - name: itemId
        in: path
        required: true
        description: ID do item (`_id` devolvido ao vincular).
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                quantity:
                  type: number
                  description: Quantidade.
                unitPrice:
                  type: number
                  description: Preço unitário.
                discount:
                  type: number
                  description: Desconto em valor.
            example:
              quantity: 3
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductItem'
              example:
                _id: 507f1f77bcf86cd799439170
                product: 507f1f77bcf86cd799439150
                pricebookEntry: 507f1f77bcf86cd799439160
                category: CATALOG
                quantity: 3
                unitPrice: 1200
                discount: 100
                totalPrice: 3500
                deal: 507f1f77bcf86cd799439015
                active: true
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Itens de produto
      operationId: removeProductItem
      summary: Remover item
      parameters:
      - name: itemId
        in: path
        required: true
        description: ID do item (`_id` devolvido ao vincular).
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Item removido.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductItem'
              example:
                _id: 507f1f77bcf86cd799439170
                product: 507f1f77bcf86cd799439150
                pricebookEntry: 507f1f77bcf86cd799439160
                category: CATALOG
                quantity: 2
                unitPrice: 1200
                discount: 100
                totalPrice: 2300
                deal: 507f1f77bcf86cd799439015
                active: false
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `products.delete`'
  /api/resource-keywords:
    get:
      tags:
      - Campos para filtros
      operationId: listResourceKeywords
      summary: Listar campos para filtros
      description: Devolve todos os campos da conta que podem entrar numa regra de filtro, inclusive os campos personalizados. Lista simples,
        sem paginação.
      parameters:
      - name: modelRef
        in: query
        description: Só os campos de um recurso, como `deal`, `contact` ou `task`.
        schema:
          type: string
      - name: module
        in: query
        description: Só os campos disponíveis num uso, como `dashboard`.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ResourceKeyword'
              example:
              - _id: 6570e1b3a306ee5fbcb651a9
                slug: deal.amount
                modelRef: deal
                type: monetary
                fieldRef: dealObj.amount
                settings:
                  label: Valor
                module:
                - rule
                - dashboard
                - report
                active: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/resource-keywords/models:
    get:
      tags:
      - Campos para filtros
      operationId: listResourceKeywordModels
      summary: Listar recursos que têm campos para filtros
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  models:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          description: Valor de `modelRef`.
                          example: deal
                        label:
                          type: string
                          description: Nome exibido.
                          example: Oportunidade
              example:
                models:
                - id: deal
                  label: Oportunidade
                - id: contact
                  label: Contato
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/resource-keywords/{slug}:
    get:
      tags:
      - Campos para filtros
      operationId: getResourceKeyword
      summary: Obter campo para filtros
      parameters:
      - name: slug
        in: path
        required: true
        description: '`slug` do campo.'
        schema:
          type: string
          example: deal.amount
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResourceKeyword'
              example:
                _id: 6570e1b3a306ee5fbcb651a9
                slug: deal.amount
                modelRef: deal
                type: monetary
                fieldRef: dealObj.amount
                settings:
                  label: Valor
                module:
                - rule
                - dashboard
                - report
                active: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/dashboards:
    get:
      tags:
      - Dashboards
      operationId: listDashboards
      summary: Listar dashboards
      description: Devolve os dashboards que o usuário da credencial vê. Lista simples, sem paginação.
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Dashboard'
              example:
              - _id: 507f1f77bcf86cd799439180
                title: Comercial
                forModel: Staff
                staffs:
                - 507f191e810c19729de860eb
                globalRuleset:
                  condition: AND
                  rules: []
                roles: []
                workspace: null
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Dashboards
      operationId: createDashboard
      summary: Criar dashboard
      description: 'O único campo obrigatório é `title`. Para a credencial ver o dashboard que criou, use `forModel: Staff` com o ID do usuário
        dela em `staffs`.


        **Permissão do Service Account:** `dashboards.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/DashboardInput'
              - type: object
                required:
                - title
            example:
              title: Comercial
              forModel: Staff
              staffs:
              - 507f191e810c19729de860eb
              globalRuleset:
                condition: AND
                rules: []
      responses:
        '200':
          description: Dashboard criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Dashboard'
              example:
                _id: 507f1f77bcf86cd799439180
                title: Comercial
                forModel: Staff
                staffs:
                - 507f191e810c19729de860eb
                globalRuleset:
                  condition: AND
                  rules: []
                roles: []
                workspace: null
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/dashboards/{dashboard}:
    get:
      tags:
      - Dashboards
      operationId: getDashboard
      summary: Obter dashboard
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Dashboard'
              example:
                _id: 507f1f77bcf86cd799439180
                title: Comercial
                forModel: Staff
                staffs:
                - 507f191e810c19729de860eb
                globalRuleset:
                  condition: AND
                  rules: []
                roles: []
                workspace: null
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Dashboards
      operationId: updateDashboard
      summary: Atualizar dashboard
      description: Envie `title` e os campos que mudam.
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/DashboardInput'
              - type: object
                required:
                - title
            example:
              title: Comercial 2027
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Dashboard'
              example:
                _id: 507f1f77bcf86cd799439180
                title: Comercial
                forModel: Staff
                staffs:
                - 507f191e810c19729de860eb
                globalRuleset:
                  condition: AND
                  rules: []
                roles: []
                workspace: null
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Dashboards
      operationId: deleteDashboard
      summary: Excluir dashboard
      description: 'Exclui o dashboard e os widgets dele. Não há como desfazer.


        **Permissão do Service Account:** `dashboards.delete`'
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
              example:
                ok: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/dashboards/{dashboard}/duplicate:
    post:
      tags:
      - Dashboards
      operationId: duplicateDashboard
      summary: Duplicar dashboard
      description: Cria uma cópia com os mesmos widgets. A cópia recebe o título "Cópia de …"; o `title` do corpo é obrigatório, mas não é usado.
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - title
              properties:
                title:
                  type: string
                  description: Obrigatório.
            example:
              title: Comercial
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Dashboard'
              example:
                _id: 507f1f77bcf86cd799439180
                title: Cópia de Comercial
                forModel: Staff
                staffs:
                - 507f191e810c19729de860eb
                globalRuleset:
                  condition: AND
                  rules: []
                roles: []
                workspace: null
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/dashboards/{dashboard}/widgets:
    get:
      tags:
      - Dashboards
      operationId: listWidgets
      summary: Listar widgets do dashboard
      description: Lista simples, sem paginação.
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Widget'
              example:
              - _id: 507f1f77bcf86cd799439190
                dashboard: 507f1f77bcf86cd799439180
                title: Valor por etapa
                displayAs: CHART/BAR
                sizeX: 4
                sizeY: 2
                row: 0
                col: 0
                content:
                  datasource: deal
                  filter:
                    ruleset:
                      condition: AND
                      rules:
                      - fieldId: deal.amount
                        operator: greater
                        value: 1000
                      - condition: OR
                        rules:
                        - fieldId: deal.won
                          operator: false_value
                        - fieldId: deal.stage_name
                          operator: contains
                          value: Proposta
                  axisX:
                    resourceKeywordSlug: deal.stage_name
                    aggregation: GROUP
                  axisY:
                    resourceKeywordSlug: deal.amount
                    aggregation: SUM
                showTitle: true
                reportfields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
      - Dashboards
      operationId: createWidget
      summary: Criar widget
      description: 'Obrigatórios: `title`, `displayAs`, `content.datasource`, `sizeX`, `sizeY`, `row` e `col`.


        As regras de `content.filter.ruleset` não são validadas na gravação: um operador ou campo inexistente é aceito e o widget passa a devolver
        resultado vazio. Confira os nomes em `GET /api/resource-keywords`.


        **Permissão do Service Account:** `dashboards.create-widget`'
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/WidgetInput'
              - type: object
                required:
                - title
                - displayAs
                - content
                - sizeX
                - sizeY
                - row
                - col
            example:
              title: Valor por etapa
              displayAs: CHART/BAR
              sizeX: 4
              sizeY: 2
              row: 0
              col: 0
              content:
                datasource: deal
                filter:
                  ruleset:
                    condition: AND
                    rules:
                    - fieldId: deal.amount
                      operator: greater
                      value: 1000
                    - condition: OR
                      rules:
                      - fieldId: deal.won
                        operator: false_value
                      - fieldId: deal.stage_name
                        operator: contains
                        value: Proposta
                axisX:
                  resourceKeywordSlug: deal.stage_name
                  aggregation: GROUP
                axisY:
                  resourceKeywordSlug: deal.amount
                  aggregation: SUM
      responses:
        '200':
          description: Widget criado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Widget'
              example:
                _id: 507f1f77bcf86cd799439190
                dashboard: 507f1f77bcf86cd799439180
                title: Valor por etapa
                displayAs: CHART/BAR
                sizeX: 4
                sizeY: 2
                row: 0
                col: 0
                content:
                  datasource: deal
                  filter:
                    ruleset:
                      condition: AND
                      rules:
                      - fieldId: deal.amount
                        operator: greater
                        value: 1000
                      - condition: OR
                        rules:
                        - fieldId: deal.won
                          operator: false_value
                        - fieldId: deal.stage_name
                          operator: contains
                          value: Proposta
                  axisX:
                    resourceKeywordSlug: deal.stage_name
                    aggregation: GROUP
                  axisY:
                    resourceKeywordSlug: deal.amount
                    aggregation: SUM
                showTitle: true
                reportfields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/dashboards/{dashboard}/widgets/{widget}:
    get:
      tags:
      - Dashboards
      operationId: getWidget
      summary: Obter widget
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: widget
        in: path
        required: true
        description: ID do widget.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Widget'
              example:
                _id: 507f1f77bcf86cd799439190
                dashboard: 507f1f77bcf86cd799439180
                title: Valor por etapa
                displayAs: CHART/BAR
                sizeX: 4
                sizeY: 2
                row: 0
                col: 0
                content:
                  datasource: deal
                  filter:
                    ruleset:
                      condition: AND
                      rules:
                      - fieldId: deal.amount
                        operator: greater
                        value: 1000
                      - condition: OR
                        rules:
                        - fieldId: deal.won
                          operator: false_value
                        - fieldId: deal.stage_name
                          operator: contains
                          value: Proposta
                  axisX:
                    resourceKeywordSlug: deal.stage_name
                    aggregation: GROUP
                  axisY:
                    resourceKeywordSlug: deal.amount
                    aggregation: SUM
                showTitle: true
                reportfields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
      - Dashboards
      operationId: updateWidget
      summary: Atualizar widget
      description: 'Envie só os campos que mudam.


        **Permissão do Service Account:** `dashboards.change-widget-position`'
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: widget
        in: path
        required: true
        description: ID do widget.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WidgetInput'
            example:
              title: Valor por etapa (mês)
              reportfields:
              - slug: deal.title
                label: Título
              - slug: deal.amount
                label: Valor
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Widget'
              example:
                _id: 507f1f77bcf86cd799439190
                dashboard: 507f1f77bcf86cd799439180
                title: Valor por etapa
                displayAs: CHART/BAR
                sizeX: 4
                sizeY: 2
                row: 0
                col: 0
                content:
                  datasource: deal
                  filter:
                    ruleset:
                      condition: AND
                      rules:
                      - fieldId: deal.amount
                        operator: greater
                        value: 1000
                      - condition: OR
                        rules:
                        - fieldId: deal.won
                          operator: false_value
                        - fieldId: deal.stage_name
                          operator: contains
                          value: Proposta
                  axisX:
                    resourceKeywordSlug: deal.stage_name
                    aggregation: GROUP
                  axisY:
                    resourceKeywordSlug: deal.amount
                    aggregation: SUM
                showTitle: true
                reportfields: []
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Dashboards
      operationId: deleteWidget
      summary: Excluir widget
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: widget
        in: path
        required: true
        description: ID do widget.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Widget excluído.
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `dashboards.delete-widget`'
  /api/dashboards/{dashboard}/widgets/{widget}/content:
    get:
      tags:
      - Dashboards
      operationId: getWidgetContent
      summary: Obter o resultado do widget
      description: 'O formato depende de `displayAs`:


        - `NUMBER`: `{ "value": 12 }`.

        - `PERCENTAGE`: `{ "value": 3, "total": 1, "percentage": 300 }`, em que `percentage` é `value ÷ total × 100`.

        - Gráficos: `{ "labels": [...], "data": [...] }`, um valor por rótulo.

        - Gráficos com `seriesBy`: `{ "series": [...], "labels": [...], "data": [[{ "x": rótulo, "y": valor }]] }`, uma lista por série.'
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: widget
        in: path
        required: true
        description: ID do widget.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  value:
                    type: number
                    description: Em `NUMBER` e `PERCENTAGE`.
                  total:
                    type: number
                    description: Em `PERCENTAGE`.
                  percentage:
                    type: number
                    description: Em `PERCENTAGE`.
                  labels:
                    type: array
                    items:
                      type: string
                    description: Rótulos do eixo X, em gráficos.
                  data:
                    type: array
                    items: {}
                    description: Valores, em gráficos.
                  series:
                    type: array
                    items:
                      type: string
                    description: Nomes das séries, com `seriesBy`.
              example:
                labels:
                - Prospecção
                - Proposta
                - Fechamento
                data:
                - 150000
                - 90000
                - 42000
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/dashboards/{dashboard}/widgets/{widget}/ruleset:
    get:
      tags:
      - Dashboards
      operationId: getWidgetRuleset
      summary: Obter o filtro do widget
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: widget
        in: path
        required: true
        description: ID do widget.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  widget:
                    type: object
                    properties:
                      _id:
                        type: string
                        pattern: ^[a-fA-F0-9]{24}$
                        example: 507f1f77bcf86cd799439011
                      title:
                        type: string
                      displayAs:
                        type: string
                      datasource:
                        type: string
                  ruleset:
                    description: '`null` quando o widget não tem filtro.'
                    allOf:
                    - $ref: '#/components/schemas/Ruleset'
                  period:
                    type: string
                  axisX:
                    type: object
                  seriesBy:
                    type: object
              example:
                widget:
                  _id: 507f1f77bcf86cd799439190
                  title: Valor por etapa
                  displayAs: CHART/BAR
                  datasource: deal
                ruleset:
                  condition: AND
                  rules:
                  - fieldId: deal.amount
                    operator: greater
                    value: 1000
                  - condition: OR
                    rules:
                    - fieldId: deal.won
                      operator: false_value
                    - fieldId: deal.stage_name
                      operator: contains
                      value: Proposta
                axisX:
                  resourceKeywordSlug: deal.stage_name
                seriesBy: {}
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
      - Dashboards
      operationId: updateWidgetRuleset
      summary: Trocar o filtro do widget
      description: Substitui `content.filter.ruleset` e descarta o resultado guardado, para o próximo cálculo já usar o filtro novo.
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: widget
        in: path
        required: true
        description: ID do widget.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - ruleset
              properties:
                ruleset:
                  $ref: '#/components/schemas/Ruleset'
            example:
              ruleset:
                condition: AND
                rules:
                - fieldId: deal.amount
                  operator: between
                  valueMin: 100
                  valueMax: 500000
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  widget:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  dashboard:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  rulesetUpdated:
                    type: boolean
                  cacheCleared:
                    type: boolean
              example:
                widget: 507f1f77bcf86cd799439190
                dashboard: 507f1f77bcf86cd799439180
                rulesetUpdated: true
                cacheCleared: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/dashboards/{dashboard}/widgets/{widget}/results/list:
    get:
      tags:
      - Dashboards
      operationId: listWidgetResults
      summary: Listar os registros por trás do widget
      description: Devolve os registros que o widget contou, com as colunas de `reportfields`. Cada coluna vem com o `slug` do campo, trocando
        o ponto por sublinhado (`deal.amount` vira `deal_amount`).
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: widget
        in: path
        required: true
        description: ID do widget.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  docs:
                    type: array
                    items:
                      type: object
                    description: Registros.
                  total:
                    type: integer
                  pages:
                    type: integer
                  page:
                    type: integer
                  limit:
                    type: integer
                  hasNextPage:
                    type: boolean
              example:
                docs:
                - deal_id: 507f1f77bcf86cd799439015
                  deal_title: Implantação CRM Enterprise
                  deal_amount: 75000
                total: 1
                pages: 1
                page: 1
                limit: 15
                hasNextPage: false
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/dashboards/{dashboard}/filter-presets:
    get:
      tags:
      - Dashboards
      operationId: listFilterPresets
      summary: Listar predefinições de filtro
      description: Filtros prontos que quem abre o dashboard pode aplicar. Lista simples, sem paginação.
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/FilterPreset'
              example:
              - _id: 507f1f77bcf86cd799439195
                dashboard: 507f1f77bcf86cd799439180
                name: Só ganhas
                ruleset:
                  condition: AND
                  rules:
                  - fieldId: deal.won
                    operator: true_value
                position: 0
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
      - Dashboards
      operationId: createFilterPreset
      summary: Criar predefinição de filtro
      description: O nome não pode repetir no mesmo dashboard (`409`).
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              - ruleset
              properties:
                name:
                  type: string
                  description: Nome.
                ruleset:
                  $ref: '#/components/schemas/Ruleset'
                position:
                  type: number
                  description: Ordem.
            example:
              name: Só ganhas
              ruleset:
                condition: AND
                rules:
                - fieldId: deal.won
                  operator: true_value
      responses:
        '200':
          description: Predefinição criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FilterPreset'
              example:
                _id: 507f1f77bcf86cd799439195
                dashboard: 507f1f77bcf86cd799439180
                name: Só ganhas
                ruleset:
                  condition: AND
                  rules:
                  - fieldId: deal.won
                    operator: true_value
                position: 0
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /api/dashboards/{dashboard}/filter-presets/{preset}:
    put:
      tags:
      - Dashboards
      operationId: updateFilterPreset
      summary: Atualizar predefinição de filtro
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: preset
        in: path
        required: true
        description: ID da predefinição.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Nome.
                ruleset:
                  $ref: '#/components/schemas/Ruleset'
                position:
                  type: number
                  description: Ordem.
            example:
              name: Ganhas no mês
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FilterPreset'
              example:
                _id: 507f1f77bcf86cd799439195
                dashboard: 507f1f77bcf86cd799439180
                name: Só ganhas
                ruleset:
                  condition: AND
                  rules:
                  - fieldId: deal.won
                    operator: true_value
                position: 0
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Dashboards
      operationId: deleteFilterPreset
      summary: Excluir predefinição de filtro
      parameters:
      - name: dashboard
        in: path
        required: true
        description: ID do dashboard.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      - name: preset
        in: path
        required: true
        description: ID da predefinição.
        schema:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
              example:
                ok: true
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/processautomations:
    get:
      tags:
      - Automações
      operationId: listAutomations
      summary: Listar automações
      description: 'Tamanho de página padrão: 15.


        **Permissão do Service Account:** `automations.list`'
      parameters:
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      - $ref: '#/components/parameters/SortBy'
      - $ref: '#/components/parameters/SortType'
      - name: search
        in: query
        description: Busca no nome.
        schema:
          type: string
      - name: folderId
        in: query
        description: ID da pasta.
        schema:
          type: string
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        $ref: '#/components/schemas/Automation'
              example:
                docs:
                - _id: 507f1f77bcf86cd799439200
                  title: Alerta de contato sem interação
                  sourceEntity: Contact
                  triggerType: filter
                  status: FREEZE
                  triggerConfig:
                    type: scheduled
                    recurrence: daily
                    schedule:
                      time: 09:00
                      timezone: America/Sao_Paulo
                  filter:
                    ruleset:
                      condition: AND
                      rules:
                      - fieldId: contact.lasttreatmentcreatedat
                        operator: older_than_days
                        value: 60
                  automationActions:
                  - actionType: notify
                    order: 1
                    config:
                      notifyConfig:
                        channel: push
                        recipients:
                        - owner
                        subject: Contato sem interação
                        content: '{contact.fullname} está há 60 dias sem interação.'
                  limit: 50
                  eventTriggers: []
                  lifeCycles: []
                  workspaces: []
                  staffs: []
                  active: true
                  executionCount: 0
                  createdAt: '2026-04-28T10:30:00Z'
                  updatedAt: '2026-04-28T10:30:00Z'
                total: 7
                pages: 1
                page: 1
                limit: 15
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    post:
      tags:
      - Automações
      operationId: createAutomation
      summary: Criar automação
      description: 'O único campo obrigatório é `title`.


        **Atenção:** sem `status`, a automação nasce `WAITING`, ou seja, ativa: uma automação por filtro executa no próximo horário agendado,
        e uma por evento, no próximo evento. Para criar pausada, envie `status: FREEZE`, confira o alcance com `POST /api/processautomations/{id}/simulate`
        e só então ative.


        A exclusão de automações é feita por um administrador, na tela do CRM.


        **Permissão do Service Account:** `automations.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
              - $ref: '#/components/schemas/AutomationInput'
              - type: object
                required:
                - title
            examples:
              filtro:
                summary: Por filtro, agendada
                value:
                  title: Alerta de contato sem interação
                  sourceEntity: Contact
                  triggerType: filter
                  status: FREEZE
                  triggerConfig:
                    type: scheduled
                    recurrence: daily
                    schedule:
                      time: 09:00
                      timezone: America/Sao_Paulo
                  filter:
                    ruleset:
                      condition: AND
                      rules:
                      - fieldId: contact.lasttreatmentcreatedat
                        operator: older_than_days
                        value: 60
                  automationActions:
                  - actionType: notify
                    order: 1
                    config:
                      notifyConfig:
                        channel: push
                        recipients:
                        - owner
                        subject: Contato sem interação
                        content: '{contact.fullname} está há 60 dias sem interação.'
                  limit: 50
              evento:
                summary: Por evento, com webhook
                value:
                  title: Avisar o ERP quando uma venda fecha
                  sourceEntity: Deal
                  triggerType: event
                  status: FREEZE
                  eventTriggers:
                  - event: won
                    fieldConditions:
                    - field: amount
                      operator: greater_than
                      value: 10000
                  automationActions:
                  - actionType: webhook
                    order: 1
                    config:
                      webhookConfig:
                        url: https://erp.exemplo.com/vendas
                        method: POST
                        payload:
                          titulo: '{deal.title}'
      responses:
        '200':
          description: Automação criada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Automation'
              example:
                _id: 507f1f77bcf86cd799439200
                title: Alerta de contato sem interação
                sourceEntity: Contact
                triggerType: filter
                status: FREEZE
                triggerConfig:
                  type: scheduled
                  recurrence: daily
                  schedule:
                    time: 09:00
                    timezone: America/Sao_Paulo
                filter:
                  ruleset:
                    condition: AND
                    rules:
                    - fieldId: contact.lasttreatmentcreatedat
                      operator: older_than_days
                      value: 60
                automationActions:
                - actionType: notify
                  order: 1
                  config:
                    notifyConfig:
                      channel: push
                      recipients:
                      - owner
                      subject: Contato sem interação
                      content: '{contact.fullname} está há 60 dias sem interação.'
                limit: 50
                eventTriggers: []
                lifeCycles: []
                workspaces: []
                staffs: []
                active: true
                executionCount: 0
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/processautomations/{id}:
    get:
      tags:
      - Automações
      operationId: getAutomation
      summary: Obter automação
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Automation'
              example:
                _id: 507f1f77bcf86cd799439200
                title: Alerta de contato sem interação
                sourceEntity: Contact
                triggerType: filter
                status: FREEZE
                triggerConfig:
                  type: scheduled
                  recurrence: daily
                  schedule:
                    time: 09:00
                    timezone: America/Sao_Paulo
                filter:
                  ruleset:
                    condition: AND
                    rules:
                    - fieldId: contact.lasttreatmentcreatedat
                      operator: older_than_days
                      value: 60
                automationActions:
                - actionType: notify
                  order: 1
                  config:
                    notifyConfig:
                      channel: push
                      recipients:
                      - owner
                      subject: Contato sem interação
                      content: '{contact.fullname} está há 60 dias sem interação.'
                limit: 50
                eventTriggers: []
                lifeCycles: []
                workspaces: []
                staffs: []
                active: true
                executionCount: 0
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
      description: '**Permissão do Service Account:** `automations.read`'
    put:
      tags:
      - Automações
      operationId: updateAutomation
      summary: Atualizar automação
      description: 'Envie a automação com os campos que mudam. `status` não muda por aqui: use a operação de situação.


        **Permissão do Service Account:** `automations.update`'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AutomationInput'
            example:
              title: Alerta de contato sem interação
              sourceEntity: Contact
              triggerType: filter
              status: FREEZE
              triggerConfig:
                type: scheduled
                recurrence: daily
                schedule:
                  time: 09:00
                  timezone: America/Sao_Paulo
              filter:
                ruleset:
                  condition: AND
                  rules:
                  - fieldId: contact.lasttreatmentcreatedat
                    operator: older_than_days
                    value: 60
              automationActions:
              - actionType: notify
                order: 1
                config:
                  notifyConfig:
                    channel: push
                    recipients:
                    - owner
                    subject: Contato sem interação
                    content: '{contact.fullname} está há 60 dias sem interação.'
              limit: 100
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Automation'
              example:
                _id: 507f1f77bcf86cd799439200
                title: Alerta de contato sem interação
                sourceEntity: Contact
                triggerType: filter
                status: FREEZE
                triggerConfig:
                  type: scheduled
                  recurrence: daily
                  schedule:
                    time: 09:00
                    timezone: America/Sao_Paulo
                filter:
                  ruleset:
                    condition: AND
                    rules:
                    - fieldId: contact.lasttreatmentcreatedat
                      operator: older_than_days
                      value: 60
                automationActions:
                - actionType: notify
                  order: 1
                  config:
                    notifyConfig:
                      channel: push
                      recipients:
                      - owner
                      subject: Contato sem interação
                      content: '{contact.fullname} está há 60 dias sem interação.'
                limit: 100
                eventTriggers: []
                lifeCycles: []
                workspaces: []
                staffs: []
                active: true
                executionCount: 0
                createdAt: '2026-04-28T10:30:00Z'
                updatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/processautomations/{id}/status:
    put:
      tags:
      - Automações
      operationId: updateAutomationStatus
      summary: Ativar, pausar ou concluir automação
      description: 'Transições aceitas: de `WAITING` para `FREEZE`, `FINISHED` ou `RECURRENCE`; de `FREEZE` para `WAITING` ou `RECURRENCE`; de
        `RECURRENCE` para `FREEZE`, `WAITING` ou `FINISHED`; de `FINISHED` para `WAITING` ou `RECURRENCE`. Fora disso a resposta é `400` com `allowedTransitions`.'
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - status
              properties:
                status:
                  type: string
                  enum:
                  - WAITING
                  - FREEZE
                  - RECURRENCE
                  - FINISHED
                  description: '`WAITING`: ativa, aguardando a próxima execução. `FREEZE`: pausada. `RECURRENCE`: ativa e recorrente. `FINISHED`:
                    concluída.'
            example:
              status: FREEZE
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  processAutomation:
                    type: object
                    properties:
                      _id:
                        type: string
                        pattern: ^[a-fA-F0-9]{24}$
                        example: 507f1f77bcf86cd799439011
                      title:
                        type: string
                      status:
                        type: string
                        enum:
                        - WAITING
                        - FREEZE
                        - RECURRENCE
                        - FINISHED
                        description: '`WAITING`: ativa, aguardando a próxima execução. `FREEZE`: pausada. `RECURRENCE`: ativa e recorrente. `FINISHED`:
                          concluída.'
                      previousStatus:
                        type: string
                        enum:
                        - WAITING
                        - FREEZE
                        - RECURRENCE
                        - FINISHED
                        description: '`WAITING`: ativa, aguardando a próxima execução. `FREEZE`: pausada. `RECURRENCE`: ativa e recorrente. `FINISHED`:
                          concluída.'
                      updatedAt:
                        type: string
                        format: date-time
                        description: Alteração.
                        example: '2026-05-15T14:00:00Z'
              example:
                success: true
                message: Status da automação alterado para FREEZE
                processAutomation:
                  _id: 507f1f77bcf86cd799439200
                  title: Alerta de contato sem interação
                  status: FREEZE
                  previousStatus: WAITING
                  updatedAt: '2026-04-28T10:35:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/processautomations/simulate-draft:
    post:
      tags:
      - Automações
      operationId: simulateAutomationDraft
      summary: Simular uma automação antes de gravar
      description: 'Calcula quantos registros a automação alcançaria, sem gravar nada e sem executar ações.


        **Permissão do Service Account:** `automations.create`'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - automation
              properties:
                automation:
                  $ref: '#/components/schemas/AutomationInput'
                automationId:
                  type: string
                  pattern: ^[a-fA-F0-9]{24}$
                  example: 507f1f77bcf86cd799439011
                  description: 'Opcional: ID da automação que está sendo editada.'
            example:
              automation:
                title: Alerta de contato sem interação
                sourceEntity: Contact
                triggerType: filter
                status: FREEZE
                triggerConfig:
                  type: scheduled
                  recurrence: daily
                  schedule:
                    time: 09:00
                    timezone: America/Sao_Paulo
                filter:
                  ruleset:
                    condition: AND
                    rules:
                    - fieldId: contact.lasttreatmentcreatedat
                      operator: older_than_days
                      value: 60
                automationActions:
                - actionType: notify
                  order: 1
                  config:
                    notifyConfig:
                      channel: push
                      recipients:
                      - owner
                      subject: Contato sem interação
                      content: '{contact.fullname} está há 60 dias sem interação.'
                limit: 50
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  simulation:
                    type: object
                    properties:
                      status:
                        type: string
                        description: Situação da simulação.
                        example: complete
                      totalMatched:
                        type: integer
                        description: Quantos registros a automação alcançaria agora.
                      truncated:
                        type: boolean
                        description: A contagem foi interrompida no limite de segurança.
                      noFilter:
                        type: boolean
                        description: A automação não tem filtro.
                      filter:
                        type: object
                        properties:
                          conditionsCount:
                            type: integer
                          lifeCycles:
                            type: array
                            items:
                              type: string
                          description:
                            type: string
                      actions:
                        type: array
                        items:
                          type: object
                        description: O que cada ação faria.
                      warnings:
                        type: array
                        items:
                          type: string
                        description: Avisos, como filtro que não casa com nada.
                      simulatedAt:
                        type: string
                        format: date-time
                        description: Quando foi simulada.
                        example: '2026-05-15T14:00:00Z'
              example:
                simulation:
                  status: complete
                  totalMatched: 12
                  truncated: false
                  noFilter: false
                  filter:
                    conditionsCount: 1
                    lifeCycles: []
                    description: ''
                  actions: []
                  warnings: []
                  simulatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/processautomations/{id}/simulate:
    post:
      tags:
      - Automações
      operationId: simulateAutomation
      summary: Simular uma automação gravada
      description: Calcula o alcance atual da automação, sem executar ações.
      parameters:
      - $ref: '#/components/parameters/Id'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  simulation:
                    type: object
                    properties:
                      status:
                        type: string
                        description: Situação da simulação.
                        example: complete
                      totalMatched:
                        type: integer
                        description: Quantos registros a automação alcançaria agora.
                      truncated:
                        type: boolean
                        description: A contagem foi interrompida no limite de segurança.
                      noFilter:
                        type: boolean
                        description: A automação não tem filtro.
                      filter:
                        type: object
                        properties:
                          conditionsCount:
                            type: integer
                          lifeCycles:
                            type: array
                            items:
                              type: string
                          description:
                            type: string
                      actions:
                        type: array
                        items:
                          type: object
                        description: O que cada ação faria.
                      warnings:
                        type: array
                        items:
                          type: string
                        description: Avisos, como filtro que não casa com nada.
                      simulatedAt:
                        type: string
                        format: date-time
                        description: Quando foi simulada.
                        example: '2026-05-15T14:00:00Z'
              example:
                simulation:
                  status: complete
                  totalMatched: 12
                  truncated: false
                  noFilter: false
                  filter:
                    conditionsCount: 1
                    lifeCycles: []
                    description: ''
                  actions: []
                  warnings: []
                  simulatedAt: '2026-04-28T10:30:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/processautomations/{id}/statistics:
    get:
      tags:
      - Automações
      operationId: getAutomationStatistics
      summary: Obter estatísticas da automação
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  processAutomation:
                    type: object
                    properties:
                      _id:
                        type: string
                        pattern: ^[a-fA-F0-9]{24}$
                        example: 507f1f77bcf86cd799439011
                      title:
                        type: string
                      status:
                        type: string
                        enum:
                        - WAITING
                        - FREEZE
                        - RECURRENCE
                        - FINISHED
                        description: '`WAITING`: ativa, aguardando a próxima execução. `FREEZE`: pausada. `RECURRENCE`: ativa e recorrente. `FINISHED`:
                          concluída.'
                  statistics:
                    type: object
                    properties:
                      contacts:
                        type: object
                        description: Registros processados.
                        properties:
                          total:
                            type: integer
                          sent:
                            type: integer
                          pending:
                            type: integer
                          withError:
                            type: integer
                          successful:
                            type: integer
                      performance:
                        type: object
                        properties:
                          successRate:
                            type: number
                          errorRate:
                            type: number
                          completionRate:
                            type: number
                      errorDistribution:
                        type: array
                        items:
                          type: object
                      temporalEvolution:
                        type: array
                        items:
                          type: object
                      actionPerformance:
                        type: array
                        items:
                          type: object
              example:
                processAutomation:
                  _id: 507f1f77bcf86cd799439200
                  title: Alerta de contato sem interação
                  status: WAITING
                statistics:
                  contacts:
                    total: 40
                    sent: 38
                    pending: 0
                    withError: 2
                    successful: 38
                  performance:
                    successRate: 95
                    errorRate: 5
                    completionRate: 100
                  errorDistribution: []
                  temporalEvolution: []
                  actionPerformance: []
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/processautomations/{id}/schedule:
    get:
      tags:
      - Automações
      operationId: getAutomationSchedule
      summary: Obter a agenda da automação
      description: Próxima execução e as execuções recentes.
      parameters:
      - $ref: '#/components/parameters/Id'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: object
                properties:
                  schedule:
                    type: object
                    properties:
                      nextExecution:
                        type: string
                        format: date-time
                        nullable: true
                        description: '`null` quando pausada ou sem agenda.'
                      isRecurrent:
                        type: boolean
                  executionStats:
                    type: object
                    properties:
                      totalContacts:
                        type: integer
                      processedContacts:
                        type: integer
                      pendingContacts:
                        type: integer
                      errorContacts:
                        type: integer
                  recentExecutions:
                    type: array
                    items:
                      type: object
              example:
                schedule:
                  nextExecution: '2026-04-29T12:00:00Z'
                  isRecurrent: false
                executionStats:
                  totalContacts: 40
                  processedContacts: 40
                  pendingContacts: 0
                  errorContacts: 2
                recentExecutions: []
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/processautomations/{id}/logs:
    get:
      tags:
      - Automações
      operationId: listAutomationLogs
      summary: Listar o histórico de execução
      description: 'Tamanho de página padrão: 25.


        **Permissão do Service Account:** `automations.read-logs`'
      parameters:
      - $ref: '#/components/parameters/Id'
      - $ref: '#/components/parameters/Page'
      - $ref: '#/components/parameters/Limit'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Page'
                - type: object
                  properties:
                    docs:
                      type: array
                      items:
                        type: object
              example:
                docs: []
                total: 0
                pages: 0
                page: 1
                limit: 25
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/processautomations/entities:
    get:
      tags:
      - Automações
      operationId: listAutomationEntities
      summary: Listar recursos, eventos e campos aceitos
      description: 'Para cada recurso: os eventos que podem disparar uma automação (`supportedEvents`) e os campos que podem entrar em `fieldConditions`
        (`availableFields`).


        **Permissão do Service Account:** `automations.read`'
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      description: Valor de `sourceEntity`.
                      example: Deal
                    name:
                      type: string
                      description: Nome exibido.
                    description:
                      type: string
                    supportedEvents:
                      type: array
                      items:
                        type: string
                      description: Eventos aceitos.
                    availableFields:
                      type: array
                      items:
                        type: object
                        properties:
                          field:
                            type: string
                          label:
                            type: string
                          type:
                            type: string
                      description: Campos para `fieldConditions`.
              example:
              - id: Deal
                name: Negócio
                description: Oportunidades de venda
                supportedEvents:
                - created
                - updated
                - deleted
                - stage_changed
                - won
                - lost
                - assigned
                availableFields:
                - field: title
                  label: Título
                  type: string
                - field: amount
                  label: Valor
                  type: number
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
  /api/processautomations/templates/actions:
    get:
      tags:
      - Automações
      operationId: listAutomationActionTemplates
      summary: Listar modelos de ação
      description: Ações prontas, com a configuração padrão (`defaultConfig`), as variáveis disponíveis (`availableVariables`) e os recursos com
        que cada uma é compatível.
      responses:
        '200':
          description: Sucesso.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    _id:
                      type: string
                      pattern: ^[a-fA-F0-9]{24}$
                      example: 507f1f77bcf86cd799439011
                    name:
                      type: string
                      description: Nome.
                      example: Criar Tarefa
                    description:
                      type: string
                    category:
                      type: string
                      description: Tipo de ação.
                      example: create
                    compatibleSourceEntities:
                      type: array
                      items:
                        type: string
                    defaultConfig:
                      type: object
                    availableVariables:
                      type: array
                      items:
                        type: object
                        properties:
                          name:
                            type: string
                          description:
                            type: string
                          path:
                            type: string
              example:
              - _id: 507f1f77bcf86cd799439210
                name: Criar Tarefa
                description: Cria uma nova tarefa para o responsável
                category: create
                compatibleSourceEntities:
                - Contact
                - Deal
                - Organization
                - Ticket
                defaultConfig:
                  createFields:
                    type: follow_up
                    priority: normal
                availableVariables:
                - name: contact.fullname
                  description: Nome completo do contato
                  path: contact.fullname
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
components:
  securitySchemes:
    ServiceAccount:
      type: oauth2
      description: Token obtido com o Client ID e o Client Secret de um Service Account (Configurações › Service Accounts).
      flows:
        clientCredentials:
          tokenUrl: https://magnetcustomer.net/realms/platform/protocol/openid-connect/token
          scopes: {}
  parameters:
    Id:
      name: id
      in: path
      required: true
      description: ID do registro.
      schema:
        type: string
        pattern: ^[a-fA-F0-9]{24}$
        example: 507f1f77bcf86cd799439011
    Page:
      name: page
      in: query
      description: 'Página, a partir de 1. **Envie sempre**: sem `page`, a resposta vem como lista simples, sem o envelope de paginação, e `limit`
        é ignorado.'
      schema:
        type: integer
        default: 1
        minimum: 1
        example: 1
    Limit:
      name: limit
      in: query
      description: 'Registros por página. Padrão: 15, salvo indicação na operação. Máximo: 100 — acima disso a API usa 100.'
      schema:
        type: integer
        default: 15
        minimum: 1
        maximum: 100
    SortBy:
      name: sortBy
      in: query
      description: Campo de ordenação. O padrão varia por recurso.
      schema:
        type: string
    SortType:
      name: sortType
      in: query
      description: Direção da ordenação.
      schema:
        type: string
        enum:
        - asc
        - desc
        default: desc
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: Chave única da operação (UUID). Repita a mesma chave nas novas tentativas.
      schema:
        type: string
        format: uuid
  responses:
    ValidationError:
      description: Corpo ou parâmetros inválidos.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            reason: Missing required parameters
            message: Missing required parameters
            code: VALIDATION_ERROR
            errors:
            - field: title
              message: must NOT have fewer than 1 characters
              keyword: minLength
    Unauthorized:
      description: Token ausente, inválido ou vencido. Solicite um novo token.
      content:
        application/json:
          schema:
            type: object
            properties:
              reason:
                type: string
          example:
            reason: Access Denied
    Forbidden:
      description: O Service Account não tem a permissão que a rota exige.
      content:
        application/json:
          schema:
            type: object
            properties:
              reason:
                type: string
              type:
                type: string
                description: '`SA_PERMISSION_DENIED` quando falta uma permissão; `ADMIN_REQUIRED` quando a rota exige administrador.'
              requiredPermission:
                type: string
                description: 'Permissão que falta: ligue-a no Service Account.'
          example:
            reason: Service account sem permissão para este recurso.
            type: SA_PERMISSION_DENIED
            requiredPermission: deals.delete
    NotFound:
      description: Registro não encontrado.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            reason: Contact not found
            message: Contact not found
            code: NOT_FOUND
    Conflict:
      description: O registro não está no estado que a operação exige.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooManyRequests:
      description: Limite de requisições excedido. Espere o tempo do cabeçalho `Retry-After`.
      headers:
        Retry-After:
          description: Segundos até poder tentar de novo.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Page:
      type: object
      description: Envelope de toda listagem paginada.
      required:
      - docs
      - total
      - pages
      - page
      - limit
      properties:
        docs:
          type: array
          items: {}
          description: Registros da página.
        total:
          type: integer
          description: Total de registros que casam com a consulta.
          example: 132
        pages:
          type: integer
          description: Total de páginas.
          example: 9
        page:
          type: integer
          description: Página atual.
          example: 1
        limit:
          type: integer
          description: Registros por página aplicados (no máximo 100).
          example: 15
    Error:
      type: object
      description: Formato de erro da API.
      properties:
        reason:
          type: string
          description: Mensagem do erro.
        message:
          type: string
          description: Igual a `reason`.
        code:
          type: string
          description: Código do erro.
          enum:
          - BAD_REQUEST
          - VALIDATION_ERROR
          - MISSING_REQUIRED
          - NOT_FOUND
          - PERMISSION_DENIED
          - DUPLICATE
          - INTERNAL_ERROR
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
                description: Campo com problema.
              message:
                type: string
                description: O que está errado.
              keyword:
                type: string
                description: Regra violada.
          description: Detalhes, em `VALIDATION_ERROR`.
        fields:
          type: array
          items:
            type: object
          description: Campos obrigatórios que faltaram, em `MISSING_REQUIRED`.
        details:
          type: object
          description: Detalhes adicionais, quando houver.
    CustomFieldValue:
      type: object
      required:
      - customField
      - v
      properties:
        customField:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do campo personalizado (de `GET /api/customfields`).
        k:
          type: string
          description: Chave (`slug`) do campo. Recomendado.
          example: segmento
        v:
          description: 'Valor do campo: texto, número, booleano, lista ou objeto. Envie em `v` — `value` é ignorado sem erro.'
          example: Tecnologia
    StaffSummary:
      type: object
      description: Usuário resumido.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        fullname:
          type: string
          description: Nome completo.
        name:
          type: string
          description: Primeiro nome.
        lastname:
          type: string
          description: Sobrenome.
        email:
          type: string
          description: E-mail. Nem toda resposta traz.
    ContactSummary:
      type: object
      description: Contato resumido.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        fullname:
          type: string
          description: Nome completo.
        lifeCycle:
          type: string
          description: 'Fase: `lead`, `prospect` ou `customer`.'
        email:
          type: string
          description: E-mail, quando o contato tem.
        phones:
          type: array
          items:
            type: object
          description: Telefones.
        owners:
          type: array
          items: {}
          description: 'Responsáveis: IDs nas listagens; objetos resumidos no detalhe.'
    DealInput:
      type: object
      properties:
        title:
          type: string
          description: Título da oportunidade.
          example: Implantação CRM Enterprise
        pipeline:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do pipeline (de `GET /api/pipelines`).
        stage:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID da etapa do pipeline (de `GET /api/pipelines/{id}/stages`).
        staff:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do responsável (de `GET /api/staffs`).
        amount:
          type: number
          description: Valor monetário.
          example: 75000
        expectedCloseDate:
          type: string
          format: date-time
          description: Data prevista de fechamento.
          example: '2026-05-15T14:00:00Z'
        contact:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do contato vinculado.
        organization:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID da empresa vinculada.
        participants:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários participantes.
        observers:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários observadores.
        tags:
          type: array
          items:
            type: string
          description: Tags livres.
        observation:
          type: string
          description: Observação inicial.
        avatarUrl:
          type: string
          description: URL de imagem.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
        active:
          type: boolean
          description: 'Padrão: `true`.'
    DealCreate:
      allOf:
      - $ref: '#/components/schemas/DealInput'
      - type: object
        required:
        - title
        - pipeline
        - stage
        - staff
    Deal:
      type: object
      description: Oportunidade. Nas listagens as referências vêm resumidas, com os campos abaixo.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        code:
          type: integer
          description: Número sequencial da oportunidade na conta.
          example: 39
        title:
          type: string
          description: Título.
        amount:
          type: number
          description: Valor.
        expectedCloseDate:
          type: string
          format: date-time
          description: Fechamento previsto. Ausente quando não informado.
          example: '2026-05-15T14:00:00Z'
        won:
          type: boolean
          description: Ganha.
        lost:
          type: boolean
          description: Perdida.
        closed:
          type: boolean
          description: Fechada (ganha ou perdida).
        closedDate:
          type: string
          format: date-time
          nullable: true
          description: Quando foi fechada.
        enteredStageDate:
          type: string
          format: date-time
          description: Quando entrou na etapa atual.
          example: '2026-05-15T14:00:00Z'
        source:
          type: string
          description: Origem do registro.
          example: platform
        active:
          type: boolean
          description: '`false` quando excluída.'
        pipeline:
          description: Nas listagens, o ID do pipeline. Em `GET /api/deals/{id}`, um objeto com `_id` e `title`.
          oneOf:
          - type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          - type: object
            properties:
              _id:
                type: string
                pattern: ^[a-fA-F0-9]{24}$
                example: 507f1f77bcf86cd799439011
              title:
                type: string
                description: Nome do pipeline.
        stage:
          type: object
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
              description: Nome da etapa.
            position:
              type: integer
              description: Só nas listagens.
            won:
              type: boolean
              description: Só nas listagens.
            lost:
              type: boolean
              description: Só nas listagens.
        staff:
          $ref: '#/components/schemas/StaffSummary'
        contact:
          $ref: '#/components/schemas/ContactSummary'
        organization:
          type: object
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            fullname:
              type: string
              description: Nome da empresa.
        participants:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        observers:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        lostReason:
          type: string
          nullable: true
          description: ID do motivo de perda.
        labels:
          type: array
          items:
            type: object
            properties:
              _id:
                type: string
                pattern: ^[a-fA-F0-9]{24}$
                example: 507f1f77bcf86cd799439011
              name:
                type: string
              color:
                type: string
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    MeetingInput:
      type: object
      properties:
        title:
          type: string
          description: Título da reunião.
          example: Apresentação da proposta
        start:
          type: string
          format: date-time
          description: Início.
          example: '2026-05-15T14:00:00Z'
        end:
          type: string
          format: date-time
          description: Término.
          example: '2026-05-15T14:00:00Z'
        staff:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do organizador. Se omitido, usa o usuário da credencial.
        calendar:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do calendário do organizador. Se omitido, a API escolhe o calendário padrão dele.
        description:
          type: string
          description: Descrição ou pauta.
        visibility:
          type: string
          enum:
          - public
          - private
          description: Quem vê a reunião.
        status:
          type: string
          enum:
          - pending
          - cancelled
          - finished
          - no-show
          description: 'Padrão: `pending`. Com `finished`, `cancelled` ou `no-show`, a API preenche `dateFinished`.'
        priority:
          type: string
          enum:
          - p1
          - p2
          - p3
          - p4
          description: Prioridade.
        type:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do tipo de reunião (de `GET /api/meetingtypes`).
        room:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID da sala (de `GET /api/meetingrooms`).
        googleHangoutLink:
          type: string
          description: Link da videoconferência.
        contact:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Contato vinculado.
        organization:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Empresa vinculada.
        deal:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Oportunidade vinculada.
        product:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Produto vinculado.
        participants:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários participantes.
        observers:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários observadores.
        labels:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs de etiquetas.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
        active:
          type: boolean
          description: 'Padrão: `true`.'
    MeetingCreate:
      allOf:
      - $ref: '#/components/schemas/MeetingInput'
      - type: object
        required:
        - title
        - start
    Meeting:
      type: object
      description: Reunião. Nas listagens as referências vêm resumidas.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        title:
          type: string
          description: Título.
        description:
          type: string
          description: Descrição.
        start:
          type: string
          format: date-time
          description: Início.
          example: '2026-05-15T14:00:00Z'
        end:
          type: string
          format: date-time
          description: Término.
          example: '2026-05-15T14:00:00Z'
        status:
          type: string
          enum:
          - pending
          - cancelled
          - finished
          - no-show
          description: 'Padrão: `pending`. Com `finished`, `cancelled` ou `no-show`, a API preenche `dateFinished`.'
        priority:
          type: string
          enum:
          - p1
          - p2
          - p3
          - p4
          description: Prioridade.
        active:
          type: boolean
        type:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
            slug:
              type: string
        room:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
        staff:
          $ref: '#/components/schemas/StaffSummary'
        participants:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        observers:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        contact:
          $ref: '#/components/schemas/ContactSummary'
        organization:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
        deal:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            title:
              type: string
            amount:
              type: number
        dateFinished:
          type: string
          format: date-time
          description: Quando foi concluída, cancelada ou marcada como não comparecimento.
          example: '2026-05-15T14:00:00Z'
        labels:
          type: array
          items:
            type: object
            properties:
              _id:
                type: string
                pattern: ^[a-fA-F0-9]{24}$
                example: 507f1f77bcf86cd799439011
              name:
                type: string
              color:
                type: string
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    TaskInput:
      type: object
      properties:
        title:
          type: string
          description: Título da tarefa.
          minLength: 1
          example: Enviar proposta comercial
        dateOfExpires:
          type: string
          format: date-time
          description: Vencimento.
          example: '2026-05-15T14:00:00Z'
        type:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do tipo de tarefa (de `GET /api/tasks/types`).
        owner:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do responsável. Se omitido, usa o usuário da credencial.
        status:
          type: string
          enum:
          - pending
          - finished
          description: 'Padrão: `pending`.'
        priority:
          type: string
          enum:
          - p1
          - p2
          - p3
          - p4
          description: Prioridade.
        source:
          type: string
          description: 'De onde a tarefa veio. Texto livre; padrão: `task`. Valores usados pelo CRM: `task`, `deal`, `contact`, `organization`,
            `ticket`, `meeting`, `email`.'
          example: deal
        observation:
          type: string
          description: Detalhes.
        participants:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários participantes.
        observers:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários observadores.
        contact:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Contato vinculado.
        organization:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Empresa vinculada.
        deal:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Oportunidade vinculada. Contato e empresa são herdados dela quando omitidos.
        product:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Produto vinculado.
        treatment:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Atendimento vinculado.
        estimatedMinutes:
          type: number
          description: Tempo estimado, em minutos.
          example: 60
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
        active:
          type: boolean
          description: 'Padrão: `true`.'
    TaskCreate:
      allOf:
      - $ref: '#/components/schemas/TaskInput'
      - type: object
        required:
        - title
        - dateOfExpires
        - type
    Task:
      type: object
      description: Tarefa. Nas listagens as referências vêm resumidas.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        title:
          type: string
          description: Título.
        observation:
          type: string
          description: Detalhes.
        dateOfExpires:
          type: string
          format: date-time
          description: Vencimento.
          example: '2026-05-15T14:00:00Z'
        status:
          type: string
          enum:
          - pending
          - finished
          description: 'Padrão: `pending`.'
        priority:
          type: string
          enum:
          - p1
          - p2
          - p3
          - p4
          description: Prioridade.
        source:
          type: string
          description: Origem.
        active:
          type: boolean
        code:
          type: integer
          description: Número sequencial da tarefa na conta.
        type:
          type: object
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
        owner:
          $ref: '#/components/schemas/StaffSummary'
        participants:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        observers:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        contact:
          $ref: '#/components/schemas/ContactSummary'
        organization:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
        deal:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            title:
              type: string
            amount:
              type: number
            pipeline:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
              description: ID do pipeline.
            stage:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
              description: ID da etapa.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    TaskType:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome.
          example: Ligação
        slug:
          type: string
          description: Gerado a partir do nome; não muda depois.
          example: ligacao
        active:
          type: boolean
        default:
          type: boolean
          description: Tipo padrão da conta.
        defaultForNewTask:
          type: boolean
          description: Vem selecionado ao criar tarefa.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    ObservationInput:
      type: object
      properties:
        observation:
          type: string
          description: 'Texto. Para mencionar um usuário, use `@[Nome](idDoUsuario)`: ele recebe uma notificação.'
          example: Cliente pediu desconto de 15%.
        staff:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do autor.
        contact:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Contato vinculado.
        organization:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Empresa vinculada.
        deal:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Oportunidade vinculada.
        product:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Produto vinculado.
        active:
          type: boolean
          description: 'Padrão: `true`.'
    Observation:
      type: object
      description: Observação. Na criação as referências vêm como ID; nas listagens, resumidas.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        observation:
          type: string
          description: Texto.
        active:
          type: boolean
        staff:
          $ref: '#/components/schemas/StaffSummary'
        contact:
          $ref: '#/components/schemas/ContactSummary'
        organization:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            fullname:
              type: string
        deal:
          type: object
          nullable: true
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            title:
              type: string
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    ContactInput:
      type: object
      properties:
        fullname:
          type: string
          description: Nome completo.
          example: Maria Santos
        email:
          type: string
          description: E-mail.
          example: maria@cliente.com
        phones:
          type: array
          items:
            type: object
            properties:
              number:
                type: string
                description: Número, só dígitos.
                example: '11987654321'
              ddi:
                type: string
                description: Código do país, com `+`.
                example: '+55'
              typePhone:
                type: string
                description: Tipo do telefone.
                example: cellphone
          description: Telefones.
        doc:
          type: string
          description: CPF ou CNPJ.
        type:
          type: string
          description: Tipo de pessoa.
        birthDate:
          type: string
          description: Data de nascimento (ISO 8601).
        gender:
          type: string
          description: Gênero.
        work:
          type: string
          description: Profissão.
        maritalStatus:
          type: string
          description: Estado civil.
        state:
          type: string
          description: Estado.
        uf:
          type: string
          description: Sigla do estado.
          example: SP
        city:
          type: string
          description: Cidade.
        address:
          type: string
          description: Logradouro.
        addressNumber:
          type: string
          description: Número.
        complement:
          type: string
          description: Complemento.
        neighborhood:
          type: string
          description: Bairro.
        cep:
          type: string
          description: CEP.
          example: 01310-100
        source:
          type: string
          description: Origem do contato.
          example: site
        owners:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários responsáveis.
        participants:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários participantes.
        observers:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários observadores.
        avatarUrl:
          type: string
          description: URL da foto.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
    Contact:
      type: object
      description: Contato. Além dos campos abaixo, a resposta traz os demais dados do cadastro.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        lifeCycle:
          type: string
          enum:
          - lead
          - prospect
          - customer
          description: Fase do contato.
        fullname:
          type: string
          description: Nome completo.
        name:
          type: string
          description: Primeiro nome.
        lastname:
          type: string
          description: Sobrenome.
        email:
          type: string
          description: E-mail. Ausente quando não informado.
        phones:
          type: array
          items:
            type: object
            properties:
              number:
                type: string
                description: Número, só dígitos.
                example: '11987654321'
              ddi:
                type: string
                description: Código do país, com `+`.
                example: '+55'
              typePhone:
                type: string
                description: Tipo do telefone.
                example: cellphone
        doc:
          type: string
          description: CPF ou CNPJ. Ausente quando não informado.
        type:
          type: string
          description: Tipo de pessoa.
          example: physical
        active:
          type: boolean
          description: '`false` quando o registro foi excluído.'
        owners:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        source:
          type: string
          description: Origem.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    OrganizationInput:
      type: object
      properties:
        fullname:
          type: string
          description: Nome da empresa.
          example: Cliente XYZ Ltda
        doc:
          type: string
          description: CNPJ.
        email:
          type: string
          description: E-mail.
        phones:
          type: array
          items:
            type: object
            properties:
              number:
                type: string
                description: Número, só dígitos.
                example: '11987654321'
              ddi:
                type: string
                description: Código do país, com `+`.
                example: '+55'
              typePhone:
                type: string
                description: Tipo do telefone.
                example: cellphone
          description: Telefones.
        state:
          type: string
          description: Estado.
        uf:
          type: string
          description: Sigla do estado.
          example: SP
        city:
          type: string
          description: Cidade.
        address:
          type: string
          description: Logradouro.
        addressNumber:
          type: string
          description: Número.
        complement:
          type: string
          description: Complemento.
        neighborhood:
          type: string
          description: Bairro.
        cep:
          type: string
          description: CEP.
          example: 01310-100
        source:
          type: string
          description: Origem.
        owners:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários responsáveis.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
    Organization:
      type: object
      description: Empresa. Além dos campos abaixo, a resposta traz os demais dados do cadastro.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        fullname:
          type: string
          description: Nome.
        doc:
          type: string
          description: CNPJ. Ausente quando não informado.
        email:
          type: string
          description: E-mail. Ausente quando não informado.
        phones:
          type: array
          items:
            type: object
            properties:
              number:
                type: string
                description: Número, só dígitos.
                example: '11987654321'
              ddi:
                type: string
                description: Código do país, com `+`.
                example: '+55'
              typePhone:
                type: string
                description: Tipo do telefone.
                example: cellphone
        owners:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        contacts:
          type: array
          items:
            type: object
          description: Contatos vinculados.
        source:
          type: string
          description: Origem.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        active:
          type: boolean
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    Pipeline:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        title:
          type: string
          description: Nome do pipeline.
          example: Comercial
        active:
          type: boolean
        defaultView:
          type: boolean
          description: Pipeline padrão da conta.
        stages:
          type: array
          items:
            $ref: '#/components/schemas/Stage'
          description: Etapas. Só em `GET /api/pipelines/{id}`.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    Stage:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome da etapa.
          example: Prospecção
        position:
          type: integer
          description: Ordem no pipeline.
        pipeline:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do pipeline.
        probability:
          type: number
          description: Probabilidade de ganho, em %.
          example: 10
        active:
          type: boolean
        rottingEnabled:
          type: boolean
          description: Alerta de oportunidade parada ligado.
        rottingDays:
          type: integer
          nullable: true
          description: Dias até a oportunidade ser considerada parada.
    Staff:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        fullname:
          type: string
          description: Nome completo.
        name:
          type: string
          description: Primeiro nome.
        lastname:
          type: string
          description: Sobrenome.
        email:
          type: string
          description: E-mail.
        active:
          type: boolean
        type:
          type: string
          description: '`user` para pessoas; `service_account` para credenciais de integração.'
        role:
          description: 'Perfil de acesso: objeto na listagem, ID em `GET /api/staffs/{id}`.'
        roleName:
          type: string
          description: Nome do perfil de acesso.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    CustomField:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID a enviar em `customFields[].customField`.
        name:
          type: string
          description: Nome exibido.
          example: Segmento
        slug:
          type: string
          description: Chave a enviar em `customFields[].k`.
          example: segmento
        feature:
          type: string
          description: Recurso a que o campo pertence.
          example: deal
        fieldRef:
          type: string
          description: Campo do registro, para campos de sistema.
          example: title
        system:
          type: boolean
          description: Campo de sistema.
        active:
          type: boolean
        fieldTypeSlug:
          type: string
          description: Tipo do campo.
        values:
          type: array
          items:
            type: object
          description: Opções, em campos de lista.
        settings:
          type: object
          properties:
            required:
              type: boolean
              description: Obrigatório.
            unique:
              type: boolean
              description: Não aceita valor repetido.
    TicketInput:
      type: object
      properties:
        subject:
          type: string
          description: Assunto do ticket.
          example: Erro ao emitir boleto
        description:
          type: string
          description: Descrição do pedido. Aceita HTML.
        priority:
          type: string
          enum:
          - low
          - medium
          - high
          - urgent
          description: Prioridade.
        status:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID da situação (de `GET /api/tickets/status`). Se omitido, a API usa a situação padrão da área.
        receiver:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do usuário responsável pelo atendimento do ticket.
        workspaceReceiver:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID da área de trabalho que recebe o ticket (de `GET /api/workspaces`).
        contact:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Contato vinculado.
        organization:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Empresa vinculada.
        deal:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Oportunidade vinculada.
        treatment:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Atendimento vinculado.
        product:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Produto vinculado.
        tags:
          type: array
          items:
            type: string
          description: Tags livres.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
        active:
          type: boolean
          description: 'Padrão: `true`.'
    TicketCreate:
      allOf:
      - $ref: '#/components/schemas/TicketInput'
      - type: object
        required:
        - subject
        - workspaceReceiver
    Ticket:
      type: object
      description: Ticket. Além dos campos abaixo, a resposta traz os demais dados do registro.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        code:
          type: integer
          description: Número sequencial do ticket na conta.
        subject:
          type: string
          description: Assunto.
        description:
          type: string
          description: Descrição.
        priority:
          type: string
          enum:
          - low
          - medium
          - high
          - urgent
          description: Prioridade.
        status:
          type: object
          description: Situação atual.
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
              description: Nome.
            slug:
              type: string
              description: Chave.
            category:
              type: string
              description: Categoria.
              example: waiting
        statusChangedAt:
          type: string
          format: date-time
          description: Última troca de situação.
          example: '2026-05-15T14:00:00Z'
        sender:
          description: Quem abriu o ticket.
          allOf:
          - $ref: '#/components/schemas/StaffSummary'
        receiver:
          description: Responsável pelo atendimento. Ausente até alguém assumir.
          allOf:
          - $ref: '#/components/schemas/StaffSummary'
        workspaceReceiver:
          type: object
          description: Área de trabalho que recebeu o ticket.
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
              description: Nome.
        contact:
          $ref: '#/components/schemas/ContactSummary'
        participants:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        observers:
          type: array
          items:
            $ref: '#/components/schemas/StaffSummary'
        labels:
          type: array
          items:
            type: object
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        startAt:
          type: string
          format: date-time
          description: Quando o atendimento foi iniciado.
          example: '2026-05-15T14:00:00Z'
        finishedAt:
          type: string
          format: date-time
          description: Quando foi finalizado. Ausente enquanto aberto.
          example: '2026-05-15T14:00:00Z'
        active:
          type: boolean
          description: '`false` quando excluído.'
        createdAt:
          type: string
          format: date-time
          description: Abertura.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    TicketMessage:
      type: object
      description: Mensagem de um ticket. Além dos campos abaixo, a resposta traz os demais dados da mensagem.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        ticket:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do ticket.
        type:
          type: string
          description: Tipo da mensagem.
          example: text
        content:
          type: string
          description: Texto.
        internal:
          type: boolean
          description: '`true` em nota interna, que o contato não vê.'
        source:
          type: string
          description: 'Quem escreveu: `staff` para usuários.'
          example: staff
        owner:
          description: Autor.
          allOf:
          - $ref: '#/components/schemas/StaffSummary'
        timestamp:
          type: integer
          description: Momento do envio, em milissegundos.
        createdAt:
          type: string
          format: date-time
          description: Envio.
          example: '2026-05-15T14:00:00Z'
    TicketStatus:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome da situação.
          example: Aguardando responsável
        slug:
          type: string
          description: Chave.
        category:
          type: string
          description: 'Categoria da situação: agrupa as situações de espera, andamento e conclusão.'
          example: waiting
        default:
          type: boolean
          description: Situação inicial dos tickets da área.
        active:
          type: boolean
        nextStatusAllowed:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: Situações para as quais o ticket pode ir a partir desta.
        workspace:
          type: object
          description: Área de trabalho dona da situação.
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            name:
              type: string
    TreatmentInput:
      type: object
      properties:
        subject:
          type: string
          description: Assunto do atendimento.
          example: Ligação de acompanhamento
        description:
          type: string
          description: O que foi tratado.
        type:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do tipo de atendimento (de `GET /api/treatments/types`).
        contact:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Contato atendido.
        organization:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Empresa vinculada.
        deal:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Oportunidade vinculada.
        product:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Produto vinculado.
        workspace:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID da área de trabalho. Se omitido, a API usa a da credencial.
        returnTreatment:
          type: boolean
          description: Marca que o atendimento pede retorno.
        nextDateOfAttempt:
          type: string
          format: date-time
          description: Quando tentar de novo, se `returnTreatment` for `true`.
          example: '2026-05-15T14:00:00Z'
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
    Treatment:
      type: object
      description: 'Atendimento: o registro de um contato feito com o cliente. Além dos campos abaixo, a resposta traz os demais dados do registro.'
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        code:
          type: integer
          description: Número sequencial do atendimento na conta.
        subject:
          type: string
          description: Assunto.
        description:
          type: string
          description: O que foi tratado.
        type:
          description: Tipo de atendimento.
          allOf:
          - $ref: '#/components/schemas/TreatmentType'
        staff:
          description: Usuário que registrou.
          allOf:
          - $ref: '#/components/schemas/StaffSummary'
        done:
          type: boolean
          description: Atendimento concluído.
        contact:
          $ref: '#/components/schemas/ContactSummary'
        organization:
          description: Empresa vinculada. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        deal:
          description: Oportunidade vinculada. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        returnTreatment:
          type: boolean
        nextDateOfAttempt:
          type: string
          format: date-time
          description: Próxima tentativa.
          example: '2026-05-15T14:00:00Z'
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        active:
          type: boolean
          description: '`false` quando excluído.'
        createdAt:
          type: string
          format: date-time
          description: Registro.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    TreatmentType:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome.
          example: Ligação
        slug:
          type: string
          description: Chave.
        color:
          type: string
          description: Cor.
          example: '#8955ef'
        icon:
          type: string
          description: Ícone.
        active:
          type: boolean
        default:
          type: boolean
          description: Tipo padrão da conta.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    Journey:
      type: object
      description: 'Jornada do cliente: o acompanhamento depois da venda. Além dos campos abaixo, a resposta traz os demais dados do registro.'
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        title:
          type: string
          description: Título.
        status:
          type: string
          enum:
          - active
          - success
          - churn
          description: Situação da jornada.
        journeyFlow:
          description: Fluxo de jornada. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        phase:
          description: Fase atual. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        staff:
          description: Responsável. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        contact:
          description: Contato. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        organization:
          description: Empresa. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        sourceDeal:
          description: Oportunidade que originou a jornada. Vem como objeto resumido nas respostas; ao gravar, envie o ID em texto.
        healthScore:
          type: number
          description: Health score, de 0 a 100.
        healthScoreMode:
          type: string
          enum:
          - manual
          - auto
          description: Como o health score é definido.
        mrr:
          type: number
          description: Receita recorrente mensal.
        contractValue:
          type: number
          description: Valor do contrato.
        contractStartDate:
          type: string
          format: date-time
          description: Início do contrato.
          example: '2026-05-15T14:00:00Z'
        contractEndDate:
          type: string
          format: date-time
          description: Fim do contrato.
          example: '2026-05-15T14:00:00Z'
        renewalDate:
          type: string
          format: date-time
          description: Data de renovação.
          example: '2026-05-15T14:00:00Z'
        renewalStatus:
          type: string
          enum:
          - active
          - approaching
          - at_risk
          - renewed
          - churned
          description: Situação da renovação.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
        active:
          type: boolean
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    LostReason:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID a enviar em `lostReason` ao fechar uma oportunidade como perdida.
        reason:
          type: string
          description: Motivo.
          example: Preço
        pipeline:
          type: object
          properties:
            _id:
              type: string
              pattern: ^[a-fA-F0-9]{24}$
              example: 507f1f77bcf86cd799439011
            title:
              type: string
              description: Nome do pipeline.
        active:
          type: boolean
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    MeetingType:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome.
          example: Apresentação
        slug:
          type: string
          description: Chave.
        default:
          type: boolean
          description: Tipo padrão.
        active:
          type: boolean
    MeetingRoom:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome.
          example: Presencial
        slug:
          type: string
          description: Chave.
        provider:
          type: string
          enum:
          - google
          - office365
          - caldav
          - personal
          description: De onde a sala vem.
        default:
          type: boolean
          description: Sala padrão.
        active:
          type: boolean
    Team:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome da equipe.
          example: Comercial SP
        slug:
          type: string
          description: Chave.
        members:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários da equipe.
        active:
          type: boolean
    Workspace:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome da área de trabalho.
          example: Geral
        slug:
          type: string
          description: Chave.
        active:
          type: boolean
    WebhookTriggerGroup:
      type: object
      properties:
        _id:
          type: string
          description: Nome do grupo de eventos.
          example: Oportunidades
        triggers:
          type: array
          items:
            type: object
            properties:
              key:
                type: string
                description: Nome do evento, como chega no webhook.
                example: deal.won
              label:
                type: string
                description: Descrição.
                example: Oportunidade ganha
              group:
                type: string
                description: Recurso do evento.
                example: deal
              groupName:
                type: string
                description: Nome do grupo.
          description: Eventos do grupo.
    ProductInput:
      type: object
      properties:
        name:
          type: string
          description: Nome do produto.
          example: Plano Enterprise
        code:
          type: string
          description: Código interno (SKU).
          example: ENT-001
        description:
          type: string
          description: Descrição.
        family:
          type: string
          description: Família do produto.
        type:
          type: string
          enum:
          - ONE_TIME
          - RECURRING
          description: '`ONE_TIME`: venda única. `RECURRING`: assinatura. Padrão: `ONE_TIME`.'
        category:
          type: string
          enum:
          - CATALOG
          - PORTFOLIO
          description: '`CATALOG`: produto com preço, vendido em oportunidades. `PORTFOLIO`: produto de carteira, sem preço. Padrão: `CATALOG`.'
        price:
          type: number
          description: Preço base.
          example: 1200
        unitOfMeasure:
          type: string
          description: Unidade de medida.
        recurring:
          type: object
          description: Para `RECURRING`.
          properties:
            frequency:
              type: string
              description: Periodicidade.
            interval:
              type: integer
              description: A cada quantos períodos.
        inventoryControl:
          type: object
          description: Controle de estoque.
          properties:
            enabled:
              type: boolean
            allowNegative:
              type: boolean
            lowStockThreshold:
              type: number
            trackQuantity:
              type: boolean
        stockQuantity:
          type: number
          description: Quantidade em estoque.
        attributes:
          type: object
          description: Atributos livres.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
        active:
          type: boolean
          description: 'Padrão: `true`.'
    Product:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome do produto.
          example: Plano Enterprise
        code:
          type: string
          description: Código interno (SKU).
          example: ENT-001
        description:
          type: string
          description: Descrição.
        family:
          type: string
          description: Família do produto.
        type:
          type: string
          enum:
          - ONE_TIME
          - RECURRING
          description: '`ONE_TIME`: venda única. `RECURRING`: assinatura. Padrão: `ONE_TIME`.'
        category:
          type: string
          enum:
          - CATALOG
          - PORTFOLIO
          description: '`CATALOG`: produto com preço, vendido em oportunidades. `PORTFOLIO`: produto de carteira, sem preço. Padrão: `CATALOG`.'
        price:
          type: number
          description: Preço base.
          example: 1200
        unitOfMeasure:
          type: string
          description: Unidade de medida.
        recurring:
          type: object
          description: Para `RECURRING`.
          properties:
            frequency:
              type: string
              description: Periodicidade.
            interval:
              type: integer
              description: A cada quantos períodos.
        inventoryControl:
          type: object
          description: Controle de estoque.
          properties:
            enabled:
              type: boolean
            allowNegative:
              type: boolean
            lowStockThreshold:
              type: number
            trackQuantity:
              type: boolean
        stockQuantity:
          type: number
          description: Quantidade em estoque.
        attributes:
          type: object
          description: Atributos livres.
        customFields:
          type: array
          items:
            $ref: '#/components/schemas/CustomFieldValue'
          description: Campos personalizados.
        active:
          type: boolean
          description: 'Padrão: `true`.'
        createdBy:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Quem criou.
        updatedBy:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Quem alterou por último.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    PricebookEntry:
      type: object
      description: Preço de um produto numa tabela.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        product:
          description: 'Produto: ID ao gravar; objeto ao listar os produtos da tabela.'
        pricebook:
          description: 'Tabela de preço: ID, ou objeto ao listar as tabelas de um produto.'
        price:
          type: number
          description: Preço nesta tabela.
          example: 1100
        currency:
          type: string
          description: Moeda.
          example: BRL
        active:
          type: boolean
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    PricebookInput:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          description: Nome da tabela.
          example: Parceiros
        description:
          type: string
          description: Descrição.
        isStandard:
          type: boolean
          description: 'Tabela padrão da conta: usada quando o item não informa outra.'
        active:
          type: boolean
          description: 'Padrão: `true`.'
    Pricebook:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        name:
          type: string
          description: Nome da tabela.
          example: Parceiros
        description:
          type: string
          description: Descrição.
        isStandard:
          type: boolean
          description: 'Tabela padrão da conta: usada quando o item não informa outra.'
        active:
          type: boolean
          description: 'Padrão: `true`.'
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    ProductItem:
      type: object
      description: Produto vinculado a um registro, com quantidade e preço.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        product:
          description: 'Produto: ID na gravação, objeto nas listagens.'
        pricebookEntry:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Preço de tabela usado.
        category:
          type: string
          enum:
          - CATALOG
          - PORTFOLIO
          description: '`CATALOG`: produto com preço, vendido em oportunidades. `PORTFOLIO`: produto de carteira, sem preço. Padrão: `CATALOG`.'
        quantity:
          type: number
          description: Quantidade.
          example: 2
        unitPrice:
          type: number
          description: Preço unitário.
          example: 1200
        discount:
          type: number
          description: Desconto em valor.
          example: 100
        totalPrice:
          type: number
          description: '`unitPrice × quantity − discount`.'
          example: 2300
        deal:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: Oportunidade, quando o item é de uma.
        active:
          type: boolean
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    Rule:
      type: object
      description: Uma condição sobre um campo. Um grupo aninhado usa `condition` e `rules` no lugar de `fieldId` e `operator`.
      properties:
        fieldId:
          type: string
          description: 'Campo testado: o `slug` de um campo de `GET /api/resource-keywords`.'
          example: deal.amount
        resourceKeywordId:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do mesmo campo (`_id` em `GET /api/resource-keywords`). Opcional; envie quando a conta tiver campos personalizados de
            nome parecido, para não haver ambiguidade.
        operator:
          type: string
          enum:
          - equals
          - not_equal
          - less
          - less_or_equals
          - greater
          - greater_or_equals
          - between
          - contains
          - not_contains
          - starts_with
          - ends_with
          - regex
          - exists
          - not_exists
          - true_value
          - false_value
          - in
          - not_in
          - contains_all
          - current_day
          - current_week
          - current_month
          - current_year
          - before_day
          - before_week
          - before_month
          - before_year
          - last_n_days
          - next_n_days
          - next_n_business_days
          - next_month
          - older_than_days
          - older_than_hours
          - newer_than_days
          - before_date
          - after_date
          - date_between
          - month_equals
          description: Operador. Os de data relativa (`current_month`, `last_n_days`...) e os de existência (`exists`, `true_value`...) não usam
            `value`, ou usam `value` como o número de dias.
        value:
          description: 'Valor comparado: texto, número, booleano ou lista (em `in`, `not_in`, `contains_all`).'
        valueMin:
          description: Limite inferior, em `between` e `date_between`.
        valueMax:
          description: Limite superior, em `between` e `date_between`.
        condition:
          type: string
          enum:
          - AND
          - OR
          description: 'Só em grupo aninhado: como combinar as `rules` do grupo.'
        rules:
          type: array
          items:
            type: object
          description: 'Só em grupo aninhado: as condições do grupo, no mesmo formato.'
    Ruleset:
      type: object
      description: Conjunto de regras de filtro. É o mesmo formato em dashboards, widgets e predefinições de filtro.
      required:
      - condition
      - rules
      properties:
        condition:
          type: string
          enum:
          - AND
          - OR
          description: '`AND`: todas as regras precisam valer. `OR`: basta uma.'
        rules:
          type: array
          items:
            $ref: '#/components/schemas/Rule'
          description: Condições e grupos aninhados. Lista vazia não filtra nada.
    ResourceKeyword:
      type: object
      description: Campo que pode ser usado em regras de filtro, eixos de gráfico e colunas de resultado.
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do campo.
        slug:
          type: string
          description: Identificador a usar em `fieldId` e em `resourceKeywordSlug`.
          example: deal.amount
        modelRef:
          type: string
          description: Recurso a que o campo pertence.
          example: deal
        type:
          type: string
          enum:
          - string
          - number
          - monetary
          - boolean
          - date
          - days
          - select
          - month_select
          - date_select
          description: 'Tipo do campo: define quais operadores fazem sentido.'
        fieldRef:
          type: string
          description: Caminho interno do campo.
          example: dealObj.amount
        settings:
          type: object
          properties:
            label:
              type: string
              description: Nome exibido.
              example: Valor
        module:
          type: array
          items:
            type: string
          description: Onde o campo pode ser usado, como `dashboard` e `rule`.
        active:
          type: boolean
    DashboardInput:
      type: object
      properties:
        title:
          type: string
          description: Título do dashboard.
          example: Comercial
        forModel:
          type: string
          enum:
          - Role
          - Staff
          description: 'A quem o dashboard é oferecido: `Role` usa a lista `roles`; `Staff` usa a lista `staffs`. Padrão: `Role`.'
        roles:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos perfis de acesso que veem o dashboard.
        staffs:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários que veem o dashboard.
        position:
          type: number
          description: Ordem na lista.
        globalRuleset:
          description: Filtro aplicado a todos os widgets do dashboard.
          allOf:
          - $ref: '#/components/schemas/Ruleset'
    Dashboard:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        title:
          type: string
          description: Título do dashboard.
          example: Comercial
        forModel:
          type: string
          enum:
          - Role
          - Staff
          description: 'A quem o dashboard é oferecido: `Role` usa a lista `roles`; `Staff` usa a lista `staffs`. Padrão: `Role`.'
        roles:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos perfis de acesso que veem o dashboard.
        staffs:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: IDs dos usuários que veem o dashboard.
        position:
          type: number
          description: Ordem na lista.
        globalRuleset:
          description: Filtro aplicado a todos os widgets do dashboard.
          allOf:
          - $ref: '#/components/schemas/Ruleset'
        workspace:
          type: string
          nullable: true
          description: Área de trabalho do dashboard.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    WidgetContent:
      type: object
      description: O que o widget calcula.
      required:
      - datasource
      properties:
        datasource:
          type: string
          enum:
          - contact
          - organization
          - deal
          - task
          - ticket
          - meeting
          - treatment
          - journey
          description: Recurso de onde vêm os registros.
        filter:
          type: object
          description: Quais registros entram.
          properties:
            ruleset:
              $ref: '#/components/schemas/Ruleset'
        percentage:
          type: object
          description: 'Em `PERCENTAGE`: a regra que define a parte comparada com o total filtrado.'
          properties:
            ruleset:
              $ref: '#/components/schemas/Ruleset'
        period:
          type: string
          enum:
          - DAY
          - WEEK
          - MONTH
          - TWO_MONTHS
          - QUARTER
          - SEMESTER
          - YEAR
          description: Agrupamento de datas no eixo X.
        axisX:
          description: 'Eixo X: o campo que separa os pontos do gráfico.'
          allOf:
          - type: object
            properties:
              resourceKeywordSlug:
                type: string
                description: '`slug` do campo (de `GET /api/resource-keywords`).'
                example: deal.stage_name
              aggregation:
                type: string
                enum:
                - GROUP
                - INTERVAL
                - SUM
                - AVG
                - MIN
                - MAX
                description: '`GROUP`: um ponto por valor do campo. `INTERVAL`: agrupa datas pelo `period`. `SUM`, `AVG`, `MIN`, `MAX`: calcula
                  sobre um campo numérico.'
              interval:
                type: number
                description: 'Tamanho do intervalo, em campos numéricos. Padrão: 0.'
              mask:
                type: string
                description: Máscara de exibição.
        axisY:
          description: 'Eixo Y: sem ele, o gráfico conta registros; com `aggregation: SUM` e um campo numérico, soma o campo em cada ponto.'
          allOf:
          - type: object
            properties:
              resourceKeywordSlug:
                type: string
                description: '`slug` do campo (de `GET /api/resource-keywords`).'
                example: deal.stage_name
              aggregation:
                type: string
                enum:
                - GROUP
                - INTERVAL
                - SUM
                - AVG
                - MIN
                - MAX
                description: '`GROUP`: um ponto por valor do campo. `INTERVAL`: agrupa datas pelo `period`. `SUM`, `AVG`, `MIN`, `MAX`: calcula
                  sobre um campo numérico.'
              interval:
                type: number
                description: 'Tamanho do intervalo, em campos numéricos. Padrão: 0.'
              mask:
                type: string
                description: Máscara de exibição.
        seriesBy:
          description: Separa o gráfico em séries, uma por valor do campo.
          allOf:
          - type: object
            properties:
              resourceKeywordSlug:
                type: string
                description: '`slug` do campo (de `GET /api/resource-keywords`).'
                example: deal.stage_name
              aggregation:
                type: string
                enum:
                - GROUP
                - INTERVAL
                - SUM
                - AVG
                - MIN
                - MAX
                description: '`GROUP`: um ponto por valor do campo. `INTERVAL`: agrupa datas pelo `period`. `SUM`, `AVG`, `MIN`, `MAX`: calcula
                  sobre um campo numérico.'
              interval:
                type: number
                description: 'Tamanho do intervalo, em campos numéricos. Padrão: 0.'
              mask:
                type: string
                description: Máscara de exibição.
        countBy:
          description: 'Em `NUMBER`: com `aggregation: SUM` e um campo numérico, o número é a soma do campo; sem ele, é a contagem de registros.'
          allOf:
          - type: object
            properties:
              resourceKeywordSlug:
                type: string
                description: '`slug` do campo (de `GET /api/resource-keywords`).'
                example: deal.stage_name
              aggregation:
                type: string
                enum:
                - GROUP
                - INTERVAL
                - SUM
                - AVG
                - MIN
                - MAX
                description: '`GROUP`: um ponto por valor do campo. `INTERVAL`: agrupa datas pelo `period`. `SUM`, `AVG`, `MIN`, `MAX`: calcula
                  sobre um campo numérico.'
              interval:
                type: number
                description: 'Tamanho do intervalo, em campos numéricos. Padrão: 0.'
              mask:
                type: string
                description: Máscara de exibição.
        refreshIntervalMinutes:
          type: integer
          nullable: true
          description: De quantos em quantos minutos o resultado é recalculado.
    WidgetInput:
      type: object
      properties:
        title:
          type: string
          description: Título do widget.
          example: Valor por etapa
        displayAs:
          type: string
          enum:
          - NUMBER
          - PERCENTAGE
          - CHART/BAR
          - CHART/LINE
          - CHART/AREA
          - CHART/PIE
          - CHART/DOUGHNUT
          - CHART/RADAR
          - CHART/POLAR_AREA
          - CHART/SCATTER
          - CHART/BUBBLE
          - CHART/MIXED
          description: Forma de exibição.
        content:
          $ref: '#/components/schemas/WidgetContent'
        sizeX:
          type: integer
          description: Largura, em colunas da grade.
        sizeY:
          type: integer
          description: Altura, em linhas da grade.
        row:
          type: integer
          description: Linha na grade, a partir de 0.
        col:
          type: integer
          description: Coluna na grade, a partir de 0.
        showTitle:
          type: boolean
          description: 'Mostra o título. Padrão: `true`.'
        bgColor:
          type: string
          description: Cor de fundo.
        reportfields:
          type: array
          items:
            type: object
            properties:
              slug:
                type: string
                description: '`slug` do campo.'
                example: deal.title
              label:
                type: string
                description: Título da coluna.
                example: Título
          description: Colunas da listagem de resultados do widget.
    Widget:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        dashboard:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do dashboard.
        title:
          type: string
          description: Título do widget.
          example: Valor por etapa
        displayAs:
          type: string
          enum:
          - NUMBER
          - PERCENTAGE
          - CHART/BAR
          - CHART/LINE
          - CHART/AREA
          - CHART/PIE
          - CHART/DOUGHNUT
          - CHART/RADAR
          - CHART/POLAR_AREA
          - CHART/SCATTER
          - CHART/BUBBLE
          - CHART/MIXED
          description: Forma de exibição.
        content:
          $ref: '#/components/schemas/WidgetContent'
        sizeX:
          type: integer
          description: Largura, em colunas da grade.
        sizeY:
          type: integer
          description: Altura, em linhas da grade.
        row:
          type: integer
          description: Linha na grade, a partir de 0.
        col:
          type: integer
          description: Coluna na grade, a partir de 0.
        showTitle:
          type: boolean
          description: 'Mostra o título. Padrão: `true`.'
        bgColor:
          type: string
          description: Cor de fundo.
        reportfields:
          type: array
          items:
            type: object
            properties:
              slug:
                type: string
                description: '`slug` do campo.'
                example: deal.title
              label:
                type: string
                description: Título da coluna.
                example: Título
          description: Colunas da listagem de resultados do widget.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    FilterPreset:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        dashboard:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
          description: ID do dashboard.
        name:
          type: string
          description: Nome da predefinição.
          example: Só ganhas
        ruleset:
          $ref: '#/components/schemas/Ruleset'
        position:
          type: number
          description: Ordem.
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
    AutomationEventTrigger:
      type: object
      required:
      - event
      properties:
        event:
          type: string
          enum:
          - created
          - updated
          - deleted
          - status_changed
          - stage_changed
          - assigned
          - completed
          - reopened
          - won
          - lost
          - lifecycle_changed
          - submitted
          description: Evento que dispara a automação. Os eventos de cada recurso estão em `GET /api/processautomations/entities` (`supportedEvents`).
        fieldConditions:
          type: array
          items:
            type: object
            required:
            - field
            - operator
            properties:
              field:
                type: string
                description: Campo do registro, como vem em `availableFields` de `GET /api/processautomations/entities`.
                example: amount
              operator:
                type: string
                enum:
                - equals
                - not_equals
                - contains
                - not_contains
                - starts_with
                - ends_with
                - greater_than
                - less_than
                - greater_or_equal
                - less_or_equal
                - is_empty
                - is_not_empty
                - in
                - not_in
                - changed
                - not_changed
                - changed_to
                - changed_from
                description: Operador. Os de mudança (`changed`, `changed_to`...) comparam o valor antes e depois do evento.
              value:
                description: Valor comparado.
          description: Condições sobre os campos do registro. Todas precisam valer.
    AutomationAction:
      type: object
      required:
      - actionType
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        actionType:
          type: string
          enum:
          - create
          - update
          - notify
          - assign
          - webhook
          description: O que a ação faz.
        targetEntity:
          type: string
          enum:
          - Contact
          - Deal
          - Organization
          - Task
          - Meeting
          - Ticket
          - Email
          - SMS
          - WhatsApp
          - Push
          - Observation
          description: 'Em `create` e `update`: o recurso criado ou alterado.'
        order:
          type: integer
          description: Ordem de execução, a partir de 1.
        active:
          type: boolean
          description: 'Padrão: `true`.'
        condition:
          type: object
          description: Condição para a ação rodar.
          properties:
            enabled:
              type: boolean
            fieldConditions:
              type: array
              items:
                type: object
                required:
                - field
                - operator
                properties:
                  field:
                    type: string
                    description: Campo do registro, como vem em `availableFields` de `GET /api/processautomations/entities`.
                    example: amount
                  operator:
                    type: string
                    enum:
                    - equals
                    - not_equals
                    - contains
                    - not_contains
                    - starts_with
                    - ends_with
                    - greater_than
                    - less_than
                    - greater_or_equal
                    - less_or_equal
                    - is_empty
                    - is_not_empty
                    - in
                    - not_in
                    - changed
                    - not_changed
                    - changed_to
                    - changed_from
                    description: Operador. Os de mudança (`changed`, `changed_to`...) comparam o valor antes e depois do evento.
                  value:
                    description: Valor comparado.
        config:
          type: object
          description: Configuração da ação. Use a parte que corresponde ao `actionType`.
          properties:
            createFields:
              type: object
              description: 'Em `create`: campos do registro criado. Aceita variáveis entre chaves, como `{deal.title}`.'
              example:
                title: 'Follow-up: {deal.title}'
                taskTypeSlug: follow-up
                dueDateOffset: 1
                ownerSource: contact_owner
            updateFields:
              type: object
              description: 'Em `update`: campos alterados.'
            notifyConfig:
              type: object
              description: Em `notify`.
              properties:
                channel:
                  type: string
                  enum:
                  - email
                  - sms
                  - whatsapp
                  - push
                  - system
                  - slack
                  description: Canal.
                recipients:
                  type: array
                  items:
                    type: string
                    enum:
                    - owner
                    - creator
                    - assigned
                    - contact
                    - workspace
                    - workspace_fixed
                    - all_agents
                    - staff
                    - requester_and_ccs
                    - participants
                    - custom
                  description: Quem recebe.
                staffIds:
                  type: array
                  items:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  description: 'Com `staff` em `recipients`: os usuários.'
                customRecipients:
                  type: array
                  items: {}
                  description: 'Com `custom` em `recipients`: os destinatários.'
                subject:
                  type: string
                  description: Assunto. Aceita variáveis.
                content:
                  type: string
                  description: Texto. Aceita variáveis, como `{contact.fullname}`.
            assignConfig:
              type: object
              description: Em `assign`.
              properties:
                assignType:
                  type: string
                  enum:
                  - specific
                  - round_robin
                  - least_busy
                  description: Como escolher o responsável.
                staffIds:
                  type: array
                  items:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  description: Usuários candidatos.
                workspaceIds:
                  type: array
                  items:
                    type: string
                    pattern: ^[a-fA-F0-9]{24}$
                    example: 507f1f77bcf86cd799439011
                  description: Áreas de trabalho candidatas.
            webhookConfig:
              type: object
              description: 'Em `webhook`: chamada HTTP para o seu sistema.'
              properties:
                url:
                  type: string
                  description: Endereço chamado.
                method:
                  type: string
                  enum:
                  - GET
                  - POST
                  - PUT
                  - PATCH
                  - DELETE
                  default: POST
                headers:
                  type: object
                  description: Cabeçalhos.
                payload:
                  type: object
                  description: Corpo enviado. Aceita variáveis.
                retryOnFail:
                  type: boolean
                  default: true
                maxRetries:
                  type: integer
                  default: 3
    AutomationInput:
      type: object
      properties:
        title:
          type: string
          description: Nome da automação.
          example: Alerta de contato sem interação
        description:
          type: string
          description: Descrição.
        sourceEntity:
          type: string
          enum:
          - Contact
          - Deal
          - Organization
          - Task
          - Meeting
          - Ticket
          - Form
          description: Recurso sobre o qual a automação age.
        triggerType:
          type: string
          enum:
          - filter
          - event
          - hybrid
          description: '`filter`: roda no horário agendado sobre os registros que casam com `filter.ruleset`. `event`: roda quando um evento de
            `eventTriggers` acontece. `hybrid`: evento e filtro. Padrão: `filter`.'
        eventTriggers:
          type: array
          items:
            $ref: '#/components/schemas/AutomationEventTrigger'
          description: Eventos que disparam a automação, em `event` e `hybrid`.
        triggerConfig:
          type: object
          description: Agenda, em `filter`.
          properties:
            type:
              type: string
              enum:
              - scheduled
              - onCreate
              - onUpdate
              - onFieldChange
              - onStageChange
              - manual
              default: scheduled
            recurrence:
              type: string
              enum:
              - once
              - hourly
              - daily
              - weekly
              - monthly
              - custom
              default: daily
            schedule:
              type: object
              properties:
                time:
                  type: string
                  description: Horário, `HH:mm`.
                  example: 09:00
                timezone:
                  type: string
                  description: Fuso horário.
                  example: America/Sao_Paulo
                weekDays:
                  type: array
                  items:
                    type: integer
                    minimum: 0
                    maximum: 6
                  description: Dias da semana, de 0 (domingo) a 6.
                dayOfMonth:
                  type: integer
                  minimum: 1
                  maximum: 31
                interval:
                  type: integer
                  minimum: 1
                  maximum: 24
        filter:
          type: object
          description: Quais registros entram.
          properties:
            ruleset:
              $ref: '#/components/schemas/Ruleset'
        automationActions:
          type: array
          items:
            $ref: '#/components/schemas/AutomationAction'
          description: Ações, executadas na ordem de `order`.
        lifeCycles:
          type: array
          items:
            type: string
            enum:
            - lead
            - prospect
            - customer
          description: 'Em automações de contato: restringe às fases.'
        limit:
          type: integer
          description: 'Máximo de registros processados por execução. Padrão: 50.'
        reentryWindowDays:
          type: integer
          nullable: true
          minimum: 1
          maximum: 365
          description: Depois de quantos dias o mesmo registro pode entrar de novo.
        maxContactsPerCycle:
          type: integer
          nullable: true
          minimum: 1
          description: Máximo de registros por ciclo.
        workspaces:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: Áreas de trabalho a que a automação se aplica.
        staffs:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: Usuários a que a automação se aplica.
        status:
          type: string
          enum:
          - WAITING
          - FREEZE
          - RECURRENCE
          - FINISHED
          description: '`WAITING`: ativa, aguardando a próxima execução. `FREEZE`: pausada. `RECURRENCE`: ativa e recorrente. `FINISHED`: concluída.'
    Automation:
      type: object
      properties:
        _id:
          type: string
          pattern: ^[a-fA-F0-9]{24}$
          example: 507f1f77bcf86cd799439011
        title:
          type: string
          description: Nome da automação.
          example: Alerta de contato sem interação
        description:
          type: string
          description: Descrição.
        sourceEntity:
          type: string
          enum:
          - Contact
          - Deal
          - Organization
          - Task
          - Meeting
          - Ticket
          - Form
          description: Recurso sobre o qual a automação age.
        triggerType:
          type: string
          enum:
          - filter
          - event
          - hybrid
          description: '`filter`: roda no horário agendado sobre os registros que casam com `filter.ruleset`. `event`: roda quando um evento de
            `eventTriggers` acontece. `hybrid`: evento e filtro. Padrão: `filter`.'
        eventTriggers:
          type: array
          items:
            $ref: '#/components/schemas/AutomationEventTrigger'
          description: Eventos que disparam a automação, em `event` e `hybrid`.
        triggerConfig:
          type: object
          description: Agenda, em `filter`.
          properties:
            type:
              type: string
              enum:
              - scheduled
              - onCreate
              - onUpdate
              - onFieldChange
              - onStageChange
              - manual
              default: scheduled
            recurrence:
              type: string
              enum:
              - once
              - hourly
              - daily
              - weekly
              - monthly
              - custom
              default: daily
            schedule:
              type: object
              properties:
                time:
                  type: string
                  description: Horário, `HH:mm`.
                  example: 09:00
                timezone:
                  type: string
                  description: Fuso horário.
                  example: America/Sao_Paulo
                weekDays:
                  type: array
                  items:
                    type: integer
                    minimum: 0
                    maximum: 6
                  description: Dias da semana, de 0 (domingo) a 6.
                dayOfMonth:
                  type: integer
                  minimum: 1
                  maximum: 31
                interval:
                  type: integer
                  minimum: 1
                  maximum: 24
        filter:
          type: object
          description: Quais registros entram.
          properties:
            ruleset:
              $ref: '#/components/schemas/Ruleset'
        automationActions:
          type: array
          items:
            $ref: '#/components/schemas/AutomationAction'
          description: Ações, executadas na ordem de `order`.
        lifeCycles:
          type: array
          items:
            type: string
            enum:
            - lead
            - prospect
            - customer
          description: 'Em automações de contato: restringe às fases.'
        limit:
          type: integer
          description: 'Máximo de registros processados por execução. Padrão: 50.'
        reentryWindowDays:
          type: integer
          nullable: true
          minimum: 1
          maximum: 365
          description: Depois de quantos dias o mesmo registro pode entrar de novo.
        maxContactsPerCycle:
          type: integer
          nullable: true
          minimum: 1
          description: Máximo de registros por ciclo.
        workspaces:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: Áreas de trabalho a que a automação se aplica.
        staffs:
          type: array
          items:
            type: string
            pattern: ^[a-fA-F0-9]{24}$
            example: 507f1f77bcf86cd799439011
          description: Usuários a que a automação se aplica.
        status:
          type: string
          enum:
          - WAITING
          - FREEZE
          - RECURRENCE
          - FINISHED
          description: '`WAITING`: ativa, aguardando a próxima execução. `FREEZE`: pausada. `RECURRENCE`: ativa e recorrente. `FINISHED`: concluída.'
        active:
          type: boolean
          description: '`false` quando excluída.'
        executionCount:
          type: integer
          description: Quantas vezes já executou.
        lastExecutionAt:
          type: string
          format: date-time
          description: Última execução.
          example: '2026-05-15T14:00:00Z'
        createdAt:
          type: string
          format: date-time
          description: Criação.
          example: '2026-05-15T14:00:00Z'
        updatedAt:
          type: string
          format: date-time
          description: Última alteração.
          example: '2026-05-15T14:00:00Z'
