{
  "openapi": "3.1.0",
  "info": {
    "title": "Chega de Fraude — API para parceiros",
    "version": "1.0.0",
    "summary": "Verificação de links, mensagens, Pix, boletos e domínios para prevenção a fraudes.",
    "description": "Acesso servidor a servidor ao verificador do Chega de Fraude. Cada chave pertence a um parceiro, tem escopos mínimos e está sujeita a cota mensal. O conteúdo enviado para análise não é armazenado. Termos de uso: https://chegadefraude.wmartins.app/parceiros",
    "contact": {
      "name": "Chega de Fraude",
      "url": "https://chegadefraude.wmartins.app/parceiros"
    },
    "termsOfService": "https://chegadefraude.wmartins.app/parceiros#termos"
  },
  "servers": [
    {
      "url": "https://chegadefraude.wmartins.app/api/v1"
    }
  ],
  "security": [
    {
      "bearer": []
    },
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/domains/{host}": {
      "get": {
        "operationId": "getDomain",
        "summary": "Avalia um domínio",
        "tags": [
          "Domínios"
        ],
        "description": "Analisa apenas o nome do domínio (sem acesso à rede): imitação de marca, caracteres que imitam letras, endereço IP, encurtador e terminação de risco. Quando a base comunitária está ativa, informa quantas denúncias aprovadas citam o domínio. Escopo: domain:read.",
        "parameters": [
          {
            "name": "host",
            "in": "path",
            "required": true,
            "description": "Nome do domínio, sem protocolo nem caminho.",
            "schema": {
              "type": "string",
              "maxLength": 253
            },
            "example": "itau-atendimento.xyz"
          }
        ],
        "responses": {
          "200": {
            "description": "Avaliação do domínio.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainResult"
                }
              }
            },
            "headers": {
              "X-Quota-Limit": {
                "$ref": "#/components/headers/X-Quota-Limit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/X-Quota-Remaining"
              }
            }
          },
          "400": {
            "description": "Domínio inválido (invalid_request). Não consome cota.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada (code: invalid_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Chave sem o escopo exigido (insufficient_scope) ou chamada feita por navegador de outra origem (browser_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rajada acima do limite por minuto (rate_limited), cota mensal esgotada (quota_exceeded) ou cota diária de IA do parceiro atingida (ai_quota_exceeded).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Quota-Limit": {
                "$ref": "#/components/headers/X-Quota-Limit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/X-Quota-Remaining"
              }
            }
          },
          "503": {
            "description": "Serviço temporariamente indisponível (unavailable).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/check": {
      "post": {
        "operationId": "check",
        "summary": "Verifica um conteúdo",
        "tags": [
          "Verificação"
        ],
        "description": "Mesma análise do verificador público. Links, Pix e boletos têm verificação técnica determinística; mensagens de texto dependem de inteligência artificial e estão sujeitas à cota diária de IA do parceiro. Na ausência de IA, o resultado nunca é BAIXO. Escopo: check:url, check:text, check:pix ou check:boleto, conforme kind.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckRequest"
              },
              "examples": {
                "link": {
                  "value": {
                    "kind": "url",
                    "content": "https://nubank-premio.click/resgate"
                  }
                },
                "pix": {
                  "value": {
                    "kind": "pix",
                    "content": "00020126...6304ABCD",
                    "context": "Cobrança recebida por WhatsApp"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado da verificação.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResult"
                }
              }
            },
            "headers": {
              "X-Quota-Limit": {
                "$ref": "#/components/headers/X-Quota-Limit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/X-Quota-Remaining"
              }
            }
          },
          "400": {
            "description": "Requisição inválida (invalid_request). Não consome cota.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada (code: invalid_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Chave sem o escopo exigido (insufficient_scope) ou chamada feita por navegador de outra origem (browser_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rajada acima do limite por minuto (rate_limited), cota mensal esgotada (quota_exceeded) ou cota diária de IA do parceiro atingida (ai_quota_exceeded).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Quota-Limit": {
                "$ref": "#/components/headers/X-Quota-Limit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/X-Quota-Remaining"
              }
            }
          },
          "503": {
            "description": "Serviço temporariamente indisponível (unavailable).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Consulta o uso do mês",
        "tags": [
          "Conta"
        ],
        "description": "Chamadas realizadas no mês corrente (UTC) somando todas as chaves do parceiro. Não consome cota.",
        "responses": {
          "200": {
            "description": "Uso do mês.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "Chave ausente, inválida, expirada ou revogada (code: invalid_key).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Chave sem o escopo exigido (insufficient_scope) ou chamada feita por navegador de outra origem (browser_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rajada acima do limite por minuto (rate_limited), cota mensal esgotada (quota_exceeded) ou cota diária de IA do parceiro atingida (ai_quota_exceeded).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Quota-Limit": {
                "$ref": "#/components/headers/X-Quota-Limit"
              },
              "X-Quota-Remaining": {
                "$ref": "#/components/headers/X-Quota-Remaining"
              }
            }
          },
          "503": {
            "description": "Serviço temporariamente indisponível (unavailable).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization: Bearer cdf_<prefixo>_<segredo>"
      },
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Api-Key"
      }
    },
    "headers": {
      "X-Quota-Limit": {
        "description": "Cota mensal do parceiro.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Quota-Remaining": {
        "description": "Chamadas restantes no mês.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Segundos até a liberação (fim da janela por minuto ou início do próximo mês).",
        "schema": {
          "type": "integer"
        }
      }
    },
    "schemas": {
      "Risk": {
        "type": "string",
        "enum": [
          "BAIXO",
          "MEDIO",
          "ALTO"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Mensagem em português."
          },
          "code": {
            "type": "string",
            "enum": [
              "invalid_key",
              "browser_not_allowed",
              "insufficient_scope",
              "invalid_request",
              "rate_limited",
              "quota_exceeded",
              "ai_quota_exceeded",
              "not_found",
              "unavailable",
              "internal_error"
            ]
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "Signal": {
        "type": "object",
        "required": [
          "weight",
          "message"
        ],
        "properties": {
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Identificador estável: ip, punycode, userinfo, shortener, tld, brand:<marca>."
          },
          "weight": {
            "type": "string",
            "enum": [
              "high",
              "medium",
              "low"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Fact": {
        "type": "object",
        "required": [
          "label",
          "value"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "tone": {
            "type": "string",
            "enum": [
              "ok",
              "warn",
              "bad",
              "info"
            ]
          }
        }
      },
      "DomainResult": {
        "type": "object",
        "required": [
          "hostname",
          "domain",
          "official",
          "risk",
          "signals",
          "community_reports"
        ],
        "properties": {
          "hostname": {
            "type": "string"
          },
          "domain": {
            "type": "string",
            "description": "Domínio registrável."
          },
          "official": {
            "type": "boolean",
            "description": "Domínio oficial de marca conhecida, gov.br ou b.br."
          },
          "risk": {
            "$ref": "#/components/schemas/Risk"
          },
          "signals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Signal"
            }
          },
          "community_reports": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Denúncias aprovadas que citam o domínio; null quando a base comunitária está desativada."
          }
        }
      },
      "CheckRequest": {
        "type": "object",
        "required": [
          "kind",
          "content"
        ],
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "url",
              "text",
              "pix",
              "boleto"
            ]
          },
          "content": {
            "type": "string",
            "minLength": 3,
            "maxLength": 6000,
            "description": "Link, texto da mensagem, código Pix Copia e Cola ou linha digitável."
          },
          "context": {
            "type": "string",
            "maxLength": 1000,
            "description": "Contexto opcional (como o conteúdo chegou)."
          }
        }
      },
      "CheckResult": {
        "type": "object",
        "required": [
          "request_id",
          "kind",
          "risk",
          "score",
          "headline",
          "summary",
          "red_flags",
          "safe_signals",
          "action_plan",
          "facts",
          "ai"
        ],
        "properties": {
          "request_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "url",
              "text",
              "pix",
              "boleto"
            ]
          },
          "risk": {
            "$ref": "#/components/schemas/Risk"
          },
          "score": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Probabilidade estimada de golpe."
          },
          "headline": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "red_flags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "safe_signals": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "action_plan": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "category": {
            "type": "string"
          },
          "facts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Fact"
            }
          },
          "ai": {
            "type": "boolean",
            "description": "Indica se a inteligência artificial participou da análise."
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "partner",
          "key_prefix",
          "scopes",
          "month",
          "used",
          "quota",
          "remaining"
        ],
        "properties": {
          "partner": {
            "type": "string"
          },
          "key_prefix": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "check:url",
                "check:text",
                "check:pix",
                "check:boleto",
                "domain:read"
              ]
            }
          },
          "month": {
            "type": "string",
            "example": "2026-10"
          },
          "used": {
            "type": "integer"
          },
          "quota": {
            "type": "integer"
          },
          "remaining": {
            "type": "integer"
          }
        }
      }
    }
  }
}
