{
  "openapi": "3.0.3",
  "info": {
    "title": "Atlas API",
    "version": "1.0.0",
    "description": "API REST do Atlas, o CRM da Skill-Hub.\n\nEsta especificação descreve **exatamente** o que a API faz hoje em produção.\nOnde existe uma inconsistência entre recursos, ela está documentada em vez de\nescondida.\n\n## Autenticação\n\nTodo request precisa de dois cabeçalhos:\n\n```\nAuthorization: Bearer SEU_TOKEN\nAccept: application/json\n```\n\nO `Accept: application/json` **não é opcional**. Sem ele, um request sem token\nou com token inválido recebe um `302` para a tela de login em HTML, em vez de\num `401` em JSON.\n\nO token é gerado em Configurações → Tokens de API e é exibido uma única vez.\nSão 40 caracteres. Guarde no momento da criação.\n\n## Escopos do token\n\nCada token carrega um ou mais escopos, escolhidos na criação e **não editáveis**\ndepois. Para mudar o escopo, revogue o token e crie outro.\n\n- `read`: leitura. Todos os `GET`.\n- `write`: criar, alterar e apagar dados: contatos, conversas (status e\n  atribuição), faturas, propostas, agendamentos, produtos e webhooks.\n- `send`: disparos que saem para o cliente ou para terceiros e consomem\n  crédito: enviar mensagem (`POST /messages/send` e\n  `POST /conversations/{id}/messages`), enviar template\n  (`POST /wa-connector/send-template`), enviar e-mail por uma caixa\n  (`POST /emails/send`) e testar webhook (`POST /webhooks/{id}/test`).\n\n`send` é separado de propósito: um token só de leitura e escrita nunca dispara\nmensagem por acidente. `write` **não** implica `send`.\n\nTokens criados antes dos escopos têm acesso total (`*`) e continuam valendo\npara tudo.\n\nCada operação declara o escopo exigido em `x-scope`. Sem ele, a resposta é\n`403` com código de máquina:\n\n```json\n{ \"error\": \"Este token não tem o escopo 'send'. Crie um token com esse escopo em Configurações → Tokens de API.\", \"code\": \"insufficient_scope\", \"required_scope\": \"send\" }\n```\n\n## Negócios: os dois interruptores\n\nMover, ganhar ou perder um negócio pela tela dispara automações (WhatsApp,\ntarefa, e-mail), grava métrica de conversão e envia eventos para a conta de\nanúncios do cliente (Meta CAPI). Pela API, **nada disso acontece por padrão**.\n`POST /deals`, `/deals/{id}/move`, `/win` e `/lose` aceitam dois interruptores,\nambos `false` se omitidos:\n\n| Parâmetro | Liga | Exige |\n|---|---|---|\n| `trigger_automations` | automações da etapa (WhatsApp, tarefas, e-mail, notificações) e a métrica de conversão | escopo `send` |\n| `fire_pixel_events` | eventos da Meta Conversions API (`Purchase` no ganho, eventos mapeados por etapa) | escopo `send` |\n\nSem os interruptores, um token `write` move o card, registra a atividade e\nnada sai da conta. Com qualquer um ligado, a chamada envia mensagem ou grava\nconversão em terceiro, e por isso exige **também** o escopo `send`; sem ele,\n`403 insufficient_scope` com `required_scope: send`. Uma sincronização de 200\nnegócios sem as flags não dispara 200 automações nem 200 conversões falsas.\n\n`PATCH /deals/{id}` **nunca** muda etapa nem status: `stage_id`, `status`,\n`pipeline_id` e afins respondem `422` mandando usar `/move`, `/win` ou `/lose`.\nO caminho perigoso é um só.\n\n## Pré-requisitos da conta\n\nAlém do token, o acesso à API exige que a licença do ambiente tenha a API\nhabilitada e que o addon \"Acesso à API\" esteja ativo no tenant. Sem isso, todo\nrequest responde `403`.\n\n## Escopo de dados\n\nO `tenant_id` **nunca** é aceito no corpo de um request. Ele é derivado do\ntoken. Um recurso que pertença a outra conta responde `404`, não `403`.\n\n## Limite de requisições\n\n60 requisições por minuto, por token, em janela fixa de 60 segundos.\nToda resposta traz `X-RateLimit-Limit` e `X-RateLimit-Remaining`.\nAo estourar, a resposta é `429` com `Retry-After: 60`.\n\n## Paginação\n\nA maior parte das listagens aceita `page` e `per_page` (padrão 50, teto 100) e\nresponde com um envelope `meta`:\n\n```json\n{ \"data\": [], \"meta\": { \"current_page\": 1, \"last_page\": 10, \"per_page\": 50, \"total\": 500 } }\n```\n\nTrês exceções, documentadas em cada endpoint: `/webhooks` e\n`/wa-connector/*` não paginam, e `/conversations/{id}/messages` usa cursor\n(`before_id`, `after_id`, `limit`) com `has_more` no lugar de `meta`.\n\n## Datas\n\nISO 8601 com offset (`2026-09-03T19:57:10-03:00`), exceto `connected_at` e\n`last_synced_at` do WhatsApp Connector, que vêm em UTC com `Z`.\nDatas puras usam `YYYY-MM-DD`.\n\n## Valores monetários\n\nDevolvidos como número (`float`), não como string.\n\n## Inconsistências conhecidas\n\nDocumentadas aqui porque existem hoje, e não porque sejam desejáveis:\n\n- `discount_type` aceita `percent` em faturas e `percentage` em propostas.\n- A ordenação padrão (`sort_dir`) é `desc` em contatos, conversas, faturas e\n  propostas, e `asc` em agendamentos e produtos.\n- O campo `filename` de uma mensagem sempre responde `null`.\n- Oito eventos de webhook são aceitos no cadastro mas nunca são disparados.\n  Veja a seção de Webhooks.\n",
    "contact": {
      "name": "Suporte Skill-Hub",
      "url": "https://atlas.skill-hub.app"
    },
    "license": {
      "name": "Proprietária",
      "url": "https://atlas.skill-hub.app/terms-of-service"
    }
  },
  "servers": [
    {
      "url": "https://atlas.skill-hub.app/api/v1",
      "description": "Produção"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Contatos",
      "description": "Pessoas e empresas, seus negócios e conversas."
    },
    {
      "name": "Funis e etapas",
      "description": "Funis de venda e suas etapas. É aqui que se descobre o id da etapa de destino."
    },
    {
      "name": "Negócios",
      "description": "Negócios do Kanban. Mudança de etapa e fechamento só por /move, /win e /lose, com os dois interruptores."
    },
    {
      "name": "Campos personalizados",
      "description": "Definições dos campos personalizados de negócios e contatos (as chaves que custom_fields aceita)."
    },
    {
      "name": "Conversas",
      "description": "Conversas de WhatsApp e envio de mensagens."
    },
    {
      "name": "Faturas",
      "description": "Faturas, itens e pagamentos."
    },
    {
      "name": "Propostas",
      "description": "Propostas comerciais e itens."
    },
    {
      "name": "Agendamentos",
      "description": "Compromissos da agenda."
    },
    {
      "name": "Produtos",
      "description": "Produtos e serviços do catálogo."
    },
    {
      "name": "WhatsApp Connector",
      "description": "Canais oficiais e envio por template aprovado na Meta."
    },
    {
      "name": "E-mail",
      "description": "Envio de e-mail pelas caixas do Atlas (pessoal, do time ou Geral)."
    },
    {
      "name": "Webhooks",
      "description": "Notificações de saída para o seu sistema."
    }
  ],
  "paths": {
    "/custom-fields": {
      "get": {
        "tags": [
          "Campos personalizados"
        ],
        "summary": "Listar campos personalizados",
        "operationId": "listCustomFields",
        "x-scope": "read",
        "description": "Definições dos campos personalizados da sua conta: é daqui que saem as chaves que\n`custom_fields` aceita em negócios e contatos. Por padrão só os ativos.\n",
        "parameters": [
          {
            "name": "entity_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "deal",
                "contact"
              ]
            },
            "description": "Sem ele, vêm os dois."
          },
          {
            "name": "include_inactive",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustomFieldDefinition"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/pipelines": {
      "get": {
        "tags": [
          "Funis e etapas"
        ],
        "summary": "Listar funis",
        "operationId": "listPipelines",
        "x-scope": "read",
        "description": "Funis **ativos** da conta, em ordem alfabética, cada um com suas etapas já\nordenadas (inclusive as de ganho e de perda). Sem paginação.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Pipeline"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/pipelines/{pipeline}/stages": {
      "get": {
        "tags": [
          "Funis e etapas"
        ],
        "summary": "Listar etapas de um funil",
        "operationId": "listPipelineStages",
        "x-scope": "read",
        "description": "Etapas do funil em `sort_order`. Por padrão **exclui** as etapas de ganho e\nde perda; passe `include_won_lost=true` para incluí-las. Funil de outra\nconta responde `404`.\n",
        "parameters": [
          {
            "name": "pipeline",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "include_won_lost",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PipelineStage"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/deals": {
      "get": {
        "tags": [
          "Negócios"
        ],
        "summary": "Listar negócios",
        "operationId": "listDeals",
        "x-scope": "read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "pipeline_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "stage_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "won",
                "lost"
              ]
            }
          },
          {
            "name": "created_from",
            "in": "query",
            "description": "Criação, a partir de (data ou data-hora).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "created_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "stage_changed_from",
            "in": "query",
            "description": "Entrada na etapa atual, a partir de.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "stage_changed_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "value_min",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "value_max",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Busca parcial no título do negócio e no nome do contato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "updated_at",
                "value",
                "title",
                "expected_close_date",
                "stage_changed_at"
              ],
              "default": "created_at"
            }
          },
          {
            "$ref": "#/components/parameters/SortDirDesc"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Deal"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Negócios"
        ],
        "summary": "Criar negócio",
        "operationId": "createDeal",
        "x-scope": "write",
        "description": "Cria o negócio na etapa informada, que precisa pertencer ao funil informado\n(`GET /pipelines`). Contato, responsável, origem e campanha são verificados\ncontra a sua conta. Se `conversation_id` vier, é vinculado após verificação\nde posse; se não vier e o contato tiver exatamente uma conversa, ela é\nvinculada.\n\nNa criação o único efeito colateral existente é a automação `deal_created`,\ncontrolada por `trigger_automations` (padrão `false`, exige `send`).\n`fire_pixel_events` é aceito por consistência e não tem efeito aqui.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title",
                  "pipeline_id",
                  "stage_id"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "pipeline_id": {
                    "type": "integer"
                  },
                  "stage_id": {
                    "type": "integer",
                    "description": "Etapa do funil informado."
                  },
                  "contact_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "assigned_to": {
                    "type": "integer",
                    "nullable": true
                  },
                  "value": {
                    "type": "number",
                    "minimum": 0,
                    "default": 0
                  },
                  "currency": {
                    "type": "string",
                    "maxLength": 10,
                    "default": "BRL"
                  },
                  "expected_close_date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 5000,
                    "nullable": true
                  },
                  "custom_fields": {
                    "$ref": "#/components/schemas/CustomFieldsInput"
                  },
                  "source_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "campaign_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "conversation_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "trigger_automations": {
                    "type": "boolean",
                    "default": false
                  },
                  "fire_pixel_events": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criado",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DealDetailed"
                    },
                    "message": {
                      "type": "string",
                      "example": "Deal created successfully."
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/CustomFieldWarnings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/CustomFieldValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/deals/{deal}": {
      "parameters": [
        {
          "name": "deal",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "tags": [
          "Negócios"
        ],
        "summary": "Ler negócio",
        "operationId": "getDeal",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DealDetailed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "patch": {
        "tags": [
          "Negócios"
        ],
        "summary": "Alterar dados do negócio",
        "operationId": "updateDeal",
        "x-scope": "write",
        "description": "Altera título, valor, moeda, responsável, contato, data prevista, notas,\ncampos personalizados, origem, campanha e conversa vinculada.\n\n**Campos personalizados no PATCH são parciais:** só as chaves enviadas mudam;\nas que não vierem ficam como estão; `null` apaga o valor daquele campo.\nSe qualquer campo for inválido, a resposta é `422` e **nada** do request é gravado.\n\n**Não muda etapa nem status.** `stage_id`, `status`, `pipeline_id`,\n`probability`, `closed_at`, `lost_at`, `loss_reason_id`, `won` e `lost`\nrespondem `422` mandando usar `/move`, `/win` ou `/lose`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "contact_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "assigned_to": {
                    "type": "integer",
                    "nullable": true
                  },
                  "value": {
                    "type": "number",
                    "minimum": 0
                  },
                  "currency": {
                    "type": "string",
                    "maxLength": 10
                  },
                  "expected_close_date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 5000,
                    "nullable": true
                  },
                  "custom_fields": {
                    "$ref": "#/components/schemas/CustomFieldsInput"
                  },
                  "source_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "campaign_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "conversation_id": {
                    "type": "integer",
                    "nullable": true
                  }
                }
              },
              "example": {
                "custom_fields": {
                  "cargo_da_vaga": "SDR",
                  "tipo_de_vaga": "SDR",
                  "data_da_candidatura": "2026-09-30",
                  "link_da_vaga": null
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DealDetailed"
                    },
                    "message": {
                      "type": "string",
                      "example": "Deal updated successfully."
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/CustomFieldWarnings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validação, campo personalizado inválido (ver `CustomFieldValidationError`), ou tentativa de\nmudar etapa/status por PATCH:\n`{\"message\":\"…\",\"errors\":{\"stage_id\":[\"Este campo não pode ser alterado por PATCH. Use POST /deals/{id}/move…\"]}}`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomFieldErrorBody"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/deals/{deal}/documents": {
      "parameters": [
        {
          "name": "deal",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "tags": [
          "Negócios"
        ],
        "summary": "Listar documentos",
        "operationId": "listDealDocuments",
        "x-scope": "read",
        "description": "Lista os arquivos da aba Documentos do negócio, do mais novo para o mais antigo.\nCada item traz um link assinado novo em `url`, válido por 30 minutos (`url_expires_at`).\n",
        "responses": {
          "200": {
            "description": "Lista de documentos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DealDocument"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Negócios"
        ],
        "summary": "Anexar documento",
        "operationId": "uploadDealDocument",
        "x-scope": "write",
        "description": "Anexa um arquivo na aba Documentos do negócio. `multipart/form-data`, campo `file`.\nMesmos limites da tela: até 50 MB; arquivos executáveis ou que o navegador\ninterpreta (php, html, svg, js, exe…) são recusados. Mesmo limite de requisições\npor minuto dos outros endpoints.\n\nO arquivo fica em área privada. A resposta traz `url`, um link assinado e temporário\npara baixar o arquivo sem token, válido por 30 minutos (`url_expires_at`). Depois disso\no link responde 403; para baixar de novo, chame `GET /deals/{deal}/documents`, que\ndevolve links novos. Não guarde a URL como endereço permanente do arquivo.\n",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Anexado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DealDocument"
                    },
                    "message": {
                      "type": "string",
                      "example": "Document uploaded successfully."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/deals/{deal}/move": {
      "parameters": [
        {
          "name": "deal",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "tags": [
          "Negócios"
        ],
        "summary": "Mover de etapa",
        "operationId": "moveDeal",
        "x-scope": "write",
        "description": "Move o negócio para a etapa informada, que pode ser de outro funil da\nmesma conta. Etapa de ganho ou de perda responde `422`: use `/win` ou\n`/lose`. Registra a atividade com o nome do token.\n\nSem os interruptores: só a etapa muda. Com `trigger_automations=true` as\nautomações da etapa disparam e a métrica de conversão é gravada; com\n`fire_pixel_events=true` o evento mapeado da etapa vai para a Meta CAPI.\nQualquer um ligado exige o escopo `send`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "stage_id"
                ],
                "properties": {
                  "stage_id": {
                    "type": "integer"
                  },
                  "trigger_automations": {
                    "type": "boolean",
                    "default": false
                  },
                  "fire_pixel_events": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Movido",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DealDetailed"
                    },
                    "effects": {
                      "$ref": "#/components/schemas/DealEffects"
                    },
                    "message": {
                      "type": "string",
                      "example": "Deal moved successfully."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/deals/{deal}/win": {
      "parameters": [
        {
          "name": "deal",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "tags": [
          "Negócios"
        ],
        "summary": "Marcar como ganho",
        "operationId": "winDeal",
        "x-scope": "write",
        "description": "Status `won`, etapa de ganho do funil, `closed_at` hoje e o contato vira\ncliente. Negócio já ganho responde `422`.\n\nSem os interruptores nada mais acontece. Com `trigger_automations=true`\na automação `deal_won` dispara e a venda entra na métrica de conversão;\ncom `fire_pixel_events=true` a Meta CAPI recebe `Purchase` (ou o evento\nmapeado). **`Purchase` falso na conta de anúncios não se desfaz.**\nQualquer um ligado exige o escopo `send`.\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "trigger_automations": {
                    "type": "boolean",
                    "default": false
                  },
                  "fire_pixel_events": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ganho",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DealDetailed"
                    },
                    "effects": {
                      "$ref": "#/components/schemas/DealEffects"
                    },
                    "message": {
                      "type": "string",
                      "example": "Deal marked as won."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Negócio já ganho.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                },
                "example": {
                  "error": "Deal is already won."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/deals/{deal}/lose": {
      "parameters": [
        {
          "name": "deal",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "tags": [
          "Negócios"
        ],
        "summary": "Marcar como perdido",
        "operationId": "loseDeal",
        "x-scope": "write",
        "description": "Status `lost`, etapa de perda do funil, `lost_at` agora, motivo e notas\nde perda. O motivo precisa ser da sua conta. Negócio já perdido responde\n`422`. Interruptores como em `/win` (`deal_lost` e evento mapeado).\n",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "loss_reason_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "loss_notes": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  },
                  "trigger_automations": {
                    "type": "boolean",
                    "default": false
                  },
                  "fire_pixel_events": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Perdido",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/DealDetailed"
                    },
                    "effects": {
                      "$ref": "#/components/schemas/DealEffects"
                    },
                    "message": {
                      "type": "string",
                      "example": "Deal marked as lost."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/deals/{deal}/activities": {
      "parameters": [
        {
          "name": "deal",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "tags": [
          "Negócios"
        ],
        "summary": "Histórico do negócio",
        "operationId": "listDealActivities",
        "x-scope": "read",
        "description": "Atividades do mais recente para o mais antigo, com `page` e `per_page` (padrão 50, teto 100).",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DealActivity"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "tags": [
          "Contatos"
        ],
        "summary": "Listar contatos",
        "operationId": "listContacts",
        "x-scope": "read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "type",
            "in": "query",
            "description": "Igualdade exata.",
            "schema": {
              "type": "string",
              "enum": [
                "lead",
                "customer",
                "prospect",
                "supplier"
              ]
            }
          },
          {
            "name": "person_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "individual",
                "company"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Busca parcial, sem diferenciar maiúsculas, em nome, nome fantasia, e-mail, telefone, celular e documento.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "group_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "source",
            "in": "query",
            "description": "Igualdade exata e **sensível a maiúsculas**. A base contém grafias\ndiferentes para a mesma origem, então prefira listar e filtrar do seu\nlado quando precisar de precisão.\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "is_active",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Array ou lista separada por vírgula. As tags são combinadas com E, não com OU.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "email",
                "type",
                "created_at",
                "updated_at",
                "last_contact_at"
              ],
              "default": "created_at"
            }
          },
          {
            "$ref": "#/components/parameters/SortDirDesc"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1001,
                      "name": "Maria Exemplo",
                      "trading_name": null,
                      "display_name": "Maria Exemplo",
                      "person_type": "individual",
                      "type": "lead",
                      "email": "maria@exemplo.com",
                      "phone": null,
                      "mobile": "(11) 91234-5678",
                      "document": null,
                      "source": null,
                      "tags": [],
                      "is_active": true,
                      "group": null,
                      "assigned_to": null,
                      "deals_count": 0,
                      "contracts_count": 0,
                      "created_at": "2026-09-03T19:57:10-03:00",
                      "updated_at": "2026-09-03T19:57:10-03:00"
                    }
                  ],
                  "meta": {
                    "current_page": 1,
                    "last_page": 12,
                    "per_page": 50,
                    "total": 580
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Contatos"
        ],
        "summary": "Criar contato",
        "operationId": "createContact",
        "x-scope": "write",
        "description": "Aplica os padrões `type = lead` e `person_type = individual` quando os\ncampos não são enviados, e grava o usuário do token em `created_by`.\n\nCampos de rastreamento (`utm_*`, `captured_at`) **não** são aceitos.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactCreate"
              },
              "example": {
                "name": "Maria Exemplo",
                "email": "maria@exemplo.com",
                "mobile": "11912345678",
                "type": "lead",
                "person_type": "individual",
                "tags": [
                  "campanha-setembro"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criado. Devolve a representação detalhada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactDetailed"
                    },
                    "message": {
                      "type": "string",
                      "example": "Contact created successfully."
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/CustomFieldWarnings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/CustomFieldValidationError"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/contacts/{contact}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContactId"
        }
      ],
      "get": {
        "tags": [
          "Contatos"
        ],
        "summary": "Obter contato",
        "operationId": "getContact",
        "x-scope": "read",
        "description": "Devolve a representação detalhada, incluindo `employees` quando o contato for uma empresa com pessoas vinculadas.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactDetailed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Contatos"
        ],
        "summary": "Atualizar contato",
        "operationId": "updateContact",
        "x-scope": "write",
        "description": "Grava o usuário do token em `updated_by`. Aceita dois campos que a criação não aceita,\n`is_active` e `bot_protected`. `PATCH` faz o mesmo. Campos personalizados são parciais:\nsó as chaves enviadas mudam e `null` apaga o valor do campo.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactDetailed"
                    },
                    "message": {
                      "type": "string"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/CustomFieldWarnings"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/CustomFieldValidationError"
          }
        }
      },
      "patch": {
        "tags": [
          "Contatos"
        ],
        "summary": "Atualizar contato (parcial)",
        "operationId": "patchContact",
        "x-scope": "write",
        "description": "Igual ao `PUT`. Só os campos enviados mudam; em `custom_fields`, `null` apaga o valor do campo.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactUpdate"
              },
              "example": {
                "custom_fields": {
                  "segmento": "clinica"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactDetailed"
                    },
                    "message": {
                      "type": "string"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/CustomFieldWarnings"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/CustomFieldValidationError"
          }
        }
      },
      "delete": {
        "tags": [
          "Contatos"
        ],
        "summary": "Remover contato",
        "operationId": "deleteContact",
        "x-scope": "write",
        "description": "Exclusão lógica. O registro deixa de aparecer nas listagens mas não é apagado do banco.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/contacts/{contact}/deals": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContactId"
        }
      ],
      "get": {
        "tags": [
          "Contatos"
        ],
        "summary": "Negócios do contato",
        "operationId": "listContactDeals",
        "x-scope": "read",
        "description": "Ordenado por data de criação, do mais novo para o mais antigo. Sem filtros.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage25"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DealSummary"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/contacts/{contact}/conversations": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ContactId"
        }
      ],
      "get": {
        "tags": [
          "Contatos"
        ],
        "summary": "Conversas do contato",
        "operationId": "listContactConversations",
        "x-scope": "read",
        "description": "Ordenado pela última mensagem, da mais recente para a mais antiga. Sem filtros.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage25"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/conversations": {
      "get": {
        "tags": [
          "Conversas"
        ],
        "summary": "Listar conversas",
        "operationId": "listConversations",
        "x-scope": "read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "pending",
                "resolved"
              ]
            }
          },
          {
            "name": "channel_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "assigned_to",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Busca parcial em nome exibido, identificador remoto e prévia da última mensagem.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "is_group",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "last_message_at",
                "created_at",
                "unread_count"
              ],
              "default": "last_message_at"
            }
          },
          {
            "$ref": "#/components/parameters/SortDirDesc"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/conversations/{conversation}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationId"
        }
      ],
      "get": {
        "tags": [
          "Conversas"
        ],
        "summary": "Obter conversa",
        "operationId": "getConversation",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Conversas"
        ],
        "summary": "Atualizar conversa",
        "operationId": "updateConversation",
        "x-scope": "write",
        "description": "Enviar `status: resolved` executa a rotina de resolução da conversa,\nregistrando quem resolveu.\n\nCuidado com dois comportamentos:\n\n- `assigned_user_id` só atribui se o usuário pertencer à sua conta. Se não\n  pertencer, o valor é **ignorado em silêncio** e a resposta é `200`.\n- `assigned_department_id` é gravado sem essa verificação.\n\nEnvie `null` em `assigned_user_id` para desatribuir.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "open",
                      "pending",
                      "resolved"
                    ],
                    "nullable": true
                  },
                  "assigned_user_id": {
                    "type": "integer",
                    "nullable": true
                  },
                  "assigned_department_id": {
                    "type": "integer",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/conversations/{conversation}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationId"
        }
      ],
      "get": {
        "tags": [
          "Conversas"
        ],
        "summary": "Listar mensagens",
        "operationId": "listMessages",
        "x-scope": "read",
        "description": "Paginação por cursor, e não por página. A resposta traz `has_more` no\nlugar de `meta`. As mensagens vêm da mais antiga para a mais nova.\nMensagens apagadas não são incluídas.\n",
        "parameters": [
          {
            "name": "before_id",
            "in": "query",
            "description": "Devolve mensagens anteriores a este id. Use para carregar histórico.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "after_id",
            "in": "query",
            "description": "Devolve mensagens posteriores a este id. Use para buscar novidades.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Conversas"
        ],
        "summary": "Enviar mensagem em uma conversa",
        "operationId": "sendMessage",
        "x-scope": "send",
        "description": "Envia para o canal já vinculado à conversa.\n\nÉ obrigatório ao menos um entre `content`, `media` e `media_url`.\n\n**Efeitos colaterais**, que acontecem sempre:\n\n- A mensagem é marcada com `metadata.via = \"api\"`.\n- Se a conversa não tiver responsável, ela é atribuída ao usuário do token.\n- O status da conversa passa para `open`.\n\nÁudio é enviado como mensagem de voz. Quando há mídia sem `type`, o padrão\né `document`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageSend"
              },
              "example": {
                "content": "Olá! Seu atendimento foi retomado.",
                "type": "text"
              }
            },
            "multipart/form-data": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/MessageSend"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "media": {
                        "type": "string",
                        "format": "binary",
                        "description": "Arquivo. O teto de tamanho segue a configuração do ambiente, padrão 250 MB."
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Enviada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Message"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "O canal recusou o envio. Ocorre com canal desconectado\n(`WhatsApp channel is disconnected.`), com erro devolvido pelo\nprovedor, ou com falha genérica de envio.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Falha de validação, ou nenhum conteúdo enviado.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                },
                "example": {
                  "error": "Message content or media is required."
                }
              }
            }
          }
        }
      }
    },
    "/messages/send": {
      "post": {
        "tags": [
          "Conversas"
        ],
        "summary": "Enviar mensagem por telefone",
        "operationId": "sendMessageByPhone",
        "x-scope": "send",
        "description": "Envia sem conhecer a conversa. Dois modos:\n\n- Com `conversation_id`: usa a conversa existente e o canal dela.\n- Sem `conversation_id`: `channel_id` passa a ser obrigatório. O telefone é\n  reduzido a dígitos e a conversa é criada se ainda não existir.\n\nEmbora `phone` seja declarado como opcional, ele é obrigatório na prática\nno segundo modo.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageSendByPhone"
              },
              "example": {
                "phone": "5511912345678",
                "channel_id": 12,
                "content": "Olá! Recebemos seu contato."
              }
            },
            "multipart/form-data": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/MessageSendByPhone"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "media": {
                        "type": "string",
                        "format": "binary"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Enviada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Message"
                    },
                    "conversation_id": {
                      "type": "integer"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Canal desconectado ou erro do provedor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          },
          "404": {
            "description": "Conversa ou canal não encontrado. As mensagens possíveis são\n`Conversation not found.`, `Channel not found for this conversation.`\ne `Channel not found.`\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "description": "Não foi possível criar a conversa.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          }
        }
      }
    },
    "/invoices": {
      "get": {
        "tags": [
          "Faturas"
        ],
        "summary": "Listar faturas",
        "operationId": "listInvoices",
        "x-scope": "read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "pending",
                "sent",
                "paid",
                "cancelled"
              ]
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Busca parcial em número e título.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "due_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "due_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "is_recurring",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "due_date",
                "total",
                "number",
                "status"
              ],
              "default": "created_at"
            }
          },
          {
            "$ref": "#/components/parameters/SortDirDesc"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invoice"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Faturas"
        ],
        "summary": "Criar fatura",
        "operationId": "createInvoice",
        "x-scope": "write",
        "description": "Os totais são calculados pelo servidor a partir dos itens. Não envie\n`subtotal` nem `total`.\n\nPadrões aplicados: `issue_date` igual a hoje, `currency` igual a `BRL`,\n`discount_type` igual a `fixed`, `status` igual a `draft`, `unit` igual a\n`un`, e o gateway de pagamento padrão da conta.\n\nAtenção: o `contact_id` enviado **não é verificado** contra a sua conta.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceCreate"
              },
              "example": {
                "contact_id": 1001,
                "due_date": "2026-10-15",
                "title": "Mensalidade de outubro",
                "items": [
                  {
                    "name": "Plano Pro",
                    "quantity": 1,
                    "unit_price": 499.9,
                    "unit": "un"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InvoiceDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/invoices/{invoice}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InvoiceId"
        }
      ],
      "get": {
        "tags": [
          "Faturas"
        ],
        "summary": "Obter fatura",
        "operationId": "getInvoice",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InvoiceDetailed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Faturas"
        ],
        "summary": "Atualizar fatura",
        "operationId": "updateInvoice",
        "x-scope": "write",
        "description": "**Não aceita itens** e **não recalcula os totais**. Para mudar os itens de\numa fatura, crie uma nova.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/InvoiceDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "tags": [
          "Faturas"
        ],
        "summary": "Remover fatura",
        "operationId": "deleteInvoice",
        "x-scope": "write",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/invoices/{invoice}/payments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/InvoiceId"
        }
      ],
      "get": {
        "tags": [
          "Faturas"
        ],
        "summary": "Listar pagamentos da fatura",
        "operationId": "listInvoicePayments",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Faturas"
        ],
        "summary": "Registrar pagamento",
        "operationId": "recordInvoicePayment",
        "x-scope": "write",
        "description": "Registra um pagamento e atualiza o status da fatura. Uma fatura em status\nque não aceita pagamento responde `422` antes mesmo da validação dos\ncampos.\n\nPadrões: `method` igual a `manual` e `paid_at` igual a agora.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "number",
                    "minimum": 0.01
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "pix",
                      "boleto",
                      "credit_card",
                      "debit_card",
                      "transfer",
                      "manual"
                    ],
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 1000,
                    "nullable": true
                  },
                  "paid_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  }
                }
              },
              "example": {
                "amount": 499.9,
                "method": "pix"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registrado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PaymentSummary"
                    },
                    "invoice_status": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validação, ou a fatura não aceita pagamento no status atual.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                },
                "example": {
                  "error": "Invoice cannot receive payments in current status."
                }
              }
            }
          }
        }
      }
    },
    "/proposals": {
      "get": {
        "tags": [
          "Propostas"
        ],
        "summary": "Listar propostas",
        "operationId": "listProposals",
        "x-scope": "read",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "sent",
                "accepted",
                "rejected",
                "expired"
              ]
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "deal_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Busca parcial em número, título e assunto.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "proposal_date",
                "valid_until",
                "total",
                "number",
                "status"
              ],
              "default": "created_at"
            }
          },
          {
            "$ref": "#/components/parameters/SortDirDesc"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Proposal"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Propostas"
        ],
        "summary": "Criar proposta",
        "operationId": "createProposal",
        "x-scope": "write",
        "description": "Totais calculados pelo servidor.\n\nPadrões: `assigned_to` igual ao usuário do token, `proposal_date` igual a\nhoje, `valid_until` igual a hoje mais 30 dias, `currency` igual a `BRL` e\n`status` igual a `draft`.\n\nAtenção ao `discount_type`: aqui os valores são `fixed` e **`percentage`**,\ndiferente das faturas, que usam `percent`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProposalCreate"
              },
              "example": {
                "contact_id": 1001,
                "title": "Proposta comercial",
                "items": [
                  {
                    "name": "Implantação",
                    "quantity": 1,
                    "unit_price": 3500
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProposalDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/proposals/{proposal}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProposalId"
        }
      ],
      "get": {
        "tags": [
          "Propostas"
        ],
        "summary": "Obter proposta",
        "operationId": "getProposal",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProposalDetailed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Propostas"
        ],
        "summary": "Atualizar proposta",
        "operationId": "updateProposal",
        "x-scope": "write",
        "description": "Não aceita itens.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProposalUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProposalDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "tags": [
          "Propostas"
        ],
        "summary": "Remover proposta",
        "operationId": "deleteProposal",
        "x-scope": "write",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/appointments": {
      "get": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Listar agendamentos",
        "operationId": "listAppointments",
        "x-scope": "read",
        "description": "A ordenação padrão aqui é **crescente**, diferente dos outros recursos.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "scheduled",
                "confirmed",
                "cancelled",
                "completed"
              ]
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "meeting",
                "call",
                "task",
                "event",
                "other"
              ]
            }
          },
          {
            "name": "user_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "contact_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "deal_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "start_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "start_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Busca parcial em título, descrição e local.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "start_at",
                "end_at",
                "created_at",
                "title",
                "status"
              ],
              "default": "start_at"
            }
          },
          {
            "$ref": "#/components/parameters/SortDirAsc"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Appointment"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Criar agendamento",
        "operationId": "createAppointment",
        "x-scope": "write",
        "description": "Padrões: `user_id` igual ao usuário do token, `type` igual a `meeting`,\n`status` igual a `scheduled` e `end_at` igual a `start_at` mais uma hora.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppointmentCreate"
              },
              "example": {
                "title": "Reunião de apresentação",
                "start_at": "2026-09-20T14:00:00-03:00",
                "end_at": "2026-09-20T15:00:00-03:00",
                "contact_id": 1001,
                "meeting_type": "online"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AppointmentDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/appointments/{appointment}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AppointmentId"
        }
      ],
      "get": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Obter agendamento",
        "operationId": "getAppointment",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AppointmentDetailed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Atualizar agendamento",
        "operationId": "updateAppointment",
        "x-scope": "write",
        "description": "A validação de que `end_at` vem depois de `start_at` existe na criação, mas\n**não** na atualização. Garanta a coerência do seu lado.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppointmentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AppointmentDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "tags": [
          "Agendamentos"
        ],
        "summary": "Remover agendamento",
        "operationId": "deleteAppointment",
        "x-scope": "write",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/products": {
      "get": {
        "tags": [
          "Produtos"
        ],
        "summary": "Listar produtos",
        "operationId": "listProducts",
        "x-scope": "read",
        "description": "A ordenação padrão aqui é **crescente por nome**.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/PerPage"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "product",
                "service"
              ]
            }
          },
          {
            "name": "category_id",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "is_active",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "has_variations",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Busca parcial em nome, SKU e descrição.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort_by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "price",
                "sku",
                "created_at",
                "stock_quantity",
                "sort_order"
              ],
              "default": "name"
            }
          },
          {
            "$ref": "#/components/parameters/SortDirAsc"
          }
        ],
        "responses": {
          "200": {
            "description": "Lista paginada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Produtos"
        ],
        "summary": "Criar produto",
        "operationId": "createProduct",
        "x-scope": "write",
        "description": "Padrões: `type` igual a `product` e `is_active` igual a verdadeiro.\n\nNão existe endpoint para criar ou editar variações. Elas são apenas\nlistadas na leitura de um produto.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductCreate"
              },
              "example": {
                "name": "Plano Pro",
                "type": "service",
                "price": 499.9,
                "unit": "mês"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/products/{product}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ProductId"
        }
      ],
      "get": {
        "tags": [
          "Produtos"
        ],
        "summary": "Obter produto",
        "operationId": "getProduct",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductDetailed"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Produtos"
        ],
        "summary": "Atualizar produto",
        "operationId": "updateProduct",
        "x-scope": "write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProductUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ProductDetailed"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "tags": [
          "Produtos"
        ],
        "summary": "Remover produto",
        "operationId": "deleteProduct",
        "x-scope": "write",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/wa-connector/channels": {
      "get": {
        "tags": [
          "WhatsApp Connector"
        ],
        "summary": "Listar canais oficiais",
        "operationId": "listWaChannels",
        "x-scope": "read",
        "description": "Devolve apenas canais da API Oficial da Meta que estejam ativos.\nSem paginação, com envelope próprio `{ data, total }`.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WaChannel"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/wa-connector/templates": {
      "get": {
        "tags": [
          "WhatsApp Connector"
        ],
        "summary": "Listar templates aprovados",
        "operationId": "listWaTemplates",
        "x-scope": "read",
        "description": "Somente templates aprovados pela Meta, ordenados por nome.\nSem paginação, com envelope próprio `{ data, total }`.\n",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "description": "Convertido para maiúsculas automaticamente.",
            "schema": {
              "type": "string",
              "enum": [
                "UTILITY",
                "MARKETING",
                "AUTHENTICATION"
              ]
            }
          },
          {
            "name": "language",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "pt_BR"
            }
          },
          {
            "name": "channel_id",
            "in": "query",
            "description": "Filtra pelos templates da conta de WhatsApp Business daquele canal.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WaTemplate"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/wa-connector/send-template": {
      "post": {
        "tags": [
          "WhatsApp Connector"
        ],
        "summary": "Enviar template",
        "operationId": "sendWaTemplate",
        "x-scope": "send",
        "description": "Envia um template aprovado pela API Oficial. É o caminho para iniciar\nconversa fora da janela de 24 horas.\n\nO telefone é reduzido a dígitos. O idioma padrão é `pt_BR`.\n\n`components` segue o formato da Cloud API da Meta.\n\n**Registro no Atlas.** Depois que a Meta aceita o envio, o Atlas registra\na mensagem como um envio feito pela tela:\n\n- **Contato:** procurado pelo telefone na conta (com e sem `55`, com e sem\n  o 9º dígito). Se não existir, é criado como lead, com o nome de\n  `contact_name` (ou o próprio número). Contato existente sem nome (ou com o\n  número no lugar do nome) recebe o `contact_name`; nome de verdade não é trocado.\n- **Conversa:** a deste número **no mesmo canal** é reaproveitada, pela\n  mesma regra que a entrada da Meta usa. Por isso, quando o lead responde,\n  a resposta cai nessa conversa. Se não existir, é criada; se estiver\n  resolvida, é reaberta. Ela termina com status `open`.\n- **Mensagem:** gravada como mensagem de saída, com o texto do template\n  já com as variáveis (ou `[Template: nome]` e as variáveis, quando o\n  template não está sincronizado no Atlas), o `message_id` da Meta (os\n  status entregue/lido atualizam por ele) e `metadata.via = \"api\"`. A\n  conversa aparece na hora no inbox. A IA não é acionada por essa mensagem.\n\nSe a Meta recusar, nada é criado no Atlas e a resposta é `400`, como antes.\n\nAtenção: o serviço **não verifica** se o template existe na conta antes de\nenviar. Um nome errado só falha na resposta da Meta.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "channel_id",
                  "to",
                  "template_name"
                ],
                "properties": {
                  "channel_id": {
                    "type": "integer"
                  },
                  "to": {
                    "type": "string",
                    "maxLength": 30,
                    "example": "5511912345678"
                  },
                  "template_name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "language": {
                    "type": "string",
                    "maxLength": 10,
                    "default": "pt_BR"
                  },
                  "components": {
                    "type": "array",
                    "description": "Formato da Cloud API da Meta.",
                    "items": {
                      "type": "object"
                    }
                  },
                  "contact_name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Opcional. Nome do contato ao criar, ou para completar um contato existente sem nome."
                  }
                }
              },
              "example": {
                "channel_id": 12,
                "to": "5511912345678",
                "template_name": "appointment_reminder",
                "contact_name": "Maria Souza",
                "language": "pt_BR",
                "components": [
                  {
                    "type": "body",
                    "parameters": [
                      {
                        "type": "text",
                        "text": "Maria"
                      },
                      {
                        "type": "text",
                        "text": "Consulta"
                      },
                      {
                        "type": "text",
                        "text": "20/09 às 14h"
                      },
                      {
                        "type": "text",
                        "text": "Dra. Ana"
                      }
                    ]
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Enviado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "message": {
                      "type": "string",
                      "example": "Template enviado com sucesso."
                    },
                    "message_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "Identificador da mensagem na Meta."
                    },
                    "conversation_id": {
                      "type": "integer",
                      "nullable": true,
                      "example": 4821,
                      "description": "Conversa do Atlas onde o template ficou registrado. Nulo só se o registro falhar depois do envio (o template já saiu)."
                    },
                    "contact_id": {
                      "type": "integer",
                      "nullable": true,
                      "example": 9310,
                      "description": "Contato do Atlas ligado à conversa."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A Meta recusou o envio. A mensagem devolvida é a da própria Meta. Nada é criado no Atlas.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaError"
                }
              }
            }
          },
          "404": {
            "description": "Canal não encontrado ou inativo para esta conta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaError"
                }
              }
            }
          },
          "422": {
            "description": "Validação, canal desconectado, ou canal sem token de acesso e\nidentificador de telefone configurados.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaError"
                }
              }
            }
          }
        }
      }
    },
    "/email-accounts": {
      "get": {
        "tags": [
          "E-mail"
        ],
        "summary": "Listar caixas de e-mail",
        "operationId": "listEmailAccounts",
        "x-scope": "read",
        "description": "Caixas que o dono do token pode usar para enviar: a caixa pessoal dele, as caixas do time em que\nele participa e a caixa Geral (E-mail SMTP das configurações) quando ele tem a permissão de\nconfigurações de e-mail. São as mesmas caixas da tela E-mail.\n\nO campo `account` é a chave usada em `POST /emails/send`: o id numérico da caixa ou `general`.\nSem paginação, com envelope `{ data }`.\n",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailAccount"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "account": "3",
                      "name": "Comercial",
                      "from_email": "comercial@suaempresa.com.br",
                      "from_name": "Comercial",
                      "provider": "imap_smtp",
                      "is_general": false,
                      "is_personal": true,
                      "is_active": true
                    },
                    {
                      "account": "general",
                      "name": "Geral",
                      "from_email": "contato@suaempresa.com.br",
                      "from_name": "Sua Empresa",
                      "provider": "smtp",
                      "is_general": true,
                      "is_personal": false,
                      "is_active": false
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/emails/send": {
      "post": {
        "tags": [
          "E-mail"
        ],
        "summary": "Enviar e-mail por uma caixa",
        "operationId": "sendEmail",
        "x-scope": "send",
        "description": "Envia um e-mail por uma caixa do Atlas, do mesmo jeito que a tela Novo e-mail. Funciona com caixas\nIMAP/SMTP e Microsoft. A mensagem fica na pasta Enviados da caixa, com o usuário do token como\nremetente interno, e o resultado aparece em Configurações, Logs de Integração.\n\n`account` é a chave devolvida por `GET /email-accounts`. Sem ela, usa a caixa pessoal do dono do\ntoken; se ele não tiver, a primeira acessível. Caixa inativa (teste de SMTP falhou ou desligada)\nresponde 422.\n\nDestinatários (`to`, `cc`, `bcc`) aceitam três formas: uma string com um e-mail, uma lista de\nstrings, ou uma lista de objetos `{ email, name }`. Endereço inválido é descartado; sem nenhum\ndestinatário válido em `to`, 422.\n\nCorpo: `body_html` (apelido `body`) ou `body_text`. Mandando só um, o outro é derivado. Anexos\nvão em base64, até 10 arquivos e 10 MB somados.\n\nO envio é síncrono: a resposta só volta depois que o servidor da caixa aceitou a mensagem. Com\nanexo grande ou servidor lento isso pode levar alguns segundos.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "subject"
                ],
                "properties": {
                  "account": {
                    "type": "string",
                    "maxLength": 20,
                    "description": "Id numérico da caixa ou `general`. Padrão: caixa pessoal do dono do token."
                  },
                  "to": {
                    "description": "String, lista de strings ou lista de { email, name }.",
                    "oneOf": [
                      {
                        "type": "string",
                        "format": "email"
                      },
                      {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/EmailRecipient"
                        }
                      }
                    ]
                  },
                  "cc": {
                    "description": "Mesmo formato de `to`.",
                    "oneOf": [
                      {
                        "type": "string",
                        "format": "email"
                      },
                      {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/EmailRecipient"
                        }
                      }
                    ]
                  },
                  "bcc": {
                    "description": "Mesmo formato de `to`. Cópia oculta.",
                    "oneOf": [
                      {
                        "type": "string",
                        "format": "email"
                      },
                      {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/EmailRecipient"
                        }
                      }
                    ]
                  },
                  "subject": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "body_html": {
                    "type": "string",
                    "maxLength": 500000,
                    "description": "Corpo em HTML. Apelido: `body`."
                  },
                  "body_text": {
                    "type": "string",
                    "maxLength": 200000,
                    "description": "Corpo em texto puro. Obrigatório quando não há `body_html`."
                  },
                  "reply_to": {
                    "type": "string",
                    "format": "email",
                    "description": "Endereço de resposta (Reply-To)."
                  },
                  "in_reply_to_id": {
                    "type": "integer",
                    "description": "Id de um e-mail recebido no Atlas, para responder na mesma conversa."
                  },
                  "attachments": {
                    "type": "array",
                    "maxItems": 10,
                    "description": "Até 10 arquivos e 10 MB somados.",
                    "items": {
                      "$ref": "#/components/schemas/EmailAttachment"
                    }
                  }
                }
              },
              "example": {
                "account": "3",
                "to": [
                  {
                    "email": "cliente@exemplo.com.br",
                    "name": "Cliente"
                  }
                ],
                "cc": "financeiro@suaempresa.com.br",
                "subject": "Proposta comercial",
                "body_html": "<p>Olá! Segue a proposta em anexo.</p>",
                "attachments": [
                  {
                    "filename": "proposta.pdf",
                    "content_base64": "JVBERi0xLjQK...",
                    "mime": "application/pdf"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Enviado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "E-mail enviado."
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailSent"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Token sem o escopo `send`, ou caixa que o dono do token não pode usar (a caixa de outra pessoa responde 403 mesmo existindo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          },
          "422": {
            "description": "Validação (sem destinatário válido, sem corpo, anexo inválido ou acima de 10 MB), caixa inativa ou nenhuma caixa disponível para o usuário.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          },
          "502": {
            "description": "O servidor da caixa (SMTP ou Microsoft) recusou ou não respondeu. A mensagem fica em Enviados com status `failed` e o detalhe em Logs de Integração.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          }
        }
      }
    },
    "/emails/{id}": {
      "get": {
        "tags": [
          "E-mail"
        ],
        "summary": "Consultar e-mail enviado",
        "operationId": "getEmail",
        "x-scope": "read",
        "description": "Status e destinatários de um e-mail enviado pela API ou pela tela (só mensagens de saída da conta).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/EmailSent"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "E-mail inexistente, de entrada, ou de outra conta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              }
            }
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Listar webhooks",
        "operationId": "listWebhooks",
        "x-scope": "read",
        "description": "Sem paginação e sem filtros. O segredo não é exibido na listagem.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Criar webhook",
        "operationId": "createWebhook",
        "x-scope": "write",
        "description": "O segredo é gerado pelo servidor e **devolvido apenas nesta resposta e nas\nleituras individuais**. Use `*` em `events` para receber todos.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "$ref": "#/components/schemas/WebhookEvent"
                    }
                  },
                  "is_active": {
                    "type": "boolean",
                    "default": true,
                    "nullable": true
                  }
                }
              },
              "example": {
                "url": "https://seusistema.com/hooks/atlas",
                "events": [
                  "contact.created",
                  "message.received"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Criado. Guarde o `secret`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookWithSecret"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/webhooks/{webhook}": {
      "parameters": [
        {
          "name": "webhook",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Obter webhook",
        "operationId": "getWebhook",
        "x-scope": "read",
        "responses": {
          "200": {
            "description": "OK. Inclui o segredo.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookWithSecret"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Atualizar webhook",
        "operationId": "updateWebhook",
        "x-scope": "write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "$ref": "#/components/schemas/WebhookEvent"
                    }
                  },
                  "is_active": {
                    "type": "boolean",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Atualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookWithSecret"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Remover webhook",
        "operationId": "deleteWebhook",
        "x-scope": "write",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/webhooks/{webhook}/test": {
      "parameters": [
        {
          "name": "webhook",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Disparar teste",
        "operationId": "testWebhook",
        "x-scope": "send",
        "description": "Envia o evento `webhook.test` para a URL cadastrada, com o corpo\n`{\"message\":\"This is a test webhook event.\",\"timestamp\":\"...\"}`.\n",
        "responses": {
          "200": {
            "description": "Teste despachado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Test webhook dispatched."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer SEU_TOKEN`, sempre acompanhado de\n`Accept: application/json`.\n\nO token carrega escopos, definidos na criação e não editáveis:\n\n- `read`: leitura (todos os `GET`).\n- `write`: criar, alterar e apagar dados.\n- `send`: disparos que saem para o cliente ou terceiros e consomem\n  crédito (mensagens, templates, e-mail por caixa, teste de webhook).\n\nCada operação declara o escopo exigido em `x-scope`. Token com acesso\ntotal (`*`, anterior aos escopos) passa em todos.\n"
      }
    },
    "parameters": {
      "Page": {
        "name": "page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        }
      },
      "PerPage": {
        "name": "per_page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "PerPage25": {
        "name": "per_page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "SortDirDesc": {
        "name": "sort_dir",
        "in": "query",
        "description": "Qualquer valor diferente de `asc` é tratado como `desc`.",
        "schema": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ],
          "default": "desc"
        }
      },
      "SortDirAsc": {
        "name": "sort_dir",
        "in": "query",
        "description": "Qualquer valor diferente de `desc` é tratado como `asc`.",
        "schema": {
          "type": "string",
          "enum": [
            "asc",
            "desc"
          ],
          "default": "asc"
        }
      },
      "ContactId": {
        "name": "contact",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      },
      "ConversationId": {
        "name": "conversation",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      },
      "InvoiceId": {
        "name": "invoice",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      },
      "ProposalId": {
        "name": "proposal",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      },
      "AppointmentId": {
        "name": "appointment",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      },
      "ProductId": {
        "name": "product",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Token ausente, inválido ou expirado.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "example": "Unauthenticated."
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Quatro causas, distinguíveis pelo corpo:\n\n- **token sem o escopo exigido** (`code: insufficient_scope`, com\n  `required_scope`), ver a seção *Escopos do token*;\n- usuário sem conta vinculada;\n- licença sem API habilitada;\n- addon de acesso à API inativo.\n",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/ErrorScope"
                },
                {
                  "$ref": "#/components/schemas/ErrorSimple"
                }
              ]
            },
            "examples": {
              "escopoInsuficiente": {
                "summary": "Token sem o escopo exigido",
                "value": {
                  "error": "Este token não tem o escopo 'send'. Crie um token com esse escopo em Configurações → Tokens de API.",
                  "code": "insufficient_scope",
                  "required_scope": "send"
                }
              },
              "semTenant": {
                "value": {
                  "error": "Tenant not found."
                }
              },
              "semLicenca": {
                "value": {
                  "error": "API access is not available on this license."
                }
              },
              "semAddon": {
                "value": {
                  "error": "API Access addon is not active. Please activate it in Settings → Addons."
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Recurso inexistente **ou** pertencente a outra conta. As duas situações\nrespondem igual, por decisão de segurança.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorSimple"
            },
            "example": {
              "error": "Contact not found."
            }
          }
        }
      },
      "ValidationError": {
        "description": "Falha de validação. Formato padrão do framework.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "description": "A primeira mensagem de erro."
                },
                "errors": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "example": {
              "message": "The name field is required.",
              "errors": {
                "name": [
                  "The name field is required."
                ]
              }
            }
          }
        }
      },
      "CustomFieldValidationError": {
        "description": "Validação, incluindo campos personalizados. Quando algum campo é inválido ou a chave não\nexiste, **nada** do request é gravado.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CustomFieldErrorBody"
            },
            "example": {
              "message": "Campos personalizados inválidos. Nada foi gravado.",
              "errors": {
                "custom_fields.tipo_de_vaga": [
                  "Valor fora das opções do campo."
                ]
              },
              "custom_field_errors": [
                {
                  "field": "tipo_de_vaga",
                  "label": "Tipo de vaga",
                  "type": "select",
                  "received": "Inexistente",
                  "reason": "Valor fora das opções do campo.",
                  "valid_options": [
                    {
                      "value": "SDR",
                      "label": "SDR"
                    },
                    {
                      "value": "Vendas / Closer",
                      "label": "Vendas / Closer"
                    }
                  ]
                }
              ],
              "unknown_keys": []
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Limite de 60 requisições por minuto estourado.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer",
              "example": 60
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorSimple"
            },
            "example": {
              "error": "Too many requests. Limit: 60/minute."
            }
          }
        }
      },
      "Deleted": {
        "description": "Removido.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string",
                  "example": "Contact deleted successfully."
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "CustomFieldsInput": {
        "type": "object",
        "additionalProperties": true,
        "description": "`{chave: valor}` com as chaves do módulo de Campos Personalizados (`GET /custom-fields`).\n`cf_chave` também vale (dentro de `custom_fields` ou solto na raiz do corpo).\nFormato aceito por tipo:\n- `text`, `textarea`: texto (respeita tamanho mínimo e máximo do campo)\n- `select`, `radio`: o valor **ou** o rótulo de uma opção, sem diferenciar maiúsculas\n- `multiselect`: lista de valores ou rótulos das opções\n- `checkbox`: `true`/`false` ou lista de valores das opções\n- `number`, `rating`: número inteiro\n- `decimal`, `currency`: número (`1234.56`) ou texto `\"1.234,56\"`\n- `date`: `AAAA-MM-DD`\n- `datetime`: ISO 8601 (`2026-09-30T14:00:00-03:00`)\n- `url`: começando com `http://` ou `https://`\n- `email`, `phone`, `color` (`#RRGGBB`)\n- `file`, `image`: não gravados pela API (enviar pela tela)\n\nCampos obrigatórios só são exigidos na criação (POST). No PATCH, só as chaves enviadas\nmudam e `null` apaga o valor do campo. Chave inexistente: `422` com `unknown_keys`\n(contas em modo compatível recebem `warnings` e a chave vai para `legacy_custom_fields`).\n",
        "example": {
          "cargo_da_vaga": "SDR",
          "tipo_de_vaga": "SDR",
          "data_da_candidatura": "2026-09-30"
        }
      },
      "CustomFieldValues": {
        "type": "object",
        "additionalProperties": true,
        "description": "Valores do módulo de Campos Personalizados, os mesmos da tela, com as chaves do módulo. Só campos ativos e preenchidos. Tipos como em `CustomFieldsInput` (data em `AAAA-MM-DD`, data e hora em ISO 8601, número como número, múltipla seleção como lista).",
        "example": {
          "cargo_da_vaga": "SDR",
          "tipo_de_vaga": "SDR",
          "data_da_candidatura": "2026-09-30"
        }
      },
      "LegacyCustomFields": {
        "type": "object",
        "additionalProperties": true,
        "description": "Somente leitura. Chaves antigas que não existem no módulo (contas em modo compatível e dados de importação). Não aparecem na tela nem para a IA."
      },
      "CustomFieldWarnings": {
        "type": "array",
        "items": {
          "type": "string"
        },
        "description": "Só em contas no modo compatível, quando alguma chave enviada não existe no módulo."
      },
      "CustomFieldErrorBody": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "custom_field_errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "description": "Chave do campo (ou a chave desconhecida)."
                },
                "label": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "received": {
                  "description": "Valor recebido, como veio."
                },
                "reason": {
                  "type": "string"
                },
                "valid_options": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "value": {
                        "type": "string"
                      },
                      "label": {
                        "type": "string"
                      }
                    }
                  },
                  "description": "Só em seleção, radio e checkbox."
                }
              }
            }
          },
          "unknown_keys": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CustomFieldDefinition": {
        "type": "object",
        "properties": {
          "entity_type": {
            "type": "string",
            "enum": [
              "deal",
              "contact"
            ]
          },
          "key": {
            "type": "string",
            "example": "cargo_da_vaga",
            "description": "Fixa depois de criada; renomear o rótulo não muda a chave."
          },
          "label": {
            "type": "string",
            "example": "Cargo da vaga"
          },
          "field_type": {
            "type": "string",
            "enum": [
              "text",
              "textarea",
              "number",
              "decimal",
              "date",
              "datetime",
              "select",
              "multiselect",
              "checkbox",
              "radio",
              "email",
              "phone",
              "url",
              "file",
              "image",
              "color",
              "rating",
              "currency"
            ]
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "is_required": {
            "type": "boolean"
          },
          "is_active": {
            "type": "boolean"
          },
          "order": {
            "type": "integer"
          }
        }
      },
      "DealDocument": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "example": "contrato.pdf"
          },
          "size": {
            "type": "integer",
            "description": "Bytes."
          },
          "mime_type": {
            "type": "string",
            "nullable": true
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Link assinado e temporário para baixar o arquivo, sem token. Vale 30 minutos; vencido ou alterado, responde 403."
          },
          "url_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Quando o link de `url` deixa de valer."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ErrorScope": {
        "type": "object",
        "description": "Erro de escopo de token. Único erro da API com código de máquina.",
        "required": [
          "error",
          "code",
          "required_scope"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Mensagem legível."
          },
          "code": {
            "type": "string",
            "enum": [
              "insufficient_scope"
            ]
          },
          "required_scope": {
            "type": "string",
            "enum": [
              "read",
              "write",
              "send"
            ]
          }
        }
      },
      "Pipeline": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 38
          },
          "name": {
            "type": "string",
            "example": "Vendas"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "is_default": {
            "type": "boolean"
          },
          "is_active": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "stages": {
            "type": "array",
            "description": "Etapas do funil em ordem, inclusive as de ganho e de perda.",
            "items": {
              "$ref": "#/components/schemas/PipelineStage"
            }
          }
        }
      },
      "PipelineStage": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 279
          },
          "pipeline_id": {
            "type": "integer",
            "example": 38
          },
          "name": {
            "type": "string",
            "example": "Qualificado"
          },
          "color": {
            "type": "string",
            "nullable": true,
            "example": "#10b981"
          },
          "probability": {
            "type": "integer",
            "description": "Probabilidade de fechamento atribuída à etapa, de 0 a 100."
          },
          "rotting_days": {
            "type": "integer",
            "nullable": true,
            "description": "Dias parado na etapa a partir dos quais o negócio é sinalizado como esfriando."
          },
          "is_won": {
            "type": "boolean",
            "description": "Etapa de ganho."
          },
          "is_lost": {
            "type": "boolean",
            "description": "Etapa de perda."
          },
          "sort_order": {
            "type": "integer"
          }
        }
      },
      "DealRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 42
          },
          "name": {
            "type": "string",
            "example": "Vendas"
          }
        }
      },
      "Deal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 23690
          },
          "title": {
            "type": "string",
            "example": "Novo lead - Maria Exemplo"
          },
          "value": {
            "type": "number",
            "format": "float",
            "example": 3500
          },
          "currency": {
            "type": "string",
            "example": "BRL"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "won",
              "lost"
            ]
          },
          "probability": {
            "type": "integer",
            "description": "Herdada da etapa, 0 a 100."
          },
          "pipeline": {
            "$ref": "#/components/schemas/DealRef"
          },
          "stage": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "integer",
                "example": 279
              },
              "name": {
                "type": "string",
                "example": "Qualificado"
              },
              "is_won": {
                "type": "boolean"
              },
              "is_lost": {
                "type": "boolean"
              }
            }
          },
          "contact": {
            "$ref": "#/components/schemas/DealRef"
          },
          "assigned_to": {
            "$ref": "#/components/schemas/DealRef"
          },
          "conversation_id": {
            "type": "integer",
            "nullable": true,
            "description": "Conversa vinculada ao negócio, quando houver."
          },
          "expected_close_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "stage_changed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Última entrada na etapa atual."
          },
          "closed_at": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/CustomFieldValues"
          }
        }
      },
      "DealDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Deal"
          },
          {
            "type": "object",
            "properties": {
              "notes": {
                "type": "string",
                "nullable": true
              },
              "legacy_custom_fields": {
                "$ref": "#/components/schemas/LegacyCustomFields"
              },
              "source_id": {
                "type": "integer",
                "nullable": true
              },
              "campaign_id": {
                "type": "integer",
                "nullable": true
              },
              "loss_reason": {
                "$ref": "#/components/schemas/DealRef"
              },
              "loss_notes": {
                "type": "string",
                "nullable": true
              },
              "lost_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          }
        ]
      },
      "DealEffects": {
        "type": "object",
        "description": "Interruptores efetivamente aplicados na chamada. Ambos `false` quando omitidos.",
        "properties": {
          "trigger_automations": {
            "type": "boolean"
          },
          "fire_pixel_events": {
            "type": "boolean"
          }
        }
      },
      "DealActivity": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "example": "stage_change",
            "description": "created, stage_change, status_change, note, call, email, whatsapp, meeting…"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "user": {
            "$ref": "#/components/schemas/DealRef"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Em movimentações: old_stage_id, new_stage_id, actor_label, via, trigger_automations, fire_pixel_events."
          },
          "activity_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Meta": {
        "type": "object",
        "properties": {
          "current_page": {
            "type": "integer"
          },
          "last_page": {
            "type": "integer"
          },
          "per_page": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "ErrorSimple": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "EmailAccount": {
        "type": "object",
        "properties": {
          "account": {
            "type": "string",
            "example": "3",
            "description": "Chave para `POST /emails/send`: id numérico da caixa ou `general`."
          },
          "name": {
            "type": "string",
            "example": "Comercial"
          },
          "from_email": {
            "type": "string",
            "nullable": true,
            "example": "comercial@suaempresa.com.br"
          },
          "from_name": {
            "type": "string",
            "nullable": true,
            "example": "Comercial"
          },
          "provider": {
            "type": "string",
            "enum": [
              "imap_smtp",
              "microsoft",
              "smtp"
            ],
            "description": "`smtp` é a caixa Geral das configurações."
          },
          "is_general": {
            "type": "boolean"
          },
          "is_personal": {
            "type": "boolean",
            "description": "Caixa pessoal do dono do token."
          },
          "is_active": {
            "type": "boolean",
            "description": "Só caixa ativa envia."
          }
        }
      },
      "EmailRecipient": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "EmailAttachment": {
        "type": "object",
        "required": [
          "filename",
          "content_base64"
        ],
        "properties": {
          "filename": {
            "type": "string",
            "maxLength": 255,
            "example": "proposta.pdf"
          },
          "content_base64": {
            "type": "string",
            "description": "Conteúdo do arquivo em base64."
          },
          "mime": {
            "type": "string",
            "maxLength": 100,
            "default": "application/octet-stream",
            "example": "application/pdf"
          }
        }
      },
      "EmailSent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1234
          },
          "account": {
            "type": "string",
            "example": "3"
          },
          "message_id": {
            "type": "string",
            "description": "Message-ID do cabeçalho."
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "failed",
              "draft"
            ]
          },
          "from_email": {
            "type": "string",
            "nullable": true
          },
          "from_name": {
            "type": "string",
            "nullable": true
          },
          "to": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailRecipient"
            }
          },
          "cc": {
            "type": "array",
            "nullable": true,
            "items": {
              "$ref": "#/components/schemas/EmailRecipient"
            }
          },
          "bcc": {
            "type": "array",
            "nullable": true,
            "items": {
              "$ref": "#/components/schemas/EmailRecipient"
            }
          },
          "subject": {
            "type": "string"
          },
          "in_reply_to": {
            "type": "string",
            "nullable": true,
            "description": "Message-ID do e-mail respondido."
          },
          "sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "sent_by_user_id": {
            "type": "integer",
            "nullable": true,
            "description": "Usuário do token que enviou."
          }
        }
      },
      "WaError": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Ref": {
        "type": "object",
        "nullable": true,
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "trading_name": {
            "type": "string",
            "nullable": true
          },
          "display_name": {
            "type": "string",
            "description": "Calculado a partir do nome e do nome fantasia."
          },
          "person_type": {
            "type": "string",
            "enum": [
              "individual",
              "company"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "lead",
              "customer",
              "prospect",
              "supplier"
            ]
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "mobile": {
            "type": "string",
            "nullable": true
          },
          "document": {
            "type": "string",
            "nullable": true
          },
          "source": {
            "type": "string",
            "nullable": true,
            "description": "Texto livre. É um rótulo de origem, não um canal detectado automaticamente."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "group": {
            "$ref": "#/components/schemas/Ref"
          },
          "assigned_to": {
            "$ref": "#/components/schemas/Ref"
          },
          "deals_count": {
            "type": "integer",
            "description": "Preenchido na listagem. Na leitura individual vem zero."
          },
          "contracts_count": {
            "type": "integer",
            "description": "Preenchido na listagem. Na leitura individual vem zero."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "custom_fields": {
            "$ref": "#/components/schemas/CustomFieldValues"
          }
        }
      },
      "ContactDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Contact"
          },
          {
            "type": "object",
            "properties": {
              "website": {
                "type": "string",
                "nullable": true
              },
              "company": {
                "type": "string",
                "nullable": true
              },
              "position": {
                "type": "string",
                "nullable": true
              },
              "state_registration": {
                "type": "string",
                "nullable": true
              },
              "municipal_registration": {
                "type": "string",
                "nullable": true
              },
              "address": {
                "type": "string",
                "nullable": true
              },
              "address_number": {
                "type": "string",
                "nullable": true
              },
              "address_complement": {
                "type": "string",
                "nullable": true
              },
              "neighborhood": {
                "type": "string",
                "nullable": true
              },
              "city": {
                "type": "string",
                "nullable": true
              },
              "state": {
                "type": "string",
                "nullable": true
              },
              "zip_code": {
                "type": "string",
                "nullable": true
              },
              "country": {
                "type": "string",
                "nullable": true
              },
              "birth_date": {
                "type": "string",
                "format": "date",
                "nullable": true
              },
              "notes": {
                "type": "string",
                "nullable": true
              },
              "legacy_custom_fields": {
                "$ref": "#/components/schemas/LegacyCustomFields"
              },
              "notification_settings": {
                "type": "object",
                "additionalProperties": true
              },
              "parent_id": {
                "type": "integer",
                "nullable": true
              },
              "bot_protected": {
                "type": "boolean"
              },
              "last_contact_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "employees": {
                "type": "array",
                "description": "Pessoas vinculadas a este contato. Só é carregado na leitura individual.",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string",
                      "nullable": true
                    },
                    "phone": {
                      "type": "string",
                      "nullable": true
                    },
                    "mobile": {
                      "type": "string",
                      "nullable": true
                    },
                    "position": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "ContactCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "trading_name": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 255,
            "nullable": true
          },
          "phone": {
            "type": "string",
            "maxLength": 50,
            "nullable": true
          },
          "mobile": {
            "type": "string",
            "maxLength": 50,
            "nullable": true
          },
          "website": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "company": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "position": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "document": {
            "type": "string",
            "maxLength": 50,
            "nullable": true
          },
          "state_registration": {
            "type": "string",
            "maxLength": 50,
            "nullable": true
          },
          "municipal_registration": {
            "type": "string",
            "maxLength": 50,
            "nullable": true
          },
          "person_type": {
            "type": "string",
            "enum": [
              "individual",
              "company"
            ],
            "default": "individual",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "lead",
              "customer",
              "prospect",
              "supplier"
            ],
            "default": "lead",
            "nullable": true
          },
          "source": {
            "type": "string",
            "maxLength": 100,
            "nullable": true
          },
          "contact_group_id": {
            "type": "integer",
            "nullable": true
          },
          "assigned_to": {
            "type": "integer",
            "nullable": true
          },
          "parent_id": {
            "type": "integer",
            "nullable": true
          },
          "address": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "address_number": {
            "type": "string",
            "maxLength": 20,
            "nullable": true
          },
          "address_complement": {
            "type": "string",
            "maxLength": 100,
            "nullable": true
          },
          "neighborhood": {
            "type": "string",
            "maxLength": 100,
            "nullable": true
          },
          "city": {
            "type": "string",
            "maxLength": 100,
            "nullable": true
          },
          "state": {
            "type": "string",
            "maxLength": 50,
            "nullable": true
          },
          "zip_code": {
            "type": "string",
            "maxLength": 20,
            "nullable": true
          },
          "country": {
            "type": "string",
            "maxLength": 100,
            "nullable": true
          },
          "birth_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "custom_fields": {
            "$ref": "#/components/schemas/CustomFieldsInput"
          },
          "notification_settings": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true
          }
        }
      },
      "ContactUpdate": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ContactCreate"
          },
          {
            "type": "object",
            "properties": {
              "is_active": {
                "type": "boolean",
                "nullable": true
              },
              "bot_protected": {
                "type": "boolean",
                "nullable": true
              }
            }
          }
        ],
        "description": "Todos os campos são opcionais. `is_active` e `bot_protected` só existem aqui."
      },
      "DealSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "value": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "stage": {
            "$ref": "#/components/schemas/Ref"
          },
          "assigned_to": {
            "$ref": "#/components/schemas/Ref"
          },
          "expected_close_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "won_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lost_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Conversation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "remote_jid": {
            "type": "string",
            "description": "Identificador no WhatsApp, no formato 5511912345678@s.whatsapp.net."
          },
          "display_name": {
            "type": "string",
            "nullable": true
          },
          "avatar_url": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "pending",
              "resolved"
            ]
          },
          "is_group": {
            "type": "boolean"
          },
          "unread_count": {
            "type": "integer"
          },
          "last_message_preview": {
            "type": "string",
            "nullable": true
          },
          "last_message_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "channel": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "provider": {
                "type": "string",
                "description": "Provedor do canal, por exemplo official, uazapi ou instagram."
              }
            }
          },
          "contact": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "nullable": true
              },
              "phone": {
                "type": "string",
                "nullable": true
              },
              "mobile": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "assigned_to": {
            "$ref": "#/components/schemas/Ref"
          },
          "assigned_department": {
            "$ref": "#/components/schemas/Ref"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "conversation_id": {
            "type": "integer"
          },
          "direction": {
            "type": "string",
            "enum": [
              "incoming",
              "outgoing"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "document",
              "video",
              "audio"
            ]
          },
          "content": {
            "type": "string",
            "nullable": true
          },
          "media_url": {
            "type": "string",
            "nullable": true
          },
          "media_mimetype": {
            "type": "string",
            "nullable": true
          },
          "filename": {
            "type": "string",
            "nullable": true,
            "description": "**Sempre responde `null`.** O campo é montado a partir de um atributo\nque não existe na tabela. Use `media_url` para identificar o arquivo.\n"
          },
          "status": {
            "type": "string"
          },
          "remote_message_id": {
            "type": "string",
            "nullable": true
          },
          "sender_user": {
            "$ref": "#/components/schemas/Ref"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "Mensagens enviadas por esta API trazem via = \"api\"."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MessageSend": {
        "type": "object",
        "description": "É obrigatório ao menos um entre `content`, `media` e `media_url`.",
        "properties": {
          "content": {
            "type": "string",
            "maxLength": 10000,
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "image",
              "document",
              "video",
              "audio"
            ],
            "nullable": true
          },
          "media_url": {
            "type": "string",
            "format": "uri",
            "nullable": true
          },
          "filename": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          }
        }
      },
      "MessageSendByPhone": {
        "allOf": [
          {
            "$ref": "#/components/schemas/MessageSend"
          },
          {
            "type": "object",
            "properties": {
              "phone": {
                "type": "string",
                "maxLength": 50,
                "description": "Obrigatório quando não há `conversation_id`."
              },
              "conversation_id": {
                "type": "integer",
                "nullable": true
              },
              "channel_id": {
                "type": "integer",
                "description": "Obrigatório quando não há `conversation_id`."
              }
            }
          }
        ]
      },
      "Invoice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "number": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending",
              "sent",
              "paid",
              "cancelled"
            ]
          },
          "issue_date": {
            "type": "string",
            "format": "date"
          },
          "due_date": {
            "type": "string",
            "format": "date"
          },
          "subtotal": {
            "type": "number"
          },
          "discount_amount": {
            "type": "number"
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percent"
            ]
          },
          "tax_total": {
            "type": "number"
          },
          "total": {
            "type": "number"
          },
          "amount_paid": {
            "type": "number"
          },
          "amount_due": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "is_recurring": {
            "type": "boolean"
          },
          "contact": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "created_by": {
            "$ref": "#/components/schemas/Ref"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InvoiceDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Invoice"
          },
          {
            "type": "object",
            "properties": {
              "description": {
                "type": "string",
                "nullable": true
              },
              "notes": {
                "type": "string",
                "nullable": true
              },
              "terms": {
                "type": "string",
                "nullable": true
              },
              "public_url": {
                "type": "string",
                "description": "Link público da fatura, para envio ao cliente."
              },
              "sent_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "paid_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "viewed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "items": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/InvoiceItem"
                }
              },
              "payments": {
                "type": "array",
                "description": "Preenchido na leitura individual. Na criação e na atualização vem vazio.",
                "items": {
                  "$ref": "#/components/schemas/PaymentSummary"
                }
              }
            }
          }
        ]
      },
      "InvoiceItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "quantity": {
            "type": "number"
          },
          "unit": {
            "type": "string",
            "nullable": true
          },
          "unit_price": {
            "type": "number"
          },
          "discount_amount": {
            "type": "number"
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percent"
            ]
          },
          "tax_rate": {
            "type": "number"
          },
          "tax_amount": {
            "type": "number"
          },
          "total": {
            "type": "number"
          },
          "product_id": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "InvoiceCreate": {
        "type": "object",
        "required": [
          "contact_id",
          "due_date",
          "items"
        ],
        "properties": {
          "contact_id": {
            "type": "integer"
          },
          "title": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "issue_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "due_date": {
            "type": "string",
            "format": "date"
          },
          "currency": {
            "type": "string",
            "maxLength": 10,
            "default": "BRL",
            "nullable": true
          },
          "discount_amount": {
            "type": "number",
            "minimum": 0,
            "nullable": true
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percent"
            ],
            "default": "fixed",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "terms": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "name",
                "quantity",
                "unit_price"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "maxLength": 255
                },
                "description": {
                  "type": "string",
                  "maxLength": 1000,
                  "nullable": true
                },
                "quantity": {
                  "type": "number",
                  "minimum": 0.01
                },
                "unit_price": {
                  "type": "number",
                  "minimum": 0
                },
                "unit": {
                  "type": "string",
                  "maxLength": 50,
                  "nullable": true
                },
                "product_id": {
                  "type": "integer",
                  "nullable": true
                },
                "tax_rate": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "nullable": true
                },
                "discount_amount": {
                  "type": "number",
                  "minimum": 0,
                  "nullable": true
                },
                "discount_type": {
                  "type": "string",
                  "enum": [
                    "fixed",
                    "percent"
                  ],
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "InvoiceUpdate": {
        "type": "object",
        "description": "Não aceita itens e não recalcula totais.",
        "properties": {
          "contact_id": {
            "type": "integer"
          },
          "title": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "issue_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "due_date": {
            "type": "string",
            "format": "date"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "pending",
              "sent",
              "paid",
              "cancelled"
            ]
          },
          "currency": {
            "type": "string",
            "maxLength": 10,
            "nullable": true
          },
          "discount_amount": {
            "type": "number",
            "minimum": 0,
            "nullable": true
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percent"
            ],
            "nullable": true
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "terms": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          }
        }
      },
      "PaymentSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "amount": {
            "type": "number"
          },
          "method": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "paid_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Payment": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PaymentSummary"
          },
          {
            "type": "object",
            "properties": {
              "fee": {
                "type": "number"
              },
              "net_amount": {
                "type": "number"
              },
              "gateway": {
                "type": "string",
                "nullable": true
              },
              "transaction_id": {
                "type": "string",
                "nullable": true
              },
              "notes": {
                "type": "string",
                "nullable": true
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "Proposal": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "number": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "subject": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "accepted",
              "rejected",
              "expired"
            ]
          },
          "proposal_date": {
            "type": "string",
            "format": "date"
          },
          "valid_until": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "subtotal": {
            "type": "number"
          },
          "discount_amount": {
            "type": "number"
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percentage"
            ]
          },
          "tax_amount": {
            "type": "number"
          },
          "total": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "contact": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "assigned_to": {
            "$ref": "#/components/schemas/Ref"
          },
          "created_by": {
            "$ref": "#/components/schemas/Ref"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProposalDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Proposal"
          },
          {
            "type": "object",
            "properties": {
              "deal": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "title": {
                    "type": "string"
                  }
                }
              },
              "content": {
                "type": "string",
                "nullable": true
              },
              "notes": {
                "type": "string",
                "nullable": true
              },
              "terms": {
                "type": "string",
                "nullable": true
              },
              "public_url": {
                "type": "string",
                "description": "Link público da proposta, para envio e assinatura."
              },
              "sent_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "viewed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "accepted_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "rejected_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "signed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "signer_name": {
                "type": "string",
                "nullable": true
              },
              "items": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ProposalItem"
                }
              }
            }
          }
        ]
      },
      "ProposalItem": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "quantity": {
            "type": "number"
          },
          "unit": {
            "type": "string",
            "nullable": true
          },
          "unit_price": {
            "type": "number"
          },
          "discount_amount": {
            "type": "number"
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percentage"
            ]
          },
          "tax_rate": {
            "type": "number"
          },
          "tax_amount": {
            "type": "number"
          },
          "total": {
            "type": "number"
          },
          "is_optional": {
            "type": "boolean"
          },
          "product_id": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "ProposalCreate": {
        "type": "object",
        "required": [
          "contact_id",
          "items"
        ],
        "properties": {
          "contact_id": {
            "type": "integer"
          },
          "deal_id": {
            "type": "integer",
            "nullable": true
          },
          "assigned_to": {
            "type": "integer",
            "nullable": true
          },
          "title": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "subject": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "proposal_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "valid_until": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "currency": {
            "type": "string",
            "maxLength": 10,
            "default": "BRL",
            "nullable": true
          },
          "discount_amount": {
            "type": "number",
            "minimum": 0,
            "nullable": true
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percentage"
            ],
            "nullable": true
          },
          "content": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "terms": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "name",
                "quantity",
                "unit_price"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "maxLength": 255
                },
                "description": {
                  "type": "string",
                  "maxLength": 1000,
                  "nullable": true
                },
                "quantity": {
                  "type": "number",
                  "minimum": 0.01
                },
                "unit_price": {
                  "type": "number",
                  "minimum": 0
                },
                "unit": {
                  "type": "string",
                  "maxLength": 50,
                  "nullable": true
                },
                "product_id": {
                  "type": "integer",
                  "nullable": true
                },
                "tax_rate": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "nullable": true
                },
                "discount_amount": {
                  "type": "number",
                  "minimum": 0,
                  "nullable": true
                },
                "discount_type": {
                  "type": "string",
                  "enum": [
                    "fixed",
                    "percentage"
                  ],
                  "nullable": true
                },
                "is_optional": {
                  "type": "boolean",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "ProposalUpdate": {
        "type": "object",
        "description": "Não aceita itens.",
        "properties": {
          "contact_id": {
            "type": "integer"
          },
          "deal_id": {
            "type": "integer",
            "nullable": true
          },
          "assigned_to": {
            "type": "integer",
            "nullable": true
          },
          "title": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "subject": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "proposal_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "valid_until": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "sent",
              "accepted",
              "rejected",
              "expired"
            ]
          },
          "currency": {
            "type": "string",
            "maxLength": 10,
            "nullable": true
          },
          "discount_amount": {
            "type": "number",
            "minimum": 0,
            "nullable": true
          },
          "discount_type": {
            "type": "string",
            "enum": [
              "fixed",
              "percentage"
            ],
            "nullable": true
          },
          "content": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "terms": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          }
        }
      },
      "Appointment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "meeting",
              "call",
              "task",
              "event",
              "other"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "confirmed",
              "cancelled",
              "completed"
            ]
          },
          "start_at": {
            "type": "string",
            "format": "date-time"
          },
          "end_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "all_day": {
            "type": "boolean"
          },
          "location": {
            "type": "string",
            "nullable": true
          },
          "meeting_type": {
            "type": "string",
            "enum": [
              "in_person",
              "online"
            ],
            "nullable": true
          },
          "color": {
            "type": "string",
            "nullable": true
          },
          "user": {
            "$ref": "#/components/schemas/Ref"
          },
          "contact": {
            "$ref": "#/components/schemas/Ref"
          },
          "deal": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "integer"
              },
              "title": {
                "type": "string"
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AppointmentDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Appointment"
          },
          {
            "type": "object",
            "properties": {
              "description": {
                "type": "string",
                "nullable": true
              },
              "notes": {
                "type": "string",
                "nullable": true
              },
              "google_meet_link": {
                "type": "string",
                "nullable": true
              },
              "google_event_id": {
                "type": "string",
                "nullable": true
              }
            }
          }
        ]
      },
      "AppointmentCreate": {
        "type": "object",
        "required": [
          "title",
          "start_at"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "start_at": {
            "type": "string",
            "format": "date-time"
          },
          "end_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Precisa ser igual ou posterior a `start_at`."
          },
          "all_day": {
            "type": "boolean",
            "nullable": true
          },
          "location": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "meeting",
              "call",
              "task",
              "event",
              "other"
            ],
            "default": "meeting",
            "nullable": true
          },
          "meeting_type": {
            "type": "string",
            "enum": [
              "in_person",
              "online"
            ],
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "confirmed",
              "cancelled",
              "completed"
            ],
            "default": "scheduled",
            "nullable": true
          },
          "color": {
            "type": "string",
            "maxLength": 20,
            "nullable": true
          },
          "user_id": {
            "type": "integer",
            "nullable": true
          },
          "contact_id": {
            "type": "integer",
            "nullable": true
          },
          "deal_id": {
            "type": "integer",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          }
        }
      },
      "AppointmentUpdate": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AppointmentCreate"
          }
        ],
        "description": "Todos os campos opcionais. A verificação de que `end_at` vem depois de `start_at` não é aplicada aqui."
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "sku": {
            "type": "string",
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "product",
              "service"
            ]
          },
          "price": {
            "type": "number"
          },
          "cost": {
            "type": "number"
          },
          "tax_rate": {
            "type": "number"
          },
          "stock_quantity": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "unit": {
            "type": "string",
            "nullable": true
          },
          "is_active": {
            "type": "boolean"
          },
          "track_stock": {
            "type": "boolean"
          },
          "has_variations": {
            "type": "boolean"
          },
          "category": {
            "$ref": "#/components/schemas/Ref"
          },
          "image": {
            "type": "string",
            "nullable": true,
            "description": "Caminho relativo do arquivo, não uma URL completa."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProductDetailed": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Product"
          },
          {
            "type": "object",
            "properties": {
              "description": {
                "type": "string",
                "nullable": true
              },
              "short_description": {
                "type": "string",
                "nullable": true
              },
              "min_stock": {
                "type": "number"
              },
              "custom_fields": {
                "type": "object",
                "additionalProperties": true
              },
              "margin": {
                "type": "number",
                "description": "Margem calculada a partir de preço e custo."
              },
              "variations": {
                "type": "array",
                "description": "Somente na leitura individual. Não há endpoint para criar ou editar variações.",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "sku": {
                      "type": "string",
                      "nullable": true
                    },
                    "price": {
                      "type": "number"
                    },
                    "cost": {
                      "type": "number"
                    },
                    "stock_quantity": {
                      "type": "number"
                    },
                    "is_active": {
                      "type": "boolean"
                    },
                    "is_default": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        ]
      },
      "ProductCreate": {
        "type": "object",
        "required": [
          "name",
          "price"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "sku": {
            "type": "string",
            "maxLength": 100,
            "nullable": true
          },
          "description": {
            "type": "string",
            "maxLength": 5000,
            "nullable": true
          },
          "short_description": {
            "type": "string",
            "maxLength": 500,
            "nullable": true
          },
          "type": {
            "type": "string",
            "enum": [
              "product",
              "service"
            ],
            "default": "product",
            "nullable": true
          },
          "category": {
            "type": "string",
            "maxLength": 255,
            "nullable": true
          },
          "category_id": {
            "type": "integer",
            "nullable": true
          },
          "price": {
            "type": "number",
            "minimum": 0
          },
          "cost": {
            "type": "number",
            "minimum": 0,
            "nullable": true
          },
          "currency": {
            "type": "string",
            "maxLength": 10,
            "nullable": true
          },
          "unit": {
            "type": "string",
            "maxLength": 50,
            "nullable": true
          },
          "tax_rate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "nullable": true
          },
          "track_stock": {
            "type": "boolean",
            "nullable": true
          },
          "stock_quantity": {
            "type": "number",
            "minimum": 0,
            "nullable": true
          },
          "min_stock": {
            "type": "number",
            "minimum": 0,
            "nullable": true
          },
          "is_active": {
            "type": "boolean",
            "default": true,
            "nullable": true
          },
          "custom_fields": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true
          }
        }
      },
      "ProductUpdate": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ProductCreate"
          }
        ],
        "description": "Todos os campos opcionais."
      },
      "WaChannel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "phone_number": {
            "type": "string"
          },
          "profile_name": {
            "type": "string",
            "nullable": true
          },
          "profile_picture": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string"
          },
          "is_connected": {
            "type": "boolean"
          },
          "is_primary": {
            "type": "boolean"
          },
          "is_default": {
            "type": "boolean"
          },
          "waba_id": {
            "type": "string",
            "nullable": true,
            "description": "Identificador da conta de WhatsApp Business na Meta."
          },
          "connected_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Em UTC, com sufixo Z."
          }
        }
      },
      "WaTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "UTILITY",
              "MARKETING",
              "AUTHENTICATION"
            ]
          },
          "category_label": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "example": "APPROVED"
          },
          "header_type": {
            "type": "string"
          },
          "body_text": {
            "type": "string",
            "description": "Corpo do template, com as variáveis no formato {{1}}, {{2}} e assim por diante."
          },
          "footer_text": {
            "type": "string",
            "nullable": true
          },
          "buttons": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object"
            }
          },
          "dynamic_url_buttons": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "has_dynamic_url_buttons": {
            "type": "boolean"
          },
          "variables": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Nomes das variáveis, por exemplo [\"1\",\"2\"]."
          },
          "variables_count": {
            "type": "integer"
          },
          "waba_id": {
            "type": "string",
            "nullable": true
          },
          "last_synced_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Em UTC, com sufixo Z."
          }
        }
      },
      "WebhookEvent": {
        "type": "string",
        "description": "Use `*` para receber todos. Os oito eventos de fatura e produto são\naceitos no cadastro mas **nunca são disparados** hoje.\n",
        "enum": [
          "*",
          "contact.created",
          "contact.updated",
          "contact.deleted",
          "conversation.created",
          "conversation.updated",
          "message.received",
          "message.sent",
          "invoice.created",
          "invoice.updated",
          "invoice.paid",
          "invoice.overdue",
          "invoice.cancelled",
          "product.created",
          "product.updated",
          "product.deleted"
        ]
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEvent"
            }
          },
          "is_active": {
            "type": "boolean"
          },
          "failure_count": {
            "type": "integer",
            "description": "Falhas consecutivas. Em 50, o webhook é desativado automaticamente."
          },
          "last_triggered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Webhook"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "64 caracteres. Usado para calcular a assinatura HMAC-SHA256 de cada\nentrega. Não aparece na listagem.\n"
              }
            }
          }
        ]
      }
    }
  }
}