{
  "openapi": "3.1.0",
  "info": {
    "title": "API do Rumo CRM",
    "version": "1.0.0",
    "summary": "Leia e grave contatos, negócios e conversas do Rumo CRM a partir do seu sistema.",
    "description": "API pública v1 do Rumo CRM. Todo endereço começa com `https://rumocrm.com/api/v1`.\n\n## Autenticação\nCrie um token pessoal em Configurações e envie em todo pedido no cabeçalho\n`Authorization: Bearer <token>`. O token carrega a empresa, o seu papel e os escopos\nque você escolheu (`crm:read`, `crm:write`, `inbox:read`, `inbox:send`). A sessão do\nnavegador não vale na v1: sem o cabeçalho a resposta é `401`.\n\nO token enxerga o mesmo que você enxerga no Rumo CRM: o gerente vê a empresa inteira, o\nvendedor vê só o que é dele. Um registro de outra empresa ou fora do seu papel responde\n`404`, como se não existisse.\n\n## Versionamento\nA v1 só muda de forma aditiva. Podem surgir a qualquer momento: campos novos nas\nrespostas, parâmetros opcionais, endpoints, códigos de erro (`code`) e cabeçalhos.\nNenhum campo é removido ou renomeado e nenhum tipo muda. Uma mudança incompatível vira\n`/api/v2`, com aviso antes. Por isso, seu código deve ignorar campos que não conhece.\n\n## Formatos\nJSON em camelCase. Datas em ISO 8601, sempre em UTC (`2026-10-09T13:45:00.000Z`).\nValores em dinheiro vêm em centavos inteiros (`valueCents`) com `currency: \"BRL\"`.\nOs ids são textos opacos: guarde e compare, não interprete.\n\n## Paginação\nListas respondem `{ \"data\": [...], \"nextCursor\": \"...\", \"hasMore\": true }`. Para a página\nseguinte, repita o mesmo pedido com `cursor=<nextCursor>`. Quando `hasMore` for `false`,\n`nextCursor` vem `null` e a lista acabou. A ordem é estável: um registro nunca aparece\nduas vezes nem fica de fora entre páginas, mesmo com datas de criação iguais.\n\nPara sincronizar só o que mudou, guarde a hora em que a sincronização começou e, na\npróxima, envie `updatedSince` com essa hora.\n\n## Limite de pedidos\nCada token pode fazer até **120 pedidos por minuto** e a empresa inteira, somando todos\nos tokens, até **600 por minuto**. A janela é deslizante: vale a soma dos últimos 60\nsegundos, sem virada de minuto. Os limites são iguais em todos os planos.\n\nToda resposta de um pedido autenticado informa a situação do limite mais apertado no\nmomento (o do token ou o da empresa):\n\n| Cabeçalho | Significado |\n| --- | --- |\n| `RateLimit-Limit` | Pedidos permitidos por minuto nesse limite. |\n| `RateLimit-Remaining` | Quantos ainda cabem agora. |\n| `RateLimit-Reset` | Segundos até abrir a próxima vaga. |\n\nOs mesmos valores saem também em `X-RateLimit-Limit`, `X-RateLimit-Remaining` e\n`X-RateLimit-Reset`. Ao passar do limite a resposta é `429` com `code: rate_limited` e o\ncabeçalho `Retry-After`: espere esse número de segundos antes de repetir o pedido.\n\n## Idempotência\nPara repetir com segurança um `POST`, `PATCH` ou `DELETE` (depois de uma queda de\nconexão, por exemplo), envie o cabeçalho `Idempotency-Key` com um valor único por\noperação, como um UUID. O cabeçalho é opcional.\n\n- Mesma chave e mesmo pedido (método, caminho e corpo idênticos, byte a byte) dentro de\n  24 horas: a operação não roda de novo. Você recebe a resposta guardada da primeira vez,\n  com o cabeçalho `Idempotent-Replayed: true`.\n- Mesma chave com outro pedido: `409 idempotency_key_reused`. Gere uma chave nova.\n- Mesma chave enquanto o primeiro pedido ainda está em andamento:\n  `409 idempotency_key_in_progress`. Espere a resposta do primeiro e repita.\n- Respostas `429` e de erro `5xx` não ficam guardadas: repetir com a mesma chave executa\n  de novo. Os demais erros `4xx` ficam guardados como qualquer resposta.\n\nA chave vale só para o token que a enviou. Em pedidos `GET` ela é ignorada.\n\n## Erros\nTodo erro responde `{ \"error\": { \"type\", \"code\", \"message\", \"param\", \"requestId\" } }`.\n`type` é a família do erro e `code` o motivo exato, estável para o seu código tratar.\n`message` é texto para gente ler e pode mudar. `param` aparece quando o erro é de um\nparâmetro específico.\n\n| Status | `type` | `code` | Quando |\n| --- | --- | --- | --- |\n| 400 | `invalid_request_error` | `invalid_parameter` | Parâmetro com valor inválido (veja `param`). |\n| 400 | `invalid_request_error` | `unknown_parameter` | Parâmetro que a operação não aceita (erro de digitação, por exemplo). |\n| 400 | `invalid_request_error` | `invalid_cursor` | `cursor` alterado ou de outra lista. |\n| 400 | `invalid_request_error` | `invalid_json` | Corpo do pedido que não é um JSON válido. |\n| 400 | `invalid_request_error` | `company_not_found` | `companyId` que não é uma empresa ativa da sua conta. |\n| 400 | `invalid_request_error` | `owner_not_found` | `ownerId` que não é um usuário ativo da sua empresa. |\n| 400 | `invalid_request_error` | `contact_not_found` | `contactId` que não é um contato que você vê. |\n| 400 | `invalid_request_error` | `pipeline_not_found` | `pipelineId` que não é um funil da sua conta. |\n| 400 | `invalid_request_error` | `pipeline_inactive` | Funil desativado: negócio novo não nasce nele. |\n| 400 | `invalid_request_error` | `stage_not_found` | `stageId` que não é uma etapa da sua conta. |\n| 400 | `invalid_request_error` | `stage_not_in_pipeline` | A etapa enviada não pertence ao funil enviado. |\n| 400 | `invalid_request_error` | `stage_is_final` | Etapa de ganho ou perda onde a operação não aceita etapa final (para fechar, use `/win` ou `/lose`). |\n| 400 | `invalid_request_error` | `stage_change_not_allowed` | Etapa ou resultado do negócio enviados no PATCH. Use `/move`, `/win` ou `/lose`. |\n| 400 | `invalid_request_error` | `lost_reason_not_found` | `lostReasonId` que não é um motivo de perda da conta válido para o funil do negócio. |\n| 400 | `invalid_request_error` | `product_not_found` | `productId` que não é um produto do catálogo da conta. |\n| 400 | `invalid_request_error` | `unknown_custom_field` | Campo personalizado (`slug`) que não existe (veja `param`). |\n| 400 | `invalid_request_error` | `invalid_custom_field_value` | Valor fora do tipo do campo personalizado (veja `param`). |\n| 400 | `invalid_request_error` | `invalid_idempotency_key` | `Idempotency-Key` vazia, com mais de 255 caracteres ou fora do ASCII visível. |\n| 401 | `authentication_error` | `missing_api_token` | Pedido sem o cabeçalho `Authorization`. |\n| 401 | `authentication_error` | `invalid_bearer_token` | Cabeçalho `Authorization` fora do formato `Bearer <token>`. |\n| 401 | `authentication_error` | `invalid_api_token` | Token inexistente, expirado, revogado ou de usuário sem acesso à empresa. |\n| 402 | `permission_error` | `subscription_inactive` | A assinatura da empresa não está ativa. |\n| 403 | `permission_error` | `insufficient_api_token_scope` | O token não tem o escopo exigido pela operação. |\n| 403 | `permission_error` | `permission_denied` | O seu papel não permite a operação. |\n| 404 | `not_found_error` | `resource_not_found` | O registro não existe, é de outra empresa ou está fora do seu papel. |\n| 409 | `conflict_error` | `idempotency_key_reused` | `Idempotency-Key` já usada com outro pedido. |\n| 409 | `conflict_error` | `idempotency_key_in_progress` | Pedido com a mesma `Idempotency-Key` ainda em andamento. |\n| 409 | `conflict_error` | `phone_already_in_use` | O telefone já pertence a outro contato da empresa. |\n| 409 | `conflict_error` | `cnpj_already_in_use` | Outra empresa da conta já usa este CNPJ. |\n| 409 | `conflict_error` | `channel_disconnected` | Envio por um canal desligado ou que precisa ser reconectado. |\n| 422 | `invalid_request_error` | `missing_required_custom_fields` | Falta valor em campo personalizado obrigatório (veja `param` e `message`). |\n| 422 | `invalid_request_error` | `pipeline_without_won_stage` | O funil do negócio não tem etapa de ganho. |\n| 422 | `invalid_request_error` | `pipeline_without_lost_stage` | O funil do negócio não tem etapa de perda. |\n| 422 | `invalid_request_error` | (vários) | O pedido é válido, mas a mensagem não pode ser enviada nesta conversa agora: janela de 24 horas, template, descadastro, grupo. Veja a tabela de \"Enviar mensagem na conversa\". |\n| 429 | `rate_limit_error` | `rate_limited` | Limite de pedidos por minuto atingido. Espere `Retry-After` segundos. |\n| 500 | `api_error` | `internal_error` | Falha nossa. Tente de novo e, se continuar, informe o `Request-Id`. |\n| 503 | `api_error` | `api_tokens_disabled` | Acesso por token suspenso temporariamente. |\n| 503 | `api_error` | `queue_unavailable` | A fila de envio de mensagens está fora do ar. Nada foi enviado; tente de novo. |\n| 503 | `api_error` | `template_check_unavailable` | Não deu para consultar os templates do WhatsApp agora. Nada foi enviado; tente de novo. |\n| 503 | `api_error` | `idempotency_unavailable` | Não foi possível garantir a idempotência numa operação que exige isso (envio de mensagem). Nada foi executado; tente de novo. |\n\nToda resposta, de sucesso ou de erro, traz o cabeçalho `Request-Id`. Informe esse valor\nao suporte para localizarmos o pedido.\n"
  },
  "servers": [
    {
      "url": "https://rumocrm.com/api/v1",
      "description": "Produção"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Contatos",
      "description": "Pessoas com quem a sua empresa fala."
    },
    {
      "name": "Conversas",
      "description": "Conversas do inbox (WhatsApp, Instagram, LinkedIn e e-mail), as mensagens e os canais conectados."
    },
    {
      "name": "Empresas",
      "description": "Clientes pessoa jurídica, com CNPJ, ligados a contatos e negócios."
    },
    {
      "name": "Funis",
      "description": "Funis de vendas e as etapas (colunas do quadro) de cada um."
    },
    {
      "name": "Negócios",
      "description": "Oportunidades de venda, cada uma numa etapa de um funil."
    },
    {
      "name": "Webhooks",
      "description": "Avisos que o Rumo CRM envia para o seu sistema quando algo acontece, assinados com a sua chave."
    }
  ],
  "paths": {
    "/contacts": {
      "get": {
        "operationId": "listContacts",
        "tags": [
          "Contatos"
        ],
        "summary": "Listar contatos",
        "description": "Lista os contatos da empresa do token, do mais novo para o mais antigo.\n\nO gerente vê todos os contatos da empresa. O vendedor vê os contatos dos quais é o\ndono e os que são o contato principal de um negócio dele.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Busca por parte do nome, do e-mail ou do telefone (com ou sem máscara).",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Uma página de contatos.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "post": {
        "operationId": "upsertContact",
        "tags": [
          "Contatos"
        ],
        "summary": "Criar ou atualizar contato",
        "description": "Cria o contato ou, se ele já existe na empresa, atualiza o existente e devolve o mesmo\n`id`. Use para registrar leads sem se preocupar com duplicidade.\n\nComo o contato existente é encontrado, nesta ordem:\n1. pelo `phone` (com ou sem máscara, com ou sem DDI);\n2. pelo `email` (sem diferenciar maiúsculas), quando não veio `phone` ou quando o\n   contato com esse e-mail ainda não tem telefone. Um contato com OUTRO telefone\n   nunca é sobrescrito: nesse caso nasce um contato novo.\n\nResponde `201` quando cria e `200` quando atualiza. Envie ao menos `name`, `phone`\nou `email`. Campo nulo ou vazio é ignorado (para apagar um valor, use o PATCH). Na\natualização, os campos enviados substituem os atuais, as `tags` enviadas são\nacrescentadas às que o contato já tem e os `customFields` enviados são gravados sem\nmexer nos demais.\n\nExige o papel de gerente (o vendedor recebe `403 permission_denied`).\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactUpsert"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O contato já existia e foi atualizado.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "201": {
            "description": "Contato criado.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/contacts/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Id do contato.",
          "schema": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          }
        }
      ],
      "get": {
        "operationId": "getContact",
        "tags": [
          "Contatos"
        ],
        "summary": "Ver contato",
        "description": "Devolve um contato. Contato de outra empresa ou fora do seu papel (o vendedor só vê\nos contatos dos quais é o dono e os que são o contato principal de um negócio dele)\nresponde `404`.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "O contato.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "patch": {
        "operationId": "updateContact",
        "tags": [
          "Contatos"
        ],
        "summary": "Editar contato",
        "description": "Altera só os campos enviados. `null` (ou texto vazio) apaga o valor: `phone: null`\ntira o telefone, `companyId: null` desvincula a empresa e `ownerId: null` deixa o\ncontato sem dono. `tags` substitui a lista inteira de tags do contato (`[]` tira\ntodas). Em `customFields`, cada campo enviado é gravado e `null` apaga aquele campo;\nos não enviados ficam como estão.\n\nTrocar o telefone para um que já pertence a outro contato da empresa responde\n`409 phone_already_in_use`. Exige o papel de gerente (o vendedor recebe\n`403 permission_denied`).\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O contato depois da alteração.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Contact"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/companies": {
      "get": {
        "operationId": "listCompanies",
        "tags": [
          "Empresas"
        ],
        "summary": "Listar empresas",
        "description": "Lista as empresas (clientes pessoa jurídica) da conta do token, da mais nova para a\nmais antiga. Empresas desativadas ficam de fora, a menos que você envie\n`includeInactive=true`.\n\nO gerente vê todas as empresas da conta. O vendedor vê as empresas das quais é o\ndono, as ligadas a um negócio dele (como empresa principal ou participante) e as\nempresas dos contatos que ele vê.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Busca por parte da razão social, do nome fantasia ou do CNPJ (com ou sem\nmáscara).\n",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          {
            "name": "includeInactive",
            "in": "query",
            "required": false,
            "description": "Inclui as empresas desativadas (`isActive` igual a `false`).",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Uma página de empresas.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompanyList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "post": {
        "operationId": "createCompany",
        "tags": [
          "Empresas"
        ],
        "summary": "Criar empresa",
        "description": "Cria uma empresa na conta do token.\n\nO CNPJ é opcional, mas único na conta: se outra empresa (ativa ou desativada) já\nusa o mesmo CNPJ, a resposta é `409` com `code` igual a `cnpj_already_in_use`.\nEnvie com ou sem máscara; ele é gravado só com os dígitos e precisa ter dígitos\nverificadores válidos.\n\nSem `ownerId`, o vendedor vira o dono da empresa e, para o gerente, ela fica sem\ndono. O vendedor só pode criar empresas das quais ele mesmo é o dono.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompanyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Empresa criada.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/companies/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CompanyId"
        }
      ],
      "get": {
        "operationId": "getCompany",
        "tags": [
          "Empresas"
        ],
        "summary": "Ver empresa",
        "description": "Devolve uma empresa, inclusive desativada. Empresa de outra conta ou fora do papel\ndo vendedor responde `404`.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "A empresa.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "patch": {
        "operationId": "updateCompany",
        "tags": [
          "Empresas"
        ],
        "summary": "Editar empresa",
        "description": "Altera só os campos enviados. Em `address`, cada parte enviada substitui a atual e\nas demais ficam como estão; `emails` e `phones` substituem a lista inteira. Em\n`customFields`, envie `null` para apagar o valor de um campo.\n\nO CNPJ segue as regras da criação (`409` com `cnpj_already_in_use` se outra\nempresa da conta já usa o mesmo). Só o gerente troca o dono (`ownerId`).\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompanyUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A empresa atualizada.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "delete": {
        "operationId": "deleteCompany",
        "tags": [
          "Empresas"
        ],
        "summary": "Desativar empresa",
        "description": "Desativa a empresa (`isActive` passa a `false`): ela some das listas, mas\ncontinua ligada aos contatos e negócios e pode ser vista pelo id. Repetir o pedido\ndevolve a mesma empresa, sem erro. Só o gerente desativa empresas.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "A empresa desativada.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Company"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/pipelines": {
      "get": {
        "operationId": "listPipelines",
        "tags": [
          "Funis"
        ],
        "summary": "Listar funis",
        "description": "Lista os funis de vendas da empresa do token, na mesma ordem do seletor de funis do\nRumo CRM: o funil padrão primeiro, depois por `order` e por nome.\n\nO gerente e o vendedor veem os mesmos funis, como no quadro de negócios. Funis\ndesativados ficam de fora, a menos que você envie `includeInactive=true`.\n\nA lista vem inteira numa página só: `nextCursor` é sempre `null` e `hasMore` é\nsempre `false`.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "includeInactive",
            "in": "query",
            "required": false,
            "description": "Inclui os funis desativados.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Os funis da empresa.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PipelineList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/pipelines/{id}": {
      "get": {
        "operationId": "getPipeline",
        "tags": [
          "Funis"
        ],
        "summary": "Ver um funil",
        "description": "Devolve um funil da empresa do token, inclusive um funil desativado (`isActive: false`),\npara você conseguir ler o funil de um negócio antigo.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PipelineId"
          }
        ],
        "responses": {
          "200": {
            "description": "O funil.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pipeline"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/pipelines/{id}/stages": {
      "get": {
        "operationId": "listPipelineStages",
        "tags": [
          "Funis"
        ],
        "summary": "Listar as etapas de um funil",
        "description": "Lista as etapas (colunas do quadro) de um funil, na ordem do quadro: primeiro as\netapas abertas, por `order`; depois a de ganho e por último a de perdido.\n\nFunciona também para um funil desativado. A lista vem inteira numa página só:\n`nextCursor` é sempre `null` e `hasMore` é sempre `false`.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/PipelineId"
          }
        ],
        "responses": {
          "200": {
            "description": "As etapas do funil.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PipelineStageList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/deals": {
      "get": {
        "operationId": "listDeals",
        "tags": [
          "Negócios"
        ],
        "summary": "Listar negócios",
        "description": "Lista os negócios da conta do token, do mais novo para o mais antigo. Os filtros se\nsomam (todos precisam valer).\n\nO gerente vê todos os negócios da conta. O vendedor vê só os negócios dos quais é o\nresponsável (`ownerId`), como no quadro de negócios.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "pipelineId",
            "in": "query",
            "required": false,
            "description": "Só os negócios deste funil.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "stageId",
            "in": "query",
            "required": false,
            "description": "Só os negócios nesta etapa.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "ownerId",
            "in": "query",
            "required": false,
            "description": "Só os negócios deste responsável.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "contactId",
            "in": "query",
            "required": false,
            "description": "Só os negócios cujo contato principal é este.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "companyId",
            "in": "query",
            "required": false,
            "description": "Só os negócios cuja empresa principal é esta.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Só os negócios em andamento (`open`), ganhos (`won`) ou perdidos (`lost`).",
            "schema": {
              "$ref": "#/components/schemas/DealStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Uma página de negócios.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "post": {
        "operationId": "createDeal",
        "tags": [
          "Negócios"
        ],
        "summary": "Criar negócio",
        "description": "Cria um negócio em andamento. Envie `stageId` (a etapa onde ele nasce) ou\n`pipelineId` (ele nasce na primeira etapa em andamento desse funil). Com os dois, a\netapa precisa ser desse funil. O negócio nunca nasce numa etapa de ganho ou de perda\nnem num funil desativado.\n\nSem `ownerId`, o vendedor vira o responsável. Para o gerente, o negócio segue a\nroleta de leads da conta, quando ela está ligada; senão, o gerente vira o\nresponsável. O vendedor só cria negócios dos quais ele mesmo é o responsável.\n\nCampo personalizado de negócio marcado como obrigatório que ficar sem valor responde\n`422 missing_required_custom_fields`, sem criar nada.\n\nCriar um negócio tem o mesmo efeito de criar pela tela: dispara o webhook\n`deal.created` e as automações de \"negócio criado\".\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DealCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Negócio criado.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deal"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/deals/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DealId"
        }
      ],
      "get": {
        "operationId": "getDeal",
        "tags": [
          "Negócios"
        ],
        "summary": "Ver negócio",
        "description": "Devolve um negócio. Negócio de outra conta ou, para o vendedor, de outro responsável\nresponde `404`.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "O negócio.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deal"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "patch": {
        "operationId": "updateDeal",
        "tags": [
          "Negócios"
        ],
        "summary": "Editar negócio",
        "description": "Altera só os campos enviados. `tags` substitui a lista inteira de tags do negócio\n(`[]` tira todas). Em `customFields`, cada campo enviado é gravado e `null` apaga\naquele campo; os não enviados ficam como estão.\n\nA etapa e o resultado (ganho ou perdido) não mudam por esta operação: enviar\n`stageId`, `pipelineId`, `status` ou os campos de fechamento responde\n`400 stage_change_not_allowed`. Use `POST /deals/{id}/move`, `/win` ou `/lose`.\n\nSó o gerente troca o responsável (`ownerId`); o novo responsável é avisado, como na\ntela.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DealUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O negócio atualizado.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deal"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "delete": {
        "operationId": "deleteDeal",
        "tags": [
          "Negócios"
        ],
        "summary": "Apagar negócio",
        "description": "Apaga o negócio de vez, com o histórico, as tarefas, os produtos e as tags dele. O\ncontato e a empresa continuam; as conversas ligadas a ele ficam sem negócio. Não dá\npara desfazer. Só o gerente apaga negócios.\n\nRepetir o pedido depois de apagado responde `404` (ou a mesma resposta, se você\nenviar a mesma `Idempotency-Key`).\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Negócio apagado.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealDeleted"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/deals/{id}/move": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DealId"
        }
      ],
      "post": {
        "operationId": "moveDeal",
        "tags": [
          "Negócios"
        ],
        "summary": "Mover negócio de etapa",
        "description": "Leva o negócio para uma etapa em andamento, do mesmo funil ou de outro (o negócio muda\nde funil junto, como no quadro). O funil de destino precisa estar ativo. Negócio ganho ou\nperdido levado para uma etapa em andamento é reaberto: datas e motivos de fechamento são\napagados.\n\nPara ganhar ou perder use `/win` e `/lose`: etapa de ganho ou perda aqui responde\n`400 stage_is_final`. Assim todo fechamento passa pelas mesmas informações que a tela pede.\n\nCampo personalizado obrigatório na etapa de destino sem valor responde\n`422 missing_required_custom_fields`. Mover para a etapa em que o negócio já está devolve o\nnegócio sem mudar nada.\n\nTem o mesmo efeito de mover na tela: grava no histórico, reinicia o tempo na etapa,\ndispara o webhook `deal.stage_changed`, as automações de mudança de fase e as cadências\nda etapa de destino.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DealMove"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O negócio depois da mudança.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deal"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/deals/{id}/win": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DealId"
        }
      ],
      "post": {
        "operationId": "winDeal",
        "tags": [
          "Negócios"
        ],
        "summary": "Ganhar negócio",
        "description": "Marca o negócio como ganho: ele vai para a etapa de ganho do funil em que está (a\nprimeira, na ordem do quadro) ou para a etapa de ganho enviada em `stageId`, que precisa\nser desse funil. Funil sem etapa de ganho responde `422 pipeline_without_won_stage`.\n\n`wonAt` registra a data real do fechamento (padrão: agora; data futura responde `400`).\n`valueCents` grava o valor final fechado, como o campo de valor do modal de ganho.\n\nTem o mesmo efeito de ganhar na tela: dispara os webhooks `deal.stage_changed` e\n`deal.won` e as automações de mudança de fase e de fechamento. Repetir com o negócio já\nnessa etapa devolve o negócio sem mudar nada.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DealWin"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O negócio depois da mudança.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deal"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/deals/{id}/lose": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DealId"
        }
      ],
      "post": {
        "operationId": "loseDeal",
        "tags": [
          "Negócios"
        ],
        "summary": "Perder negócio",
        "description": "Marca o negócio como perdido, com o motivo: envie `lostReasonId` (um motivo cadastrado\nem Configurações, que valha para o funil do negócio), `lostReason` (texto livre) ou os\ndois. Como na tela, perder sem motivo não é aceito.\n\nO negócio vai para a etapa de perda do funil em que está (a primeira, na ordem do quadro)\nou para a etapa de perda enviada em `stageId`, que precisa ser desse funil. Funil sem\netapa de perda responde `422 pipeline_without_lost_stage`.\n\nTem o mesmo efeito de perder na tela: dispara os webhooks `deal.stage_changed` e\n`deal.lost` e as automações de mudança de fase e de fechamento. Repetir com o negócio já\nnessa etapa devolve o negócio sem mudar nada.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DealLose"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "O negócio depois da mudança.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deal"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/deals/{id}/products": {
      "parameters": [
        {
          "$ref": "#/components/parameters/DealId"
        }
      ],
      "get": {
        "operationId": "listDealProducts",
        "tags": [
          "Negócios"
        ],
        "summary": "Listar produtos do negócio",
        "description": "Lista os produtos do negócio, na ordem em que foram adicionados. A lista vem inteira\nnuma página só. Os produtos são informativos: não mudam o valor do negócio.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Os produtos do negócio.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealProductList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "put": {
        "operationId": "replaceDealProducts",
        "tags": [
          "Negócios"
        ],
        "summary": "Trocar os produtos do negócio",
        "description": "Substitui a lista inteira de produtos do negócio pela enviada (`items: []` tira todos).\nCada produto aparece uma vez. Sem `unitPriceCents`, vale o preço do catálogo no momento\n(como ao adicionar pela tela); `null` deixa sem preço. Os produtos não mudam o valor do\nnegócio (`valueCents`). Repetir o mesmo pedido dá o mesmo resultado.\n",
        "security": [
          {
            "bearerAuth": [
              "crm:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DealProductsReplace"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Os produtos do negócio depois da troca.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealProductList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/channels": {
      "get": {
        "operationId": "listChannels",
        "tags": [
          "Conversas"
        ],
        "summary": "Listar canais",
        "description": "Lista os canais conectados à empresa (números de WhatsApp, contas de Instagram e\nLinkedIn, caixas de e-mail), do mais novo para o mais antigo.\n\nQuem tem acesso restrito a alguns canais (Configurações → Equipe) vê só esses.\n",
        "security": [
          {
            "bearerAuth": [
              "inbox:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          }
        ],
        "responses": {
          "200": {
            "description": "Uma página de canais.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChannelList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/conversations": {
      "get": {
        "operationId": "listConversations",
        "tags": [
          "Conversas"
        ],
        "summary": "Listar conversas",
        "description": "Lista as conversas do inbox, da mais nova para a mais antiga (pela data de criação).\nPara acompanhar as que tiveram mensagem nova, use `updatedSince`: toda mensagem\nrecebida ou enviada atualiza o `updatedAt` da conversa.\n\nO token vê as mesmas conversas que o dono dele vê no inbox:\n- só dos canais liberados para ele (Configurações → Equipe);\n- o vendedor vê só as conversas atribuídas a ele, a menos que o gerente tenha\n  liberado \"ver todas as conversas\" para ele;\n- conversas excluídas não aparecem.\n\nFiltrar por um canal ou responsável que o token não pode ver devolve lista vazia.\n",
        "security": [
          {
            "bearerAuth": [
              "inbox:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "channelId",
            "in": "query",
            "required": false,
            "description": "Só conversas deste canal.",
            "schema": {
              "$ref": "#/components/schemas/IdFilter"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Só conversas nesta situação.",
            "schema": {
              "$ref": "#/components/schemas/ConversationStatus"
            }
          },
          {
            "name": "assignedToId",
            "in": "query",
            "required": false,
            "description": "Só conversas atribuídas a este usuário (o `ownerId` da conversa).",
            "schema": {
              "$ref": "#/components/schemas/IdFilter"
            }
          },
          {
            "name": "contactId",
            "in": "query",
            "required": false,
            "description": "Só conversas deste contato.",
            "schema": {
              "$ref": "#/components/schemas/IdFilter"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Uma página de conversas.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/conversations/{id}": {
      "get": {
        "operationId": "getConversation",
        "tags": [
          "Conversas"
        ],
        "summary": "Ver conversa",
        "description": "Devolve uma conversa. Conversa de outra empresa, de canal não liberado, fora do\npapel do vendedor ou excluída responde `404 resource_not_found`.\n",
        "security": [
          {
            "bearerAuth": [
              "inbox:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ConversationId"
          }
        ],
        "responses": {
          "200": {
            "description": "A conversa.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/conversations/{id}/messages": {
      "get": {
        "operationId": "listMessages",
        "tags": [
          "Conversas"
        ],
        "summary": "Listar mensagens da conversa",
        "description": "Lista as mensagens da conversa, das mais novas para as mais antigas (`order=desc`,\npadrão) ou o contrário (`order=asc`). A ordem segue `createdAt`, o momento em que a\nmensagem entrou no Rumo CRM, que é estável para paginar. Histórico importado depois\nentra com `createdAt` recente e `sentAt` antigo: para mostrar como no chat, ordene\npor `sentAt`.\n\nAparecem as mesmas mensagens que o dono do token vê na conversa:\n- mensagens apagadas não aparecem;\n- notas internas do time aparecem com `internal: true` (nunca foram enviadas ao\n  contato);\n- para o vendedor, a conversa começa no momento em que foi atribuída a ele (a\n  triagem anterior fica oculta, como na tela).\n\nConversa sem acesso ou excluída responde `404 resource_not_found`.\n",
        "security": [
          {
            "bearerAuth": [
              "inbox:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ConversationId"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "order",
            "in": "query",
            "required": false,
            "description": "`desc` traz as mais novas primeiro; `asc`, as mais antigas.",
            "schema": {
              "type": "string",
              "enum": [
                "desc",
                "asc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Uma página de mensagens.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      },
      "post": {
        "operationId": "sendMessage",
        "tags": [
          "Conversas"
        ],
        "summary": "Enviar mensagem na conversa",
        "description": "Envia texto, arquivo ou template numa conversa que já existe no inbox, de WhatsApp ou\nde Instagram. A resposta `202` quer dizer que a mensagem foi aceita e entrou na fila,\ncom `status: pending` (ou `scheduled`, quando veio `scheduledFor`). Ela ainda não\nchegou ao contato. Para saber o resultado, consulte\n`GET /conversations/{id}/messages/{messageId}` até o `status` virar `sent`,\n`delivered`, `read` ou `failed`. No `failed`, `error.code` diz o motivo.\n\n**Antes de integrar, leia:**\n- **Número não oficial pode ser bloqueado.** Num canal `unofficial` (conectado por QR\n  code), o WhatsApp e o Instagram bloqueiam o número ou a conta quando percebem envio\n  automático em volume, mensagem para quem não conhece a empresa ou texto repetido.\n  Use a API para responder e acompanhar quem já conversa com você, não para\n  prospecção fria. Quem bloqueia é a rede, e o Rumo CRM não consegue desfazer.\n- **Janela de 24 horas.** No canal `official`, passadas 24 horas da última mensagem\n  do contato (veja `windowExpiresAt` na conversa), o WhatsApp só aceita template\n  aprovado (`template_required`). O Instagram não tem template: só volta a aceitar\n  quando o contato escrever de novo (`window_expired`).\n- **Limite por número.** Além do limite do token e da empresa, cada canal aceita até\n  **30 envios por minuto** pela API. Passou disso, a resposta é `429 rate_limited`\n  com `Retry-After`.\n- **Idempotência.** Mande sempre `Idempotency-Key`. Se a conexão cair, repita com a\n  mesma chave e a mensagem não sai duas vezes. Sem a chave, repetir o pedido envia de\n  novo. Se o controle de idempotência estiver fora do ar, a resposta é\n  `503 idempotency_unavailable` e nada é enviado. Uma recusa `4xx` fica guardada com\n  a chave: depois de corrigir a causa, use uma chave nova.\n- **Descadastro.** Contato que pediu para não receber mensagens (LGPD) é recusado com\n  `recipient_opted_out`.\n\nA mensagem sai em nome do dono do token. Ela aparece com ele como autor no inbox,\nconta como resposta do time no SLA e, se a empresa ativou a assinatura do atendente,\nleva o nome dele no início do texto.\n\nO acesso segue as regras do `GET` da conversa: canal liberado para o dono do token e,\npara o vendedor, só conversas atribuídas a ele, a menos que tenha \"ver todas as\nconversas\". Fora disso, a resposta é `404 resource_not_found`. Grupos, e-mail e\nLinkedIn ainda não são suportados.\n\n| Status | `code` | Quando |\n| --- | --- | --- |\n| 409 | `channel_disconnected` | O canal está desligado ou precisa ser reconectado em Configurações. |\n| 422 | `template_required` | WhatsApp oficial com a janela de 24 horas fechada (ou agendamento para depois dela) e o pedido não é template. |\n| 422 | `window_expired` | Instagram oficial com a janela de 24 horas fechada: espere o contato escrever. |\n| 422 | `template_not_supported` | Template num canal que não é o WhatsApp oficial, ou template com mídia ou variável no cabeçalho, ou botão de link com variável. |\n| 422 | `template_not_found` | Não existe template aprovado com esse `name` e `language` na conta do WhatsApp do canal. |\n| 422 | `template_params_mismatch` | A quantidade de `params` é diferente da quantidade de variáveis do corpo do template. |\n| 422 | `recipient_opted_out` | O contato pediu para não receber mensagens (LGPD). |\n| 422 | `contact_without_phone` | A conversa não tem um número ou perfil de destino. |\n| 422 | `group_not_supported` | A conversa é de um grupo. |\n| 422 | `channel_not_supported` | O canal não é de WhatsApp nem de Instagram. |\n| 422 | `scheduling_not_supported` | `scheduledFor` num canal `unofficial`. |\n| 422 | `invalid_media_url` | `mediaUrl` que não é `https`, aponta para endereço interno ou para arquivo de outra empresa. |\n| 429 | `rate_limited` | Limite do token, da empresa ou de 30 envios por minuto do número. |\n| 503 | `queue_unavailable` | A fila de envio está fora do ar. Nada foi enviado. |\n| 503 | `template_check_unavailable` | Não deu para consultar os templates da conta agora. Nada foi enviado. |\n| 503 | `idempotency_unavailable` | Pedido com `Idempotency-Key` e o controle de idempotência fora do ar. Nada foi enviado. |\n",
        "security": [
          {
            "bearerAuth": [
              "inbox:send"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ConversationId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessage"
              },
              "examples": {
                "texto": {
                  "summary": "Texto",
                  "value": {
                    "type": "text",
                    "text": "Oi, Ana! Segue a proposta que combinamos."
                  }
                },
                "arquivo": {
                  "summary": "Arquivo com legenda",
                  "value": {
                    "type": "media",
                    "mediaUrl": "https://arquivos.suaempresa.com.br/propostas/ana-souza.pdf",
                    "mediaType": "document",
                    "caption": "Proposta revisada"
                  }
                },
                "template": {
                  "summary": "Template agendado",
                  "value": {
                    "type": "template",
                    "template": {
                      "name": "retomar_contato",
                      "language": "pt_BR",
                      "params": [
                        "Ana"
                      ]
                    },
                    "scheduledFor": "2026-10-10T12:00:00Z"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Mensagem aceita e na fila de envio (`pending`) ou agendada (`scheduled`).",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/conversations/{id}/messages/{messageId}": {
      "get": {
        "operationId": "getMessage",
        "tags": [
          "Conversas"
        ],
        "summary": "Ver mensagem",
        "description": "Devolve uma mensagem da conversa, com o `status` atual. Serve para acompanhar o\nenvio até `delivered`, `read` ou `failed`. As regras de acesso são as da lista de\nmensagens: mensagem apagada, de outra conversa ou oculta para o vendedor responde\n`404 resource_not_found`.\n",
        "security": [
          {
            "bearerAuth": [
              "inbox:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ConversationId"
          },
          {
            "name": "messageId",
            "in": "path",
            "required": true,
            "description": "Id da mensagem.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A mensagem.",
            "headers": {
              "Request-Id": {
                "$ref": "#/components/headers/Request-Id"
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    }
  },
  "webhooks": {
    "evento": {
      "post": {
        "operationId": "receberEventoDeWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Evento enviado para o seu endereço",
        "description": "Quando algo acontece no Rumo CRM, um `POST` com o evento em JSON chega ao endereço que você\ncadastrou em **Configurações → Integrações → Técnico → Webhooks**.\n\n## Como confirmar que o aviso veio do Rumo CRM\nTodo webhook com chave secreta leva o cabeçalho `Rumo-Signature`:\n\n```\nRumo-Signature: t=1791547200,v1=9f2c5b0e7d1a4c3b8e6f0a2d4c6b8e0f1a3c5e7d9b1f3a5c7e9d1b3f5a7c9e1d\n```\n\n- `t` é a hora do envio, em segundos desde 1970 (UTC).\n- `v1` é o HMAC-SHA256, em hexadecimal, do texto `<t>.<corpo>`: o valor de `t`, um ponto e o corpo\n  da requisição exatamente como chegou.\n\nPara conferir:\n1. Leia o corpo **cru**, antes de converter para JSON. Qualquer espaço ou ordem de campo diferente\n   muda a assinatura.\n2. Separe o cabeçalho pelas vírgulas e cada parte no primeiro `=`. Guarde o `t` e **todos** os `v1`.\n   Ignore partes que você não conhece: no futuro pode haver mais de um `v1` (por exemplo, uma\n   assinatura por chave) e outros esquemas.\n3. Recuse se `t` estiver a mais de **5 minutos** (300 segundos) do seu relógio, para trás ou para\n   frente. É isso que impede alguém de capturar um aviso e reenviar depois.\n4. Calcule o HMAC-SHA256 de `<t>.<corpo>` com a sua chave. A chave é o texto de 64 caracteres que\n   apareceu ao criar o webhook: use como texto, sem decodificar o hexadecimal.\n5. Compare com cada `v1` usando comparação de tempo constante. Basta um bater.\n6. Responda `2xx` e processe. Se o `id` do evento já foi processado, responda `2xx` e ignore:\n   a mesma entrega pode chegar mais de uma vez.\n\nMantenha o relógio do servidor sincronizado (NTP). Um relógio adiantado ou atrasado em mais de\n5 minutos recusa avisos verdadeiros.\n\n### Node.js\n```js\nconst crypto = require(\"node:crypto\");\n\nconst TOLERANCIA_SEGUNDOS = 5 * 60;\n\n// corpo: o corpo cru (Buffer ou string). cabecalho: o valor de Rumo-Signature.\nfunction verificarAssinaturaRumo(corpo, cabecalho, segredo, agora = Math.floor(Date.now() / 1000)) {\n  let timestamp = NaN;\n  const assinaturas = [];\n  for (const parte of String(cabecalho || \"\").split(\",\")) {\n    const separador = parte.indexOf(\"=\");\n    const chave = parte.slice(0, separador).trim();\n    const valor = parte.slice(separador + 1).trim();\n    if (chave === \"t\" && /^\\d+$/.test(valor)) timestamp = Number(valor);\n    if (chave === \"v1\" && valor) assinaturas.push(valor);\n  }\n  if (!Number.isInteger(timestamp) || assinaturas.length === 0) return false;\n  if (Math.abs(agora - timestamp) > TOLERANCIA_SEGUNDOS) return false;\n\n  const esperada = crypto.createHmac(\"sha256\", segredo).update(`${timestamp}.`).update(corpo).digest();\n  return assinaturas.some((hex) => {\n    const recebida = Buffer.from(hex, \"hex\");\n    return recebida.length === esperada.length && crypto.timingSafeEqual(recebida, esperada);\n  });\n}\n```\n\nCom Express, receba o corpo cru só nesta rota:\n\n```js\napp.post(\"/webhooks/rumo\", express.raw({ type: \"application/json\" }), (req, res) => {\n  if (!verificarAssinaturaRumo(req.body, req.get(\"Rumo-Signature\"), process.env.RUMO_WEBHOOK_SECRET)) {\n    return res.status(400).send(\"assinatura inválida\");\n  }\n  const evento = JSON.parse(req.body);\n  // Guarde evento.id e ignore repetidos.\n  res.sendStatus(200);\n});\n```\n\n### Python\n```python\nimport hashlib\nimport hmac\nimport time\n\nTOLERANCIA_SEGUNDOS = 5 * 60\n\n\ndef verificar_assinatura_rumo(corpo: bytes, cabecalho: str, segredo: str, agora: int | None = None) -> bool:\n    timestamp, assinaturas = None, []\n    for parte in (cabecalho or \"\").split(\",\"):\n        chave, _, valor = parte.strip().partition(\"=\")\n        if chave == \"t\" and valor.isdigit():\n            timestamp = int(valor)\n        elif chave == \"v1\" and valor:\n            assinaturas.append(valor)\n    if timestamp is None or not assinaturas:\n        return False\n    agora = int(time.time()) if agora is None else agora\n    if abs(agora - timestamp) > TOLERANCIA_SEGUNDOS:\n        return False\n    esperada = hmac.new(segredo.encode(), f\"{timestamp}.\".encode() + corpo, hashlib.sha256).hexdigest()\n    return any(hmac.compare_digest(esperada, recebida) for recebida in assinaturas)\n```\n\nCom Flask, o corpo cru é `request.get_data()` e o cabeçalho, `request.headers.get(\"Rumo-Signature\")`.\n\n### PHP\n```php\nfunction verificarAssinaturaRumo(string $corpo, string $cabecalho, string $segredo, ?int $agora = null): bool\n{\n    $timestamp = null;\n    $assinaturas = [];\n    foreach (explode(',', $cabecalho) as $parte) {\n        [$chave, $valor] = array_pad(explode('=', trim($parte), 2), 2, '');\n        if ($chave === 't' && ctype_digit($valor)) {\n            $timestamp = (int) $valor;\n        } elseif ($chave === 'v1' && $valor !== '') {\n            $assinaturas[] = $valor;\n        }\n    }\n    if ($timestamp === null || $assinaturas === []) {\n        return false;\n    }\n    if (abs(($agora ?? time()) - $timestamp) > 300) {\n        return false;\n    }\n    $esperada = hash_hmac('sha256', $timestamp . '.' . $corpo, $segredo);\n    foreach ($assinaturas as $recebida) {\n        if (hash_equals($esperada, $recebida)) {\n            return true;\n        }\n    }\n    return false;\n}\n\n$corpo = file_get_contents('php://input');\n$cabecalho = $_SERVER['HTTP_RUMO_SIGNATURE'] ?? '';\nif (!verificarAssinaturaRumo($corpo, $cabecalho, getenv('RUMO_WEBHOOK_SECRET'))) {\n    http_response_code(400);\n    exit;\n}\n```\n\n## Cabeçalho antigo\n`X-Webhook-Signature: sha256=<hex>` continua saindo igual: HMAC-SHA256 só do corpo, com a mesma\nchave. Ele não leva hora, então não protege contra reenvio. Integrações novas devem usar\n`Rumo-Signature`.\n\n## Entrega e novas tentativas\n- O Rumo CRM espera a resposta por até **10 segundos**. Qualquer status fora de `2xx`, erro de\n  conexão ou demora maior conta como falha.\n- Redirecionamento (`3xx`) não é seguido: conta como falha e entra nas novas tentativas.\n  Cadastre a URL final. Endereço de rede interna é recusado, inclusive quando o domínio\n  passa a apontar para um.\n- São até **8 tentativas**. Depois de cada falha, a próxima sai em cerca de 1 min, 5 min, 30 min,\n  2 h, 4 h, 7 h e 10 h (variação de até 10% para cima ou para baixo). A última tentativa sai\n  cerca de 24 horas depois da primeira. Esgotadas as 8, a entrega fica como **Falhou**.\n- Cada tentativa é assinada na hora: `t` e `v1` mudam, o corpo e o `id` não.\n- A ordem de chegada não é garantida. Use `timestamp` do corpo para ordenar.\n- Em **Histórico de entregas**, **Tentar agora** reenvia uma entrega que não foi confirmada, com\n  assinatura nova. A contagem de tentativas recomeça.\n- Um endereço sem **nenhuma** entrega confirmada por **5 dias** é desativado automaticamente e os\n  gerentes da empresa recebem um aviso no sino. Enquanto estiver desativado, os eventos não são\n  guardados. Corrija o endereço e ative de novo em Configurações.\n\n## Troca de chave\n**Gerar nova chave** troca a chave na hora: a anterior para de valer no mesmo instante, sem\nperíodo em que as duas valem. Os avisos que o seu sistema recusar enquanto você atualiza a chave\nvoltam nas próximas tentativas, já assinados com a chave nova. Atualize dentro do período de\nnovas tentativas (cerca de 24 horas) e nada se perde.\n\n## Teste\n**Testar webhook** envia `{\"event\": \"test\", \"timestamp\": ..., \"data\": {...}}` com os mesmos\ncabeçalhos de assinatura. Esse aviso não tem `id`, não leva `X-Webhook-Id` nem `X-Webhook-Attempt`\ne não entra no histórico.\n",
        "security": [],
        "parameters": [
          {
            "name": "Rumo-Signature",
            "in": "header",
            "required": false,
            "description": "`t=<unix segundos>,v1=<HMAC-SHA256 hex de \"<t>.<corpo>\">`. Só vai quando o webhook tem chave secreta. Pode trazer mais de um `v1`; aceite se qualquer um bater.",
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+(,[a-z0-9]+=[0-9a-f]+)+$"
            },
            "example": "t=1791547200,v1=9f2c5b0e7d1a4c3b8e6f0a2d4c6b8e0f1a3c5e7d9b1f3a5c7e9d1b3f5a7c9e1d"
          },
          {
            "name": "X-Webhook-Signature",
            "in": "header",
            "required": false,
            "deprecated": true,
            "description": "Formato antigo, mantido igual: `sha256=<HMAC-SHA256 hex do corpo>`. Sem hora, não protege contra reenvio. Prefira `Rumo-Signature`.",
            "schema": {
              "type": "string",
              "pattern": "^sha256=[0-9a-f]{64}$"
            }
          },
          {
            "name": "X-Webhook-Event",
            "in": "header",
            "required": true,
            "description": "Nome do evento, igual ao campo `event` do corpo.",
            "schema": {
              "$ref": "#/components/schemas/TipoDeEvento"
            }
          },
          {
            "name": "X-Webhook-Id",
            "in": "header",
            "required": true,
            "description": "Igual ao campo `id` do corpo. Repete em todas as tentativas; use para ignorar repetidos.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Webhook-Timestamp",
            "in": "header",
            "required": true,
            "description": "Hora do evento (ISO 8601, UTC), igual ao campo `timestamp` do corpo. Não é a hora da assinatura.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "X-Webhook-Attempt",
            "in": "header",
            "required": true,
            "description": "Número desta tentativa, de 1 a 8. Recomeça em 1 no reenvio manual.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 8
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventoDeWebhook"
              },
              "example": {
                "id": "deal.created:cmg1x2y3z0001ab12cd34ef56",
                "event": "deal.created",
                "timestamp": "2026-10-09T13:45:00.000Z",
                "data": {
                  "dealId": "cmg1x2y3z0001ab12cd34ef56",
                  "title": "Proposta anual",
                  "contactId": "cmg1x2y3z0002ab12cd34ef56",
                  "pipelineId": "cmg1x2y3z0003ab12cd34ef56",
                  "stageId": "cmg1x2y3z0004ab12cd34ef56",
                  "value": 4800,
                  "assignedToId": "cmg1x2y3z0005ab12cd34ef56"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recebido. Qualquer status 2xx confirma a entrega; o corpo da resposta é ignorado."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Token pessoal criado em Configurações. Cada operação informa o escopo exigido."
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Quantos itens por página.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Valor de `nextCursor` da página anterior. Envie sem alterar.",
        "schema": {
          "type": "string",
          "minLength": 1
        }
      },
      "UpdatedSince": {
        "name": "updatedSince",
        "in": "query",
        "required": false,
        "description": "Só registros com `updatedAt` igual ou posterior a esta data (ISO 8601).",
        "schema": {
          "type": "string",
          "format": "date-time"
        },
        "examples": {
          "utc": {
            "value": "2026-10-01T00:00:00Z"
          }
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Valor único por operação (um UUID, por exemplo) para repetir o pedido com segurança.\nVálido por 24 horas e só para o token que o enviou. Veja \"Idempotência\".\n",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255,
          "pattern": "^[!-~]+$"
        },
        "examples": {
          "uuid": {
            "value": "6f1c2d7e-8a4b-4c3d-9e2f-0a1b2c3d4e5f"
          }
        }
      },
      "CompanyId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Id da empresa.",
        "schema": {
          "type": "string",
          "minLength": 1
        }
      },
      "PipelineId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Id do funil.",
        "schema": {
          "type": "string",
          "minLength": 1
        }
      },
      "DealId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Id do negócio.",
        "schema": {
          "type": "string",
          "minLength": 1
        }
      },
      "ConversationId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Id da conversa.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "Request-Id": {
        "description": "Identificador deste pedido. Informe ao suporte quando precisar de ajuda.",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/RequestId"
        }
      },
      "RateLimit-Limit": {
        "description": "Pedidos permitidos por minuto no limite mais apertado agora (o do token ou o da\nempresa). Não vem nos pedidos recusados antes da autenticação nem quando o controle de\nlimite está indisponível.\n",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "RateLimit-Remaining": {
        "description": "Quantos pedidos ainda cabem nesse limite agora.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "RateLimit-Reset": {
        "description": "Segundos até abrir a próxima vaga nesse limite.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "X-RateLimit-Limit": {
        "description": "Mesmo valor de `RateLimit-Limit`.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Mesmo valor de `RateLimit-Remaining`.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "X-RateLimit-Reset": {
        "description": "Mesmo valor de `RateLimit-Reset`.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "Retry-After": {
        "description": "Segundos a esperar antes de repetir o pedido.",
        "required": true,
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "Idempotent-Replayed": {
        "description": "Vem como `true` quando a resposta é a guardada de um pedido anterior com a mesma\n`Idempotency-Key` (a operação não rodou de novo).\n",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    },
    "schemas": {
      "RequestId": {
        "type": "string",
        "pattern": "^req_[a-f0-9]{32}$",
        "examples": [
          "req_7f3c2a9be1d04c6f8a5e2b7c9d1e0f34"
        ]
      },
      "NextCursor": {
        "type": [
          "string",
          "null"
        ],
        "description": "Cursor da próxima página. `null` quando não há mais itens."
      },
      "HasMore": {
        "type": "boolean",
        "description": "Se existe mais uma página depois desta."
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "type",
              "code",
              "message",
              "requestId"
            ],
            "properties": {
              "type": {
                "type": "string",
                "description": "Família do erro.",
                "enum": [
                  "invalid_request_error",
                  "authentication_error",
                  "permission_error",
                  "not_found_error",
                  "conflict_error",
                  "rate_limit_error",
                  "api_error"
                ]
              },
              "code": {
                "type": "string",
                "pattern": "^[a-z0-9_]+$",
                "description": "Motivo exato do erro, estável para tratar no código.",
                "examples": [
                  "invalid_cursor"
                ]
              },
              "message": {
                "type": "string",
                "description": "Explicação em português para gente ler. Pode mudar de texto."
              },
              "param": {
                "type": "string",
                "description": "Parâmetro ou campo que causou o erro, quando houver um."
              },
              "requestId": {
                "$ref": "#/components/schemas/RequestId"
              }
            }
          }
        }
      },
      "CustomFieldValue": {
        "description": "Valor de um campo personalizado na leitura (o tipo segue o tipo do campo).",
        "type": [
          "string",
          "number",
          "boolean",
          "array"
        ],
        "items": {
          "type": "string"
        }
      },
      "CustomFieldsInput": {
        "type": "object",
        "maxProperties": 50,
        "description": "Campos personalizados do registro (contato, empresa ou negócio) pelo identificador\n(`slug`). O valor segue o tipo do campo: texto e link como texto (até 2.000\ncaracteres; link com http ou https); número como número; moeda em centavos inteiros\n(`150000` = R$ 1.500,00); sim/não como booleano; data como texto ISO 8601\n(`2026-10-09` ou com hora); seleção como um dos valores das opções do campo; seleção\nmúltipla como lista desses valores. Na edição (PATCH), `null`, texto vazio ou lista\nvazia apagam o valor. `slug` desconhecido (ou de campo de outro tipo de registro)\nresponde `400 unknown_custom_field` e valor fora do tipo responde\n`400 invalid_custom_field_value`.\n",
        "additionalProperties": {
          "$ref": "#/components/schemas/CustomFieldInputValue"
        },
        "examples": [
          {
            "origem_lead": "indicacao",
            "ticket_medio": 150000
          }
        ]
      },
      "CustomFieldInputValue": {
        "type": [
          "string",
          "number",
          "boolean",
          "array",
          "null"
        ],
        "items": {
          "type": "string"
        }
      },
      "ContactTag": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Contact": {
        "type": "object",
        "required": [
          "id",
          "name",
          "phone",
          "email",
          "companyId",
          "jobTitle",
          "ownerId",
          "leadStatus",
          "tags",
          "customFields",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "cm1x9k2ab0001qz8f3h7t6v5w"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Maria Souza"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Só dígitos, com DDI. `null` quando o contato não tem telefone.",
            "examples": [
              "5515998073400"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "maria@empresa.com.br"
            ]
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Empresa à qual o contato pertence."
          },
          "jobTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "Cargo."
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Usuário dono do contato (o \"Dono\" da tela de contatos)."
          },
          "leadStatus": {
            "type": [
              "string",
              "null"
            ],
            "description": "Temperatura do lead. Valores atuais: `novo`, `quente`, `morno`, `frio`,\n`convertido`, `perdido`. Trate valores desconhecidos sem quebrar.\n"
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContactTag"
            }
          },
          "customFields": {
            "type": "object",
            "description": "Campos personalizados preenchidos, pelo identificador (`slug`) do campo.\nCampos vazios não aparecem. O tipo do valor segue o tipo do campo: texto, link,\nseleção e data (ISO 8601) vêm como texto; número como número; moeda em centavos\ninteiros; sim/não como booleano; seleção múltipla como lista de textos.\n",
            "additionalProperties": {
              "$ref": "#/components/schemas/CustomFieldValue"
            },
            "examples": [
              {
                "origem_lead": "indicacao",
                "ticket_medio": 150000
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ContactList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Contact"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "ContactPhoneInput": {
        "type": [
          "string",
          "null"
        ],
        "maxLength": 30,
        "description": "Com ou sem máscara. Sem DDI, vale o do Brasil (55). Fica gravado só com dígitos e com\no nono dígito do celular. Precisa ter de 8 a 15 dígitos.\n",
        "examples": [
          "(15) 99807-3400"
        ]
      },
      "ContactEmailInput": {
        "type": [
          "string",
          "null"
        ],
        "maxLength": 254,
        "description": "Um e-mail válido. Fica gravado em letras minúsculas.",
        "examples": [
          "maria@empresa.com.br"
        ]
      },
      "ContactTagNames": {
        "type": "array",
        "maxItems": 20,
        "description": "Nomes das tags. Tag que ainda não existe na empresa é criada; a comparação com as\nexistentes não diferencia maiúsculas.\n",
        "items": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50
        },
        "examples": [
          [
            "VIP",
            "Site"
          ]
        ]
      },
      "ContactUpsert": {
        "type": "object",
        "additionalProperties": false,
        "description": "Envie ao menos `name`, `phone` ou `email`. Campo nulo ou vazio é ignorado.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "examples": [
              "Maria Souza"
            ]
          },
          "phone": {
            "$ref": "#/components/schemas/ContactPhoneInput"
          },
          "email": {
            "$ref": "#/components/schemas/ContactEmailInput"
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "description": "Id de uma empresa ativa da sua conta."
          },
          "jobTitle": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120,
            "description": "Cargo."
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "description": "Id de um usuário ativo da sua empresa, que passa a ser o dono do contato."
          },
          "tags": {
            "$ref": "#/components/schemas/ContactTagNames"
          },
          "customFields": {
            "$ref": "#/components/schemas/CustomFieldsInput"
          }
        }
      },
      "ContactUpdate": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "description": "Só os campos enviados mudam. `null` ou texto vazio apaga o valor.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "phone": {
            "$ref": "#/components/schemas/ContactPhoneInput"
          },
          "email": {
            "$ref": "#/components/schemas/ContactEmailInput"
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "description": "Id de uma empresa ativa da sua conta. `null` desvincula."
          },
          "jobTitle": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "description": "Id de um usuário ativo da sua empresa. `null` deixa o contato sem dono."
          },
          "tags": {
            "$ref": "#/components/schemas/ContactTagNames"
          },
          "customFields": {
            "$ref": "#/components/schemas/CustomFieldsInput"
          }
        }
      },
      "CompanyPhone": {
        "type": "object",
        "required": [
          "type",
          "number"
        ],
        "properties": {
          "type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Rótulo livre do telefone, como `comercial`."
          },
          "number": {
            "type": "string",
            "description": "Só dígitos, com DDI.",
            "examples": [
              "551533334444"
            ]
          }
        }
      },
      "CompanyAddress": {
        "type": "object",
        "description": "Endereço. Toda parte é `null` quando não foi preenchida.",
        "required": [
          "street",
          "neighborhood",
          "city",
          "state",
          "postalCode"
        ],
        "properties": {
          "street": {
            "type": [
              "string",
              "null"
            ],
            "description": "Logradouro, número e complemento.",
            "examples": [
              "Rua das Indústrias, 120"
            ]
          },
          "neighborhood": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Sorocaba"
            ]
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "description": "UF, como foi cadastrada.",
            "examples": [
              "SP"
            ]
          },
          "postalCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "CEP, só dígitos.",
            "examples": [
              "18087000"
            ]
          }
        }
      },
      "Company": {
        "type": "object",
        "required": [
          "id",
          "name",
          "tradeName",
          "cnpj",
          "cnae",
          "size",
          "emails",
          "phones",
          "address",
          "ownerId",
          "isActive",
          "customFields",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "cm1x9k2ab0001qz8f3h7t6v5w"
            ]
          },
          "name": {
            "type": "string",
            "description": "Razão social ou nome principal.",
            "examples": [
              "Metalúrgica Andrade Ltda"
            ]
          },
          "tradeName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Nome fantasia.",
            "examples": [
              "Andrade Metais"
            ]
          },
          "cnpj": {
            "type": [
              "string",
              "null"
            ],
            "description": "Só dígitos. `null` quando a empresa não tem CNPJ.",
            "examples": [
              "11222333000181"
            ]
          },
          "cnae": {
            "type": [
              "string",
              "null"
            ],
            "description": "CNAE principal, como foi cadastrado.",
            "examples": [
              "2511000"
            ]
          },
          "size": {
            "type": [
              "string",
              "null"
            ],
            "description": "Porte, como foi cadastrado. Valores comuns: `MEI`, `ME`, `EPP`, `DEMAIS`. Trate\nvalores desconhecidos sem quebrar.\n"
          },
          "emails": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "contato@andrade.com.br"
              ]
            ]
          },
          "phones": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyPhone"
            }
          },
          "address": {
            "$ref": "#/components/schemas/CompanyAddress"
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Usuário dono da empresa (o \"Responsável\" da tela de empresas)."
          },
          "isActive": {
            "type": "boolean",
            "description": "`false` quando a empresa foi desativada."
          },
          "customFields": {
            "type": "object",
            "description": "Campos personalizados de empresa preenchidos, pelo identificador (`slug`) do\ncampo, no mesmo formato dos contatos: moeda em centavos inteiros, data em ISO\n8601, sim/não como booleano, seleção múltipla como lista de textos. Campos\nvazios não aparecem.\n",
            "additionalProperties": {
              "$ref": "#/components/schemas/CustomFieldValue"
            },
            "examples": [
              {
                "segmento_cliente": "industria",
                "faturamento_estimado": 120000000
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CompanyList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Company"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "CompanyPhoneInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "number"
        ],
        "properties": {
          "type": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 40,
            "description": "Rótulo livre, como `comercial`."
          },
          "number": {
            "type": "string",
            "description": "Com ou sem máscara. Número brasileiro sem DDI ganha o 55.",
            "examples": [
              "(15) 3333-4444"
            ]
          }
        }
      },
      "CompanyAddressInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "street": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          },
          "neighborhood": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          },
          "city": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          },
          "state": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 60
          },
          "postalCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "CEP com ou sem máscara (8 dígitos).",
            "examples": [
              "18087-000"
            ]
          }
        }
      },
      "CompanyCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Razão social ou nome principal."
          },
          "tradeName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "cnpj": {
            "type": [
              "string",
              "null"
            ],
            "description": "Com ou sem máscara. Precisa ter dígitos verificadores válidos.",
            "examples": [
              "11.222.333/0001-81"
            ]
          },
          "cnae": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "size": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 40,
            "description": "Porte, como `MEI`, `ME`, `EPP` ou `DEMAIS`."
          },
          "emails": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "phones": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/CompanyPhoneInput"
            }
          },
          "address": {
            "$ref": "#/components/schemas/CompanyAddressInput"
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Usuário dono da empresa, ativo na conta. Sem este campo, o vendedor vira o\ndono e, para o gerente, a empresa fica sem dono.\n"
          },
          "customFields": {
            "$ref": "#/components/schemas/CustomFieldsInput"
          }
        }
      },
      "CompanyUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "tradeName": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "cnpj": {
            "type": [
              "string",
              "null"
            ],
            "description": "Com ou sem máscara. `null` remove o CNPJ."
          },
          "cnae": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20
          },
          "size": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 40
          },
          "emails": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "phones": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "$ref": "#/components/schemas/CompanyPhoneInput"
            }
          },
          "address": {
            "$ref": "#/components/schemas/CompanyAddressInput"
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Novo dono. Só o gerente troca o dono."
          },
          "customFields": {
            "$ref": "#/components/schemas/CustomFieldsInput"
          }
        }
      },
      "Pipeline": {
        "type": "object",
        "required": [
          "id",
          "name",
          "isDefault",
          "order",
          "isActive"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "cm1x9k2ab0001qz8f3h7t6v5w"
            ]
          },
          "name": {
            "type": "string",
            "examples": [
              "Vendas"
            ]
          },
          "isDefault": {
            "type": "boolean",
            "description": "Se é o funil padrão da empresa, onde nasce o negócio criado sem funil escolhido.\nA empresa tem um funil padrão por vez.\n"
          },
          "order": {
            "type": "integer",
            "description": "Posição relativa entre os funis. Valores podem se repetir e ter saltos."
          },
          "isActive": {
            "type": "boolean",
            "description": "`false` quando o funil foi desativado no Rumo CRM."
          }
        }
      },
      "PipelineList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Pipeline"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "PipelineStage": {
        "type": "object",
        "required": [
          "id",
          "pipelineId",
          "name",
          "color",
          "order",
          "type"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "cm1x9k2ab0002qz8f3h7t6v5w"
            ]
          },
          "pipelineId": {
            "type": "string",
            "description": "Funil ao qual a etapa pertence."
          },
          "name": {
            "type": "string",
            "examples": [
              "Em negociação"
            ]
          },
          "color": {
            "type": "string",
            "description": "Cor da coluna no quadro, normalmente em hexadecimal.",
            "examples": [
              "#F59E0B"
            ]
          },
          "order": {
            "type": "integer",
            "description": "Posição relativa da etapa dentro do funil. Valores podem se repetir e ter saltos;\na lista já vem na ordem do quadro.\n"
          },
          "type": {
            "type": "string",
            "enum": [
              "open",
              "won",
              "lost"
            ],
            "description": "`open` para etapa em andamento, `won` para a etapa de negócio ganho e `lost` para a\nde negócio perdido.\n"
          }
        }
      },
      "PipelineStageList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineStage"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "DealStatus": {
        "type": "string",
        "enum": [
          "open",
          "won",
          "lost"
        ],
        "description": "`open` em andamento, `won` ganho e `lost` perdido. Segue o tipo da etapa em que o\nnegócio está (o `type` da etapa em `/pipelines/{id}/stages`).\n"
      },
      "DealLeadSource": {
        "type": "object",
        "description": "Fonte de captação (formulário, página ou integração) que criou o negócio.",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "examples": [
              "Página de contato"
            ]
          }
        }
      },
      "DealLostReason": {
        "type": "object",
        "description": "Por que o negócio foi perdido: o motivo cadastrado na conta (`id` e `name`) e/ou o\ntexto livre (`text`). O que não foi informado vem `null`.\n",
        "required": [
          "id",
          "name",
          "text"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Motivo de perda cadastrado em Configurações."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Preço"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Observação livre de quem marcou a perda."
          }
        }
      },
      "Deal": {
        "type": "object",
        "required": [
          "id",
          "title",
          "pipelineId",
          "stageId",
          "status",
          "valueCents",
          "currency",
          "probability",
          "expectedCloseDate",
          "contactId",
          "companyId",
          "ownerId",
          "leadSource",
          "wonReason",
          "lostReason",
          "wonAt",
          "lostAt",
          "stageEnteredAt",
          "tags",
          "customFields",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "examples": [
              "cm1x9k2ab0003qz8f3h7t6v5w"
            ]
          },
          "title": {
            "type": "string",
            "examples": [
              "Plano anual — Metalúrgica Andrade"
            ]
          },
          "pipelineId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Funil do negócio. `null` só em negócio antigo que ficou sem funil gravado."
          },
          "stageId": {
            "type": "string",
            "description": "Etapa (coluna do quadro) onde o negócio está."
          },
          "status": {
            "$ref": "#/components/schemas/DealStatus"
          },
          "valueCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Valor do negócio em centavos (`150050` = R$ 1.500,50). `null` sem valor.",
            "examples": [
              150050
            ]
          },
          "currency": {
            "type": "string",
            "const": "BRL",
            "description": "Moeda de `valueCents`. Hoje sempre `BRL`."
          },
          "probability": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Chance de fechar, de 0 a 100. É o peso do negócio na previsão ponderada."
          },
          "expectedCloseDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Previsão de fechamento (só a data).",
            "examples": [
              "2026-11-30"
            ]
          },
          "contactId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Contato principal do negócio."
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Empresa principal do negócio."
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Usuário responsável pelo negócio."
          },
          "leadSource": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DealLeadSource"
              },
              {
                "type": "null"
              }
            ]
          },
          "wonReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Como o negócio foi ganho, em texto livre. `null` fora de negócio ganho."
          },
          "lostReason": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DealLostReason"
              },
              {
                "type": "null"
              }
            ]
          },
          "wonAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Quando o negócio foi ganho. `null` se não está ganho."
          },
          "lostAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Quando o negócio foi perdido. `null` se não está perdido."
          },
          "stageEnteredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Quando o negócio entrou na etapa atual. Pode ser `null` em negócio antigo."
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ContactTag"
            }
          },
          "customFields": {
            "type": "object",
            "description": "Campos personalizados de negócio preenchidos, pelo identificador (`slug`) do\ncampo, no mesmo formato dos contatos: moeda em centavos inteiros, data em ISO\n8601, sim/não como booleano, seleção múltipla como lista de textos. Campos\nvazios não aparecem.\n",
            "additionalProperties": {
              "$ref": "#/components/schemas/CustomFieldValue"
            },
            "examples": [
              {
                "origem_lead": "indicacao",
                "ticket_medio": 150000
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DealList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Deal"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "DealCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title"
        ],
        "description": "Envie `stageId` ou `pipelineId` (ou os dois).",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "stageId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Etapa em andamento onde o negócio nasce."
          },
          "pipelineId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Funil ativo onde o negócio nasce, na primeira etapa em andamento. Com `stageId`,\na etapa precisa ser deste funil.\n"
          },
          "valueCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999999,
            "description": "Valor em centavos inteiros (`150050` = R$ 1.500,50)."
          },
          "probability": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 50
          },
          "expectedCloseDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "examples": [
              "2026-11-30"
            ]
          },
          "contactId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Contato principal: um contato que você vê na API. Se ele tem empresa e você não\nenviar `companyId`, o negócio nasce com a empresa do contato.\n"
          },
          "companyId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Empresa principal, ativa e visível para você."
          },
          "ownerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Responsável, um usuário ativo da conta."
          },
          "tags": {
            "$ref": "#/components/schemas/ContactTagNames"
          },
          "customFields": {
            "$ref": "#/components/schemas/CustomFieldsInput"
          }
        }
      },
      "DealDeleted": {
        "type": "object",
        "required": [
          "id",
          "deleted"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "DealUpdate": {
        "type": "object",
        "additionalProperties": false,
        "description": "Só os campos enviados mudam.",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "valueCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999999,
            "description": "Valor em centavos inteiros. `null` tira o valor."
          },
          "probability": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "expectedCloseDate": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "`null` tira a previsão."
          },
          "contactId": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "description": "Contato principal visível para você. `null` desvincula."
          },
          "companyId": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "description": "Empresa principal, ativa e visível para você. `null` desvincula."
          },
          "ownerId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Novo responsável, um usuário ativo da conta. Só o gerente troca."
          },
          "tags": {
            "$ref": "#/components/schemas/ContactTagNames"
          },
          "customFields": {
            "$ref": "#/components/schemas/CustomFieldsInput"
          }
        }
      },
      "DealMove": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "stageId"
        ],
        "properties": {
          "stageId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Etapa em andamento de destino (de qualquer funil ativo da conta)."
          }
        }
      },
      "DealWin": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "stageId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Etapa de ganho do funil do negócio. Sem ela, vale a primeira etapa de ganho do funil."
          },
          "wonReason": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "Como o negócio foi ganho, em texto livre."
          },
          "wonAt": {
            "type": "string",
            "format": "date-time",
            "description": "Data real do fechamento (padrão: agora). Não pode ser futura.",
            "examples": [
              "2026-10-08T15:00:00Z"
            ]
          },
          "valueCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999999,
            "description": "Valor final fechado, em centavos. Sem este campo, o valor fica como está."
          }
        }
      },
      "DealLose": {
        "type": "object",
        "additionalProperties": false,
        "description": "Envie `lostReasonId`, `lostReason` ou os dois.",
        "anyOf": [
          {
            "required": [
              "lostReasonId"
            ]
          },
          {
            "required": [
              "lostReason"
            ]
          }
        ],
        "properties": {
          "stageId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Etapa de perda do funil do negócio. Sem ela, vale a primeira etapa de perda do funil."
          },
          "lostReasonId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Motivo de perda cadastrado na conta, que valha para o funil do negócio."
          },
          "lostReason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2000,
            "description": "Motivo em texto livre.",
            "examples": [
              "Escolheu um concorrente mais barato"
            ]
          }
        }
      },
      "DealProduct": {
        "type": "object",
        "required": [
          "productId",
          "name",
          "quantity",
          "unitPriceCents",
          "discountCents",
          "totalCents"
        ],
        "properties": {
          "productId": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "description": "Nome do produto no catálogo.",
            "examples": [
              "Plano anual"
            ]
          },
          "quantity": {
            "type": "integer",
            "minimum": 1
          },
          "unitPriceCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Preço unitário neste negócio, em centavos. `null` sem preço."
          },
          "discountCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Desconto na linha (valor, não porcentagem), em centavos."
          },
          "totalCents": {
            "type": "integer",
            "description": "`unitPriceCents × quantity − discountCents`, em centavos (preço ausente conta como zero)."
          }
        }
      },
      "DealProductList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DealProduct"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "DealProductInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "productId"
        ],
        "properties": {
          "productId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Produto do catálogo da conta."
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "default": 1
          },
          "unitPriceCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999999,
            "description": "Preço unitário em centavos. Sem este campo, vale o preço do catálogo."
          },
          "discountCents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 999999999999,
            "description": "Desconto na linha (valor), em centavos."
          }
        }
      },
      "DealProductsReplace": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/DealProductInput"
            }
          }
        }
      },
      "Channel": {
        "type": "object",
        "required": [
          "id",
          "type",
          "provider",
          "name",
          "status",
          "phone",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "Rede do canal. Valores atuais: `whatsapp`, `instagram`, `linkedin`, `email`.\nTrate valores desconhecidos sem quebrar.\n",
            "examples": [
              "whatsapp"
            ]
          },
          "provider": {
            "type": "string",
            "enum": [
              "official",
              "unofficial"
            ],
            "description": "`official` = conexão pela API oficial da rede. No WhatsApp e no Instagram\noficiais vale a janela de 24 horas: depois dela, só um template aprovado\nreabre a conversa (veja `windowExpiresAt` na conversa). `unofficial` =\nconexão por QR code ou integração não oficial, sem janela.\n"
          },
          "name": {
            "type": "string",
            "description": "Nome dado ao canal em Configurações.",
            "examples": [
              "WhatsApp Comercial"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "connected",
              "disconnected"
            ],
            "description": "`disconnected` quando o canal foi desligado em Configurações ou precisa ser\nreconectado. Canal desconectado não envia mensagens.\n"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Número do WhatsApp do canal, só dígitos com DDI. `null` nos outros canais ou\nquando o número ainda não foi identificado.\n",
            "examples": [
              "5515998073400"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ChannelList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Channel"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "IdFilter": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64
      },
      "ConversationStatus": {
        "type": "string",
        "enum": [
          "open",
          "closed",
          "archived"
        ],
        "description": "`open` = em atendimento, `closed` = encerrada, `archived` = arquivada."
      },
      "Conversation": {
        "type": "object",
        "required": [
          "id",
          "channelId",
          "contactId",
          "dealId",
          "ownerId",
          "status",
          "lastMessageAt",
          "windowExpiresAt",
          "unreadCount",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "channelId": {
            "type": "string",
            "description": "Canal por onde a conversa acontece."
          },
          "contactId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Contato da conversa. `null` enquanto não foi vinculada a um contato."
          },
          "dealId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Negócio vinculado à conversa."
          },
          "ownerId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Usuário responsável pelo atendimento. `null` quando ninguém assumiu."
          },
          "status": {
            "$ref": "#/components/schemas/ConversationStatus"
          },
          "lastMessageAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Data da última mensagem no canal."
          },
          "windowExpiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Só em canal `official` de WhatsApp ou Instagram: até quando a janela de 24\nhoras fica aberta. Data no passado ou `null` = janela fechada (no WhatsApp,\nsó template aprovado). Nos canais sem janela é sempre `null`.\n"
          },
          "unreadCount": {
            "type": "integer",
            "minimum": 0,
            "description": "Mensagens recebidas que o time ainda não leu."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ConversationList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Conversation"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "MessageMedia": {
        "type": "object",
        "required": [
          "url",
          "mimeType",
          "expiresAt"
        ],
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Link temporário para baixar o arquivo, sem precisar do token. Vale até\n`expiresAt` (1 hora); depois, peça a mensagem de novo para receber um link\nnovo. `null` quando o arquivo não está guardado no Rumo CRM (mídia que o\ncanal ainda não entregou ou que expirou na origem).\n"
          },
          "mimeType": {
            "type": [
              "string",
              "null"
            ],
            "description": "Tipo do arquivo, quando conhecido.",
            "examples": [
              "image/jpeg"
            ]
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Até quando `url` funciona."
          }
        }
      },
      "MessageError": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[a-z0-9_]+$",
            "description": "Motivo da falha, estável para tratar no código. Valores atuais:\n`channel_disconnected` (canal desligado ou a reconectar),\n`recipient_opted_out` (contato descadastrado), `window_expired` (janela de 24\nhoras fechada), `template_rejected` (o WhatsApp recusou o template),\n`recipient_invalid` (número sem WhatsApp ou destinatário não encontrado),\n`media_rejected` (formato de arquivo recusado), `contact_without_phone`,\n`conversation_deleted` (a conversa foi excluída antes do envio),\n`queue_unavailable` (a fila estava fora do ar), `provider_rate_limited` (a rede\nlimitou os envios do número), `provider_rejected` (outra recusa da rede) e\n`send_failed` (motivo não registrado). Trate valores desconhecidos sem quebrar.\n",
            "examples": [
              "window_expired"
            ]
          },
          "message": {
            "type": "string",
            "description": "Explicação em português, a mesma mostrada no inbox. Pode mudar de texto.",
            "examples": [
              "A janela de 24 horas terminou. Envie um template aprovado."
            ]
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "conversationId",
          "direction",
          "type",
          "text",
          "media",
          "status",
          "error",
          "internal",
          "authorId",
          "scheduledFor",
          "sentAt",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "conversationId": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ],
            "description": "`inbound` = o contato enviou; `outbound` = a sua empresa enviou."
          },
          "type": {
            "type": "string",
            "description": "Valores atuais: `text`, `image`, `audio`, `video`, `document`, `template`,\n`contact` e `other` (formato que a API ainda não detalha, como figurinha,\nstory ou convite). Trate valores desconhecidos sem quebrar.\n",
            "examples": [
              "text"
            ]
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Texto da mensagem, ou a legenda quando há mídia."
          },
          "media": {
            "description": "Arquivo anexado. `null` quando a mensagem não tem mídia.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/MessageMedia"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "scheduled",
              "sent",
              "delivered",
              "read",
              "failed"
            ],
            "description": "`pending` = aceita e aguardando envio; `scheduled` = agendada para\n`scheduledFor`; `sent`, `delivered` e `read` acompanham a entrega; `failed` =\nnão saiu (veja `error`).\n"
          },
          "error": {
            "description": "Motivo da falha quando `status` é `failed`; senão, `null`.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/MessageError"
              },
              {
                "type": "null"
              }
            ]
          },
          "internal": {
            "type": "boolean",
            "description": "`true` para nota interna do time: fica só no Rumo CRM e nunca é enviada ao\ncontato.\n"
          },
          "authorId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Usuário que escreveu a mensagem enviada ou a nota. `null` nas recebidas e nas\nenviadas por automação.\n"
          },
          "scheduledFor": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Para quando a mensagem foi agendada."
          },
          "sentAt": {
            "type": "string",
            "format": "date-time",
            "description": "Data da mensagem no canal (quando o contato enviou ou quando saiu)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Quando a mensagem entrou no Rumo CRM."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MessageList": {
        "type": "object",
        "required": [
          "data",
          "nextCursor",
          "hasMore"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/NextCursor"
          },
          "hasMore": {
            "$ref": "#/components/schemas/HasMore"
          }
        }
      },
      "ScheduledFor": {
        "type": "string",
        "format": "date-time",
        "description": "Envia nesta data em vez de agora (ISO 8601, no futuro). Até 10 segundos à frente, a\nmensagem sai na hora. Só em canal `official`: em canal `unofficial` a resposta é\n`422 scheduling_not_supported`, porque o limite de 30 por minuto do número não\nalcança mensagens agendadas todas para o mesmo horário. No WhatsApp oficial, texto e\narquivo só podem ser agendados para antes de a janela de 24 horas fechar; template\npode ser agendado para qualquer data.\n",
        "examples": [
          "2026-10-10T12:00:00Z"
        ]
      },
      "SendTextMessage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type",
          "text"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "text"
            ]
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4096,
            "description": "Texto da mensagem. Espaços no começo e no fim são removidos.",
            "examples": [
              "Oi, Ana! Segue a proposta que combinamos."
            ]
          },
          "scheduledFor": {
            "$ref": "#/components/schemas/ScheduledFor"
          }
        }
      },
      "SendMediaMessage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type",
          "mediaUrl",
          "mediaType"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "media"
            ]
          },
          "mediaUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Endereço `https` público do arquivo. Quem baixa é o WhatsApp ou o Instagram, na\nhora do envio, então o link precisa abrir sem login até a mensagem sair. O nome\ndo documento que o contato vê vem do fim do endereço (`proposta.pdf`). Link de\narquivo do Rumo CRM só vale para arquivos da sua empresa.\n",
            "examples": [
              "https://arquivos.suaempresa.com.br/propostas/ana-souza.pdf"
            ]
          },
          "mediaType": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "document"
            ],
            "description": "Tipo do arquivo."
          },
          "caption": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024,
            "description": "Legenda. Não vale para `audio`.",
            "examples": [
              "Proposta revisada"
            ]
          },
          "scheduledFor": {
            "$ref": "#/components/schemas/ScheduledFor"
          }
        }
      },
      "TemplateInput": {
        "type": "object",
        "additionalProperties": false,
        "description": "Template aprovado na conta do WhatsApp oficial do canal. É o único jeito de falar\ncom o contato depois que a janela de 24 horas fecha. A lista de templates da conta é\nconsultada com até 5 minutos de atraso: um template recém-aprovado pode levar esse\ntempo para ser aceito. Só templates com variáveis no corpo: cabeçalho com mídia ou\nvariável e botão de link com variável ainda não são suportados\n(`template_not_supported`).\n",
        "required": [
          "name",
          "language"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "description": "Nome do template, como aparece no Gerenciador do WhatsApp.",
            "examples": [
              "retomar_contato"
            ]
          },
          "language": {
            "type": "string",
            "minLength": 2,
            "maxLength": 15,
            "description": "Idioma da versão aprovada do template.",
            "examples": [
              "pt_BR"
            ]
          },
          "params": {
            "type": "array",
            "maxItems": 50,
            "default": [],
            "description": "Valores das variáveis do corpo, na ordem (`{{1}}`, `{{2}}`...). A quantidade tem\nde ser igual à de variáveis do template; diferente disso a resposta é\n`422 template_params_mismatch`, e nada é completado por conta própria. Sem\nquebra de linha nem tabulação (regra do WhatsApp).\n",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 1024,
              "pattern": "^[^\\n\\t]+$"
            },
            "examples": [
              [
                "Ana"
              ]
            ]
          }
        }
      },
      "SendTemplateMessage": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "type",
          "template"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "template"
            ]
          },
          "template": {
            "$ref": "#/components/schemas/TemplateInput"
          },
          "scheduledFor": {
            "$ref": "#/components/schemas/ScheduledFor"
          }
        }
      },
      "SendMessage": {
        "description": "O que enviar, conforme `type`: `text` (texto), `media` (arquivo) ou `template`\n(template aprovado do WhatsApp oficial).\n",
        "oneOf": [
          {
            "$ref": "#/components/schemas/SendTextMessage"
          },
          {
            "$ref": "#/components/schemas/SendMediaMessage"
          },
          {
            "$ref": "#/components/schemas/SendTemplateMessage"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "text": "#/components/schemas/SendTextMessage",
            "media": "#/components/schemas/SendMediaMessage",
            "template": "#/components/schemas/SendTemplateMessage"
          }
        }
      },
      "TipoDeEvento": {
        "type": "string",
        "enum": [
          "campaign.started",
          "campaign.completed",
          "campaign.paused",
          "message.sent",
          "message.failed",
          "reply.received",
          "form.submitted",
          "form.qualified",
          "form.disqualified",
          "form.abandoned",
          "booking.confirmed",
          "booking.canceled",
          "contact.created",
          "deal.created",
          "deal.stage_changed",
          "deal.won",
          "deal.lost"
        ]
      },
      "EventoDeWebhook": {
        "type": "object",
        "required": [
          "id",
          "event",
          "timestamp",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Identificador do evento. O mesmo em todas as tentativas e no reenvio manual."
          },
          "event": {
            "$ref": "#/components/schemas/TipoDeEvento"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Hora em que o evento aconteceu (UTC)."
          },
          "data": {
            "type": "object",
            "description": "Dados do evento. Campos novos podem aparecer a qualquer momento; ignore os que não conhece.",
            "additionalProperties": true
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Pedido inválido (parâmetro errado, desconhecido ou cursor alterado).",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Token ausente, inválido, expirado ou revogado.",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "O token não tem o escopo exigido ou o seu papel não permite a ação.",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "O pedido conflita com um registro existente (por exemplo, telefone de outro contato, CNPJ de outra empresa ou envio por canal desconectado) ou com outro pedido de mesma `Idempotency-Key` (já usada com outro corpo ou ainda em andamento).",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Limite de pedidos por minuto atingido (do token, da empresa ou, no envio de mensagem, do número). Espere `Retry-After` segundos.",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "O pedido está bem formado, mas não pode ser atendido no estado atual (por exemplo, campo personalizado obrigatório sem valor, mensagem fora da janela de 24 horas sem template ou contato descadastrado). Veja `code`.",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          },
          "Idempotent-Replayed": {
            "$ref": "#/components/headers/Idempotent-Replayed"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Um serviço de que a operação depende está fora do ar. Nada foi executado; tente de novo em instantes. Veja `code`.",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "O registro não existe, é de outra empresa ou está fora do seu papel.",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnexpectedError": {
        "description": "Outros erros (plano sem o recurso, acesso por token desligado, idempotência indisponível ou falha interna).",
        "headers": {
          "Request-Id": {
            "$ref": "#/components/headers/Request-Id"
          },
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}