{
  "openapi": "3.0.3",
  "info": {
    "title": "Umbrella API Rural",
    "version": "1.0.0",
    "description": "API de dados rurais (CAR) e gestão de conta. Autenticação Bearer.\n\nReferência humana e playground: https://umbrellatecnologia.com/ApiDocs\n\n## Cobrança\n\nApenas HTTP 200 nos endpoints de consulta (CAR, proprietário, recibo) debita. Endpoints de conta e gestão são gratuitos. Respostas servidas de cache (campo `cache: true`) são cobradas normalmente.\n\nRate limit: 10 requisições/segundo por token.\n\n## Sandbox\n\nValores reservados respondem com dados fixos, sem consultar a base, sem debitar saldo e sem contar nos limites. Toda resposta de sandbox traz `\"sandbox\": true`. A autenticação continua valendo: token inválido devolve 401.\n\n| Valor | Onde usar | Resposta |\n|---|---|---|\n| `ZZ-0000000-SANDBOXCARBASICO0000000000000000` | CAR básico, completo, recibo | 200 com dados de exemplo |\n| `ZZ-0000000-SANDBOXNAOEXISTE0000000000000000` | CAR básico, completo, recibo | 404 `CAR_NAO_ENCONTRADO` |\n| `ZZ-0000000-SANDBOXSEMSALDO00000000000000000` | CAR básico, completo, recibo | 402 `SALDO_INSUFICIENTE` |\n| `00000000000` | Proprietário | 200 com dois CARs |\n| `00000000000000` | Proprietário | 200 com `truncado: true` |\n| `11111111111` | Proprietário | 404 `CPF_CNPJ_NAO_ENCONTRADO` |\n\n## Formato dos valores\n\nCampos numéricos e de data vêm como string no padrão brasileiro, exatamente como constam na origem: `areaTotalHa` usa vírgula decimal (`\"150,0000\"`), datas usam `dd/MM/yyyy HH:mm:ss` e coordenadas vêm em graus-minutos-segundos (`\"10°00'00,00 S\"`).\n\n## LGPD\n\nA busca por CPF/CNPJ retorna dados de cadastro ambiental público (SICAR). O cliente da API é controlador dos dados que consultar e responde pela base legal do tratamento. Toda consulta fica registrada e disponível em `GET /v1/historico`.",
    "contact": {
      "name": "Umbrella Technology",
      "url": "https://umbrellatecnologia.com"
    }
  },
  "servers": [
    { "url": "https://api.umbrellatecnologia.com", "description": "Produção" }
  ],
  "tags": [
    { "name": "Consulta", "description": "Endpoints cobrados (dados do CAR)" },
    { "name": "Conta", "description": "Saldo, histórico, limites e uso — gratuitos" },
    { "name": "Recarga", "description": "Créditos via PIX — gratuitos" }
  ],
  "security": [{ "bearerAuth": [] }],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Envie `Authorization: Bearer {api_key}`."
      }
    },
    "parameters": {
      "Page": {
        "name": "page",
        "in": "query",
        "description": "Página (1-based).",
        "schema": { "type": "integer", "minimum": 1, "default": 1 }
      },
      "Size": {
        "name": "size",
        "in": "query",
        "description": "Itens por página. Valores acima de 100 são reduzidos para 100.",
        "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Token ausente ou inválido (`TOKEN_INVALIDO`).",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "PaymentRequired": {
        "description": "Saldo insuficiente (`SALDO_INSUFICIENTE`). Não há débito.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "BadRequest": {
        "description": "Requisição inválida. Ver campo `error`.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "NotFound": {
        "description": "Recurso não encontrado. Não há débito.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "TooManyRequests": {
        "description": "Rate limit (10 req/s) ou limite diário/mensal excedido (`LIMITE_EXCEDIDO`).",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "ServerError": {
        "description": "Erro interno (`ERRO_INTERNO`).",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": ["success", "error", "message"],
        "properties": {
          "success": { "type": "boolean", "example": false },
          "error": {
            "type": "string",
            "description": "Código estável do erro.",
            "enum": [
              "CODIGO_CAR_INVALIDO",
              "CPF_CNPJ_INVALIDO",
              "TOKEN_INVALIDO",
              "SALDO_INSUFICIENTE",
              "CAR_NAO_ENCONTRADO",
              "CPF_CNPJ_NAO_ENCONTRADO",
              "RECIBO_NAO_ENCONTRADO",
              "RECARGA_NAO_ENCONTRADA",
              "LIMITE_EXCEDIDO",
              "LIMITE_INVALIDO",
              "VALOR_INVALIDO",
              "VALOR_MINIMO",
              "CONFIRMACAO_PRECOS_OBRIGATORIA",
              "DOCUMENTO_AUSENTE",
              "CONTA_SEM_USUARIO",
              "ERRO_PIX",
              "RECIBO_INDISPONIVEL",
              "SERVICO_INDISPONIVEL",
              "ERRO_INTERNO",
              "SPEC_NAO_ENCONTRADA"
            ]
          },
          "message": { "type": "string", "description": "Mensagem legível em português." },
          "sandbox": { "type": "boolean", "description": "Presente e true quando o erro veio de um valor reservado de teste." }
        }
      },
      "CarDadosBasicos": {
        "type": "object",
        "description": "Números e datas são strings no formato brasileiro (vírgula decimal); coordenadas em DMS.",
        "properties": {
          "codigoCar": { "type": "string", "example": "BA-2900801-0A466FD693B247B9A397829A0D6AA2E3" },
          "nomeImovel": { "type": "string", "nullable": true },
          "areaTotalHa": { "type": "string", "nullable": true, "example": "150,0000", "description": "Hectares com vírgula decimal." },
          "dataCadastro": { "type": "string", "nullable": true, "example": "01/02/2024 00:00:00" },
          "matriculaNumero": { "type": "string", "nullable": true },
          "matriculaData": { "type": "string", "nullable": true },
          "matriculaCartorio": { "type": "string", "nullable": true },
          "tipoImovel": { "type": "string", "nullable": true },
          "municipio": { "type": "string", "nullable": true },
          "uf": { "type": "string", "nullable": true, "description": "Nome do estado por extenso." },
          "codigoProtocolo": { "type": "string", "nullable": true },
          "latitude": { "type": "string", "nullable": true, "example": "10°00'00,00 S", "description": "DMS." },
          "longitude": { "type": "string", "nullable": true, "example": "40°00'00,00 O", "description": "DMS." },
          "modulosFiscais": { "type": "string", "nullable": true }
        }
      },
      "CarDadosCompletos": {
        "allOf": [
          { "$ref": "#/components/schemas/CarDadosBasicos" },
          {
            "type": "object",
            "properties": {
              "proprietariosNomes": { "type": "string", "nullable": true, "description": "Nomes separados por vírgula." },
              "proprietariosCpfs": { "type": "string", "nullable": true, "description": "Documentos formatados, separados por vírgula." }
            }
          }
        ]
      },
      "CarResponse": {
        "type": "object",
        "required": ["success", "dados"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "sandbox": { "type": "boolean", "description": "Presente e true quando o código consultado é um valor reservado de teste." },
          "cache": {
            "type": "boolean",
            "description": "Indica que a resposta veio da base interna já consolidada. Respostas com cache=true são cobradas normalmente."
          },
          "dados": { "$ref": "#/components/schemas/CarDadosBasicos" },
          "dataConsulta": { "type": "string", "format": "date-time" },
          "dataExpiracao": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Data da última atualização do registro na base de origem. `null` quando não informada."
          }
        }
      },
      "CarCompletoResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/CarResponse" },
          {
            "type": "object",
            "properties": { "dados": { "$ref": "#/components/schemas/CarDadosCompletos" } }
          }
        ]
      },
      "ProprietarioItem": {
        "type": "object",
        "properties": {
          "registroCar": { "type": "string" },
          "codigoProtocolo": { "type": "string", "nullable": true },
          "nomeImovel": { "type": "string", "nullable": true },
          "matriculaNumero": { "type": "string", "nullable": true },
          "municipio": { "type": "string", "nullable": true },
          "uf": { "type": "string", "nullable": true },
          "areaTotalHa": { "type": "string", "nullable": true },
          "proprietarioConsultado": { "type": "string", "nullable": true }
        }
      },
      "ProprietarioResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "sandbox": { "type": "boolean", "description": "Presente e true quando o documento consultado é um valor reservado de teste." },
          "total": { "type": "integer", "description": "Quantidade de itens retornados nesta resposta." },
          "limite": { "type": "integer", "example": 200, "description": "Teto de itens por consulta." },
          "truncado": {
            "type": "boolean",
            "description": "true quando o documento possui mais CARs do que o teto retornado."
          },
          "itens": { "type": "array", "items": { "$ref": "#/components/schemas/ProprietarioItem" } }
        }
      },
      "Precos": {
        "type": "object",
        "description": "Preços efetivos da conta autenticada (podem ser legados até a próxima recarga).",
        "properties": {
          "carBasico": { "type": "number", "format": "double", "example": 1.00 },
          "carCompleto": { "type": "number", "format": "double", "example": 2.30 },
          "proprietario": { "type": "number", "format": "double", "example": 2.60 },
          "recibo": { "type": "number", "format": "double", "example": 11.00 }
        }
      },
      "SaldoResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "saldoReais": { "type": "number", "format": "double", "example": 1234.56 },
          "limiteDiarioRequisicoes": { "type": "integer", "nullable": true },
          "limiteMensalReais": { "type": "number", "format": "double", "nullable": true },
          "precos": { "$ref": "#/components/schemas/Precos" },
          "usoHoje": {
            "type": "object",
            "properties": {
              "requisicoes": { "type": "integer" },
              "gasto": { "type": "number", "format": "double" },
              "erros": { "type": "integer" }
            }
          },
          "usoMes": {
            "type": "object",
            "properties": {
              "requisicoes": { "type": "integer" },
              "gasto": { "type": "number", "format": "double" }
            }
          }
        }
      },
      "ConsumoItem": {
        "type": "object",
        "properties": {
          "data": { "type": "string", "format": "date-time" },
          "endpoint": { "type": "string", "example": "/v1/car/{codigo}" },
          "chaveConsulta": { "type": "string", "nullable": true },
          "statusHttp": { "type": "integer", "example": 200 },
          "cobrado": { "type": "boolean" },
          "valorCobrado": { "type": "number", "format": "double" },
          "saldoAntes": { "type": "number", "format": "double", "nullable": true },
          "saldoDepois": { "type": "number", "format": "double", "nullable": true }
        }
      },
      "HistoricoResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "page": { "type": "integer" },
          "size": { "type": "integer" },
          "total": { "type": "integer" },
          "totalPaginas": { "type": "integer" },
          "itens": { "type": "array", "items": { "$ref": "#/components/schemas/ConsumoItem" } }
        }
      },
      "LimitesResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "message": { "type": "string" },
          "limiteDiarioRequisicoes": { "type": "integer", "nullable": true },
          "limiteMensalReais": { "type": "number", "format": "double", "nullable": true }
        }
      },
      "AtualizarLimitesRequest": {
        "type": "object",
        "minProperties": 1,
        "description": "Merge patch com três semânticas distintas por campo:\n- campo **omitido** → preserva o valor atual;\n- campo com **null** ou **0** → remove o limite (passa a ilimitado);\n- campo com número **> 0** → define o limite.\n\nEnvie ao menos um dos dois campos.",
        "properties": {
          "limiteDiarioRequisicoes": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 100000,
            "description": "Requisições cobradas por dia (fuso de Brasília). 0/null = sem limite."
          },
          "limiteMensalReais": {
            "type": "number",
            "format": "double",
            "nullable": true,
            "minimum": 0,
            "maximum": 1000000,
            "description": "Teto de gasto mensal em reais. 0/null = sem limite."
          }
        }
      },
      "UsoResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "periodoDias": { "type": "integer", "example": 30 },
          "itens": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "endpoint": { "type": "string" },
                "quantidade": { "type": "integer" },
                "valorTotal": { "type": "number", "format": "double" }
              }
            }
          }
        }
      },
      "FaixaBonus": {
        "type": "object",
        "properties": {
          "valorMinimo": { "type": "number", "format": "double" },
          "valorMaximo": { "type": "number", "format": "double", "nullable": true },
          "percentualBonus": { "type": "number", "format": "double" }
        }
      },
      "FaixasResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "valorMinimo": { "type": "number", "format": "double", "example": 500 },
          "usaPrecoLegado": { "type": "boolean" },
          "precosAtuais": { "$ref": "#/components/schemas/Precos" },
          "precosAposRecarga": { "$ref": "#/components/schemas/Precos" },
          "confirmarNovosPrecosObrigatorio": {
            "type": "boolean",
            "description": "Quando true, POST /v1/recarga exige confirmarNovosPrecos=true."
          },
          "faixas": { "type": "array", "items": { "$ref": "#/components/schemas/FaixaBonus" } },
          "preview": {
            "type": "object",
            "nullable": true,
            "properties": {
              "valorPago": { "type": "number", "format": "double" },
              "percentualBonus": { "type": "number", "format": "double" },
              "valorBonus": { "type": "number", "format": "double" },
              "totalCreditado": { "type": "number", "format": "double" }
            }
          }
        }
      },
      "CriarRecargaRequest": {
        "type": "object",
        "required": ["valor"],
        "properties": {
          "valor": { "type": "number", "format": "double", "minimum": 500, "example": 500.00 },
          "confirmarNovosPrecos": {
            "type": "boolean",
            "default": false,
            "description": "Obrigatório quando a conta ainda está em tabela legada."
          }
        }
      },
      "RecargaCriadaResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "reaproveitada": {
            "type": "boolean",
            "description": "true quando um PIX pendente de mesmo valor (últimos 10 min) foi reutilizado."
          },
          "recargaId": { "type": "string", "format": "uuid" },
          "paymentId": { "type": "string" },
          "valor": { "type": "number", "format": "double" },
          "status": { "type": "string", "enum": ["Pendente", "Confirmado", "Rejeitado"] },
          "qrCode": { "type": "string", "description": "Payload PIX copia-e-cola." },
          "qrCodeBase64": { "type": "string", "description": "PNG do QR Code em base64." },
          "previewBonus": {
            "type": "object",
            "properties": {
              "percentualBonus": { "type": "number", "format": "double" },
              "valorBonus": { "type": "number", "format": "double" },
              "totalCreditado": { "type": "number", "format": "double" }
            }
          },
          "message": { "type": "string" }
        }
      },
      "RecargaStatusResponse": {
        "type": "object",
        "description": "Os campos de bônus só vêm preenchidos quando `pago` é true; enquanto pendente, `qrCode` e `qrCodeBase64` são devolvidos para reexibir a cobrança.",
        "required": ["success", "pago", "recargaId", "valor", "status", "saldoAtual", "message"],
        "properties": {
          "success": { "type": "boolean", "example": true },
          "pago": { "type": "boolean", "description": "true somente quando `status` é Confirmado." },
          "recargaId": { "type": "string", "format": "uuid" },
          "valor": { "type": "number", "format": "double" },
          "valorBonus": { "type": "number", "format": "double" },
          "percentualBonus": { "type": "number", "format": "double" },
          "totalCreditado": { "type": "number", "format": "double" },
          "status": { "type": "string", "enum": ["Pendente", "Confirmado", "Rejeitado"] },
          "qrCode": { "type": "string", "description": "Presente enquanto pendente." },
          "qrCodeBase64": { "type": "string", "description": "Presente enquanto pendente." },
          "saldoAtual": { "type": "number", "format": "double" },
          "message": { "type": "string" }
        }
      },
      "RecargaItem": {
        "type": "object",
        "properties": {
          "recargaId": { "type": "string", "format": "uuid" },
          "data": { "type": "string", "format": "date-time" },
          "dataConfirmacao": { "type": "string", "format": "date-time", "nullable": true },
          "valorPago": { "type": "number", "format": "double" },
          "valorBonus": { "type": "number", "format": "double" },
          "percentualBonus": { "type": "number", "format": "double" },
          "totalCreditado": { "type": "number", "format": "double" },
          "status": { "type": "string", "enum": ["Pendente", "Confirmado", "Rejeitado"] }
        }
      },
      "RecargasResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "example": true },
          "page": { "type": "integer" },
          "size": { "type": "integer" },
          "total": { "type": "integer" },
          "totalPaginas": { "type": "integer" },
          "itens": { "type": "array", "items": { "$ref": "#/components/schemas/RecargaItem" } }
        }
      }
    }
  },
  "paths": {
    "/v1/car/{codigoCar}": {
      "get": {
        "operationId": "obterCarBasico",
        "tags": ["Consulta"],
        "summary": "CAR básico (cobrado)",
        "description": "Dados cadastrais do imóvel, sem lista de proprietários. Debita o preço vigente da conta apenas em HTTP 200.",
        "parameters": [
          {
            "name": "codigoCar",
            "in": "path",
            "required": true,
            "description": "Código CAR no formato UF-CodigoMunicipio-Hash.",
            "schema": { "type": "string" },
            "example": "BA-2900801-0A466FD693B247B9A397829A0D6AA2E3"
          }
        ],
        "responses": {
          "200": {
            "description": "Dados do CAR. Gera cobrança.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CarResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/v1/car/{codigoCar}/completo": {
      "get": {
        "operationId": "obterCarCompleto",
        "tags": ["Consulta"],
        "summary": "CAR completo com proprietários (cobrado)",
        "parameters": [
          {
            "name": "codigoCar",
            "in": "path",
            "required": true,
            "schema": { "type": "string" },
            "example": "BA-2900801-0A466FD693B247B9A397829A0D6AA2E3"
          }
        ],
        "responses": {
          "200": {
            "description": "Dados completos. Gera cobrança.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CarCompletoResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/v1/car/{codigoCar}/recibo": {
      "get": {
        "operationId": "obterReciboCarPdf",
        "tags": ["Consulta"],
        "summary": "PDF do recibo CAR (cobrado)",
        "description": "Resposta de sucesso é binária (application/pdf). Erros retornam JSON no padrão ApiError.",
        "parameters": [
          {
            "name": "codigoCar",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "PDF do recibo. Gera cobrança.",
            "content": { "application/pdf": { "schema": { "type": "string", "format": "binary" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "502": {
            "description": "Falha ao transmitir o PDF (`RECIBO_INDISPONIVEL`).",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
          },
          "503": {
            "description": "Serviço de recibos indisponível (`SERVICO_INDISPONIVEL`).",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
          }
        }
      }
    },
    "/v1/proprietario/{cpfCnpj}": {
      "get": {
        "operationId": "obterCarsPorProprietario",
        "tags": ["Consulta"],
        "summary": "CARs por CPF/CNPJ (cobrado)",
        "description": "Retorna no máximo 200 CARs. Quando o documento possui mais registros, `truncado` vem true.",
        "parameters": [
          {
            "name": "cpfCnpj",
            "in": "path",
            "required": true,
            "description": "CPF (11 dígitos) ou CNPJ (14 caracteres numéricos ou alfanuméricos), com ou sem máscara.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Lista de CARs. Gera cobrança.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProprietarioResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/v1/saldo": {
      "get": {
        "operationId": "obterSaldo",
        "tags": ["Conta"],
        "summary": "Saldo, limites, preços e uso (grátis)",
        "responses": {
          "200": {
            "description": "Situação da conta.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SaldoResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/v1/historico": {
      "get": {
        "operationId": "listarHistoricoConsumo",
        "tags": ["Conta"],
        "summary": "Histórico paginado de requisições (grátis)",
        "parameters": [
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Size" },
          {
            "name": "busca",
            "in": "query",
            "description": "Trecho contido em chaveConsulta ou endpoint.",
            "schema": { "type": "string" }
          },
          {
            "name": "endpoint",
            "in": "query",
            "description": "Filtro exato pelo endpoint registrado.",
            "schema": {
              "type": "string",
              "enum": [
                "/v1/car/{codigo}",
                "/v1/car/{codigo}/completo",
                "/v1/car/{codigo}/recibo",
                "/v1/proprietario/{cpf_cnpj}"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": { "type": "string", "enum": ["sucesso", "erro"] }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de consumo.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HistoricoResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/v1/limites": {
      "get": {
        "operationId": "obterLimites",
        "tags": ["Conta"],
        "summary": "Consultar limites (grátis)",
        "responses": {
          "200": {
            "description": "Limites atuais.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LimitesResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "put": {
        "operationId": "atualizarLimites",
        "tags": ["Conta"],
        "summary": "Atualizar limites (merge patch, grátis)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AtualizarLimitesRequest" },
              "examples": {
                "somenteDiario": {
                  "summary": "Altera só o diário, preserva o mensal",
                  "value": { "limiteDiarioRequisicoes": 1000 }
                },
                "removerMensal": {
                  "summary": "Remove o teto mensal",
                  "value": { "limiteMensalReais": 0 }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Limites atualizados.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LimitesResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/v1/uso": {
      "get": {
        "operationId": "obterUsoPorEndpoint",
        "tags": ["Conta"],
        "summary": "Uso por endpoint nos últimos 30 dias (grátis)",
        "responses": {
          "200": {
            "description": "Agregados por endpoint.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UsoResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/v1/recarga/faixas": {
      "get": {
        "operationId": "obterFaixasBonus",
        "tags": ["Recarga"],
        "summary": "Faixas de bônus e preços (grátis)",
        "parameters": [
          {
            "name": "valor",
            "in": "query",
            "description": "Quando informado, retorna preview de bônus para esse valor.",
            "schema": { "type": "number", "format": "double" }
          }
        ],
        "responses": {
          "200": {
            "description": "Faixas ativas e preços.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FaixasResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/v1/recarga": {
      "post": {
        "operationId": "criarRecargaPix",
        "tags": ["Recarga"],
        "summary": "Gerar PIX de recarga (grátis; PIX real)",
        "description": "Gera cobrança PIX de no mínimo R$ 500. Um PIX pendente de mesmo valor criado nos últimos 10 minutos é reaproveitado (`reaproveitada: true`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CriarRecargaRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "PIX gerado ou reaproveitado.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecargaCriadaResponse" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "502": {
            "description": "Falha no provedor de pagamento (`ERRO_PIX`).",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
          },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/v1/recarga/{recargaId}": {
      "get": {
        "operationId": "obterStatusRecarga",
        "tags": ["Recarga"],
        "summary": "Status da recarga (grátis)",
        "description": "Somente leitura: devolve o status já registrado na Umbrella, sem consultar o provedor de pagamento e sem efeito colateral. A confirmação do PIX e o crédito de saldo são feitos automaticamente pelo webhook do provedor, com reconciliação periódica de retaguarda. Poll recomendado a cada 5 segundos ou mais.",
        "parameters": [
          {
            "name": "recargaId",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "format": "uuid" }
          }
        ],
        "responses": {
          "200": {
            "description": "Status atual da recarga.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecargaStatusResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/v1/recargas": {
      "get": {
        "operationId": "listarRecargas",
        "tags": ["Recarga"],
        "summary": "Histórico de recargas (grátis)",
        "parameters": [
          { "$ref": "#/components/parameters/Page" },
          { "$ref": "#/components/parameters/Size" },
          {
            "name": "status",
            "in": "query",
            "schema": { "type": "string", "enum": ["Pendente", "Confirmado", "Rejeitado"] }
          }
        ],
        "responses": {
          "200": {
            "description": "Página de recargas.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecargasResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    }
  }
}
