{"openapi":"3.1.0","info":{"title":"GenOpt API","version":"0.1.0","description":"API da GenOpt — AI Reputation Forensics. **Autenticação (estado atual):** todas as rotas não-públicas exigem `Authorization: Bearer <token>`, onde o token hoje é o token interno compartilhado — a API com API keys de cliente é a Fase 8 do roadmap (ADR-012) e será anunciada quando existir. As rotas de `/v1/public/evidence` não exigem autenticação por desenho (ADR-023): verificação de evidência não pede conta a ninguém. **Rate limits:** em implantação — os limites por token ainda não são aplicados nas rotas `/v1` (o servidor MCP em `/api/mcp` já aplica 60 req/min por token); quando ativos, serão documentados aqui com números.","contact":{"name":"GenOpt","email":"contato@genopt.ai","url":"https://genopt.ai/docs"}},"servers":[{"url":"https://api.genopt.ai","description":"Produção"}],"security":[{"bearerAuth":[]}],"paths":{"/v1/scans":{"post":{"operationId":"createScan","summary":"Enfileira um scan completo de uma marca","description":"Responde 202 e processa de forma assíncrona no worker (fila BullMQ). O scan executa os prompts monitorados nos engines do plano e dispara a classificação (ADR-031).","tags":["Scans"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["brandId"],"properties":{"brandId":{"type":"string"}}}}}},"responses":{"202":{"description":"Scan aceito e enfileirado.","content":{"application/json":{"schema":{"type":"object","properties":{"scanId":{"type":"string"},"jobId":{"type":"string"},"status":{"type":"string","enum":["QUEUED"]},"engines":{"type":"array","items":{"$ref":"#/components/schemas/Engine"}},"enginesNotRunnable":{"type":"array","items":{"$ref":"#/components/schemas/Engine"}},"enginesOutOfPlan":{"type":"array","items":{"$ref":"#/components/schemas/Engine"}}}}}}},"400":{"description":"brandId ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Marca não encontrada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Teto mensal de scans do plano atingido.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"maxScans":{"type":"integer"},"scansThisMonth":{"type":"integer"}}}]}}}},"503":{"description":"Nenhum engine configurado ou fila indisponível.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/scans/{scanId}":{"get":{"operationId":"getScan","summary":"Status e resultado de um scan","tags":["Scans"],"parameters":[{"name":"scanId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Estado do scan com contadores de runs por status.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"startedAt":{"type":["string","null"],"format":"date-time"},"finishedAt":{"type":["string","null"],"format":"date-time"},"totalPrompts":{"type":"integer"},"totalRuns":{"type":"integer"},"totalCostUsd":{"type":["number","string","null"]},"byStatus":{"type":"object","additionalProperties":{"type":"integer"}},"mentions":{"type":"integer"}}}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Scan não encontrado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/brands/{brandId}/confabulations":{"get":{"operationId":"listConfabulations","summary":"Matriz engine × intent × classe + execuções confabuladas","description":"Agrega os runs JÁ classificados (ADR-031) e lista as execuções CONFABULATED com evidência mínima. A lista detalhada é limitada a 100 itens; o histórico completo sai pelo evidence-bundle.","tags":["Confabulações"],"parameters":[{"name":"brandId","in":"path","required":true,"schema":{"type":"string"}},{"name":"engine","in":"query","required":false,"schema":{"$ref":"#/components/schemas/Engine"}},{"name":"intent","in":"query","required":false,"schema":{"$ref":"#/components/schemas/PromptIntent"}},{"name":"window","in":"query","required":false,"schema":{"type":"string","enum":["7d","30d","all"],"default":"30d"}}],"responses":{"200":{"description":"Matriz e lista de confabulações.","content":{"application/json":{"schema":{"type":"object","properties":{"brand":{"$ref":"#/components/schemas/BrandRef"},"classifiedRuns":{"type":"integer"},"totals":{"$ref":"#/components/schemas/CitationCounts"},"matrix":{"type":"array","items":{"type":"object","properties":{"engine":{"type":"string"},"intent":{"type":"string"},"counts":{"$ref":"#/components/schemas/CitationCounts"},"total":{"type":"integer"}}}},"confabulations":{"type":"object","properties":{"total":{"type":"integer"},"truncated":{"type":"boolean"},"items":{"type":"array","items":{"type":"object"}}}}}}}}},"400":{"description":"Query inválida (engine/intent/window fora do enum).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Marca não encontrada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/runs/{runId}/evidence":{"get":{"operationId":"getRunEvidence","summary":"Evidência completa de um run","description":"Resposta bruta, snapshot com SHA-256, status de verificação (active / stale / not_reproducible) e histórico de classificações. A evidência nunca é apagada nem editada — registro histórico com hash.","tags":["Evidência"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Evidência do run.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Run não encontrado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/runs/{runId}/verify":{"post":{"operationId":"verifyRunAgain","summary":"Enfileira a reverificação de um run (\"verificar novamente\")","description":"Só enfileira — a chamada real de engine roda no worker. Exige classificação prévia (ADR-031).","tags":["Evidência"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Reverificação enfileirada.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"}}}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Run não encontrado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Run sem classificação prévia.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Fila não configurada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/runs/{runId}/evidence-bundle":{"get":{"operationId":"getEvidenceBundle","summary":"Bundle de evidência exportável (JSON ou HTML)","description":"O artefato que o cliente leva (jurídico, RP). Exige run classificado. `format=html` devolve o documento print-friendly com o mesmo conteúdo do JSON.","tags":["Evidência"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["json","html"],"default":"json"}}],"responses":{"200":{"description":"Bundle da evidência.","content":{"application/json":{"schema":{"type":"object"}},"text/html":{"schema":{"type":"string"}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Run não encontrado ou ainda sem classificação (ADR-031).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/brands/{brandId}/ground-truth":{"get":{"operationId":"listGroundTruth","summary":"Fatos de ground truth da marca","tags":["Ground truth"],"parameters":[{"name":"brandId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fatos coletados, com fonte, hash e flag de ativo.","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Marca não encontrada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/brands/{brandId}/ground-truth/refresh":{"post":{"operationId":"refreshGroundTruth","summary":"Dispara a coleta de ground truth manualmente","description":"Só enfileira — a coleta HTTP roda no worker. 202 + jobId, mesmo contrato do POST /v1/scans.","tags":["Ground truth"],"parameters":[{"name":"brandId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Coleta enfileirada.","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"}}}}}},"401":{"description":"Token ausente ou inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Marca não encontrada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Fila não configurada.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/public/evidence/{hash}":{"get":{"operationId":"getPublicEvidence","summary":"Verificação pública: o hash existe? (sem autenticação)","description":"Confirma que a GenOpt capturou uma resposta com este SHA-256, com metadata mínima. A resposta bruta NUNCA sai por aqui — só existência e contexto (ADR-023).","tags":["Verificação pública"],"security":[],"parameters":[{"name":"hash","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-fA-F0-9]{64}$"},"description":"SHA-256 em hexadecimal (64 caracteres)."}],"responses":{"200":{"description":"Evidência encontrada — metadata mínima.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Hash SHA-256 inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Hash não encontrado.","content":{"application/json":{"schema":{"type":"object","properties":{"found":{"type":"boolean","enum":[false]}}}}}}}}},"/v1/public/evidence/{hash}/check":{"post":{"operationId":"checkPublicEvidence","summary":"Verificação pública: o conteúdo bate com o hash? (sem autenticação)","description":"Envia o conteúdo em mãos; a API recalcula o SHA-256 e responde verified/modified. A comparação é local ao pedido — nada é armazenado.","tags":["Verificação pública"],"security":[],"parameters":[{"name":"hash","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-fA-F0-9]{64}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"content":{"type":"string","minLength":1}}}}}},"responses":{"200":{"description":"Veredito da comparação.","content":{"application/json":{"schema":{"type":"object","properties":{"found":{"type":"boolean"},"match":{"type":"boolean"},"verdict":{"type":"string"},"submitted_sha256":{"type":"string"},"captured_at":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Hash inválido ou body.content ausente.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Hash não encontrado.","content":{"application/json":{"schema":{"type":"object","properties":{"found":{"type":"boolean","enum":[false]}}}}}}}}},"/v1/webhooks":{"get":{"operationId":"listWebhooks","summary":"Webhooks (planejado — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}},"post":{"operationId":"createWebhook","summary":"Webhooks (planejado — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}}},"/v1/integrations/slack":{"get":{"operationId":"getSlackIntegration","summary":"Integração Slack (planejada — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}},"post":{"operationId":"createSlackIntegration","summary":"Integração Slack (planejada — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}}},"/v1/integrations/discord":{"get":{"operationId":"getDiscordIntegration","summary":"Integração Discord (planejada — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}},"post":{"operationId":"createDiscordIntegration","summary":"Integração Discord (planejada — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}}},"/v1/integrations/zapier":{"get":{"operationId":"getZapierIntegration","summary":"Integração Zapier (planejada — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}},"post":{"operationId":"createZapierIntegration","summary":"Integração Zapier (planejada — Sprint 4E)","description":"Planejado para o Sprint 4E. Hoje responde 501 com {\"status\":\"planned\",\"roadmap\":\"Sprint 4E\"}.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}}},"/v1/exports":{"get":{"operationId":"listExports","summary":"Exports (planejado — depende de TD-045)","description":"Planejado; depende de TD-045 (pipeline de reports). Hoje responde 501 com {\"status\":\"planned\"} e nota da dependência.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}},"post":{"operationId":"createExport","summary":"Exports (planejado — depende de TD-045)","description":"Planejado; depende de TD-045 (pipeline de reports). Hoje responde 501 com {\"status\":\"planned\"} e nota da dependência.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}}},"/v1/reports":{"get":{"operationId":"listReports","summary":"Reports (planejado — depende de TD-045)","description":"Planejado; depende de TD-045 (pipeline de reports). Hoje responde 501 com {\"status\":\"planned\"} e nota da dependência.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}},"post":{"operationId":"createReport","summary":"Reports (planejado — depende de TD-045)","description":"Planejado; depende de TD-045 (pipeline de reports). Hoje responde 501 com {\"status\":\"planned\"} e nota da dependência.","tags":["Planejado"],"security":[],"responses":{"501":{"description":"Ainda não implementado — resposta honesta com status de roadmap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Planned"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Hoje: token interno compartilhado (uso server-side). API keys de cliente com prefixo próprio são a Fase 8 do roadmap (ADR-012)."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Mensagem de erro legível."}}},"Engine":{"type":"string","enum":["CHATGPT","PERPLEXITY","GEMINI","CLAUDE","COPILOT","GROK","GOOGLE_AIO","GOOGLE_AI_MODE","DEEPSEEK","META_AI","AMAZON_RUFUS"]},"PromptIntent":{"type":"string","enum":["CATEGORY","BRANDED","COMPARATIVE","DEFENSIVE"]},"CitationClass":{"type":"string","enum":["STRONG","DESCRIPTIVE","ECHO","CONFABULATED","ABSENT"],"description":"Classes de citação do ADR-031."},"CitationCounts":{"type":"object","properties":{"STRONG":{"type":"integer"},"DESCRIPTIVE":{"type":"integer"},"ECHO":{"type":"integer"},"CONFABULATED":{"type":"integer"},"ABSENT":{"type":"integer"}}},"BrandRef":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"domain":{"type":"string"}}},"Planned":{"type":"object","required":["status"],"description":"Resposta 501 de rota planejada (Endgame B2).","properties":{"status":{"type":"string","enum":["planned"]},"roadmap":{"type":"string","description":"Sprint prevista, quando definida (ex.: \"Sprint 4E\")."},"note":{"type":"string","description":"Dependência ou contexto (ex.: TD-045)."}}}}}}