{
  "openapi": "3.1.0",
  "info": {
    "title": "Radar CNPJ",
    "version": "c52b3d87",
    "description": "radar-cnpj.com — índice GET /api/."
  },
  "servers": [
    {
      "url": "https://radar-cnpj.com"
    }
  ],
  "components": {
    "schemas": {
      "SaudeOrigem": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` quando a origem responde."
          },
          "service": {
            "type": "string",
            "description": "Qual serviço respondeu."
          },
          "db": {
            "type": "string",
            "description": "Estado do banco: `up` ou o motivo de não estar."
          },
          "import": {
            "type": "object",
            "description": "`dump_date`, `loaded_at` e as contagens por tabela — é a idade real do dado."
          }
        },
        "required": [
          "ok",
          "service",
          "db",
          "import"
        ],
        "description": "Disponibilidade do serviço e data de referência dos dados."
      },
      "Avaliacao": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` quando a avaliação saiu."
          },
          "texto": {
            "type": "string",
            "description": "A ideia como você a escreveu."
          },
          "cnae": {
            "type": "string",
            "description": "CNAE a que a ideia foi mapeada.",
            "nullable": true
          },
          "fonte_cnae": {
            "type": "string",
            "description": "Como o CNAE foi determinado: pela IA ou pelo hint que você mandou."
          },
          "filtros": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Os filtros normalizados que a avaliação aplicou — dá para reusar em `GET /api/busca`."
          },
          "ficha": {
            "allOf": [
              {
                "$ref": "#/components/schemas/FichaOferta"
              }
            ],
            "description": "O retrato da oferta formal e a leitura honesta dela."
          }
        },
        "required": [
          "ok",
          "texto",
          "cnae",
          "fonte_cnae",
          "filtros",
          "ficha"
        ],
        "description": "Leitura da oferta formal de empresas para uma atividade e região."
      },
      "FichaOferta": {
        "type": "object",
        "properties": {
          "mapeou_cnae": {
            "type": "boolean",
            "description": "Se deu para mapear a ideia num CNAE. Sem isso, os números abaixo não valem."
          },
          "oferta": {
            "type": "object",
            "description": "Empresas ativas, abertas e baixadas no recorte."
          },
          "formalizacao": {
            "type": "object",
            "description": "Como essas empresas se formalizam: MEI, Simples, porte."
          },
          "leitura": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "O que os números sugerem, em frases — sem promessa de demanda."
          },
          "limites": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "O que estes dados NÃO dizem. Não há volume de busca aqui, e renda passiva isto não é."
          },
          "passivo": {
            "type": "object",
            "description": "Sinais de risco no recorte, quando existem.",
            "nullable": true
          }
        },
        "required": [
          "mapeou_cnae",
          "oferta",
          "formalizacao",
          "leitura",
          "limites",
          "passivo"
        ],
        "description": "Quantas empresas já fazem isso, como elas se formalizam e o que esses números não dizem."
      },
      "FichaCnpj": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` quando o CNPJ existe na base."
          },
          "data": {
            "type": "object",
            "description": "O cadastro: identificação, endereço, contato, sócios, CNAEs, Simples e situação. Com `mascarado: true`, o nome de sócio pessoa física sai como `LUIS F. R. P.` (sem documento) e `contato` mostra só parte dos telefones e do e-mail."
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "description": "A ficha cadastral de uma empresa, com o dado pessoal mascarado."
      },
      "FichaRevelada": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` quando a revelação foi entregue."
          },
          "data": {
            "type": "object",
            "description": "O mesmo cadastro de `GET /api/cnpj/:cnpj`, com `socios[].nome`, `socios[].documento` e `contato` inteiros e `mascarado: false`."
          },
          "cobranca": {
            "type": "object",
            "description": "`via` (`credito`, `x402` ou `gratis`), `preco_usd`, `saldo_usd` (só no crédito) e `repetido` (`true` quando o mesmo código de crédito já tinha revelado esta empresa hoje e nada foi cobrado)."
          }
        },
        "required": [
          "ok",
          "data",
          "cobranca"
        ],
        "description": "A ficha com o dado pessoal sem máscara, e o que a revelação custou."
      },
      "PaginaDeBusca": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` quando a busca rodou."
          },
          "page": {
            "type": "integer",
            "description": "Página devolvida, começando em 0."
          },
          "pageSize": {
            "type": "integer",
            "description": "Quantos resultados por página."
          },
          "hasMore": {
            "type": "boolean",
            "description": "Se existe página seguinte."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Empresa"
            },
            "description": "As empresas desta página."
          }
        },
        "required": [
          "ok",
          "page",
          "pageSize",
          "hasMore",
          "results"
        ],
        "description": "Página da busca. Paginação por `page`/`pageSize`, e `hasMore` no lugar de um total — contar 71 milhões de estabelecimentos a cada busca não muda decisão nenhuma."
      },
      "Empresa": {
        "type": "object",
        "properties": {
          "cnpj": {
            "type": "string",
            "description": "CNPJ só com dígitos, 14 posições."
          },
          "cnpjFormatted": {
            "type": "string",
            "description": "O mesmo CNPJ com pontuação, para mostrar a uma pessoa."
          },
          "razaoSocial": {
            "type": "string",
            "description": "Razão social registrada."
          },
          "nomeFantasia": {
            "type": "string",
            "description": "Nome fantasia, quando declarado.",
            "nullable": true
          },
          "situacao": {
            "type": "string",
            "description": "Situação cadastral: ativa, baixada, suspensa, inapta, nula."
          },
          "uf": {
            "type": "string",
            "description": "Unidade da federação do estabelecimento.",
            "nullable": true
          },
          "municipio": {
            "type": "string",
            "description": "Município do estabelecimento.",
            "nullable": true
          },
          "bairro": {
            "type": "string",
            "description": "Bairro do estabelecimento.",
            "nullable": true
          },
          "cnae": {
            "type": "string",
            "description": "CNAE principal do estabelecimento.",
            "nullable": true
          }
        },
        "required": [
          "cnpj",
          "cnpjFormatted",
          "razaoSocial",
          "nomeFantasia",
          "situacao",
          "uf",
          "municipio",
          "bairro",
          "cnae"
        ],
        "description": "Uma empresa no resultado de busca. É o recorte da origem, não o cadastro inteiro."
      },
      "Export": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` quando o export saiu."
          },
          "count": {
            "type": "integer",
            "description": "Quantas empresas o arquivo traz."
          },
          "capped": {
            "type": "boolean",
            "description": "`true` quando o export bateu no teto da origem e não trouxe tudo — o número acima não é o total do filtro."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Empresa"
            },
            "description": "As empresas exportadas."
          }
        },
        "required": [
          "ok",
          "count",
          "capped",
          "results"
        ],
        "description": "O envelope de `GET /api/export?format=json`. Com `format=csv` a rota devolve o arquivo, não este objeto."
      },
      "ListaSugestao": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "As sugestões, cada uma com o texto e o que ela identifica."
          }
        },
        "required": [
          "ok",
          "results"
        ],
        "description": "Sugestões de autocomplete — o suficiente para montar a lista enquanto a pessoa digita."
      },
      "ListaReferencia": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ItemReferencia"
            },
            "description": "Os itens que casam com a consulta."
          }
        },
        "required": [
          "ok",
          "results"
        ],
        "description": "Itens de um vocabulário oficial (CNAE, município, natureza jurídica) para montar seletor."
      },
      "ItemReferencia": {
        "type": "object",
        "properties": {
          "codigo": {
            "type": "integer",
            "description": "Código oficial, ex. `4721102` para padaria."
          },
          "descricao": {
            "type": "string",
            "description": "Nome do código por extenso."
          }
        },
        "required": [
          "codigo",
          "descricao"
        ],
        "description": "Um item de vocabulário oficial: o código que a Receita usa e o nome dele."
      },
      "Local": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "cidade": {
            "type": "string",
            "description": "Cidade detectada.",
            "nullable": true
          },
          "uf": {
            "type": "string",
            "description": "Unidade da federação detectada.",
            "nullable": true
          },
          "cep": {
            "type": "string",
            "description": "CEP aproximado da borda.",
            "nullable": true
          },
          "pais": {
            "type": "string",
            "description": "País detectado, ISO 3166-1 alpha-2.",
            "nullable": true
          },
          "fonte": {
            "type": "string",
            "description": "De onde veio a detecção."
          }
        },
        "required": [
          "ok",
          "cidade",
          "uf",
          "cep",
          "pais",
          "fonte"
        ],
        "description": "Localização aproximada do visitante. Nunca é cacheado: cache aqui daria o lugar de outra pessoa."
      },
      "MunicipioProximo": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "municipio": {
            "type": "object",
            "description": "`codigo` (da Receita; `null` sem par de nome), `descricao`, `uf` e `km` — 0 dentro do contorno; fora de todos (praia, mar, GPS impreciso), a distância até o contorno mais próximo, até 50 km."
          },
          "bairro": {
            "type": "string",
            "description": "Bairro mais próximo, quando existe."
          },
          "cep": {
            "type": "string",
            "description": "CEP mais próximo, quando existe."
          },
          "bairro_km": {
            "type": "number",
            "description": "Distância até o endereço de referência, em km."
          }
        },
        "required": [
          "ok",
          "municipio"
        ],
        "description": "O município que contém o ponto e o bairro mais próximo, quando disponível."
      },
      "Watches": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "watches": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Um item por CNPJ acompanhado; `suspensa: true` quando está além da cota."
          },
          "quota": {
            "type": "integer",
            "description": "Quantos monitores você pode ter agora: grátis + vagas em vigor."
          },
          "base": {
            "type": "integer",
            "description": "Quantos são grátis."
          },
          "pagos": {
            "type": "integer",
            "description": "Vagas compradas e em vigor (30 dias cada)."
          },
          "used": {
            "type": "integer",
            "description": "Quantos estão ativos."
          },
          "suspensas": {
            "type": "integer",
            "description": "Quantos ficaram além da cota — não geram alerta até a cota voltar."
          }
        },
        "required": [
          "ok",
          "watches",
          "quota",
          "base",
          "pagos",
          "used",
          "suspensas"
        ],
        "description": "Os CNPJs que você acompanha (conta ou carteira de crédito), com a cota que a ORIGEM aplica."
      },
      "Ok": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true` — a falha vem como status 4xx/5xx, não como `ok:false`."
          }
        },
        "required": [
          "ok"
        ],
        "description": "Confirmação de escrita que não tem corpo próprio a devolver."
      },
      "Alertas": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "alerts": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Um item por alteração detectada num CNPJ acompanhado."
          },
          "retidos": {
            "type": "integer",
            "description": "Quantos alertas são de monitores suspensos (além da cota) — voltam quando a cota voltar."
          }
        },
        "required": [
          "ok",
          "alerts",
          "retidos"
        ],
        "description": "Os alertas gerados para os CNPJs que você acompanha."
      },
      "HistoricoCnpj": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "Sempre `true`."
          },
          "cnpj": {
            "type": "string",
            "description": "CNPJ consultado, só dígitos."
          },
          "cnpjFormatted": {
            "type": "string",
            "description": "O mesmo CNPJ com pontuação."
          },
          "changes": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Uma entrada por alteração observada, com o campo, o valor anterior e a data."
          }
        },
        "required": [
          "ok",
          "cnpj",
          "cnpjFormatted",
          "changes"
        ],
        "description": "O que mudou no cadastro de um CNPJ ao longo do tempo — é o que o monitoramento observa."
      },
      "Metricas": {
        "type": "object",
        "properties": {
          "app": {
            "type": "string",
            "description": "Nome do produto."
          },
          "today_visits": {
            "type": "integer",
            "description": "Visitantes distintos hoje — não conta autocomplete."
          },
          "days": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Um registro por dia da janela. Só com `METRICS_TOKEN`."
          },
          "usage": {
            "type": "object",
            "description": "Uso por recurso — aqui, `queries`."
          }
        },
        "required": [
          "app",
          "today_visits",
          "usage"
        ],
        "description": "Métricas operacionais da origem, 7 dias. Sem token vêm só `app`, `today_visits` e `usage`; `days` exige `METRICS_TOKEN`."
      },
      "ApiAccess": {
        "type": "object",
        "properties": {
          "offer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ApiAccessOffer"
              }
            ],
            "description": "Current offer and payment instructions."
          },
          "enabled": {
            "type": "boolean",
            "description": "Present in public discovery; false means no purchases."
          },
          "id": {
            "type": "string",
            "description": "Purchase ID; not a credential."
          },
          "status": {
            "type": "string",
            "description": "paid, unpaid or pending."
          },
          "granted_credits": {
            "type": "integer",
            "description": "Original grant, not remaining usage."
          },
          "expires_at": {
            "type": "string",
            "description": "ISO expiry, 30 days after purchase.",
            "nullable": true
          },
          "receipt": {
            "type": "string",
            "description": "Confirmed payment receipt.",
            "nullable": true
          },
          "via": {
            "type": "string",
            "description": "x402, credito or gated homolog.",
            "nullable": true
          },
          "message": {
            "type": "string",
            "description": "Next action in the requested language."
          }
        },
        "required": [
          "offer"
        ]
      },
      "ApiAccessOffer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Package identifier."
          },
          "price_usd": {
            "type": "number",
            "description": "Price in USD."
          },
          "credits": {
            "type": "integer",
            "description": "Basic reads included."
          },
          "days": {
            "type": "integer",
            "description": "Validity after payment, in days."
          },
          "auto_renew": {
            "type": "boolean",
            "description": "False: the client explicitly buys another package."
          },
          "unit": {
            "type": "string",
            "description": "basic_data_read; one page of up to 20 metadata records."
          },
          "products": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Data indexes sharing the same package."
          },
          "purchase": {
            "type": "string",
            "description": "Absolute purchase URL."
          },
          "method": {
            "type": "string",
            "description": "HTTP method for the explicit package purchase: POST."
          },
          "status": {
            "type": "string",
            "description": "GET with X-API-Pass checks the private purchase status."
          },
          "header": {
            "type": "string",
            "description": "X-API-Pass."
          },
          "payment_methods": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "x402 or prepaid_credit."
          },
          "instructions": {
            "type": "string",
            "description": "Generate and retain the pass before payment."
          },
          "generate_pass": {
            "type": "string",
            "description": "JavaScript example using cryptographic randomness."
          },
          "client": {
            "type": "string",
            "description": "Auditable ES module client; orchestrates purchase and data retry with caller-owned wallet and durable state.",
            "nullable": true
          },
          "guide": {
            "type": "string",
            "description": "Client setup, explicit budget, recovery and data value.",
            "nullable": true
          },
          "workflow": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ApiAccessWorkflow"
              }
            ],
            "description": "Machine-readable purchase and recovery contract."
          },
          "evaluation": {
            "type": "object",
            "description": "Free evaluation: register URL, X-Agent-Pass header, 1,000 reads per product, 30 days, no renewal. Registration grants independent quotas on the three indexes; preserve the credential.",
            "nullable": true
          }
        },
        "required": [
          "id",
          "price_usd",
          "credits",
          "days",
          "auto_renew",
          "unit",
          "products",
          "purchase",
          "method",
          "status",
          "header",
          "payment_methods",
          "instructions",
          "generate_pass",
          "client",
          "guide",
          "workflow",
          "evaluation"
        ]
      },
      "ApiAccessWorkflow": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer",
            "description": "Workflow version."
          },
          "kind": {
            "type": "string",
            "description": "package_then_retry: buy at purchase, then retry the original data URL."
          },
          "purchase_requires_authority": {
            "type": "boolean",
            "description": "The client needs an explicit spending budget."
          },
          "retry_same_pass": {
            "type": "boolean",
            "description": "Persist the pass and original signed proof before submitting."
          },
          "on_unknown_payment": {
            "type": "string",
            "description": "Query the purchase or reconcile the original proof; never sign again automatically."
          }
        },
        "required": [
          "version",
          "kind",
          "purchase_requires_authority",
          "retry_same_pass",
          "on_unknown_payment"
        ]
      },
      "PaymentQuota": {
        "type": "object",
        "properties": {
          "free": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentFree"
            },
            "description": "Free allowances and their windows."
          },
          "paid": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentPrice"
            },
            "description": "List prices in USD. The operation's 402 is the payable quote."
          },
          "how_to_pay": {
            "type": "string",
            "description": "Payment instructions and availability restrictions."
          },
          "live": {
            "type": "string",
            "description": "Authoritative product quota endpoint.",
            "nullable": true
          },
          "free_now": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "SKUs temporarily free despite their list price."
          },
          "trial": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentTrial"
              }
            ],
            "description": "Registration trial, when offered."
          }
        },
        "required": [
          "free",
          "paid",
          "how_to_pay",
          "live"
        ]
      },
      "PaymentFree": {
        "type": "object",
        "properties": {
          "o_que": {
            "type": "string",
            "description": "Operation or allowance."
          },
          "limite": {
            "type": "string",
            "description": "Allowance and eligibility."
          },
          "janela": {
            "type": "string",
            "description": "Reset window, when applicable.",
            "nullable": true
          }
        },
        "required": [
          "o_que",
          "limite",
          "janela"
        ]
      },
      "PaymentPrice": {
        "type": "object",
        "properties": {
          "o_que": {
            "type": "string",
            "description": "Operation and billing unit."
          },
          "price_usd": {
            "type": "number",
            "description": "Current list price in USD."
          }
        },
        "required": [
          "o_que",
          "price_usd"
        ]
      },
      "PaymentTrial": {
        "type": "object",
        "properties": {
          "days": {
            "type": "integer",
            "description": "Trial duration in days."
          },
          "how": {
            "type": "string",
            "description": "Eligibility and activation steps."
          }
        },
        "required": [
          "days",
          "how"
        ]
      },
      "PaymentX402": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Always `x402` — the only billing protocol accepted."
          },
          "mode": {
            "type": "string",
            "description": "Seller mode: `live` charges for real, `dev` lets calls through unpaid."
          },
          "network": {
            "type": "string",
            "description": "USDC network: `base` in production, `base-sepolia` in staging."
          },
          "chain_id": {
            "type": "integer",
            "description": "EVM chain ID of the network above, so the wallet signs on the right chain."
          },
          "pay_to": {
            "type": "string",
            "description": "Address that receives the payment.",
            "nullable": true
          },
          "homolog": {
            "type": "boolean",
            "description": "Staging seam on: the loop can be closed without spending USDC."
          },
          "dev": {
            "type": "boolean",
            "description": "Development mode: the 402 is simulated."
          },
          "dev_gate": {
            "type": "boolean",
            "description": "A homologation credential is configured; this grants no access."
          },
          "gratis": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Temporarily free SKUs."
          },
          "facilitator": {
            "type": "string",
            "description": "URL of the facilitator that verifies and settles the payment."
          },
          "asset": {
            "type": "string",
            "description": "Accepted currency — always `USDC`."
          },
          "asset_address": {
            "type": "string",
            "description": "USDC contract on the network above."
          },
          "faucet": {
            "type": "string",
            "description": "Test-USDC faucet; only on base-sepolia.",
            "nullable": true
          },
          "wallets": {
            "type": "object",
            "description": "Links to wallets that speak x402 (metamask, coinbase, base_app)."
          }
        },
        "required": [
          "provider",
          "mode",
          "network",
          "chain_id",
          "pay_to",
          "homolog",
          "dev",
          "dev_gate",
          "facilitator",
          "asset",
          "asset_address",
          "faucet",
          "wallets"
        ],
        "description": "x402 payment configuration in force. Comes from `planPublic` and is the same across the products."
      },
      "PaymentCredit": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "POST to purchase credit; GET with X-Credito to inspect its balance."
          },
          "header": {
            "type": "string",
            "description": "Header for a previously issued credit token: X-Credito."
          }
        },
        "required": [
          "url",
          "header"
        ]
      }
    },
    "securitySchemes": {
      "globalAccount": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-mm-auth",
        "description": "Global session in the product's HttpOnly cookie; writes require exact Origin and X-CSRF-Token."
      },
      "contaChaveApi": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "mmk_…",
        "description": "Account API key: `Authorization: Bearer mmk_…` or `X-Api-Key: mmk_…`. Created on the account page (API keys), valid only in the product where it was created; it acts as the account (or the organization that owns it)."
      }
    }
  },
  "paths": {
    "/api/auth/bootstrap": {
      "get": {
        "operationId": "get_api_auth_bootstrap",
        "summary": "Prepara o navegador para entrar na conta global.",
        "description": "Define cookie HttpOnly restrito ao host. CSRF vinculado à sessão atual. Sem CORS.\nDevolve: { csrf, context }",
        "responses": {
          "200": {
            "description": "{ csrf, context }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "csrf": {
                      "type": "string",
                      "description": "X-CSRF-Token"
                    },
                    "context": {
                      "type": "string",
                      "description": "Opaque view context, also in X-MM-Context; not a credential / contexto opaco da vista, não é credencial."
                    }
                  },
                  "required": [
                    "csrf",
                    "context"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "invalid_request"
          },
          "403": {
            "description": "invalid_origin / invalid_csrf"
          },
          "503": {
            "description": "auth_unavailable: a sessão anterior é preservada / the previous session is preserved"
          }
        },
        "security": []
      }
    },
    "/api/account/profile": {
      "get": {
        "operationId": "get_api_account_profile",
        "summary": "Consulta seu perfil global.",
        "description": "Lê preferências atuais da conta. Altere-as na página da conta; produtos não mantêm perfil autoritativo separado.\nDevolve: {profile:{name,locale,timeZone,theme,revision}}",
        "responses": {
          "200": {
            "description": "{profile:{name,locale,timeZone,theme,revision}}"
          },
          "401": {
            "description": "invalid_session"
          },
          "503": {
            "description": "auth_unavailable"
          }
        },
        "security": [
          {
            "globalAccount": []
          }
        ]
      }
    },
    "/api/account/avatar": {
      "get": {
        "operationId": "get_api_account_avatar",
        "summary": "Consulta sua foto de perfil global.",
        "description": "WebP privado de até 64 KiB, sem cache. Altere-o na conta. Não aceita ID de usuário ou URL de objeto.\nDevolve: image/webp; Cache-Control: no-store",
        "responses": {
          "200": {
            "description": "image/webp; Cache-Control: no-store",
            "content": {
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "invalid_session"
          },
          "404": {
            "description": "not_found: no photo / sem foto"
          },
          "503": {
            "description": "auth_unavailable"
          }
        },
        "security": [
          {
            "globalAccount": []
          }
        ]
      }
    },
    "/api/me": {
      "get": {
        "operationId": "get_api_me",
        "summary": "Lê a conta global atual neste produto.",
        "description": "Devolve: {user:{identityId,sessionId,productId,audience,authTime,methods,mfaState}}",
        "responses": {
          "200": {
            "description": "{user:{identityId,sessionId,productId,audience,authTime,methods,mfaState}}"
          },
          "401": {
            "description": "invalid_session"
          },
          "503": {
            "description": "auth_unavailable"
          }
        },
        "security": [
          {
            "globalAccount": []
          }
        ]
      }
    },
    "/api/auth/logout": {
      "post": {
        "operationId": "post_api_auth_logout",
        "summary": "Revoga esta sessão do produto.",
        "description": "Exige bootstrap/CSRF deste navegador e sessão. As sessões de outros produtos permanecem ativas.\nDevolve: { ok }",
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "true"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "invalid_request"
          },
          "403": {
            "description": "invalid_origin / invalid_csrf"
          },
          "503": {
            "description": "auth_unavailable: a sessão anterior é preservada / the previous session is preserved"
          }
        },
        "security": [
          {
            "globalAccount": []
          }
        ]
      }
    },
    "/api/account/keys": {
      "get": {
        "operationId": "get_api_account_keys",
        "summary": "Lista suas chaves de API neste produto.",
        "description": "Nunca devolve a chave: nome, 4 últimos caracteres, organização, criação, último uso (por hora) e se ainda vale.\nDevolve: { keys }",
        "responses": {
          "200": {
            "description": "{ keys }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "`id`, `name`, `organizationId`, `last4`, `createdAt`, `lastUsedAt`, `revokedAt`, `active` (false quando revogada ou parada por troca de senha / encerrar todos os acessos)."
                    }
                  },
                  "required": [
                    "keys"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "invalid_session"
          },
          "503": {
            "description": "auth_unavailable"
          }
        },
        "security": [
          {
            "globalAccount": []
          }
        ]
      }
    },
    "/api/account/keys/create": {
      "post": {
        "operationId": "post_api_account_keys_create",
        "summary": "Cria uma chave de API para agentes e scripts.",
        "description": "Exige entrada nos últimos 5 minutos; a de organização também exige segundo fator na sessão e o papel de dona/administradora com o produto ligado. No máximo 10 chaves vivas por conta e produto. A chave (`secret`) volta UMA vez.\nDevolve: { key, secret }",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Até 60 caracteres."
                  },
                  "organizationId": {
                    "type": "string",
                    "description": "`null` para chave da conta."
                  }
                },
                "required": [
                  "name",
                  "organizationId"
                ]
              },
              "example": {
                "name": "agent",
                "organizationId": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ key, secret }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "object",
                      "description": "`id`, `name`, `organizationId`, `last4`, `createdAt`."
                    },
                    "secret": {
                      "type": "string",
                      "description": "`mmk_…`, mostrada uma vez."
                    }
                  },
                  "required": [
                    "key",
                    "secret"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "invalid_key_name / invalid_organization"
          },
          "401": {
            "description": "invalid_session / reauth_required"
          },
          "403": {
            "description": "invalid_origin / invalid_csrf / organization_forbidden / organization_mfa_required"
          },
          "409": {
            "description": "key_limit_reached"
          },
          "503": {
            "description": "auth_unavailable"
          }
        },
        "security": [
          {
            "globalAccount": []
          }
        ]
      }
    },
    "/api/account/keys/revoke": {
      "post": {
        "operationId": "post_api_account_keys_revoke",
        "summary": "Revoga uma das suas chaves de API.",
        "description": "Para a chave na hora. Repetir não faz mal.\nDevolve: { ok }",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "O `id` da chave."
                  }
                },
                "required": [
                  "id"
                ]
              },
              "example": {
                "id": "…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "true"
                    }
                  },
                  "required": [
                    "ok"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "invalid_key_id"
          },
          "401": {
            "description": "invalid_session"
          },
          "403": {
            "description": "invalid_origin / invalid_csrf"
          },
          "404": {
            "description": "key_not_found"
          },
          "503": {
            "description": "auth_unavailable"
          }
        },
        "security": [
          {
            "globalAccount": []
          }
        ]
      }
    },
    "/okf/{arquivo}": {
      "get": {
        "operationId": "get_okf_by_arquivo",
        "summary": "Bundle OKF (Open Knowledge Format v0.1): markdown com frontmatter para o agente ler o produto inteiro sem parsear HTML.",
        "description": "Devolve: `text/markdown`. Comece por `/okf/index.md`, que lista o bundle.",
        "parameters": [
          {
            "name": "arquivo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`index.md`, `sobre.md`, `api.md` ou `faq.md`.",
            "example": "index.md"
          }
        ],
        "responses": {
          "200": {
            "description": "`text/markdown`. Comece por `/okf/index.md`, que lista o bundle."
          },
          "404": {
            "description": "Arquivo fora do bundle."
          }
        }
      }
    },
    "/.well-known/{arquivo}": {
      "get": {
        "operationId": "get_well_known_by_arquivo",
        "summary": "Descoberta de máquina antes da home: `api-catalog` (RFC 9727, linkset com a API e o MCP), `security.txt` (RFC 9116), `x402` (manifesto de pagamento: rede, carteira e rotas que cobram), `agent-card.json` (identidade do agente: ferramentas MCP e portas de descoberta; também em `/agent.json`) e `mcp-registry-auth` (chave do registro oficial de MCP).",
        "description": "Devolve: `application/linkset+json` no api-catalog; `application/json` no x402, no agent-card.json e no apis.json; `text/plain` nos outros dois.",
        "parameters": [
          {
            "name": "arquivo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`api-catalog`, `security.txt`, `x402`, `agent-card.json`, `mcp-registry-auth` ou `apis.json`.",
            "example": "api-catalog"
          }
        ],
        "responses": {
          "200": {
            "description": "`application/linkset+json` no api-catalog; `application/json` no x402, no agent-card.json e no apis.json; `text/plain` nos outros dois."
          },
          "404": {
            "description": "Nome fora dos seis publicados."
          }
        }
      }
    },
    "/apis.json": {
      "get": {
        "operationId": "get_apis_json",
        "summary": "APIs.json (apisjson.org, 0.19): o índice que o APIs.io colhe — a API, o MCP, OpenAPI, guia e bundle OKF num arquivo só. Também em `/.well-known/apis.json`.",
        "description": "Devolve: `application/json` no formato APIs.json 0.19: `apis[]` com `baseURL`, `humanURL` e `properties[]`.",
        "responses": {
          "200": {
            "description": "`application/json` no formato APIs.json 0.19: `apis[]` com `baseURL`, `humanURL` e `properties[]`."
          }
        }
      }
    },
    "/agent.json": {
      "get": {
        "operationId": "get_agent_json",
        "summary": "Cartão do agente: identidade, quem opera, documentação, o endpoint MCP e as ferramentas que ele serve. Mesmo documento de `/.well-known/agent-card.json`.",
        "description": "Devolve: `application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` e `skills[]`.",
        "responses": {
          "200": {
            "description": "`application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` e `skills[]`."
          }
        }
      }
    },
    "/okf/{tipo}/{id}.md": {
      "get": {
        "operationId": "get_okf_by_tipo_by_id_md",
        "summary": "O mesmo registro que a API responde, em markdown OKF: `cnpj` (Empresa por CNPJ, na base da Receita). Via de acesso para quem já tem o id, não catálogo.",
        "description": "Devolve: `text/markdown` com frontmatter OKF; `resource` aponta o JSON equivalente. Sem `.md` responde 301 para o canônico.",
        "parameters": [
          {
            "name": "tipo",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Um de: `cnpj`.",
            "example": "cnpj"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "O id do registro, como a API o aceita.",
            "example": "00000000000191"
          }
        ],
        "responses": {
          "200": {
            "description": "`text/markdown` com frontmatter OKF; `resource` aponta o JSON equivalente. Sem `.md` responde 301 para o canônico."
          },
          "404": {
            "description": "Id fora da base, em markdown."
          }
        }
      }
    },
    "/api/": {
      "get": {
        "operationId": "api_index",
        "summary": "Índice da API, com operações, formatos, autenticação e limites.",
        "description": "Devolve: { name, description, build, base_url, origin_api, docs, conventions, auth, endpoints, quota, mcp, mcp_tools, quickstart }",
        "responses": {
          "200": {
            "description": "{ name, description, build, base_url, origin_api, docs, conventions, auth, endpoints, quota, mcp, mcp_tools, quickstart }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Nome do produto."
                    },
                    "description": {
                      "type": "string",
                      "description": "O que o produto faz, em uma frase."
                    },
                    "build": {
                      "type": "string",
                      "description": "Commit publicado."
                    },
                    "base_url": {
                      "type": "string",
                      "description": "Origem em que esta API está servindo."
                    },
                    "origin_api": {
                      "type": "string",
                      "description": "Endereço público da API de dados do produto."
                    },
                    "docs": {
                      "type": "object",
                      "description": "Links para llms.txt, llms-full.txt, openapi.json, MCP e a UI."
                    },
                    "conventions": {
                      "type": "object",
                      "description": "Formato de erro, CORS, x402 e a regra de paridade UI↔API."
                    },
                    "auth": {
                      "type": "object",
                      "description": "Cada modo de autenticação e como obtê-lo."
                    },
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Todo endpoint com método, caminho, auth, URL absoluta e o que devolve."
                    },
                    "quota": {
                      "type": "object",
                      "description": "O que é grátis, o que custa e como pagar."
                    },
                    "mcp": {
                      "type": "object",
                      "description": "Endereço e transporte do servidor MCP."
                    },
                    "mcp_tools": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Nome de cada tool do MCP."
                    },
                    "quickstart": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "As chamadas que levam da ideia à lista de empresas."
                    }
                  },
                  "required": [
                    "name",
                    "description",
                    "build",
                    "base_url",
                    "origin_api",
                    "docs",
                    "conventions",
                    "auth",
                    "endpoints",
                    "quota",
                    "mcp",
                    "mcp_tools",
                    "quickstart"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "operationId": "health",
        "summary": "Disponibilidade e data de referência dos registros.",
        "description": "`import.dump_date` informa a data de referência e `import.counts` traz as contagens disponíveis. A consulta não confirma mudanças em tempo real.\nDevolve: { ok, service, db, import }",
        "responses": {
          "200": {
            "description": "{ ok, service, db, import }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SaudeOrigem"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "post_mcp",
        "summary": "Servidor MCP por HTTP (Streamable HTTP, JSON-RPC 2.0) — pluga no cliente sem instalar nada.",
        "description": "As tools são as operações deste mesmo catálogo; o MCP não tem backend próprio. `GET /mcp` devolve o cartão do servidor.\nDevolve: Resposta JSON-RPC 2.0 (`initialize`, `tools/list` ou `tools/call`).\nCredencial vai nos headers de sempre (X-Guest-Token, Authorization, X-PAYMENT) e é repassada à API.\nCota estourada chega como 402 com accepts[] dentro do resultado da tool — pague e repita.",
        "responses": {
          "200": {
            "description": "Resposta JSON-RPC 2.0 (`initialize`, `tools/list` ou `tools/call`)."
          }
        }
      }
    },
    "/api/avaliar": {
      "post": {
        "operationId": "avaliar",
        "summary": "Descreva uma atividade e um lugar para consultar as empresas registradas nesse recorte.",
        "description": "É o primeiro produto da home. Devolve o CNAE a que a ideia foi mapeada, quantas empresas ativas, abertas e baixadas existem no recorte, como elas se formalizam e uma leitura honesta disso. **Não inventa volume de busca** e não promete demanda: `ficha.limites` diz o que os números não dizem. Renda passiva isto não é.\nDevolve: { ok, texto, cnae, fonte_cnae, filtros, ficha{mapeou_cnae,oferta,formalizacao,leitura,limites,passivo} }",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "texto": {
                    "type": "string",
                    "description": "A ideia em 3 a 400 caracteres."
                  },
                  "uf": {
                    "type": "string",
                    "description": "Hint de estado; só entra se a IA não resolver o lugar sozinha."
                  },
                  "municipio": {
                    "type": "integer",
                    "description": "Hint de município (código IBGE); mesma regra do `uf`."
                  }
                },
                "required": [
                  "texto"
                ]
              },
              "example": {
                "texto": "padaria em Campinas",
                "uf": "SP",
                "municipio": 6291
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, texto, cnae, fonte_cnae, filtros, ficha{mapeou_cnae,oferta,formalizacao,leitura,limites,passivo} }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Avaliacao"
                }
              }
            }
          },
          "400": {
            "description": "Texto fora de 3–400 caracteres, ou corpo que não é JSON."
          },
          "405": {
            "description": "Só POST nesta rota."
          }
        }
      }
    },
    "/api/cnpj/{cnpj}": {
      "get": {
        "operationId": "get_cnpj",
        "summary": "A ficha cadastral de uma empresa, pelos 14 dígitos do CNPJ, com o dado pessoal mascarado.",
        "description": "Nome de sócio pessoa física (`LUIS F. R. P.`), telefones e e-mail vêm mascarados, com `data.mascarado: true`; sócio empresa vem inteiro. O dado completo sai por `POST /api/revelar/:cnpj`. A resposta pode ser reutilizada por até 6 horas; confira a data de referência dos registros.\nDevolve: { ok, data }",
        "parameters": [
          {
            "name": "cnpj",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "CNPJ com 14 dígitos, sem pontuação.",
            "example": "00000000000191"
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, data }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FichaCnpj"
                }
              }
            }
          },
          "400": {
            "description": "CNPJ que não tem 14 dígitos."
          },
          "404": {
            "description": "CNPJ não existe na base."
          }
        }
      }
    },
    "/api/revelar/{cnpj}": {
      "post": {
        "operationId": "reveal_cnpj",
        "summary": "Revela o dado pessoal da ficha — nomes dos sócios, telefones e e-mail sem máscara. Pago por empresa.",
        "description": "Custa US$ 0,10 por empresa: desconta do crédito pré-pago (`Authorization: Bearer cred_…`) ou paga só esta revelação com x402 (`X-PAYMENT`). O mesmo código de crédito revendo a mesma empresa no mesmo dia (horário de Brasília) não paga de novo; o x402 avulso cobra cada revelação. CNPJ inexistente ou consulta que falha não cobra nada. A resposta não vai para cache.\nDevolve: { ok, data, cobranca }",
        "parameters": [
          {
            "name": "cnpj",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "CNPJ com 14 dígitos, sem pontuação.",
            "example": "00000000000191"
          },
          {
            "name": "authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer cred_…`, o token do crédito pré-pago. Sem ele (e sem `X-PAYMENT`), a resposta é o 402 do x402."
          },
          {
            "name": "x-payment",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Pagamento x402 assinado (base64), para pagar só esta revelação."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, data, cobranca }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FichaRevelada"
                }
              }
            }
          },
          "400": {
            "description": "CNPJ que não tem 14 dígitos."
          },
          "401": {
            "description": "Token de crédito desconhecido."
          },
          "402": {
            "description": "Sem pagamento ou com saldo insuficiente: o corpo traz `accepts[]` do x402 e como recarregar o crédito."
          },
          "404": {
            "description": "CNPJ não existe na base."
          },
          "502": {
            "description": "A consulta falhou; nada foi cobrado."
          },
          "503": {
            "description": "Revelação fora do ar; nada foi cobrado."
          }
        }
      }
    },
    "/api/busca": {
      "get": {
        "operationId": "search",
        "summary": "Busca empresas por termo e/ou filtros avançados, paginada.",
        "description": "Exige termo OU pelo menos um filtro — varrer 71 milhões de estabelecimentos sem recorte não é uma busca, é um dump.\nDevolve: { ok, page, pageSize, hasMore, results[{cnpj,cnpjFormatted,razaoSocial,nomeFantasia,situacao,uf,municipio,bairro,cnae}] }",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Termo de busca, entre 2 e 120 caracteres.",
            "example": "padaria"
          },
          {
            "name": "f",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filtros avançados em JSON (CNAE, situação, porte, data de abertura).",
            "example": "{\"uf\":\"SP\"}"
          },
          {
            "name": "tipo",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Que campo o termo procura: `nome` (razão social, o padrão), `fantasia`, `socio`, `telefone` (com DDD, 10 ou 11 dígitos), `endereco` ou `cnae`.",
            "example": "telefone"
          },
          {
            "name": "uf",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restringe a uma unidade da federação.",
            "example": "SP"
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Página, começando em 0."
          },
          {
            "name": "pageSize",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20
            },
            "description": "Resultados por página, de 1 a 50."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, page, pageSize, hasMore, results[{cnpj,cnpjFormatted,razaoSocial,nomeFantasia,situacao,uf,municipio,bairro,cnae}] }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginaDeBusca"
                }
              }
            }
          },
          "400": {
            "description": "`busca_vazia` (sem termo nem filtro), `termo_invalido` (fora de 2–120) ou `filtros_invalidos`."
          }
        }
      }
    },
    "/api/export": {
      "get": {
        "operationId": "get_api_export",
        "summary": "Exporta o resultado da busca em CSV ou JSON, com os mesmos filtros dela.",
        "description": "Duas formas na mesma rota. Com `format=json` a resposta é o envelope descrito abaixo — é o que um agente usa. Com `format=csv` (o padrão) vem o arquivo: content-type e content-disposition são repassados da origem, para o browser baixar direto do link. `capped` avisa quando o export bateu no teto e não trouxe tudo.\nDevolve: { ok, count, capped, results[{cnpj,cnpjFormatted,razaoSocial,nomeFantasia,situacao,uf,municipio,bairro,cnae}] }",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Termo de busca, entre 2 e 120 caracteres.",
            "example": "padaria"
          },
          {
            "name": "f",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Filtros avançados em JSON (CNAE, situação, porte, data de abertura).",
            "example": "{\"uf\":\"SP\"}"
          },
          {
            "name": "tipo",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Que campo o termo procura: `nome` (razão social, o padrão), `fantasia`, `socio`, `telefone` (com DDD, 10 ou 11 dígitos), `endereco` ou `cnae`.",
            "example": "telefone"
          },
          {
            "name": "uf",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restringe a uma unidade da federação.",
            "example": "SP"
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "csv",
              "enum": [
                "csv",
                "json"
              ]
            },
            "description": "Formato do arquivo."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, count, capped, results[{cnpj,cnpjFormatted,razaoSocial,nomeFantasia,situacao,uf,municipio,bairro,cnae}] }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Export"
                }
              }
            }
          },
          "400": {
            "description": "`formato_invalido`, ou os mesmos erros de `GET /api/busca`."
          }
        }
      }
    },
    "/api/sugerir": {
      "get": {
        "operationId": "suggest",
        "summary": "Autocomplete de empresas e termos, para montar a lista enquanto a pessoa digita.",
        "description": "Não conta como visita nas métricas — senão o painel mediria tecla, não gente.\nDevolve: { ok, results }",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "O que já foi digitado.",
            "example": "padar"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10
            },
            "description": "Quantas sugestões devolver, de 1 a 50."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, results }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaSugestao"
                }
              }
            }
          },
          "400": {
            "description": "`q` ausente ou curto demais."
          }
        }
      }
    },
    "/api/ref": {
      "get": {
        "operationId": "ref",
        "summary": "Vocabulários oficiais para montar seletor: CNAE, município e natureza jurídica.",
        "description": "Ou você busca por texto (`q`) ou resolve códigos que já tem (`codigos`) — `codigos` ganha quando os dois vêm.\nDevolve: { ok, results[{codigo,descricao}] }",
        "parameters": [
          {
            "name": "tipo",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "cnae",
                "municipio",
                "natureza"
              ]
            },
            "description": "Qual vocabulário consultar."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Texto a procurar no vocabulário, até 60 caracteres.",
            "example": "padaria"
          },
          {
            "name": "codigos",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Códigos separados por vírgula, para resolver os nomes deles.",
            "example": "4721102,4712100"
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, results[{codigo,descricao}] }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListaReferencia"
                }
              }
            }
          },
          "400": {
            "description": "`tipo` ausente ou fora da lista."
          }
        }
      }
    },
    "/api/ia": {
      "post": {
        "operationId": "ia_filters",
        "summary": "Transforma um texto livre nos filtros normalizados que a busca aceita.",
        "description": "Caminho síncrono: leva de 18 a 20 segundos. Quando estoura o tempo, use `POST /api/ia/jobs`.\nDevolve: { ok, filtros }",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "texto": {
                    "type": "string",
                    "description": "A descrição em linguagem natural do que você procura."
                  }
                },
                "required": [
                  "texto"
                ]
              },
              "example": {
                "texto": "padarias em SP com MEI"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, filtros }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "description": "Sempre `true` quando a IA respondeu."
                    },
                    "filtros": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Os filtros normalizados, prontos para virar o `f` de `GET /api/busca`."
                    }
                  },
                  "required": [
                    "ok",
                    "filtros"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Texto ausente ou fora do tamanho aceito."
          },
          "504": {
            "description": "O caminho síncrono estourou — enfileire em `POST /api/ia/jobs`."
          }
        }
      }
    },
    "/api/ia/jobs": {
      "post": {
        "operationId": "post_api_ia_jobs",
        "summary": "Enfileira a mesma tradução de texto para filtros, quando a síncrona não cabe no tempo.",
        "description": "Devolve: { job_id, eta }",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "texto": {
                    "type": "string",
                    "description": "A descrição em linguagem natural do que você procura."
                  }
                },
                "required": [
                  "texto"
                ]
              },
              "example": {
                "texto": "padarias em SP com MEI"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ job_id, eta }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job_id": {
                      "type": "string",
                      "description": "ID do trabalho, para consultar em `GET /api/ia/jobs/:id`."
                    },
                    "eta": {
                      "type": "integer",
                      "description": "Estimativa de segundos até ficar pronto."
                    }
                  },
                  "required": [
                    "job_id",
                    "eta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Texto ausente ou fora do tamanho aceito."
          }
        }
      }
    },
    "/api/ia/jobs/{id}": {
      "get": {
        "operationId": "get_api_ia_jobs_by_id",
        "summary": "Consulta o trabalho de IA enfileirado; quando pronto, devolve os filtros.",
        "description": "Devolve: { status, filtros? }",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID do trabalho, vindo de `POST /api/ia/jobs`.",
            "example": "9f3c2b1d7a4e58b0"
          }
        ],
        "responses": {
          "200": {
            "description": "{ status, filtros? }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "description": "Estado do trabalho."
                    },
                    "filtros": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Os filtros normalizados; só quando `status` é `done`."
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Trabalho não existe ou já expirou."
          }
        }
      }
    },
    "/api/local": {
      "get": {
        "operationId": "local",
        "summary": "Cidade e UF aproximadas de quem está chamando.",
        "description": "Nunca é cacheada: cache aqui entregaria o lugar de outra pessoa.\nDevolve: { ok, cidade, uf, cep, pais, fonte }",
        "responses": {
          "200": {
            "description": "{ ok, cidade, uf, cep, pais, fonte }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Local"
                }
              }
            }
          }
        }
      }
    },
    "/api/municipio-proximo": {
      "get": {
        "operationId": "get_api_municipio_proximo",
        "summary": "Município de um par de coordenadas, com o bairro quando disponível.",
        "description": "Também nunca é cacheada. Coordenada ausente ou vazia NÃO vira zero — (0,0) é um lugar de verdade, no golfo da Guiné.\nDevolve: { ok, municipio, bairro?, cep?, bairro_km? }",
        "parameters": [
          {
            "name": "lat",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number"
            },
            "description": "Latitude, entre -90 e 90.",
            "example": "-23.55"
          },
          {
            "name": "lon",
            "in": "query",
            "required": true,
            "schema": {
              "type": "number"
            },
            "description": "Longitude, entre -180 e 180.",
            "example": "-46.63"
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, municipio, bairro?, cep?, bairro_km? }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MunicipioProximo"
                }
              }
            }
          },
          "400": {
            "description": "`lat` ou `lon` ausentes ou fora da faixa."
          },
          "404": {
            "description": "`fora_do_brasil`: nenhum município brasileiro contém o ponto nem fica a até 50 km dele."
          },
          "503": {
            "description": "`sem_malha`: a base de contornos dos municípios está indisponível."
          }
        }
      }
    },
    "/api/me/monitor/watches": {
      "get": {
        "operationId": "list_watches",
        "summary": "Os CNPJs que você acompanha, com a cota aplicada (grátis + vagas compradas).",
        "description": "Quem confere a cota é a origem (`api.radar-cnpj.com`), dentro da transação; o Worker calcula quanto você tem (10 grátis + as vagas em vigor) e manda junto. Watch além da cota vem com `suspensa: true` e não gera e-mail.\nDevolve: { ok, watches, quota, base, pagos, used, suspensas }",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Agente: `Bearer mmk_…`, a chave de API da conta (criada na página da conta, Chaves de API) — a conta é a dona; ou `Bearer cred_…`, o token do crédito global (`POST /api/credito`) — a carteira é a dona. No navegador vale o cookie da conta; escrita leva `X-CSRF-Token`."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, watches, quota, base, pagos, used, suspensas }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Watches"
                }
              }
            }
          },
          "401": {
            "description": "Sem conta (cookie ou chave de API) nem token de crédito válido: `nao_logado`; chave recusada: `invalid_api_key`."
          },
          "503": {
            "description": "A conta não respondeu agora."
          }
        }
      }
    },
    "/api/me/monitor/watch": {
      "post": {
        "operationId": "add_watch",
        "summary": "Passa a acompanhar um CNPJ. Os 10 primeiros são grátis; cada vaga a mais, US$ 0,50 por 30 dias.",
        "description": "Estourou a cota: **402 com `accepts[]`** (x402) — ou, com token de crédito, o débito direto do saldo. Pague e repita a mesma chamada com `X-PAYMENT`; a vaga fica no `direito` global e a watch entra.\nDevolve: { ok }",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Agente: `Bearer mmk_…`, a chave de API da conta (criada na página da conta, Chaves de API) — a conta é a dona; ou `Bearer cred_…`, o token do crédito global (`POST /api/credito`) — a carteira é a dona. No navegador vale o cookie da conta; escrita leva `X-CSRF-Token`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "cnpj": {
                    "type": "string",
                    "description": "CNPJ a acompanhar, 14 dígitos sem pontuação."
                  }
                },
                "required": [
                  "cnpj"
                ]
              },
              "example": {
                "cnpj": "00000000000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "CNPJ que não tem 14 dígitos."
          },
          "401": {
            "description": "Sem conta (cookie ou chave de API) nem token de crédito válido: `nao_logado`; chave recusada: `invalid_api_key`."
          },
          "402": {
            "description": "Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`."
          },
          "403": {
            "description": "`invalid_origin` / `invalid_csrf`: escrita com o cookie da conta vinda de outra origem ou sem `X-CSRF-Token`."
          },
          "503": {
            "description": "A conta não respondeu agora."
          }
        }
      }
    },
    "/api/me/monitor/watch/{cnpj}": {
      "delete": {
        "operationId": "delete_api_me_monitor_watch_by_cnpj",
        "summary": "Para de acompanhar um CNPJ. A chave é o próprio CNPJ, não um id.",
        "description": "Devolve: { ok }",
        "parameters": [
          {
            "name": "cnpj",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "CNPJ a deixar de acompanhar, 14 dígitos sem pontuação.",
            "example": "00000000000191"
          },
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Agente: `Bearer mmk_…`, a chave de API da conta (criada na página da conta, Chaves de API) — a conta é a dona; ou `Bearer cred_…`, o token do crédito global (`POST /api/credito`) — a carteira é a dona. No navegador vale o cookie da conta; escrita leva `X-CSRF-Token`."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "description": "Sem conta (cookie ou chave de API) nem token de crédito válido: `nao_logado`; chave recusada: `invalid_api_key`."
          },
          "403": {
            "description": "`invalid_origin` / `invalid_csrf`: escrita com o cookie da conta vinda de outra origem ou sem `X-CSRF-Token`."
          },
          "404": {
            "description": "Este CNPJ não está sendo acompanhado por você."
          }
        }
      }
    },
    "/api/me/monitor/alerts": {
      "get": {
        "operationId": "get_api_me_monitor_alerts",
        "summary": "Os alertas gerados para os CNPJs que você acompanha.",
        "description": "`retidos` conta os alertas de watches suspensas (fora da cota): voltam a aparecer quando a cota volta. Conta com e-mail verificado recebe os alertas também por e-mail; carteira de agente, só aqui.\nDevolve: { ok, alerts, retidos }",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Agente: `Bearer mmk_…`, a chave de API da conta (criada na página da conta, Chaves de API) — a conta é a dona; ou `Bearer cred_…`, o token do crédito global (`POST /api/credito`) — a carteira é a dona. No navegador vale o cookie da conta; escrita leva `X-CSRF-Token`."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, alerts, retidos }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Alertas"
                }
              }
            }
          },
          "401": {
            "description": "Sem conta (cookie ou chave de API) nem token de crédito válido: `nao_logado`; chave recusada: `invalid_api_key`."
          }
        }
      }
    },
    "/api/monitor/changes/{cnpj}": {
      "get": {
        "operationId": "get_api_monitor_changes_by_cnpj",
        "summary": "O histórico de alterações cadastrais de um CNPJ.",
        "description": "É o que o monitoramento observa: cada linha diz o que mudou, de que valor para qual, e quando. O nome de sócio sai mascarado.\nDevolve: { ok, cnpj, cnpjFormatted, changes }",
        "parameters": [
          {
            "name": "cnpj",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "CNPJ a consultar, 14 dígitos sem pontuação.",
            "example": "00000000000191"
          },
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Agente: `Bearer mmk_…`, a chave de API da conta (criada na página da conta, Chaves de API) — a conta é a dona; ou `Bearer cred_…`, o token do crédito global (`POST /api/credito`) — a carteira é a dona. No navegador vale o cookie da conta; escrita leva `X-CSRF-Token`."
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, cnpj, cnpjFormatted, changes }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoricoCnpj"
                }
              }
            }
          },
          "401": {
            "description": "Sem conta (cookie ou chave de API) nem token de crédito válido: `nao_logado`; chave recusada: `invalid_api_key`."
          },
          "404": {
            "description": "CNPJ sem histórico ou fora da base."
          }
        }
      }
    },
    "/api/contato": {
      "post": {
        "operationId": "contact",
        "summary": "Fala com o suporte: humano resolve Turnstile, agente paga $0.10 em x402.",
        "description": "O primeiro envio de agente é livre; depois o backoff é 60s dobrando até o teto de 1 hora, informado em `Retry-After`. `POST /api/contact` é o mesmo recurso com os campos em inglês.\nDevolve: { ok }",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "nome": {
                    "type": "string",
                    "description": "Como chamar quem escreveu."
                  },
                  "email": {
                    "type": "string",
                    "description": "Para onde responder."
                  },
                  "mensagem": {
                    "type": "string",
                    "description": "O que você quer dizer."
                  },
                  "aberto_em": {
                    "type": "integer",
                    "description": "Momento em que o formulário abriu; é anti-robô do caminho humano."
                  },
                  "turnstile": {
                    "type": "string",
                    "description": "Resposta do Turnstile; presente só no caminho humano."
                  }
                },
                "required": [
                  "nome",
                  "email",
                  "mensagem"
                ]
              },
              "example": {
                "nome": "…",
                "email": "a@example.com",
                "mensagem": "…",
                "aberto_em": 0,
                "turnstile": "(humano)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Campo obrigatório faltando."
          },
          "402": {
            "description": "Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`."
          },
          "429": {
            "description": "Backoff de agente: espere o `Retry-After`."
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "operationId": "post_api_contact",
        "summary": "O mesmo contato de `/api/contato`, com os nomes de campo em inglês.",
        "description": "Existe porque agente que chegou pelo `llms.txt` de outro produto do mesmo dono já sabe mandar `name`/`email`/`message`. Mesmo backoff, mesmo preço.\nDevolve: { ok }",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Como chamar quem escreveu."
                  },
                  "email": {
                    "type": "string",
                    "description": "Para onde responder."
                  },
                  "message": {
                    "type": "string",
                    "description": "O que você quer dizer."
                  },
                  "form_ts": {
                    "type": "integer",
                    "description": "Momento em que o formulário abriu; é anti-robô do caminho humano."
                  },
                  "tipo": {
                    "type": "string",
                    "description": "Proposta: `patrocinio`, `parceria` ou `anuncio`. Liga os campos abaixo."
                  },
                  "empresa": {
                    "type": "string",
                    "description": "Quem propõe, quando é empresa."
                  },
                  "site": {
                    "type": "string",
                    "description": "Site de quem propõe."
                  },
                  "orcamento": {
                    "type": "string",
                    "description": "`ate_100`, `100_500`, `500_2000`, `2000_mais` ou `a_combinar`."
                  },
                  "espaco": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids de placement de `GET /api/partners`, até 6."
                  },
                  "duracao": {
                    "type": "string",
                    "description": "Dias de exposição: `30`, `90` ou `365`."
                  },
                  "pagamento": {
                    "type": "string",
                    "description": "`usdc`, `deposito` ou `a_combinar`."
                  }
                },
                "required": [
                  "name",
                  "email",
                  "message"
                ]
              },
              "example": {
                "name": "…",
                "email": "…",
                "message": "…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Campo obrigatório faltando."
          },
          "402": {
            "description": "Cota estourada. A resposta traz `accepts[]` (x402, USDC na Base): pague e repita a mesma chamada com `X-PAYMENT`."
          },
          "429": {
            "description": "Backoff de agente: espere o `Retry-After`."
          }
        }
      }
    },
    "/api/erro-cliente": {
      "post": {
        "operationId": "post_api_erro_cliente",
        "summary": "Relato de erro do navegador, enviado pela própria interface. Agente não precisa chamar.",
        "description": "A interface relata sozinha erro de JS, promessa rejeitada, script/CSS que não carregou e bloqueio de CSP — uma vez por sessão — e o app relata falha tratada por `window.mmErro.relata`. O servidor valida o envelope, redige credencial, e-mail e telefone, junta repetições da mesma falha por minuto e registra um evento operacional; nada é gravado em banco. Não guarda IP, cookie, query nem o User-Agent inteiro. Responde 204 sempre, inclusive para relato inválido.\nDevolve: 204 sem corpo, sempre — relato inválido, repetido ou acima do teto também recebe 204.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "Código da falha, `UI-` + letras/dígitos (`UI-JS-001` erro global, `UI-PROMESSA-001`, `UI-RECURSO-001`, `UI-CSP-001`, `UI-APP-001` relato do app)."
                  },
                  "phase": {
                    "type": "string",
                    "description": "Fase em que quebrou, minúsculas: `global`, `promessa`, `script`, `carregar_lista`…"
                  },
                  "path": {
                    "type": "string",
                    "description": "Caminho da página aberta, sem query."
                  },
                  "message": {
                    "type": "string",
                    "description": "Mensagem do erro, até 2000 caracteres."
                  },
                  "stack": {
                    "type": "string",
                    "description": "Stack trace, até 12000 caracteres."
                  },
                  "source": {
                    "type": "string",
                    "description": "Script de origem; só o caminho é guardado."
                  },
                  "line": {
                    "type": "integer",
                    "description": "Linha no script de origem."
                  },
                  "column": {
                    "type": "integer",
                    "description": "Coluna no script de origem."
                  },
                  "visivel": {
                    "type": "boolean",
                    "description": "Se a aba estava visível quando quebrou."
                  }
                },
                "required": [
                  "code",
                  "phase"
                ]
              },
              "example": {
                "code": "UI-APP-001",
                "phase": "carregar_lista",
                "path": "/",
                "message": "lista 500"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "204 sem corpo, sempre — relato inválido, repetido ou acima do teto também recebe 204."
          }
        }
      }
    },
    "/api/pagamento/aberto": {
      "post": {
        "operationId": "post_api_pagamento_aberto",
        "summary": "A interface relata que exibiu uma cobrança. Agentes não devem chamar.",
        "description": "Relato sem corpo, da mesma origem, enviado automaticamente quando uma cobrança fica visível. Não inicia pagamento, não concede acesso e não recebe identidade ou credencial. Não grava banco por relato. Conta eventos, não pessoas únicas. O painel privado do operador separa pedidos de pagamento da API e aberturas da interface por dia UTC; os dois números podem se sobrepor.\nDevolve: 202 sem corpo se aceito; 204 se ignorado. Sempre no-store.",
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "A origem da página, idêntica à desta rota."
          },
          {
            "name": "Sec-Fetch-Site",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`same-origin`, definido pelo navegador."
          },
          {
            "name": "X-MM-Payment-View",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`1`, definido pelo componente comum."
          }
        ],
        "responses": {
          "202": {
            "description": "202 sem corpo se aceito; 204 se ignorado. Sempre no-store."
          }
        }
      }
    },
    "/api/vitrine": {
      "get": {
        "operationId": "get_api_vitrine",
        "summary": "Os números públicos do produto: tráfego, agentes, uso e confiabilidade, sem dinheiro.",
        "description": "Projeção publicada de hora em hora pelo coletor da casa, arredondada a dois dígitos significativos; `null` é medição ausente, nunca zero. Cache de 15 minutos com ETag (`If-None-Match` → 304). Não há como enviar números por esta rota: a publicação é do coletor, com token próprio.\nDevolve: { v, produto, publicado, atualizado_em, stale, nome?, desde?, fuso?, hoje?, dias?, janelas?, visitantes?, pessoas?, agentes?, superficies?, mcp?, uso?, contas?, confiabilidade?, catalogo?, apoio? }",
        "responses": {
          "200": {
            "description": "{ v, produto, publicado, atualizado_em, stale, nome?, desde?, fuso?, hoje?, dias?, janelas?, visitantes?, pessoas?, agentes?, superficies?, mcp?, uso?, contas?, confiabilidade?, catalogo?, apoio? }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "v": {
                      "type": "integer",
                      "description": "Versão do contrato (1)."
                    },
                    "produto": {
                      "type": "string",
                      "description": "Id do produto."
                    },
                    "publicado": {
                      "type": "boolean",
                      "description": "`false` antes da primeira publicação do coletor; aí só estas cinco chaves vêm."
                    },
                    "atualizado_em": {
                      "type": "string",
                      "description": "Quando o coletor publicou (ISO 8601).",
                      "nullable": true
                    },
                    "stale": {
                      "type": "boolean",
                      "description": "`true` quando a projeção tem mais de 26 h."
                    },
                    "nome": {
                      "type": "string",
                      "description": "Nome do produto."
                    },
                    "desde": {
                      "type": "string",
                      "description": "Dia a partir do qual a série vale.",
                      "nullable": true
                    },
                    "fuso": {
                      "type": "string",
                      "description": "Fuso dos dias (`UTC`)."
                    },
                    "hoje": {
                      "type": "object",
                      "description": "O dia de hoje: páginas por classe (pessoa, IA, bot), chamadas de API por classe, leituras das superfícies de máquina e uso do produto."
                    },
                    "dias": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Até 31 dias, o mais antigo primeiro: `dia`, `paginas`, `api`, `api_ia`, `maquina`, `visitantes`, `uso`."
                    },
                    "janelas": {
                      "type": "object",
                      "description": "Somas de 7 e 30 dias (`d7`, `d30`)."
                    },
                    "visitantes": {
                      "type": "object",
                      "description": "Visitantes únicos na borda em 7 dias."
                    },
                    "pessoas": {
                      "type": "object",
                      "description": "GA4 quando há: usuários, sessões, países, aparelhos e quem chegou de IA.",
                      "nullable": true
                    },
                    "agentes": {
                      "type": "object",
                      "description": "Os agentes de IA e os bots que mais leem, 7 dias."
                    },
                    "superficies": {
                      "type": "object",
                      "description": "Leituras de OKF, llms, well-known, OpenAPI e MCP em 7 dias."
                    },
                    "mcp": {
                      "type": "object",
                      "description": "Chamadas MCP em 7 dias."
                    },
                    "uso": {
                      "type": "object",
                      "description": "Uso real do produto por recurso: rótulo, hoje, 7 e 30 dias."
                    },
                    "contas": {
                      "type": "object",
                      "description": "Usuários e convidados.",
                      "nullable": true
                    },
                    "confiabilidade": {
                      "type": "object",
                      "description": "Percentual de pedidos sem 5xx em 7 dias e o build no ar."
                    },
                    "catalogo": {
                      "type": "object",
                      "description": "Tamanho do acervo, quando o produto tem um.",
                      "nullable": true
                    },
                    "apoio": {
                      "type": "object",
                      "description": "Impressões e cliques por patrocinador, quando houver."
                    }
                  },
                  "required": [
                    "v",
                    "produto",
                    "publicado",
                    "atualizado_em",
                    "stale"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/vitrine/operador": {
      "get": {
        "operationId": "get_api_vitrine_operador",
        "summary": "O documento completo do produto no painel do operador — só com o token do operador.",
        "description": "Devolve: { produto, atualizado_em, operador }",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` — a classe operador."
          }
        ],
        "responses": {
          "200": {
            "description": "{ produto, atualizado_em, operador }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "produto": {
                      "type": "string",
                      "description": "Id do produto."
                    },
                    "atualizado_em": {
                      "type": "string",
                      "description": "Quando o coletor publicou.",
                      "nullable": true
                    },
                    "operador": {
                      "type": "object",
                      "description": "O documento completo do coletor, com o que a projeção pública não carrega.",
                      "nullable": true
                    }
                  },
                  "required": [
                    "produto",
                    "atualizado_em",
                    "operador"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem token, token errado ou token de outra classe."
          },
          "503": {
            "description": "Worker sem `METRICS_TOKEN` ou sem o control plane."
          }
        }
      }
    },
    "/api/vitrine/painel": {
      "get": {
        "operationId": "get_api_vitrine_painel",
        "summary": "O painel da casa inteira, na forma que o gm lê — só com o token do operador.",
        "description": "Devolve: { apps, updated?, totals? }",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` — a classe operador."
          }
        ],
        "responses": {
          "200": {
            "description": "{ apps, updated?, totals? }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Um documento do operador por produto, em ordem de id."
                    },
                    "updated": {
                      "type": "string",
                      "description": "Quando o coletor fechou a rodada."
                    },
                    "totals": {
                      "type": "object",
                      "description": "Os totais da casa."
                    }
                  },
                  "required": [
                    "apps"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem token, token errado ou token de outra classe."
          },
          "503": {
            "description": "Worker sem `METRICS_TOKEN` ou sem o control plane."
          }
        }
      }
    },
    "/api/vitrine/cursores": {
      "get": {
        "operationId": "get_api_vitrine_cursores",
        "summary": "O cursor de erro resolvido por produto (`borda`, `cli`) — só com o token do operador.",
        "description": "Devolve: JSON: `{ [produto]: { borda?: ISO, cli?: ISO } }`; vazio é `{}`.",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>` — a classe operador."
          }
        ],
        "responses": {
          "200": {
            "description": "JSON: `{ [produto]: { borda?: ISO, cli?: ISO } }`; vazio é `{}`."
          },
          "401": {
            "description": "Sem token, token errado ou token de outra classe."
          },
          "503": {
            "description": "Worker sem `METRICS_TOKEN` ou sem o control plane."
          }
        }
      }
    },
    "/api/partners": {
      "get": {
        "operationId": "get_api_partners",
        "summary": "Parceria, patrocínio e anúncio: os espaços do produto com preço sugerido, os números públicos ao lado e como propor.",
        "description": "Informação sob consulta, sem ativação: espaços do catálogo da casa com preço em USD por 30 dias (90 e 365 dias com desconto), patrocinadores em vigor, recorte de `/api/vitrine`, carteira da casa (USDC na Base) e o caminho de contato — depósito, PIX ou fatura são combinados na resposta. Cache de 1 hora.\nDevolve: { status, produto, idioma, titulo, descricao, publico, modalidades, placements, house_bundle, parcerias, current_sponsors, stats, payment, contact, politica, _links }",
        "responses": {
          "200": {
            "description": "{ status, produto, idioma, titulo, descricao, publico, modalidades, placements, house_bundle, parcerias, current_sponsors, stats, payment, contact, politica, _links }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "description": "`sob_consulta`: informação e proposta, sem ativação nem cobrança."
                    },
                    "produto": {
                      "type": "string",
                      "description": "Nome do produto."
                    },
                    "idioma": {
                      "type": "string",
                      "description": "Idioma dos textos (o do produto)."
                    },
                    "titulo": {
                      "type": "string",
                      "description": "Título da oferta."
                    },
                    "descricao": {
                      "type": "string",
                      "description": "Uma frase sobre a oferta."
                    },
                    "publico": {
                      "type": "string",
                      "description": "Quem usa o produto — o público que o patrocinador alcança."
                    },
                    "modalidades": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "`{ id, nome }`: patrocinio, parceria, anuncio."
                    },
                    "placements": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Os espaços do produto: `id`, `nome`, `onde`, `formato`, `exclusivo`, `medicao`, `price_usd_30d` (sugestão; `null` é sob consulta), `exposure[{ dias, price_usd }]` para 30, 90 e 365 dias, `disponivel`."
                    },
                    "house_bundle": {
                      "type": "object",
                      "description": "O pacote da casa: rodapé e menção para agentes nos dez produtos, com desconto."
                    },
                    "parcerias": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Ideias de parceria que o produto aceita discutir."
                    },
                    "current_sponsors": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Patrocinadores em vigor: `id`, `nome`, `url`, `frase`, `espacos`, `ate`."
                    },
                    "stats": {
                      "type": "object",
                      "description": "Recorte dos números públicos (`hoje`, `janelas`, `agentes`, `confiabilidade`) e o `link` para `/api/vitrine`; `publicado: false` antes da primeira publicação."
                    },
                    "payment": {
                      "type": "object",
                      "description": "Como pagar: `rede`, `chain_id`, `ativo`, `pay_to`, `eip681` (a carteira da casa, quando declarada), `alternativas` e a `nota` — depósito, PIX ou fatura pela resposta."
                    },
                    "contact": {
                      "type": "object",
                      "description": "`email`, `form_url`, `api_url` (`POST /api/contact` onde há handler), `campos` (os obrigatórios), `campos_proposta` (os opcionais da proposta, com os valores aceitos de cada um), `price_agent_usd`, `message_template`, `instructions`."
                    },
                    "politica": {
                      "type": "object",
                      "description": "Rótulo do espaço, setores recusados, pagamento adiantado, prazos."
                    },
                    "_links": {
                      "type": "object",
                      "description": "`self`, `stats`, `page` (`null` até a página existir), `contact`, `casa` (o mesmo caminho nos dez produtos)."
                    }
                  },
                  "required": [
                    "status",
                    "produto",
                    "idioma",
                    "titulo",
                    "descricao",
                    "publico",
                    "modalidades",
                    "placements",
                    "house_bundle",
                    "parcerias",
                    "current_sponsors",
                    "stats",
                    "payment",
                    "contact",
                    "politica",
                    "_links"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/metrics": {
      "get": {
        "operationId": "get_api_metrics",
        "summary": "Métricas operacionais: sem token, visitas de hoje e uso; com o token do operador, a série de 7 dias.",
        "description": "Conta visitantes distintos e **não** conta autocomplete — senão o painel mediria tecla, não gente. Sem `Authorization` devolve só `app`, `today_visits` e `usage`, com 5 min de cache na borda — é o que a linha de estado do rodapé lê, como nos outros produtos. Com `Bearer METRICS_TOKEN`, a resposta completa da origem (`days`), sem cache.\nDevolve: { app, today_visits, days?, usage }",
        "parameters": [
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`Bearer <METRICS_TOKEN>`, só para a série completa do operador."
          }
        ],
        "responses": {
          "200": {
            "description": "{ app, today_visits, days?, usage }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metricas"
                }
              }
            }
          },
          "401": {
            "description": "Token do operador errado."
          },
          "503": {
            "description": "Sem os secrets configurados no ambiente."
          }
        }
      }
    },
    "/api/credito": {
      "post": {
        "operationId": "post_api_credito",
        "summary": "Recarrega crédito pré-pago: paga uma vez com x402 e recebe o token que desconta em qualquer API da casa.",
        "description": "Devolve: { token, saldo_usd, guarde, usar, saldo_em }",
        "parameters": [
          {
            "name": "usd",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Pacote: 1, 5, 10 ou 25 dólares."
          }
        ],
        "responses": {
          "200": {
            "description": "{ token, saldo_usd, guarde, usar, saldo_em }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "Token portador do saldo (`cred_…`). Mostrado UMA vez — não há como recuperá-lo."
                    },
                    "saldo_usd": {
                      "type": "string",
                      "description": "Saldo creditado."
                    },
                    "guarde": {
                      "type": "string",
                      "description": "Aviso de que o token é o portador do crédito."
                    },
                    "usar": {
                      "type": "string",
                      "description": "Como apresentar o token nas rotas pagas."
                    },
                    "saldo_em": {
                      "type": "string",
                      "description": "Onde consultar saldo e extrato."
                    }
                  },
                  "required": [
                    "token",
                    "saldo_usd",
                    "guarde",
                    "usar",
                    "saldo_em"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Pacote fora da lista (1, 5, 10 ou 25)."
          },
          "402": {
            "description": "Sem pagamento — o corpo traz `accepts[]` do x402."
          }
        }
      },
      "get": {
        "operationId": "get_api_credito",
        "summary": "Saldo e extrato do crédito — as últimas movimentações, sem devolver o token.",
        "description": "Devolve: { saldo_micros, saldo_usd, criado_em, movimentos }",
        "responses": {
          "200": {
            "description": "{ saldo_micros, saldo_usd, criado_em, movimentos }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "saldo_micros": {
                      "type": "integer",
                      "description": "Saldo em micro-dólares (1e-6 USD)."
                    },
                    "saldo_usd": {
                      "type": "string",
                      "description": "Saldo formatado."
                    },
                    "criado_em": {
                      "type": "string",
                      "description": "Quando o crédito foi aberto."
                    },
                    "movimentos": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Entradas e saídas recentes, com produto e recurso."
                    }
                  },
                  "required": [
                    "saldo_micros",
                    "saldo_usd",
                    "criado_em",
                    "movimentos"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Sem token ou token desconhecido."
          }
        }
      }
    },
    "/api/acesso": {
      "get": {
        "operationId": "api_access",
        "summary": "Discover the monthly data package or inspect a private purchase.",
        "description": "Devolve: { offer{id,price_usd,credits,days,auto_renew,unit,products,purchase,method,status,header,payment_methods,instructions,generate_pass,client,guide,workflow,evaluation}, enabled?, id?, status?, granted_credits?, expires_at?, receipt?, via?, message? }",
        "parameters": [
          {
            "name": "X-API-Pass",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Private pass: api_<32 random hex>_<64 random hex>. Save before buying."
          }
        ],
        "responses": {
          "200": {
            "description": "{ offer{id,price_usd,credits,days,auto_renew,unit,products,purchase,method,status,header,payment_methods,instructions,generate_pass,client,guide,workflow,evaluation}, enabled?, id?, status?, granted_credits?, expires_at?, receipt?, via?, message? }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAccess"
                }
              }
            }
          },
          "400": {
            "description": "Invalid pass."
          },
          "404": {
            "description": "Unknown purchase or wrong owner."
          },
          "503": {
            "description": "Purchases disabled."
          }
        }
      },
      "post": {
        "operationId": "api_access_buy",
        "summary": "Buy 1000 basic data reads for US$1, valid for 30 days.",
        "description": "Same pass in retries recovers the same purchase. No automatic renewal. OCR, AI, documents and delivery keep their own tariffs. Send X-API-Pass on eligible data reads; remaining credits come in X-API-Credits-Remaining.\nDevolve: { offer{id,price_usd,credits,days,auto_renew,unit,products,purchase,method,status,header,payment_methods,instructions,generate_pass,client,guide,workflow,evaluation}, enabled?, id?, status?, granted_credits?, expires_at?, receipt?, via?, message? }",
        "parameters": [
          {
            "name": "X-API-Pass",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Private pass: api_<32 random hex>_<64 random hex>. Save before buying."
          },
          {
            "name": "X-Credito",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Existing prepaid credit token; alternative to x402."
          },
          {
            "name": "Authorization",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Bearer cred_… alternative to X-Credito."
          },
          {
            "name": "X-PAYMENT",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Signed x402 authorization from the 402 quote, maximum 16 KiB."
          },
          {
            "name": "PAYMENT-SIGNATURE",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Alternative name for X-PAYMENT."
          },
          {
            "name": "X-API-Transaction",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Confirmed Base transaction hash for reconciliation with the original pass and signed payment. Never creates another charge."
          }
        ],
        "responses": {
          "200": {
            "description": "{ offer{id,price_usd,credits,days,auto_renew,unit,products,purchase,method,status,header,payment_methods,instructions,generate_pass,client,guide,workflow,evaluation}, enabled?, id?, status?, granted_credits?, expires_at?, receipt?, via?, message? }",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiAccess"
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid pass/payment."
          },
          "401": {
            "description": "Invalid prepaid credit."
          },
          "402": {
            "description": "Payment required: x402 accepts[] and prepaid-credit instructions."
          },
          "409": {
            "description": "Payment pending; retain the same pass and do not pay again."
          },
          "429": {
            "description": "Purchase attempt limit; respect Retry-After."
          },
          "503": {
            "description": "Payment unavailable or pending reconciliation."
          }
        }
      }
    },
    "/api/pricing": {
      "get": {
        "operationId": "pricing",
        "summary": "Preços vigentes e franquias gratuitas.",
        "description": "Devolve: { product, quota{free,paid,how_to_pay,live,free_now?,trial?}, pricing, billing, api_index }",
        "responses": {
          "200": {
            "description": "{ product, quota{free,paid,how_to_pay,live,free_now?,trial?}, pricing, billing, api_index }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "type": "string",
                      "description": "Product name."
                    },
                    "quota": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaymentQuota"
                        }
                      ],
                      "description": "Public allowances and current list prices; not personal usage."
                    },
                    "pricing": {
                      "type": "string",
                      "description": "Absolute URL of the current price list."
                    },
                    "billing": {
                      "type": "string",
                      "description": "Absolute URL of payment discovery or the existing billing summary."
                    },
                    "api_index": {
                      "type": "string",
                      "description": "Absolute URL of the API catalog."
                    }
                  },
                  "required": [
                    "product",
                    "quota",
                    "pricing",
                    "billing",
                    "api_index"
                  ]
                }
              }
            }
          },
          "405": {
            "description": "Use GET ou HEAD."
          }
        }
      }
    },
    "/api/billing": {
      "get": {
        "operationId": "billing",
        "summary": "Descoberta pública de pagamento e crédito pré-pago.",
        "description": "Devolve: { product, quota{free,paid,how_to_pay,live,free_now?,trial?}, pricing, billing, api_index, payment{provider,mode,network,chain_id,pay_to,homolog,dev,dev_gate,gratis?,facilitator,asset,asset_address,faucet,wallets}, credit{url,header} }",
        "responses": {
          "200": {
            "description": "{ product, quota{free,paid,how_to_pay,live,free_now?,trial?}, pricing, billing, api_index, payment{provider,mode,network,chain_id,pay_to,homolog,dev,dev_gate,gratis?,facilitator,asset,asset_address,faucet,wallets}, credit{url,header} }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "product": {
                      "type": "string",
                      "description": "Product name."
                    },
                    "quota": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaymentQuota"
                        }
                      ],
                      "description": "Public allowances and current list prices; not personal usage."
                    },
                    "pricing": {
                      "type": "string",
                      "description": "Absolute URL of the current price list."
                    },
                    "billing": {
                      "type": "string",
                      "description": "Absolute URL of payment discovery or the existing billing summary."
                    },
                    "api_index": {
                      "type": "string",
                      "description": "Absolute URL of the API catalog."
                    },
                    "payment": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaymentX402"
                        }
                      ],
                      "description": "Public x402 configuration; pay_to=null means not configured."
                    },
                    "credit": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PaymentCredit"
                        }
                      ],
                      "description": "Prepaid credit entry point. Never contains a balance or token."
                    }
                  },
                  "required": [
                    "product",
                    "quota",
                    "pricing",
                    "billing",
                    "api_index",
                    "payment",
                    "credit"
                  ]
                }
              }
            }
          },
          "405": {
            "description": "Use GET ou HEAD."
          }
        }
      }
    }
  }
}