{
  "name": "PageAudit",
  "description": "Auditor técnico de SEO com workspace de abas persistente. O cliente principal é agente de IA: toda tela da UI tem endpoint equivalente e todo recurso carrega a própria URL de API.",
  "build": "bafc51af",
  "base_url": "https://pageaudit.online",
  "docs": {
    "llms": "https://pageaudit.online/llms.txt",
    "openapi": "https://pageaudit.online/openapi.json",
    "human_ui": "https://pageaudit.online/"
  },
  "conventions": {
    "format": "JSON em toda resposta de `/api/*`; erro é `{ error, detail? }` com status HTTP.",
    "cors": "`Access-Control-Allow-Origin: *` — chamável direto do browser.",
    "links": "Recurso devolve `api` (ou `_links`) com URL absoluta dos recursos relacionados, para navegar sem montar caminho na mão."
  },
  "auth": {
    "none": "Público, sem credencial.",
    "guest": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência.",
    "session": "Sessão de usuário: `Authorization: Bearer sess_…` (obtida por OTP de e-mail).",
    "token": "Token de operador em `Authorization: Bearer …` (`METRICS_TOKEN`)."
  },
  "endpoints": [
    {
      "method": "GET",
      "path": "/api/",
      "auth": "none",
      "summary": "Este índice: toda a superfície da API, auto-descrita.",
      "returns": "{ name, description, auth, endpoints[], data_retention }",
      "url": "https://pageaudit.online/api/",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "GET",
      "path": "/api/health",
      "auth": "none",
      "summary": "Liveness e commit publicado.",
      "returns": "{ ok, app, build, ts }",
      "url": "https://pageaudit.online/api/health",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "POST",
      "path": "/api/audit",
      "auth": "none",
      "summary": "Audita uma URL e devolve o relatório completo numa chamada. É o atalho para agente: não precisa de token nem de aba.",
      "body": {
        "url": "https://example.com/",
        "guest_token": "pa_… (opcional, associa o audit)"
      },
      "returns": "{ id, score, summary, issues[], counts, jsonLd[], headers{}, truncated[] }",
      "notes": [
        "Limite por IP (`MAX_AUDITS_PER_HOUR`).",
        "Depois da cota grátis diária o gate pede Turnstile; `GET /api/gate` diz quanto resta."
      ],
      "url": "https://pageaudit.online/api/audit",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "GET",
      "path": "/api/audits/:id",
      "auth": "guest",
      "summary": "Relê um audit já feito, inteiro, sem re-auditar a página.",
      "returns": "{ id, url, score, summary, issues[], counts, jsonLd[], headers{}, created_at }",
      "url": "https://pageaudit.online/api/audits/:id",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "GET",
      "path": "/api/shared/:slug",
      "auth": "none",
      "summary": "Relatório compartilhado em JSON, sem credencial. É o par legível por máquina de `/r/:slug`.",
      "returns": "{ id, url, score, summary, issues[], counts, jsonLd[], headers{}, created_at }",
      "url": "https://pageaudit.online/api/shared/:slug",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "POST",
      "path": "/api/audits/:id/share",
      "auth": "guest",
      "summary": "Publica o audit num slug não enumerável (idempotente).",
      "returns": "{ ok, shared, slug, path }",
      "url": "https://pageaudit.online/api/audits/:id/share",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "DELETE",
      "path": "/api/audits/:id/share",
      "auth": "guest",
      "summary": "Revoga o compartilhamento.",
      "returns": "{ ok, shared: false }",
      "url": "https://pageaudit.online/api/audits/:id/share",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "POST",
      "path": "/api/guest",
      "auth": "none",
      "summary": "Cria um guest token `pa_…` — é o que dá acesso às abas sem conta.",
      "returns": "{ token }",
      "url": "https://pageaudit.online/api/guest",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "GET",
      "path": "/api/tabs",
      "auth": "guest",
      "summary": "Workspace de abas do dono, com o resultado da aba ativa já reidratado.",
      "returns": "{ tabs[], active_id, active_result, limit, owner, gate, billing }",
      "url": "https://pageaudit.online/api/tabs",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "POST",
      "path": "/api/tabs",
      "auth": "guest",
      "summary": "Abre uma aba para a URL (ou foca a existente) e persiste no backend.",
      "body": {
        "url": "https://example.com/",
        "alias": "rótulo opcional"
      },
      "returns": "{ ok, tab }",
      "url": "https://pageaudit.online/api/tabs",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "GET",
      "path": "/api/tabs/:id",
      "auth": "guest",
      "summary": "Uma aba com o resultado completo do último run.",
      "returns": "{ tab, result }",
      "url": "https://pageaudit.online/api/tabs/:id",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "PATCH",
      "path": "/api/tabs/:id",
      "auth": "guest",
      "summary": "Renomeia ou ativa a aba.",
      "body": {
        "alias": "novo rótulo",
        "active": true
      },
      "returns": "{ ok, tab }",
      "url": "https://pageaudit.online/api/tabs/:id",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "DELETE",
      "path": "/api/tabs/:id",
      "auth": "guest",
      "summary": "Fecha a aba. O histórico em `audits` continua.",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/tabs/:id",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "POST",
      "path": "/api/tabs/:id/run",
      "auth": "guest",
      "summary": "Re-audita a URL da aba e grava um novo audit.",
      "returns": "{ ok, tab, gate, result }",
      "url": "https://pageaudit.online/api/tabs/:id/run",
      "auth_detail": "Guest token (`POST /api/guest`) em `X-Guest-Token: pa_…` ou `Authorization: Bearer pa_…`. Uma sessão de usuário (`sess_…`) também serve e tem precedência."
    },
    {
      "method": "GET",
      "path": "/api/gate",
      "auth": "none",
      "summary": "Quantos audits grátis restam no IP e se o Turnstile já é exigido.",
      "returns": "{ free_remaining, required, configured, verified }",
      "url": "https://pageaudit.online/api/gate",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "GET",
      "path": "/api/billing",
      "auth": "none",
      "summary": "Abas grátis, preço da aba extra e parâmetros x402.",
      "returns": "{ provider, network, homolog, free_max_tabs, price_extra_tab_usd, pay_to }",
      "url": "https://pageaudit.online/api/billing",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "GET",
      "path": "/api/me",
      "auth": "session",
      "summary": "Usuário da sessão e os últimos 50 audits dele.",
      "returns": "{ user, audits[] }",
      "url": "https://pageaudit.online/api/me",
      "auth_detail": "Sessão de usuário: `Authorization: Bearer sess_…` (obtida por OTP de e-mail)."
    },
    {
      "method": "POST",
      "path": "/api/auth/start",
      "auth": "none",
      "summary": "Envia o código OTP para o e-mail.",
      "body": {
        "email": "voce@exemplo.com"
      },
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/auth/start",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "POST",
      "path": "/api/auth/verify",
      "auth": "none",
      "summary": "Troca o código por uma sessão `sess_…`.",
      "body": {
        "email": "voce@exemplo.com",
        "code": "123456"
      },
      "returns": "{ ok, token, user }",
      "url": "https://pageaudit.online/api/auth/verify",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "POST",
      "path": "/api/auth/claim",
      "auth": "session",
      "summary": "Move audits e abas do guest para a conta.",
      "body": {
        "guest_token": "pa_…"
      },
      "returns": "{ ok, claimed }",
      "url": "https://pageaudit.online/api/auth/claim",
      "auth_detail": "Sessão de usuário: `Authorization: Bearer sess_…` (obtida por OTP de e-mail)."
    },
    {
      "method": "POST",
      "path": "/api/auth/logout",
      "auth": "session",
      "summary": "Invalida a sessão.",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/auth/logout",
      "auth_detail": "Sessão de usuário: `Authorization: Bearer sess_…` (obtida por OTP de e-mail)."
    },
    {
      "method": "POST",
      "path": "/api/contact",
      "auth": "none",
      "summary": "Suporte: humano com Turnstile (grátis) ou agente com x402 $0.10 (X-PAYMENT, sem captcha).",
      "body": {
        "name": "…",
        "email": "a@example.com",
        "message": "…",
        "form_ts": 0,
        "cf_turnstile_response": "(humano)"
      },
      "returns": "{ ok, path } | HTTP 402 accepts[] para agente",
      "notes": [
        "Sem captcha = caminho agente: 402 até pagar $0.10 USDC (Base) ou homolog em dev.",
        "Framework unificado make-money — hub AGENTS.md § Contato."
      ],
      "url": "https://pageaudit.online/api/contact",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "POST",
      "path": "/api/visit",
      "auth": "none",
      "summary": "Ping de audiência da própria UI. Agente não precisa chamar.",
      "body": {
        "p": "/"
      },
      "returns": "{ ok, counted }",
      "url": "https://pageaudit.online/api/visit",
      "auth_detail": "Público, sem credencial."
    },
    {
      "method": "GET",
      "path": "/api/metrics",
      "auth": "token",
      "summary": "Métricas de uso dos últimos 7 dias. Operação, não produto.",
      "returns": "{ app, visits[], audits[] }",
      "url": "https://pageaudit.online/api/metrics",
      "auth_detail": "Token de operador em `Authorization: Bearer …` (`METRICS_TOKEN`)."
    },
    {
      "method": "GET",
      "path": "/r/:slug",
      "auth": "none",
      "summary": "Página HTML do relatório compartilhado (noindex). Para JSON use `/api/shared/:slug`.",
      "returns": "text/html",
      "url": "https://pageaudit.online/r/:slug",
      "auth_detail": "Público, sem credencial."
    }
  ],
  "data_retention": {
    "stored": "Cada run grava uma linha em `audits` com o resultado inteiro: summary, issues, counts, o JSON-LD cru e todos os headers da resposta.",
    "not_stored": [
      "O HTML da página — baixado até 2 MB, analisado e descartado. Reler exige novo run.",
      "`Set-Cookie` do alvo, removido de propósito: é credencial de terceiro e nenhuma checagem usa.",
      "A cadeia de redirects: o fetch segue os saltos, então só a URL final sobrevive."
    ],
    "truncation": "Nada some em silêncio. Corte por tamanho aparece em `truncated[]` (`jsonld_size`, `jsonld_nodes_over_50`, `result_size`) e `summary.jsonLdBlocks` / `summary.jsonLdDropped` dizem quantos blocos existiam e quantos ficaram de fora."
  },
  "quickstart": [
    "curl -s https://pageaudit.online/api/audit -H 'Content-Type: application/json' -d '{\"url\":\"https://example.com/\"}'",
    "TOKEN=$(curl -s -XPOST https://pageaudit.online/api/guest | jq -r .token)",
    "curl -s https://pageaudit.online/api/tabs -H \"X-Guest-Token: $TOKEN\""
  ]
}