{
  "openapi": "3.1.0",
  "info": {
    "title": "PageAudit",
    "version": "bafc51af",
    "description": "Auditor técnico de SEO. Cliente principal: agente de IA. Índice navegável em GET /api/."
  },
  "servers": [
    {
      "url": "https://www.pageaudit.online"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "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."
      }
    }
  },
  "paths": {
    "/api/": {
      "get": {
        "summary": "Este índice: toda a superfície da API, auto-descrita.",
        "description": "Devolve: { name, description, auth, endpoints[], data_retention }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ name, description, auth, endpoints[], data_retention }"
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "summary": "Liveness e commit publicado.",
        "description": "Devolve: { ok, app, build, ts }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ ok, app, build, ts }"
          }
        }
      }
    },
    "/api/audit": {
      "post": {
        "summary": "Audita uma URL e devolve o relatório completo numa chamada. É o atalho para agente: não precisa de token nem de aba.",
        "description": "Devolve: { id, score, summary, issues[], counts, jsonLd[], headers{}, truncated[] }\nLimite por IP (`MAX_AUDITS_PER_HOUR`).\nDepois da cota grátis diária o gate pede Turnstile; `GET /api/gate` diz quanto resta.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "url": "https://example.com/",
                "guest_token": "pa_… (opcional, associa o audit)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ id, score, summary, issues[], counts, jsonLd[], headers{}, truncated[] }"
          }
        }
      }
    },
    "/api/audits/{id}": {
      "get": {
        "summary": "Relê um audit já feito, inteiro, sem re-auditar a página.",
        "description": "Devolve: { id, url, score, summary, issues[], counts, jsonLd[], headers{}, created_at }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ id, url, score, summary, issues[], counts, jsonLd[], headers{}, created_at }"
          }
        }
      }
    },
    "/api/shared/{slug}": {
      "get": {
        "summary": "Relatório compartilhado em JSON, sem credencial. É o par legível por máquina de `/r/:slug`.",
        "description": "Devolve: { id, url, score, summary, issues[], counts, jsonLd[], headers{}, created_at }",
        "security": [],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ id, url, score, summary, issues[], counts, jsonLd[], headers{}, created_at }"
          }
        }
      }
    },
    "/api/audits/{id}/share": {
      "post": {
        "summary": "Publica o audit num slug não enumerável (idempotente).",
        "description": "Devolve: { ok, shared, slug, path }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, shared, slug, path }"
          }
        }
      },
      "delete": {
        "summary": "Revoga o compartilhamento.",
        "description": "Devolve: { ok, shared: false }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, shared: false }"
          }
        }
      }
    },
    "/api/guest": {
      "post": {
        "summary": "Cria um guest token `pa_…` — é o que dá acesso às abas sem conta.",
        "description": "Devolve: { token }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ token }"
          }
        }
      }
    },
    "/api/tabs": {
      "get": {
        "summary": "Workspace de abas do dono, com o resultado da aba ativa já reidratado.",
        "description": "Devolve: { tabs[], active_id, active_result, limit, owner, gate, billing }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ tabs[], active_id, active_result, limit, owner, gate, billing }"
          }
        }
      },
      "post": {
        "summary": "Abre uma aba para a URL (ou foca a existente) e persiste no backend.",
        "description": "Devolve: { ok, tab }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "url": "https://example.com/",
                "alias": "rótulo opcional"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, tab }"
          }
        }
      }
    },
    "/api/tabs/{id}": {
      "get": {
        "summary": "Uma aba com o resultado completo do último run.",
        "description": "Devolve: { tab, result }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ tab, result }"
          }
        }
      },
      "patch": {
        "summary": "Renomeia ou ativa a aba.",
        "description": "Devolve: { ok, tab }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "alias": "novo rótulo",
                "active": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, tab }"
          }
        }
      },
      "delete": {
        "summary": "Fecha a aba. O histórico em `audits` continua.",
        "description": "Devolve: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok }"
          }
        }
      }
    },
    "/api/tabs/{id}/run": {
      "post": {
        "summary": "Re-audita a URL da aba e grava um novo audit.",
        "description": "Devolve: { ok, tab, gate, result }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok, tab, gate, result }"
          }
        }
      }
    },
    "/api/gate": {
      "get": {
        "summary": "Quantos audits grátis restam no IP e se o Turnstile já é exigido.",
        "description": "Devolve: { free_remaining, required, configured, verified }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ free_remaining, required, configured, verified }"
          }
        }
      }
    },
    "/api/billing": {
      "get": {
        "summary": "Abas grátis, preço da aba extra e parâmetros x402.",
        "description": "Devolve: { provider, network, homolog, free_max_tabs, price_extra_tab_usd, pay_to }",
        "security": [],
        "responses": {
          "200": {
            "description": "{ provider, network, homolog, free_max_tabs, price_extra_tab_usd, pay_to }"
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "summary": "Usuário da sessão e os últimos 50 audits dele.",
        "description": "Devolve: { user, audits[] }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ user, audits[] }"
          }
        }
      }
    },
    "/api/auth/start": {
      "post": {
        "summary": "Envia o código OTP para o e-mail.",
        "description": "Devolve: { ok }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "email": "voce@exemplo.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok }"
          }
        }
      }
    },
    "/api/auth/verify": {
      "post": {
        "summary": "Troca o código por uma sessão `sess_…`.",
        "description": "Devolve: { ok, token, user }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "email": "voce@exemplo.com",
                "code": "123456"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, token, user }"
          }
        }
      }
    },
    "/api/auth/claim": {
      "post": {
        "summary": "Move audits e abas do guest para a conta.",
        "description": "Devolve: { ok, claimed }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "guest_token": "pa_…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, claimed }"
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "summary": "Invalida a sessão.",
        "description": "Devolve: { ok }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ ok }"
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "summary": "Suporte: humano com Turnstile (grátis) ou agente com x402 $0.10 (X-PAYMENT, sem captcha).",
        "description": "Devolve: { ok, path } | HTTP 402 accepts[] para agente\nSem captcha = caminho agente: 402 até pagar $0.10 USDC (Base) ou homolog em dev.\nFramework unificado make-money — hub AGENTS.md § Contato.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "name": "…",
                "email": "a@example.com",
                "message": "…",
                "form_ts": 0,
                "cf_turnstile_response": "(humano)"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, path } | HTTP 402 accepts[] para agente"
          }
        }
      }
    },
    "/api/visit": {
      "post": {
        "summary": "Ping de audiência da própria UI. Agente não precisa chamar.",
        "description": "Devolve: { ok, counted }",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "p": "/"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok, counted }"
          }
        }
      }
    },
    "/api/metrics": {
      "get": {
        "summary": "Métricas de uso dos últimos 7 dias. Operação, não produto.",
        "description": "Devolve: { app, visits[], audits[] }",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "{ app, visits[], audits[] }"
          }
        }
      }
    },
    "/r/{slug}": {
      "get": {
        "summary": "Página HTML do relatório compartilhado (noindex). Para JSON use `/api/shared/:slug`.",
        "description": "Devolve: text/html",
        "security": [],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "text/html"
          }
        }
      }
    }
  }
}