{
  "name": "PageAudit",
  "description": "Check what search engines can read on a web page. Get evidence and suggested changes for each issue. Save reports, compare results and enable a daily watch. Rules inspect the page without a language model. Monitor domain registration, contact changes, DNS and certificate issuance. Compare a limited server sample, review TLS and configure expiry alerts with current account rights. The API supports the same operations as the human interface.",
  "build": "425670a3",
  "base_url": "https://pageaudit.online",
  "docs": {
    "llms": "https://pageaudit.online/llms.txt",
    "llms_full": "https://pageaudit.online/llms-full.txt",
    "openapi": "https://pageaudit.online/openapi.json",
    "mcp": "https://pageaudit.online/mcp",
    "pricing": "https://pageaudit.online/api/pricing",
    "billing": "https://pageaudit.online/api/billing",
    "human_ui": "https://pageaudit.online/",
    "data_indexes": [
      {
        "id": "cep",
        "produto": "https://pontofato.com",
        "caminho": "/enderecos",
        "title": "Postal codes and addresses",
        "description": "Find addresses by location, with coordinates and a 2022 reference date. Not a current postal-code certification. Each HTML page opens the product app with its public data already rendered.",
        "hierarchy": "State → municipality → locality → street → addresses",
        "url": "https://pontofato.com/enderecos/",
        "formats": {
          "html": "https://pontofato.com/enderecos/",
          "json": "https://api.pontofato.com/enderecos/index.json",
          "md": "https://api.pontofato.com/enderecos/index.md",
          "okf": "https://api.pontofato.com/enderecos/index.okf.md"
        },
        "llms": "https://api.pontofato.com/enderecos/llms.txt",
        "openapi": "https://api.pontofato.com/enderecos/openapi.json",
        "mcp": "https://api.pontofato.com/enderecos/mcp",
        "okf": "https://api.pontofato.com/enderecos/okf/index.md",
        "access": "public-read-only",
        "pagination": {
          "max_items": 20,
          "next": "links.proximo"
        },
        "updates": "manual"
      },
      {
        "id": "editais",
        "produto": "https://editalmd.com",
        "caminho": "/licitacoes",
        "title": "Public procurement",
        "description": "Find public procurement by location and date. View documents and reading options in EditalMD. Each HTML page opens the product app with its public data already rendered.",
        "hierarchy": "Procedure → state → year → month → day → municipality → purchases",
        "url": "https://editalmd.com/licitacoes/",
        "formats": {
          "html": "https://editalmd.com/licitacoes/",
          "json": "https://api.editalmd.com/licitacoes/index.json",
          "md": "https://api.editalmd.com/licitacoes/index.md",
          "okf": "https://api.editalmd.com/licitacoes/index.okf.md"
        },
        "llms": "https://api.editalmd.com/licitacoes/llms.txt",
        "openapi": "https://api.editalmd.com/licitacoes/openapi.json",
        "mcp": "https://api.editalmd.com/licitacoes/mcp",
        "okf": "https://api.editalmd.com/licitacoes/okf/index.md",
        "access": "public-read-only",
        "pagination": {
          "max_items": 20,
          "next": "links.proximo"
        },
        "updates": "manual"
      }
    ]
  },
  "conventions": {
    "format": "JSON in every `/api/*` response; an error is `{ error, detail? }` with the HTTP status.",
    "cors": "Public APIs allow browser calls. Journey collection accepts only the registered Origin; private journey reports require the account session.",
    "links": "A resource returns `api` (or `_links`) with absolute URLs of related resources, so you navigate without building paths by hand."
  },
  "auth": {
    "credito": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token.",
    "none": "Public, no credential.",
    "guest": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to.",
    "session": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans.",
    "organization": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`.",
    "token": "Operator token in `Authorization: Bearer …` (`METRICS_TOKEN`).",
    "github_app": "Only GitHub: the delivery is signed with the PageAudit GitHub App's webhook secret in `X-Hub-Signature-256`."
  },
  "endpoints": [
    {
      "method": "GET",
      "path": "/agent.json",
      "auth": "none",
      "summary": "Agent card: identity, operator, documentation, the MCP endpoint and the tools it serves. Same document as `/.well-known/agent-card.json`.",
      "grupo": "Discovery",
      "retorno": {
        "_texto": "`application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` and `skills[]`."
      },
      "exemplo": "curl -s $ORIGIN/agent.json",
      "returns": "`application/json`: `name`, `provider`, `protocol` (`mcp`), `interfaces[]` and `skills[]`.",
      "url": "https://pageaudit.online/agent.json",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/okf/:arquivo",
      "auth": "none",
      "summary": "OKF bundle (Open Knowledge Format v0.1): markdown with frontmatter so an agent reads the whole product without parsing HTML.",
      "grupo": "Discovery",
      "params": {
        "arquivo": {
          "desc": "`index.md`, `sobre.md`, `api.md` or `faq.md`.",
          "exemplo": "index.md"
        }
      },
      "retorno": {
        "_texto": "`text/markdown`. Start at `/okf/index.md`, which lists the bundle."
      },
      "erros": {
        "404": "File outside the bundle."
      },
      "exemplo": "curl -s $ORIGIN/okf/index.md",
      "returns": "`text/markdown`. Start at `/okf/index.md`, which lists the bundle.",
      "url": "https://pageaudit.online/okf/:arquivo",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/.well-known/:arquivo",
      "auth": "none",
      "summary": "Machine discovery before the home page: `api-catalog` (RFC 9727, a linkset with the API and the MCP), `security.txt` (RFC 9116), `x402` (payment manifest: network, wallet and the routes that charge) and `mcp-registry-auth` (the official MCP registry key).",
      "grupo": "Discovery",
      "params": {
        "arquivo": {
          "desc": "`api-catalog`, `security.txt`, `x402`, `mcp-registry-auth` or `apis.json`.",
          "exemplo": "api-catalog"
        }
      },
      "retorno": {
        "_texto": "`application/linkset+json` for the api-catalog; `application/json` for x402 and apis.json; `text/plain` for the other two."
      },
      "erros": {
        "404": "Name outside the five published."
      },
      "exemplo": "curl -s $ORIGIN/.well-known/api-catalog",
      "returns": "`application/linkset+json` for the api-catalog; `application/json` for x402 and apis.json; `text/plain` for the other two.",
      "url": "https://pageaudit.online/.well-known/:arquivo",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/apis.json",
      "auth": "none",
      "summary": "APIs.json (apisjson.org, 0.19): the index APIs.io harvests — the API, the MCP, OpenAPI, guide and OKF bundle in one file. Also at `/.well-known/apis.json`.",
      "grupo": "Discovery",
      "retorno": {
        "_texto": "`application/json` in the APIs.json 0.19 format: `apis[]` with `baseURL`, `humanURL` and `properties[]`."
      },
      "exemplo": "curl -s $ORIGIN/apis.json",
      "returns": "`application/json` in the APIs.json 0.19 format: `apis[]` with `baseURL`, `humanURL` and `properties[]`.",
      "url": "https://pageaudit.online/apis.json",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/",
      "auth": "none",
      "summary": "Self-describing index: the whole API surface, with quota and retention policy.",
      "grupo": "Discovery",
      "retorno": {
        "name": {
          "tipo": "string",
          "desc": "Product name."
        },
        "description": {
          "tipo": "string",
          "desc": "What the product does, in one sentence."
        },
        "build": {
          "tipo": "string",
          "desc": "Deployed commit."
        },
        "base_url": {
          "tipo": "string",
          "desc": "Origin this API is serving from."
        },
        "docs": {
          "tipo": "object",
          "desc": "Links to llms.txt, llms-full.txt, openapi.json, MCP and the UI."
        },
        "conventions": {
          "tipo": "object",
          "desc": "Error format, CORS, x402 and the UI↔API parity rule."
        },
        "auth": {
          "tipo": "object",
          "desc": "Each authentication mode and how to obtain it."
        },
        "endpoints": {
          "tipo": "object[]",
          "desc": "Every endpoint with method, path, auth, absolute URL and what it returns."
        },
        "data_retention": {
          "tipo": "object",
          "desc": "What is stored from each run and what is discarded."
        },
        "quota": {
          "tipo": "object",
          "desc": "What is free, what costs and how to pay — before you spend a call."
        },
        "mcp": {
          "tipo": "object",
          "desc": "Address and transport of the MCP server."
        },
        "quickstart": {
          "tipo": "string[]",
          "desc": "The calls that take you from zero to the first report."
        }
      },
      "returns": "{ name, description, build, base_url, docs, conventions, auth, endpoints, data_retention, quota, mcp, quickstart }",
      "url": "https://pageaudit.online/api/",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/health",
      "auth": "none",
      "summary": "Liveness and the commit deployed right now — it is how the smoke waits for its own deploy.",
      "grupo": "Discovery",
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the Worker answers."
        },
        "app": {
          "tipo": "string",
          "desc": "Product name."
        },
        "build": {
          "tipo": "string",
          "desc": "Deployed commit; CI passes the short SHA on deploy."
        },
        "ts": {
          "tipo": "string",
          "desc": "Time of the response (UTC, ISO-8601)."
        }
      },
      "returns": "{ ok, app, build, ts }",
      "url": "https://pageaudit.online/api/health",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/mcp",
      "auth": "none",
      "summary": "MCP server over HTTP (Streamable HTTP, JSON-RPC 2.0) — plugs into the client with nothing to install.",
      "grupo": "Discovery",
      "desc": "The tools are the operations of this same catalog; the MCP has no backend of its own. `GET /mcp` returns the server card.",
      "retorno": {
        "_texto": "JSON-RPC 2.0 response (`initialize`, `tools/list` or `tools/call`)."
      },
      "notes": [
        "Credentials go in the usual headers (X-Guest-Token, Authorization, X-PAYMENT) and are forwarded to the API.",
        "An exhausted quota comes back as a 402 with accepts[] inside the tool result — pay and repeat."
      ],
      "exemplo": "curl -s -XPOST $ORIGIN/mcp -H 'content-type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}'",
      "returns": "JSON-RPC 2.0 response (`initialize`, `tools/list` or `tools/call`).",
      "url": "https://pageaudit.online/mcp",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/audit",
      "auth": "none",
      "summary": "Audits a URL and returns the full report in one call, without a token or a tab.",
      "grupo": "Audit",
      "desc": "This is the product for agents. Past the account or anonymous network's daily allowance the response is **402 with `accepts[]`** and `code: allowance_spent` — pay and repeat the same call. There is no captcha: people and agents pay the same way. The free alternative: a person signs in with an account to get the trial.",
      "corpo": {
        "url": {
          "tipo": "string",
          "desc": "The page to audit, `http` or `https`.",
          "obrigatorio": true
        },
        "guest_token": {
          "tipo": "string",
          "desc": "Guest `pa_…` so the audit is tied to it and shows up in the tabs."
        },
        "compare_with": {
          "tipo": "string",
          "desc": "ID of an earlier audit of the same owner (production, for example). The answer gains `comparison`: the findings that appeared and disappeared, by code. Checked before the audit runs, so a wrong ID spends nothing."
        }
      },
      "body": {
        "url": "https://example.com/",
        "guest_token": "pa_… (optional, ties the audit)"
      },
      "retorno": "Audit",
      "erros": {
        "400": "Body is not JSON, `url` missing/not http, or `compare_with` is not an audit ID.",
        "401": "`compare_with` without an owner: send `guest_token` or sign in.",
        "402": null,
        "404": "`compare_with` is not an audit of this owner.",
        "429": "Past `MAX_AUDITS_PER_HOUR` on the same IP. Wait an hour.",
        "502": "The target failed to answer, or a firewall or rate limit answered instead of the page: `code` `target_blocked` or `target_rate_limited`, with `blocked.fix`. Nothing was audited."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/audit -H 'content-type: application/json' -d '{\"url\":\"https://example.com/\"}'",
      "agent_value": {
        "principal": true,
        "idioma": "en",
        "en": {
          "when": "What can search engines read on this page, and what needs fixing?",
          "result": "Get HTML findings with evidence, severity and suggested changes for one URL.",
          "replaces": "The HTML checks run without an LLM. You can work from the report instead of writing a fetcher and audit rules.",
          "free": "Start with POST /api/audit using a URL you are allowed to check. Use the current free allowance.",
          "paid": "For fixes, generate a saved agent prompt from the report. For repeated checks, compare monitoring plans. Prompts and monitoring have separate allowances.",
          "limits": "This checks delivered HTML. Use a browser test for performance or accessibility. Check truncated and unavailable evidence.",
          "alternative": "Your existing audit already provides the same evidence.",
          "start": {
            "href": "/",
            "label": "Audit a page"
          }
        },
        "pt": {
          "when": "O que os buscadores leem nesta página e o que precisa de correção?",
          "result": "Receba achados de HTML com evidência, gravidade e mudanças sugeridas para uma URL.",
          "replaces": "As verificações rodam sem LLM. Use o relatório sem criar um coletor e suas próprias regras de auditoria.",
          "free": "Comece por POST /api/audit com uma URL que pode conferir. Use a franquia gratuita vigente.",
          "paid": "Para corrigir, gere um prompt salvo a partir do relatório. Para repetir as verificações, compare os planos de monitoramento. As franquias são separadas.",
          "limits": "A análise usa o HTML entregue. Desempenho e acessibilidade pedem teste no navegador. Confira cortes e evidências indisponíveis.",
          "alternative": "Sua auditoria existente já fornece a mesma evidência.",
          "start": {
            "href": "/pt/",
            "label": "Auditar página"
          }
        }
      },
      "returns": "{ id, score, issues[{severity,code,message}], summary{finalUrl,status,contentType,title,metaDescription,canonical,robots,viewport,charset,lang,favicon,faviconSource,h1s,openGraph,twitter,jsonLdCount,jsonLdBlocks,jsonLdErrors?,jsonLdDropped,jsonLdTypes,images,links,words,hreflang,legacy,redirects,robotsTxt,sitemap,discovery?,security?,performance?,xRobotsTag?,canonicalHeader?,timing?,headings?,scripts?,crux?,lab?}, counts{errors,warnings,info,categories?}, headers, htmlTruncated, jsonLd, jsonLdStored, truncated, comparison?{baseline,score,from,to,broke,fixed,regression}, quota{free_per_day,price_usd}, _links{self?,prompts,share?,api_index} }",
      "url": "https://pageaudit.online/api/audit",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/audits/:id",
      "auth": "guest",
      "summary": "Re-reads an audit already made, in full, without re-auditing the page.",
      "grupo": "Audit",
      "desc": "Re-reading costs nothing and does not count against the allowance — the page's HTML is not kept, but the report is.",
      "params": {
        "id": {
          "desc": "Audit ID, from `Audit.id`.",
          "exemplo": "9f3c2b1d-7a4e-4c58-9b02-1f6d5e8a2c34"
        }
      },
      "retorno": "AuditGravado",
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s $ORIGIN/api/audits/AUDIT_ID -H \"X-Guest-Token: $PA\"",
      "returns": "{ audit_id, url, score, summary{finalUrl,status,contentType,title,metaDescription,canonical,robots,viewport,charset,lang,favicon,faviconSource,h1s,openGraph,twitter,jsonLdCount,jsonLdBlocks,jsonLdErrors?,jsonLdDropped,jsonLdTypes,images,links,words,hreflang,legacy,redirects,robotsTxt,sitemap,discovery?,security?,performance?,xRobotsTag?,canonicalHeader?,timing?,headings?,scripts?,crux?,lab?}, counts{errors,warnings,info,categories?}, issues[{severity,code,message}], jsonLd, headers, truncated, share_slug, created_at }",
      "url": "https://pageaudit.online/api/audits/:id",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/audits/:id/patch",
      "auth": "guest",
      "summary": "Retired correction endpoint. Use POST /api/audits/:id/prompts.",
      "grupo": "Audit",
      "desc": "Returns 410 with links to prompt generation and the current allowance. Reading a report returns findings and evidence; correction instructions require prompt generation.",
      "params": {
        "id": {
          "desc": "Audit ID, from `Audit.id`.",
          "exemplo": "9f3c2b1d-7a4e-4c58-9b02-1f6d5e8a2c34"
        }
      },
      "retorno": {
        "_texto": "410 JSON: code=use_fix_prompt and _links.prompts, quota, api_index."
      },
      "erros": [
        401,
        404,
        410
      ],
      "exemplo": "curl -s $ORIGIN/api/audits/AUDIT_ID/patch -H \"X-Guest-Token: $PA\"",
      "returns": "410 JSON: code=use_fix_prompt and _links.prompts, quota, api_index.",
      "url": "https://pageaudit.online/api/audits/:id/patch",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/tools",
      "auth": "none",
      "grupo": "Micro-tools",
      "summary": "List the 21 tools and their content in English or Portuguese.",
      "query": {
        "language": {
          "desc": "Content language: pt (also pt-BR) for Portuguese; any other value or none returns English.",
          "valores": [
            "en",
            "pt"
          ]
        }
      },
      "retorno": {
        "tools": {
          "tipo": "Ferramenta[]",
          "desc": "All tools from the same audit engine."
        },
        "_links": {
          "tipo": "object",
          "desc": "Language-specific HTML hub and API links."
        }
      },
      "exemplo": "curl -s \"$ORIGIN/api/tools?language=pt\"",
      "returns": "{ tools[{slug,title,h1,description,lead,category,icon,checks,serp,site,sections,example,faq,_links}], _links }",
      "url": "https://pageaudit.online/api/tools",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/tools/:slug",
      "auth": "none",
      "grupo": "Micro-tools",
      "summary": "Read one tool's metadata, content, examples and questions.",
      "params": {
        "slug": {
          "desc": "Stable tool slug, for example title-tag-checker."
        }
      },
      "query": {
        "language": {
          "desc": "Content language: pt (also pt-BR) for Portuguese; any other value or none returns English.",
          "valores": [
            "en",
            "pt"
          ]
        }
      },
      "retorno": "Ferramenta",
      "erros": {
        "404": "Unknown slug."
      },
      "exemplo": "curl -s \"$ORIGIN/api/tools/title-tag-checker?language=pt\"",
      "returns": "{ slug, title, h1, description, lead, category, icon, checks, serp, site, sections, example, faq, _links }",
      "url": "https://pageaudit.online/api/tools/:slug",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/tools",
      "auth": "none",
      "grupo": "Micro-tools",
      "summary": "English HTML hub of the 21 tools.",
      "retorno": {
        "_texto": "`text/html` — server-rendered tool hub."
      },
      "returns": "`text/html` — server-rendered tool hub.",
      "url": "https://pageaudit.online/tools",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/tools/:slug",
      "auth": "none",
      "grupo": "Micro-tools",
      "summary": "English HTML page for one tool. Unknown slugs return 404.",
      "params": {
        "slug": {
          "desc": "Stable tool slug, for example title-tag-checker."
        }
      },
      "retorno": {
        "_texto": "`text/html` — server-rendered tool page."
      },
      "erros": {
        "404": "Unknown slug."
      },
      "returns": "`text/html` — server-rendered tool page.",
      "url": "https://pageaudit.online/tools/:slug",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/pt/tools",
      "auth": "none",
      "grupo": "Micro-tools",
      "summary": "Portuguese HTML hub of the same 21 tools.",
      "retorno": {
        "_texto": "`text/html` — Portuguese hub with its own canonical and language alternatives."
      },
      "returns": "`text/html` — Portuguese hub with its own canonical and language alternatives.",
      "url": "https://pageaudit.online/pt/tools",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/pt/tools/:slug",
      "auth": "none",
      "grupo": "Micro-tools",
      "summary": "Portuguese HTML page for one tool. Uses the same audit API and limits.",
      "params": {
        "slug": {
          "desc": "Stable tool slug, for example title-tag-checker."
        }
      },
      "retorno": {
        "_texto": "`text/html` — Portuguese page, examples, questions and audit form."
      },
      "erros": {
        "404": "Unknown slug."
      },
      "returns": "`text/html` — Portuguese page, examples, questions and audit form.",
      "url": "https://pageaudit.online/pt/tools/:slug",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/audits/:id/share",
      "auth": "guest",
      "summary": "Publishes the audit under a non-enumerable slug. Calling again returns the same slug.",
      "grupo": "Share",
      "params": {
        "id": {
          "desc": "ID of the audit to publish.",
          "exemplo": "9f3c2b1d-7a4e-4c58-9b02-1f6d5e8a2c34"
        }
      },
      "retorno": "Compartilhamento",
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s -XPOST $ORIGIN/api/audits/AUDIT_ID/share -H \"X-Guest-Token: $PA\"",
      "returns": "{ ok, shared, slug, path, badge, _links }",
      "url": "https://pageaudit.online/api/audits/:id/share",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "DELETE",
      "path": "/api/audits/:id/share",
      "auth": "guest",
      "summary": "Revokes the share; the slug stops serving the report.",
      "grupo": "Share",
      "desc": "The badge keeps answering 200 with the grey `n/a` SVG — same box, so the layout of whoever pasted it in a README does not break.",
      "params": {
        "id": {
          "desc": "ID of the audit to unpublish.",
          "exemplo": "9f3c2b1d-7a4e-4c58-9b02-1f6d5e8a2c34"
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "shared": {
          "tipo": "bool",
          "desc": "Always `false` at the end of this call."
        }
      },
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s -XDELETE $ORIGIN/api/audits/AUDIT_ID/share -H \"X-Guest-Token: $PA\"",
      "returns": "{ ok, shared }",
      "url": "https://pageaudit.online/api/audits/:id/share",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/shared/:slug",
      "auth": "none",
      "summary": "Shared report as JSON, without credentials — the machine twin of `/r/:slug`.",
      "grupo": "Share",
      "params": {
        "slug": {
          "desc": "Public slug of the share.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": "AuditGravado",
      "erros": {
        "404": "Unknown slug, or share revoked."
      },
      "exemplo": "curl -s $ORIGIN/api/shared/SLUG",
      "returns": "{ audit_id, url, score, summary{finalUrl,status,contentType,title,metaDescription,canonical,robots,viewport,charset,lang,favicon,faviconSource,h1s,openGraph,twitter,jsonLdCount,jsonLdBlocks,jsonLdErrors?,jsonLdDropped,jsonLdTypes,images,links,words,hreflang,legacy,redirects,robotsTxt,sitemap,discovery?,security?,performance?,xRobotsTag?,canonicalHeader?,timing?,headings?,scripts?,crux?,lab?}, counts{errors,warnings,info,categories?}, issues[{severity,code,message}], jsonLd, headers, truncated, share_slug, created_at }",
      "url": "https://pageaudit.online/api/shared/:slug",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/r/:slug",
      "auth": "none",
      "summary": "HTML page of the shared report, indexable, canonical at `/r/:slug`.",
      "grupo": "Share",
      "desc": "A public report is content: indexable, with its own canonical. Revoked or unknown slugs answer 404 (that one is `noindex`). The JSON twin is `/api/shared/:slug`.",
      "params": {
        "slug": {
          "desc": "Public slug of the share.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": {
        "_texto": "`text/html` — the rendered report, with `rel=canonical`."
      },
      "erros": {
        "404": "Unknown or revoked slug."
      },
      "returns": "`text/html` — the rendered report, with `rel=canonical`.",
      "url": "https://pageaudit.online/r/:slug",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/badge/:slug",
      "auth": "none",
      "summary": "Metadata of the score badge, including the markdown ready for the README.",
      "grupo": "Share",
      "params": {
        "slug": {
          "desc": "Public slug of the share.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": "Badge",
      "erros": {
        "404": "Unknown slug. Revoked is different: it returns 200 with `available: false`."
      },
      "exemplo": "curl -s $ORIGIN/api/badge/SLUG",
      "returns": "{ slug, score, label, available, markdown, _links }",
      "url": "https://pageaudit.online/api/badge/:slug",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/badge/:slug.svg",
      "auth": "none",
      "summary": "SVG score badge, to paste in the README of the audited project.",
      "grupo": "Share",
      "desc": "A revoked share returns **200** with the grey `n/a` SVG — the same box, so the README of whoever pasted it does not end up with a broken image.",
      "params": {
        "slug": {
          "desc": "Public slug, with the `.svg` extension in the path.",
          "exemplo": "k7m2p9r4t6v8w1y3z5b7c9d1"
        }
      },
      "retorno": {
        "_texto": "`image/svg+xml` — the badge, coloured by the score or grey `n/a` if revoked."
      },
      "erros": {
        "404": "Malformed slug."
      },
      "returns": "`image/svg+xml` — the badge, coloured by the score or grey `n/a` if revoked.",
      "url": "https://pageaudit.online/badge/:slug.svg",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/embed",
      "auth": "none",
      "summary": "The embeddable widget's contract: what you can configure and the snippet to paste.",
      "grupo": "Embed",
      "desc": "One script tag puts the audit form on your own site, with your name and colour, and no account or key. Send the same query you would send to `/embed` and the answer comes back with `snippet` already assembled. The widget never asks the visitor for an e-mail.",
      "query": {
        "brand": {
          "desc": "Your site's name, shown above the form (up to 40 characters)."
        },
        "color": {
          "desc": "Accent colour in hex (`#0a7` or `#00aa77`); anything else falls back to ours."
        },
        "theme": {
          "desc": "`auto` (follows the visitor), `light` or `dark`."
        },
        "url": {
          "desc": "Optional http(s) URL to pre-fill in the field."
        }
      },
      "retorno": {
        "config": {
          "tipo": "object",
          "desc": "Each option, its type and what it does."
        },
        "current": {
          "tipo": "object",
          "desc": "The configuration read from this call's query."
        },
        "snippet": {
          "tipo": "string",
          "desc": "The `<script>` (plus a `<noscript>` link) ready to paste."
        },
        "limits": {
          "tipo": "object",
          "desc": "The allowance the widget runs under, and the lead capture it does not do."
        }
      },
      "exemplo": "curl -s \"$ORIGIN/api/embed?brand=Acme&color=%2300aa77\"",
      "returns": "{ config, current, snippet, limits }",
      "url": "https://pageaudit.online/api/embed",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/embed.js",
      "auth": "none",
      "summary": "The loader you paste: it creates the widget's iframe and keeps it the right height.",
      "grupo": "Embed",
      "desc": "Configuration travels in `data-brand`, `data-color`, `data-theme` and `data-url` on the script tag itself. Nothing else of ours touches the host page: the widget lives inside the iframe, so neither the site's CSS reaches it nor our code reaches the site's DOM.",
      "retorno": {
        "_texto": "`application/javascript` — the loader, cached for an hour."
      },
      "returns": "`application/javascript` — the loader, cached for an hour.",
      "url": "https://pageaudit.online/embed.js",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/embed",
      "auth": "none",
      "summary": "The widget page itself, made to be framed (`frame-ancestors *`) and not indexed.",
      "grupo": "Embed",
      "desc": "Open it directly to preview a configuration before pasting the snippet. It calls `POST /api/audit` like any visitor would, so the free daily allowance per network applies.",
      "retorno": {
        "_texto": "`text/html` — the widget, self-contained, with no third-party resource."
      },
      "returns": "`text/html` — the widget, self-contained, with no third-party resource.",
      "url": "https://pageaudit.online/embed",
      "auth_detail": "Public, no credential."
    },
    {
      "auth": "credito",
      "method": "GET",
      "path": "/api/x402/audit",
      "summary": "Audits one page for an agent, paid per call: no account, no allowance and no tab.",
      "grupo": "Audit",
      "desc": "The report of `POST /api/audit`, sold per call to agents. A request without payment gets the 402 with the price; a paid one gets the report in the same response, with the payment in `cobranca`. Nothing is stored: the report has no `id` and no tab. A page that does not open is not charged.",
      "query": {
        "url": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The page to audit, `http` or `https`.",
          "exemplo": "https://example.com/"
        }
      },
      "retorno": "AuditoriaAvulsa",
      "exemploRetorno": {
        "url": "https://example.com/",
        "score": 0,
        "issues": [
          {
            "severity": "warn",
            "code": "title_short",
            "message": "Title is short (14 chars)"
          },
          {
            "severity": "error",
            "code": "meta_description_missing",
            "message": "Missing meta description"
          }
        ],
        "summary": {
          "finalUrl": "https://example.com/",
          "status": 200,
          "contentType": "text/html; charset=utf-8",
          "title": "Example Domain",
          "metaDescription": null,
          "canonical": null,
          "robots": null,
          "viewport": false,
          "charset": "utf-8",
          "lang": null,
          "words": 27,
          "timing": {
            "responseMs": 20
          }
        },
        "counts": {
          "errors": 2,
          "warnings": 8,
          "info": 6
        },
        "headers": {
          "content-type": "text/html; charset=utf-8",
          "server": "cloudflare"
        },
        "htmlTruncated": false,
        "jsonLd": [],
        "jsonLdStored": 0,
        "truncated": [],
        "_links": {
          "api_index": "https://pageaudit.online/api/"
        }
      },
      "erros": {
        "400": "Paid without a valid `url` (`missing_input`, with `example`), or a redirect to an address that is not public. Not charged.",
        "401": "API key refused (`invalid_api_key`), OAuth token refused (`invalid_token`), credit refused (`credito_invalido`), test credit outside dev (`credito_simulado`), or recovery without wallet authentication (`wallet_proof_required`). The first x402 purchase needs no account.",
        "402": "No payment: `accepts` (x402, USDC on Base) with the price of this call, at least US$ 0.01; `credito.preco_desta_chamada`, the lower price with prepaid credit; and `pacotes` (the credit top-ups and how many calls each one pays). Credit without balance answers `saldo_insuficiente`.",
        "409": "Payment is in progress, the request differs from the original, or the amount was recovered as balance. Do not sign another authorization to retry the purchase.",
        "410": "The 24-hour response recovery period ended. The financial record remains; the same proof cannot cause another charge.",
        "413": "The page is larger than the audit reads. Not charged.",
        "422": "A redirect loop, too many redirects or an invalid `Location`. Not charged.",
        "502": "The target failed to answer, or a firewall or rate limit answered instead of the page (`code` `target_blocked` or `target_rate_limited`). Not charged."
      },
      "exemplo": "curl -s -i '$ORIGIN/api/x402/audit?url=https://example.com/'",
      "headers": {
        "authorization": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "`Bearer mmk_…` (API key) or `Bearer mmo_…` (OAuth): the paid call uses the account balance without another header. Without an account, use prepaid credit from `POST /api/credito` as `Bearer cred_…`."
        },
        "x-api-key": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "The mmk_… API key for this product. It identifies the account and authorizes its balance for the paid call."
        },
        "payment-signature": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "The signed x402 payment proof (base64): `PAYMENT-SIGNATURE` (v2) or `X-PAYMENT` (v1)."
        },
        "x-wallet-token": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "To recover an x402 response within 24 hours, repeat the same request and payment proof. Send the payer wallet's `mmw_…` credential here, issued through `/api/auth/wallet/agent/start` and `/api/auth/wallet/agent/token`. There is no new charge."
        }
      },
      "agent_value": {
        "idioma": "en",
        "en": {
          "when": "Do I need an audit paid per call without using the free allowance?",
          "result": "Findings, severity, page metadata, response headers, JSON-LD and explicit truncation in one report.",
          "replaces": "Writing and maintaining a page fetcher, HTML checks and a report schema. The audit rules run without an LLM.",
          "free": "Try POST /api/audit within its current allowance. Inspect a report before selecting the account-free paid route.",
          "paid": "The x402 route charges per audit through x402 or credit. It does not save a tab. Saved fix prompts and daily monitoring are separate operations; compare current plan inclusions.",
          "limits": "This is not a browser performance or accessibility test. A technical score does not prove ranking, revenue or the absence of security defects.",
          "alternative": "The free audit covers the task, you already have equivalent checks, or you need a browser-based measurement."
        },
        "pt": {
          "when": "Preciso pagar uma auditoria avulsa sem usar a franquia?",
          "result": "Achados, gravidade, metadados, cabeçalhos, JSON-LD e cortes explícitos em um relatório.",
          "replaces": "Criação e manutenção de coleta da página, verificações HTML e formato do relatório. As regras da auditoria rodam sem LLM.",
          "free": "Teste POST /api/audit na franquia vigente. Confira um relatório antes de escolher a rota paga sem conta.",
          "paid": "A rota x402 cobra por auditoria com x402 ou crédito. Ela não salva aba. Prompts de correção salvos e vigia diária são operações separadas; compare o que o plano vigente inclui.",
          "limits": "Não é teste de desempenho ou acessibilidade no navegador. A nota técnica não prova posição na busca, receita nem ausência de falhas de segurança.",
          "alternative": "A auditoria gratuita basta, você já tem verificações equivalentes ou precisa de medição no navegador."
        }
      },
      "returns": "{ url, score, issues[{severity,code,message}], summary{finalUrl,status,contentType,title,metaDescription,canonical,robots,viewport,charset,lang,favicon,faviconSource,h1s,openGraph,twitter,jsonLdCount,jsonLdBlocks,jsonLdErrors?,jsonLdDropped,jsonLdTypes,images,links,words,hreflang,legacy,redirects,robotsTxt,sitemap,discovery?,security?,performance?,xRobotsTag?,canonicalHeader?,timing?,headings?,scripts?,crux?,lab?}, counts{errors,warnings,info,categories?}, headers, htmlTruncated, jsonLd, jsonLdStored, truncated, _links, cobranca{via,preco_usd,repetido,saldo_usd?} }",
      "url": "https://pageaudit.online/api/x402/audit",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "GET",
      "path": "/api/prompts/quota",
      "auth": "guest",
      "grupo": "Agent prompts",
      "summary": "Read the remaining prompt allowance and current unit price.",
      "retorno": {
        "quota": {
          "tipo": "PromptQuota",
          "desc": "Private allowance. Reading it does not spend a unit."
        }
      },
      "erros": {
        "401": "Authentication required or session ended.",
        "403": "Cookie writes require the same Origin and X-CSRF-Token.",
        "503": "Prompt service unavailable. Preserve request_id when retrying."
      },
      "exemplo": "curl -s \"$ORIGIN/api/prompts/quota\" -H \"X-Guest-Token: $PA\"",
      "returns": "{ quota{free,plan,unit_price_usd,full_units,plans_url,credit_url} }",
      "url": "https://pageaudit.online/api/prompts/quota",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "POST",
      "path": "/api/audits/:id/prompts",
      "auth": "guest",
      "grupo": "Agent prompts",
      "summary": "Generate a coding-agent prompt for one finding or the full audit.",
      "desc": "Read GET /api/prompts/quota before generation; the total depends on scope and existing rights. Three lifetime free units per account or anonymous network. A check uses one; full uses three. Extra units cost US$0.10 each. Active Starter, Pro and Agency plans include prompts while valid, within 60 requests/hour. No partial free debit: a full prompt needs three free units or is charged in full. The licensed prompt is saved for this owner and organization. Reopening the same selector is free even after plan expiry. Unverified checks are excluded: missing measurements are not purchasable correction evidence. Preserve its text and license; use it only for license.domain. Reuse the UUID v4 request_id and identical body after a delivery failure. Payment uses X-Credito or the x402 challenge. Never charge without the user's authorization.",
      "params": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Owned saved audit ID."
        }
      },
      "corpo": {
        "scope": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "check or full."
        },
        "check_id": {
          "tipo": "string",
          "desc": "ID from the report's checks; required for check, absent for full."
        },
        "language": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Language of the prompt instructions and check names: en for English, pt for Portuguese."
        },
        "request_id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "New UUID v4 per generation. Keep it unchanged for retries."
        }
      },
      "retorno": "AgentPrompt",
      "erros": {
        "400": "Invalid body.",
        "401": "Authentication required or session ended.",
        "402": "Allowance spent; current quote and payment options.",
        "403": "Cookie writes require the same Origin and X-CSRF-Token.",
        "404": "Audit not found for this owner.",
        "409": "No eligible observed findings. Passing and unverified checks do not generate a prompt or charge.",
        "413": "Request or prompt too large.",
        "422": "The request_id was already used with different inputs.",
        "429": "Hourly ceiling reached.",
        "503": "Prompt service unavailable. Preserve request_id when retrying."
      },
      "exemplo": "curl -s \"$ORIGIN/api/audits/$AUDIT/prompts\" -H \"X-Guest-Token: $PA\" -H \"Content-Type: application/json\" -d '{\"scope\":\"full\",\"language\":\"en\",\"request_id\":\"bbff3661-444a-42ce-ae53-6c25715a03f7\"}'",
      "agent_value": {
        "idioma": "en",
        "en": {
          "when": "How do I turn this report into instructions for my coding agent?",
          "result": "Get a saved fix prompt with selected findings, observed field values, suggested changes and a before/after audit request.",
          "replaces": "Use the assembled instructions instead of copying findings and building a prompt yourself. No model call is needed to generate it.",
          "free": "The initial allowance has three units: one for a finding or three for the full report.",
          "paid": "After that allowance, credit pays per unit; active plans can include prompts. Read the quote for the selected scope. Reopen a saved prompt without paying again.",
          "limits": "Review placeholders and proposed changes before applying them. Evidence comes from the saved audit, not the repository. Use verification with the same owner to compare fixed and new findings after the change. Free audits already include findings; the prompt does not fix the site for you.",
          "alternative": "The free findings already give your agent enough information to make the change."
        },
        "pt": {
          "when": "Como transformar o relatório em instruções para meu agente?",
          "result": "Receba um prompt salvo com achados selecionados, valores observados, mudanças sugeridas e a chamada de comparação antes/depois.",
          "replaces": "Use as instruções montadas sem copiar os achados e preparar o prompt. A geração não chama um modelo.",
          "free": "A franquia inicial tem três unidades: uma por achado ou três pelo relatório completo.",
          "paid": "Depois, o crédito paga por unidade; planos ativos podem incluir prompts. Confira a cotação do escopo. Reabrir o prompt salvo não cobra de novo.",
          "limits": "Revise os campos a preencher e as mudanças antes de aplicar. A evidência vem do relatório salvo, não do repositório. Use verification com o mesmo dono para comparar achados resolvidos e novos. A auditoria gratuita já inclui achados; o prompt não corrige o site sozinho.",
          "alternative": "Os achados gratuitos já dão ao agente o contexto necessário."
        }
      },
      "returns": "{ id, saved, license, selector, created_at, prompt, audit_id, source, resource_id, report_id, check_ids, request_id, usage, quota{free,plan,unit_price_usd,full_units,plans_url,credit_url}, _links }",
      "url": "https://pageaudit.online/api/audits/:id/prompts",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "POST",
      "path": "/api/tabs/:id/prompts",
      "auth": "guest",
      "grupo": "Agent prompts",
      "summary": "Generate a fix prompt from saved domain observations.",
      "desc": "Read GET /api/prompts/quota before generation; the total depends on scope and existing rights. Three lifetime free units per account or anonymous network. A check uses one; full uses three. Extra units cost US$0.10 each. Active Starter, Pro and Agency plans include prompts while valid, within 60 requests/hour. No partial free debit: a full prompt needs three free units or is charged in full. The licensed prompt is saved for this owner and organization. Reopening the same selector is free even after plan expiry. Unverified checks are excluded: missing measurements are not purchasable correction evidence. Preserve its text and license; use it only for license.domain. Reuse the UUID v4 request_id and identical body after a delivery failure. Payment uses X-Credito or the x402 challenge. Never charge without the user's authorization. Uses saved observations only; does not start a new collection. Missing evidence is not charged.",
      "params": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Owned tab in the active organization ID."
        }
      },
      "corpo": {
        "scope": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "check or full."
        },
        "check_id": {
          "tipo": "string",
          "desc": "registration, certificate, dns or email. Required for check; absent for full."
        },
        "language": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Language of the prompt instructions and check names: en for English, pt for Portuguese."
        },
        "request_id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "New UUID v4 per generation. Keep it unchanged for retries."
        },
        "source": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "domain: the saved registration, certificate, DNS and email observations of the tab."
        }
      },
      "retorno": "AgentPrompt",
      "erros": {
        "400": "Invalid body.",
        "401": "Authentication required or session ended.",
        "402": "Allowance spent; current quote and payment options.",
        "403": "Cookie writes require the same Origin and X-CSRF-Token.",
        "404": "Owned tab, site, report or enabled source not found.",
        "409": "No eligible observed findings. Passing and unverified checks do not generate a prompt or charge.",
        "413": "Request or prompt too large.",
        "422": "The request_id was already used with different inputs.",
        "429": "Hourly ceiling reached.",
        "503": "Prompt service unavailable. Preserve request_id when retrying."
      },
      "exemplo": "curl -s \"$ORIGIN/api/tabs/$TAB/prompts\" -H \"X-Guest-Token: $PA\" -H \"Content-Type: application/json\" -d '{\"source\":\"domain\",\"scope\":\"check\",\"check_id\":\"dns\",\"language\":\"en\",\"request_id\":\"bbff3661-444a-42ce-ae53-6c25715a03f7\"}'",
      "returns": "{ id, saved, license, selector, created_at, prompt, audit_id, source, resource_id, report_id, check_ids, request_id, usage, quota{free,plan,unit_price_usd,full_units,plans_url,credit_url}, _links }",
      "url": "https://pageaudit.online/api/tabs/:id/prompts",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "POST",
      "path": "/api/sites/:id/prompts",
      "auth": "session",
      "grupo": "Agent prompts",
      "summary": "Generate a fix prompt for a weekly finding or a Search Console opportunity.",
      "desc": "Read GET /api/prompts/quota before generation; the total depends on scope and existing rights. Three lifetime free units per account or anonymous network. A check uses one; full uses three. Extra units cost US$0.10 each. Active Starter, Pro and Agency plans include prompts while valid, within 60 requests/hour. No partial free debit: a full prompt needs three free units or is charged in full. The licensed prompt is saved for this owner and organization. Reopening the same selector is free even after plan expiry. Unverified checks are excluded: missing measurements are not purchasable correction evidence. Preserve its text and license; use it only for license.domain. Reuse the UUID v4 request_id and identical body after a delivery failure. Payment uses X-Credito or the x402 challenge. Never charge without the user's authorization. Uses saved observations only; does not start a new collection. Missing evidence is not charged.",
      "params": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Owned site ID."
        }
      },
      "corpo": {
        "scope": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "check or full."
        },
        "check_id": {
          "tipo": "string",
          "desc": "Weekly finding type, or opportunity for Search Console. Required for check; absent for full."
        },
        "language": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Language of the prompt instructions and check names: en for English, pt for Portuguese."
        },
        "request_id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "New UUID v4 per generation. Keep it unchanged for retries."
        },
        "source": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "weekly: a finding of a saved weekly report. search_console: a saved Search Console opportunity."
        },
        "report_id": {
          "tipo": "string",
          "desc": "Required for weekly: saved report ID from GET /api/sites/:id/weekly-audit."
        },
        "query": {
          "tipo": "string",
          "desc": "Required for an individual Search Console opportunity: exact query from GET /api/sites/:id/search-console opportunities. Maximum 1024 characters."
        }
      },
      "retorno": "AgentPrompt",
      "erros": {
        "400": "Invalid body.",
        "401": "Authentication required or session ended.",
        "402": "Allowance spent; current quote and payment options.",
        "403": "Cookie writes require the same Origin and X-CSRF-Token.",
        "404": "Owned tab, site, report or enabled source not found.",
        "409": "No eligible observed findings. Passing and unverified checks do not generate a prompt or charge.",
        "413": "Request or prompt too large.",
        "422": "The request_id was already used with different inputs.",
        "429": "Hourly ceiling reached.",
        "503": "Prompt service unavailable. Preserve request_id when retrying."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/$SITE/prompts\", {method:\"POST\", headers:{\"Content-Type\":\"application/json\", \"X-CSRF-Token\":csrf}, body:JSON.stringify({source:\"search_console\", scope:\"check\", check_id:\"opportunity\", query:\"site audit\", language:\"en\", request_id:crypto.randomUUID()})});",
      "exemploLinguagem": "js",
      "returns": "{ id, saved, license, selector, created_at, prompt, audit_id, source, resource_id, report_id, check_ids, request_id, usage, quota{free,plan,unit_price_usd,full_units,plans_url,credit_url}, _links }",
      "url": "https://pageaudit.online/api/sites/:id/prompts",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/prompts/:id",
      "auth": "guest",
      "grupo": "Agent prompts",
      "summary": "Read your saved, licensed prompt without another charge.",
      "desc": "Uses the authenticated owner and active organization. Returns the immutable text. A missing or foreign prompt returns 404. Does not consume allowance, credit or require an active plan.",
      "params": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Saved prompt ID from generation or the private list."
        }
      },
      "retorno": "AgentPrompt",
      "erros": {
        "401": "Authentication required.",
        "404": "Owned prompt not found.",
        "503": "Storage unavailable."
      },
      "exemplo": "curl -s \"$ORIGIN/api/prompts/$PROMPT_ID\" -H \"X-Guest-Token: $PA\"",
      "returns": "{ id, saved, license, selector, created_at, prompt, audit_id, source, resource_id, report_id, check_ids, request_id, usage, quota{free,plan,unit_price_usd,full_units,plans_url,credit_url}, _links }",
      "url": "https://pageaudit.online/api/prompts/:id",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/audits/:id/prompts",
      "auth": "guest",
      "grupo": "Agent prompts",
      "summary": "List the saved prompts of an audit report, without generating or paying.",
      "exemplo": "curl -s \"$ORIGIN/api/audits/$AUDIT/prompts?language=en\" -H \"X-Guest-Token: $PA\"",
      "erros": {
        "400": "Invalid language, source, report_id or cursor.",
        "401": "Authentication required.",
        "404": "Owned audit, tab, site or report not found.",
        "503": "Storage unavailable."
      },
      "desc": "At most 100 saved rows per page, plus up to three earlier free audit receipts. Follow _links.next. A legacy receipt has id=null and legacy=true; the matching generation recovers it without a debit. The result belongs to the authenticated owner and active organization.",
      "params": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Owned source ID."
        }
      },
      "query": {
        "language": {
          "tipo": "string",
          "desc": "Prompt language: en (default) for English or pt for Portuguese."
        },
        "after": {
          "tipo": "string",
          "desc": "Cursor from _links.next of the preceding page."
        }
      },
      "retorno": {
        "items": {
          "tipo": "object[]",
          "desc": "id, selector, license.domain and created_at. Text is read separately."
        },
        "next": {
          "tipo": "string",
          "nulo": true,
          "desc": "Next cursor or null."
        },
        "_links": {
          "tipo": "object",
          "desc": "Absolute next URL or null."
        }
      },
      "returns": "{ items, next, _links }",
      "url": "https://pageaudit.online/api/audits/:id/prompts",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/tabs/:id/prompts",
      "auth": "guest",
      "grupo": "Agent prompts",
      "summary": "List the saved domain prompts of a tab, without generating or paying.",
      "exemplo": "curl -s \"$ORIGIN/api/tabs/$TAB/prompts?source=domain\" -H \"X-Guest-Token: $PA\"",
      "erros": {
        "400": "Invalid language, source, report_id or cursor.",
        "401": "Authentication required.",
        "404": "Owned audit, tab, site or report not found.",
        "503": "Storage unavailable."
      },
      "desc": "At most 100 saved rows per page, plus up to three earlier free audit receipts. Follow _links.next. A legacy receipt has id=null and legacy=true; the matching generation recovers it without a debit. The result belongs to the authenticated owner and active organization.",
      "params": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Owned source ID."
        }
      },
      "query": {
        "language": {
          "tipo": "string",
          "desc": "Prompt language: en (default) for English or pt for Portuguese."
        },
        "after": {
          "tipo": "string",
          "desc": "Cursor from _links.next of the preceding page."
        },
        "source": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "domain: the saved domain observations of the tab."
        }
      },
      "retorno": {
        "items": {
          "tipo": "object[]",
          "desc": "id, selector, license.domain and created_at. Text is read separately."
        },
        "next": {
          "tipo": "string",
          "nulo": true,
          "desc": "Next cursor or null."
        },
        "_links": {
          "tipo": "object",
          "desc": "Absolute next URL or null."
        }
      },
      "returns": "{ items, next, _links }",
      "url": "https://pageaudit.online/api/tabs/:id/prompts",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/sites/:id/prompts",
      "auth": "session",
      "grupo": "Agent prompts",
      "summary": "List the saved weekly or Search Console prompts of a site, without generating or paying.",
      "exemplo": "await fetch(\"$ORIGIN/api/sites/$SITE/prompts?source=search_console\").then(r => r.json());",
      "exemploLinguagem": "js",
      "erros": {
        "400": "Invalid language, source, report_id or cursor.",
        "401": "Authentication required.",
        "404": "Owned audit, tab, site or report not found.",
        "503": "Storage unavailable."
      },
      "desc": "At most 100 saved rows per page, plus up to three earlier free audit receipts. Follow _links.next. A legacy receipt has id=null and legacy=true; the matching generation recovers it without a debit. The result belongs to the authenticated owner and active organization.",
      "params": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Owned source ID."
        }
      },
      "query": {
        "language": {
          "tipo": "string",
          "desc": "Prompt language: en (default) for English or pt for Portuguese."
        },
        "after": {
          "tipo": "string",
          "desc": "Cursor from _links.next of the preceding page."
        },
        "source": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "weekly: a saved weekly report. search_console: saved Search Console opportunities."
        },
        "report_id": {
          "tipo": "string",
          "desc": "Saved weekly report ID. Required for weekly."
        }
      },
      "retorno": {
        "items": {
          "tipo": "object[]",
          "desc": "id, selector, license.domain and created_at. Text is read separately."
        },
        "next": {
          "tipo": "string",
          "nulo": true,
          "desc": "Next cursor or null."
        },
        "_links": {
          "tipo": "object",
          "desc": "Absolute next URL or null."
        }
      },
      "returns": "{ items, next, _links }",
      "url": "https://pageaudit.online/api/sites/:id/prompts",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/site-audit",
      "auth": "none",
      "summary": "Checks the first 20 URLs of a site's sitemap and returns what keeps them out of search results. Free, without an account.",
      "grupo": "Audit",
      "desc": "The free sample of the SEO audit. It reads `/sitemap.xml` (an index opens up to 5 sitemaps), checks the first 20 URLs of the same host and reports: not 200, redirects (to the home page or to a broken page), robots.txt blocks, `noindex`, a missing or foreign canonical, HTML without content, a title that is missing, generic or outside 40–60 characters, a missing description, repeated titles and descriptions, and the same plan with different prices (pages, JSON-LD and llms.txt). URLs of another host are counted, never requested. The same site within one hour returns the stored sample (`cached_at`) without requesting the site again and without spending the allowance. A new sample spends one audit of the daily allowance; past it, **402 with `accepts[]`**, as in `POST /api/audit`.",
      "corpo": {
        "url": {
          "tipo": "string",
          "desc": "Any address of the site, `http` or `https`: the sample reads its origin.",
          "obrigatorio": true
        },
        "lang": {
          "tipo": "string",
          "desc": "`pt` for the findings in Portuguese; English by default."
        },
        "guest_token": {
          "tipo": "string",
          "desc": "Guest `pa_…`, optional."
        }
      },
      "body": {
        "url": "https://example.com/"
      },
      "retorno": "SiteAudit",
      "erros": {
        "400": "Body is not JSON, or `url` is missing or is not a public http(s) address (`invalid_url`).",
        "402": null,
        "422": "`sitemap_unavailable`: the site has no `/sitemap.xml` that answers 200; `detail` has the status.",
        "429": "Past 6 new samples per hour on the same network. Wait an hour."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/site-audit -H 'content-type: application/json' -d '{\"url\":\"https://example.com/\"}'",
      "returns": "{ origin, sitemap, urls_in_sitemap, urls_checked, urls_left_out, urls_other_host, score, by_severity, top[{type,severity,url,urls,evidence,problem}], findings[{type,severity,url,urls,evidence,problem}], cached_at }",
      "url": "https://pageaudit.online/api/site-audit",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/ai-access",
      "auth": "none",
      "summary": "Requests a page as each search and AI crawler and returns who gets in, the age of the sitemap and whether Cloudflare is in front. Free, without an account.",
      "grupo": "Audit",
      "desc": "The page is requested with the published user agent of Googlebot, Bingbot, OAI-SearchBot, Claude-SearchBot, PerplexityBot, ChatGPT-User, GPTBot, ClaudeBot, and once as a browser. A 403, a 429 or a firewall challenge that only a crawler gets is the block that robots.txt does not show. The sitemap is the one that robots.txt declares, or `/sitemap.xml`; it is stale when its newest `lastmod` is more than 30 days old. Limit: Cloudflare recognises the real crawler by its IP address, so a rule that trusts verified bots can treat the real crawler differently. The same page within one hour returns the stored result (`cached_at`) without requesting the site again.",
      "corpo": {
        "url": {
          "tipo": "string",
          "desc": "The page to request, `http` or `https`.",
          "obrigatorio": true
        }
      },
      "body": {
        "url": "https://example.com/"
      },
      "retorno": "AiAccess",
      "erros": {
        "400": "Body is not JSON, or `url` is missing or is not a public http(s) address (`invalid_url`).",
        "429": "Past 6 new checks per hour on the same network (`rate_limited`), or past 3 new checks of the same site in one hour, from any network (`target_limit_reached`). Wait an hour."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/ai-access -H 'content-type: application/json' -d '{\"url\":\"https://example.com/\"}'",
      "returns": "{ url, checked_at, cloudflare, browser, bots[{token,owner,use,status,result,vendor}], sitemap{url,status,urls,newest_lastmod,age_days,stale}, cached_at }",
      "url": "https://pageaudit.online/api/ai-access",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/audits/:id/lab",
      "auth": "guest",
      "summary": "Starts a lab test of the audited page: Lighthouse run by Google PageSpeed Insights (performance, accessibility, best practices, SEO).",
      "grupo": "Audit",
      "desc": "Answers **202** at once; the test runs for ~25 s to over 2 minutes and its summary is stored in the audit (`summary.lab.<strategy>`), so it comes back in `GET /api/audits/:id`, in the tab and in the shared report. It does not change the audit score. Calling again while the same strategy is running does not start a second test. Limits: 4 tests at a time on the server, 6 per network per hour and 500 per day for the whole product. Free: no x402 charge.",
      "params": {
        "id": {
          "desc": "Audit ID, from `Audit.id`.",
          "exemplo": "9f3c2b1d-7a4e-4c58-9b02-1f6d5e8a2c34"
        }
      },
      "corpo": {
        "strategy": {
          "tipo": "string",
          "desc": "`mobile` (default) or `desktop`."
        }
      },
      "body": {
        "strategy": "mobile"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` on 202."
        },
        "audit_id": {
          "tipo": "string",
          "desc": "The audit the test belongs to."
        },
        "strategy": {
          "tipo": "string",
          "desc": "`mobile` or `desktop`."
        },
        "lab": {
          "tipo": "LabTeste",
          "desc": "`status: running` and when it started."
        },
        "_links": {
          "tipo": "object",
          "desc": "`poll` (the GET with `wait`) and `audit`."
        }
      },
      "erros": {
        "400": "`strategy` is not `mobile` or `desktop`.",
        "401": null,
        "404": null,
        "429": "A limit was reached: `lab_busy` (4 running), `lab_rate_limited` (6 per network per hour) or `lab_daily_cap`.",
        "503": "`lab_unavailable`: the server has no PageSpeed key configured."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/audits/AUDIT_ID/lab -H \"X-Guest-Token: $PA\" -H 'content-type: application/json' -d '{\"strategy\":\"mobile\"}'",
      "returns": "{ ok, audit_id, strategy, lab{status,strategy?,startedAt?,fetchedAt?,lighthouseVersion?,scores?,metrics?,opportunities?,failing?,message?}, _links }",
      "url": "https://pageaudit.online/api/audits/:id/lab",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/audits/:id/lab",
      "auth": "guest",
      "summary": "The lab tests of an audit — running, finished or failed — waiting up to `wait` seconds for a running one to finish.",
      "grupo": "Audit",
      "desc": "Long-poll: with `wait` the answer comes as soon as a running test finishes, or after `wait` seconds with `status: running`. Repeat until it is not running.",
      "params": {
        "id": {
          "desc": "Audit ID, from `Audit.id`.",
          "exemplo": "9f3c2b1d-7a4e-4c58-9b02-1f6d5e8a2c34"
        }
      },
      "query": {
        "wait": {
          "desc": "Seconds to wait for a running test, 0 to 25. Default 0 (answer at once)."
        }
      },
      "retorno": {
        "audit_id": {
          "tipo": "string",
          "desc": "The audit."
        },
        "lab": {
          "tipo": "object",
          "desc": "`mobile` and `desktop`, each a `LabTeste` or `null` when never run."
        },
        "_links": {
          "tipo": "object",
          "desc": "`self` and `audit`."
        }
      },
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s \"$ORIGIN/api/audits/AUDIT_ID/lab?wait=25\" -H \"X-Guest-Token: $PA\"",
      "returns": "{ audit_id, lab, _links }",
      "url": "https://pageaudit.online/api/audits/:id/lab",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "POST",
      "path": "/api/github/app/webhook",
      "auth": "github_app",
      "summary": "The PageAudit GitHub App's webhook: a 'PageAudit' check run on the commit of each deploy preview.",
      "grupo": "GitHub",
      "desc": "Only GitHub calls it. A `deployment_status` with `success` (as Vercel, Netlify and Cloudflare Pages publish previews) opens the check run `PageAudit` on the commit and answers at once; the audits of the preview and of the repository's website, when set, run after the answer. The check compares them by finding code: `failure` when a new error-level finding appeared, `neutral` when only warnings or notes got worse, `success` otherwise. Each installation has a free number of checks per day; past it, the check says so.",
      "headers": {
        "X-Hub-Signature-256": {
          "tipo": "string",
          "desc": "`sha256=` HMAC of the raw body with the App's webhook secret.",
          "obrigatorio": true
        },
        "X-GitHub-Event": {
          "tipo": "string",
          "desc": "The event name; only `deployment_status` checks.",
          "obrigatorio": true
        }
      },
      "corpo": {
        "deployment_status": {
          "tipo": "object",
          "desc": "GitHub's payload: `state`, `environment_url` or `target_url`."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "`true` when the delivery was accepted."
        },
        "check_run": {
          "tipo": "int",
          "desc": "The check run opened on the commit.",
          "opcional": true
        },
        "ignored": {
          "tipo": "bool",
          "desc": "`true` when the event does not check anything.",
          "opcional": true
        }
      },
      "erros": {
        "400": "The body is not JSON.",
        "401": "`invalid_signature`: the HMAC does not match the App's secret.",
        "413": "The body is over 512 KB.",
        "502": "`check_run_refused`: GitHub did not open the check run.",
        "503": "`github_app_not_configured`: this environment has no App credentials."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/github/app/webhook -H 'x-github-event: ping' -H \"x-hub-signature-256: sha256=$HMAC\" -d '{}'   # GitHub sends it; a signed ping answers 200",
      "returns": "{ ok, check_run?, ignored? }",
      "url": "https://pageaudit.online/api/github/app/webhook",
      "auth_detail": "Only GitHub: the delivery is signed with the PageAudit GitHub App's webhook secret in `X-Hub-Signature-256`."
    },
    {
      "method": "GET",
      "path": "/api/google-import",
      "auth": "organization",
      "grupo": "Google import",
      "summary": "List the Search Console sites and GA4 properties the connected Google account can read.",
      "desc": "Returns resource metadata only. Tokens never leave the server. Search Console keeps one preferred property per website. Analytics properties stay grouped by account.",
      "retorno": {
        "search_console": {
          "tipo": "object",
          "desc": "Connection state, website count and authorized website properties."
        },
        "analytics": {
          "tipo": "object",
          "desc": "Connection state, property count and GA4 accounts with their properties."
        }
      },
      "erros": {
        "401": "account_required or session_ended",
        "403": "invalid_csrf or organization access denied",
        "429": "google_import_rate_limited",
        "503": "google_import_not_configured or google_import_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/google-import\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ search_console, analytics }",
      "url": "https://pageaudit.online/api/google-import",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/google-import/search-console/connect",
      "auth": "organization",
      "grupo": "Google import",
      "summary": "Start read-only Search Console authorization for the import assistant.",
      "corpo": {
        "language": {
          "tipo": "string",
          "desc": "Language of the Google return screen and PageAudit result: `en` or `pt`.",
          "obrigatorio": true
        },
        "return_host": {
          "tipo": "string",
          "desc": "Optional website to keep in context after the popup closes.",
          "nulo": true,
          "obrigatorio": true
        }
      },
      "body": {
        "language": "en",
        "return_host": "example.com"
      },
      "retorno": {
        "redirect": {
          "tipo": "string",
          "desc": "Google consent URL."
        }
      },
      "erros": {
        "401": "account_required or session_ended",
        "403": "invalid_csrf or organization access denied",
        "429": "google_import_rate_limited",
        "503": "google_import_not_configured or google_import_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/google-import/search-console/connect\",{method:\"POST\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf},body:JSON.stringify({language:\"en\",return_host:\"example.com\"})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ redirect }",
      "url": "https://pageaudit.online/api/google-import/search-console/connect",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/google-import/analytics/connect",
      "auth": "organization",
      "grupo": "Google import",
      "summary": "Start incremental read-only Google Analytics authorization.",
      "desc": "Requests analytics.readonly only when the person opens the Analytics tab and chooses to connect it.",
      "corpo": {
        "language": {
          "tipo": "string",
          "desc": "Language of the Google return screen and PageAudit result: `en` or `pt`.",
          "obrigatorio": true
        },
        "return_host": {
          "tipo": "string",
          "desc": "Optional website to keep in context after the popup closes.",
          "nulo": true,
          "obrigatorio": true
        }
      },
      "body": {
        "language": "en",
        "return_host": null
      },
      "retorno": {
        "redirect": {
          "tipo": "string",
          "desc": "Google consent URL."
        }
      },
      "erros": {
        "401": "account_required or session_ended",
        "403": "invalid_csrf or organization access denied",
        "429": "google_import_rate_limited",
        "503": "google_import_not_configured or google_import_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/google-import/analytics/connect\",{method:\"POST\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf},body:JSON.stringify({language:\"en\",return_host:null})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ redirect }",
      "url": "https://pageaudit.online/api/google-import/analytics/connect",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/google-import",
      "auth": "organization",
      "grupo": "Google import",
      "summary": "Import selected Search Console sites and GA4 web streams into the active organization.",
      "desc": "The server re-reads the authorized catalog before writing. Import creates missing projects and queues their first audit. Existing projects receive the selected connections. Importing is free; later monitoring and intelligence use their own entitlement checks.",
      "corpo": {
        "search_console": {
          "tipo": "object",
          "desc": "{all, properties}; properties contains exact Search Console resource IDs.",
          "obrigatorio": true
        },
        "analytics": {
          "tipo": "object",
          "desc": "{all, properties}; properties contains GA4 resource names such as properties/123.",
          "obrigatorio": true
        }
      },
      "body": {
        "search_console": {
          "all": true,
          "properties": []
        },
        "analytics": {
          "all": false,
          "properties": [
            "properties/123"
          ]
        }
      },
      "retorno": {
        "imported_projects": {
          "tipo": "int",
          "desc": "New website projects created."
        },
        "linked_search_console": {
          "tipo": "int",
          "desc": "Website Search Console connections saved."
        },
        "linked_analytics_streams": {
          "tipo": "int",
          "desc": "GA4 web streams saved."
        },
        "analytics_properties": {
          "tipo": "int",
          "desc": "Selected GA4 properties read."
        },
        "websites": {
          "tipo": "string[]",
          "desc": "Website domains represented by the import."
        }
      },
      "erros": {
        "400": "invalid_google_import, google_import_empty, google_import_changed or google_import_too_large",
        "401": "account_required or session_ended",
        "403": "invalid_csrf or organization access denied",
        "409": "gsc_connection_required, ga4_connection_required, google_import_no_websites or site_limit",
        "429": "google_import_rate_limited",
        "503": "google_import_not_configured or google_import_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/google-import\",{method:\"POST\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf,\"x-organization-id\":organizationId},body:JSON.stringify({search_console:{all:true,properties:[]},analytics:{all:false,properties:[\"properties/123\"]}})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ imported_projects, linked_search_console, linked_analytics_streams, analytics_properties, websites }",
      "url": "https://pageaudit.online/api/google-import",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "DELETE",
      "path": "/api/google-import/link",
      "auth": "organization",
      "grupo": "Google import",
      "summary": "Unlink one Search Console or Analytics property from PageAudit.",
      "desc": "Keeps the project, imported metadata, samples, daily series and alert events. It pauses only the selected PageAudit connection.",
      "corpo": {
        "service": {
          "tipo": "string",
          "desc": "search_console or analytics.",
          "obrigatorio": true
        },
        "property": {
          "tipo": "string",
          "desc": "The exact Search Console property or GA4 property resource name.",
          "obrigatorio": true
        }
      },
      "body": {
        "service": "analytics",
        "property": "properties/123"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always true after the write."
        },
        "disconnected": {
          "tipo": "int",
          "desc": "Website connections paused or cleared."
        }
      },
      "erros": {
        "400": "invalid_google_import",
        "401": "account_required or session_ended",
        "403": "invalid_csrf or organization access denied",
        "429": "google_import_rate_limited",
        "503": "google_import_not_configured or google_import_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/google-import/link\",{method:\"DELETE\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf,\"x-organization-id\":organizationId},body:JSON.stringify({service:\"analytics\",property:\"properties/123\"})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ ok, disconnected }",
      "url": "https://pageaudit.online/api/google-import/link",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "GET",
      "path": "/api/sites",
      "auth": "session",
      "grupo": "Sites",
      "summary": "Read up to 50 sites owned by the account.",
      "query": {
        "cursor": {
          "tipo": "string",
          "desc": "next_cursor from the preceding page."
        },
        "host": {
          "tipo": "string",
          "desc": "Exact lowercase domain without www. Returns at most one owned site, Search Console, Analytics and site-package state in the same response. Does not create a site."
        }
      },
      "retorno": "ListaSites",
      "erros": {
        "400": "invalid_site_host or invalid_cursor",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "503": "sites_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ sites[{id,host,url,created_at,_links}], next_cursor, search_console?{available,connected,property,last_attempt_at,last_success_at,next_run_at,error,stale,snapshot,opportunities,terms}, analytics?{available,ready,connected,stale,last_success_at,next_run_at,properties,series,alerts}, site_package?{active,checkout_available}, weekly_audit? }",
      "url": "https://pageaudit.online/api/sites",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/sites",
      "auth": "session",
      "grupo": "Sites",
      "summary": "Add a site or return the same site for an existing domain.",
      "desc": "An account holds up to 1,000 sites (409 site_limit). Adding a site creates no subscription or payment. The existing guest audit workspace stays separate.",
      "corpo": {
        "url": {
          "tipo": "string",
          "desc": "Public HTTP(S) URL, without credentials.",
          "obrigatorio": true
        }
      },
      "body": {
        "url": "https://example.com/"
      },
      "retorno": {
        "site": {
          "tipo": "Site",
          "desc": "The account's site."
        }
      },
      "erros": {
        "400": "invalid_site_url or invalid_body",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "409": "site_limit",
        "503": "sites_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites\", {method:\"POST\", headers:{\"Content-Type\":\"application/json\", \"X-CSRF-Token\":csrf}, body:JSON.stringify({url:\"https://example.com/\"})});",
      "exemploLinguagem": "js",
      "returns": "{ site{id,host,url,created_at,_links} }",
      "url": "https://pageaudit.online/api/sites",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/sites/:id",
      "auth": "session",
      "grupo": "Sites",
      "summary": "Read one site owned by the account.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "retorno": {
        "site": {
          "tipo": "Site",
          "desc": "The account's site."
        }
      },
      "erros": {
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "503": "sites_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ site{id,host,url,created_at,_links} }",
      "url": "https://pageaudit.online/api/sites/:id",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/sites/:id/search-console",
      "auth": "session",
      "grupo": "Search Console",
      "summary": "Read the connection, last good sample, dates, failure and possible opportunities.",
      "desc": "The sample covers 28 days, at most 50 queries and 50 pages. Use queries for keyword research and opportunities for average positions 4–20. The terms array adds daily history and 7-day comparisons for up to 20 selected queries. Coverage is partial: impressions are for this property, not market search volume. Positions are impression-weighted averages, not fixed SERP ranks. Credentials and raw provider responses are not returned.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "retorno": "SearchConsole",
      "erros": {
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "503": "sites_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/search-console\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ available, connected, property, last_attempt_at, last_success_at, next_run_at, error, stale, snapshot{property,period,checked_at,queries,pages,coverage}, opportunities[{query,clicks,impressions,ctr,position}], terms[{query,last_alert_at,analysis,series}] }",
      "url": "https://pageaudit.online/api/sites/:id/search-console",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/sites/:id/search-console/connect",
      "auth": "session",
      "grupo": "Search Console",
      "summary": "Start Google consent for one read-only property on this site.",
      "desc": "Follow redirect in the account's browser. Consent expires after five minutes. The callback is single-use and bound to the account and site. The property must match the site and Google must authorize it before the connection is saved.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "corpo": {
        "property": {
          "tipo": "string",
          "desc": "Exact Search Console domain or URL-prefix property.",
          "obrigatorio": true
        },
        "language": {
          "tipo": "string",
          "desc": "en (default) or pt. Browser consent returns in this language."
        }
      },
      "body": {
        "property": "sc-domain:example.com"
      },
      "retorno": {
        "redirect": {
          "tipo": "string",
          "desc": "Google consent URL. Contains no credential."
        }
      },
      "erros": {
        "400": "gsc_invalid_property, gsc_property_mismatch or invalid_language",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "503": "gsc_not_configured"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/search-console/connect\", {method:\"POST\", headers:{\"Content-Type\":\"application/json\", \"X-CSRF-Token\":csrf}, body:JSON.stringify({property:\"sc-domain:example.com\"})}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ redirect }",
      "url": "https://pageaudit.online/api/sites/:id/search-console/connect",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/sites/:id/search-console/sync",
      "auth": "session",
      "grupo": "Search Console",
      "summary": "Collect if this connection is due, at most once per 24 hours.",
      "desc": "The hourly schedule uses the same durable reservation. A failure preserves the last good sample and schedules the next attempt 24 hours later. This route cannot force an extra collection.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "retorno": "ColetaSearchConsole",
      "erros": {
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "503": "gsc_not_configured"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/search-console/sync\", {method:\"POST\", headers:{\"X-CSRF-Token\":csrf}}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ available, connected, property, last_attempt_at, last_success_at, next_run_at, error, stale, snapshot{property,period,checked_at,queries,pages,coverage}, opportunities[{query,clicks,impressions,ctr,position}], terms[{query,last_alert_at,analysis,series}], started, saved? }",
      "url": "https://pageaudit.online/api/sites/:id/search-console/sync",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "PUT",
      "path": "/api/sites/:id/search-console/terms",
      "auth": "session",
      "grupo": "Search Console",
      "summary": "Replace the queries selected for daily history and drop detection.",
      "desc": "Accepts up to 20 exact queries. Removed queries become inactive; their stored history is retained. Saving schedules the Search Console connection for collection. E-mail delivery requires an active site package.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "corpo": {
        "terms": {
          "tipo": "string[]",
          "desc": "Unique exact queries, each 1–300 characters.",
          "obrigatorio": true
        }
      },
      "body": {
        "terms": [
          "page audit",
          "website audit"
        ]
      },
      "retorno": {
        "terms": {
          "tipo": "TermoSearchConsole[]",
          "desc": "The active selection and its history."
        }
      },
      "erros": {
        "400": "gsc_invalid_terms",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "429": "rate_limited",
        "503": "sites_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/search-console/terms\",{method:\"PUT\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf},body:JSON.stringify({terms:[\"page audit\"]})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ terms[{query,last_alert_at,analysis,series}] }",
      "url": "https://pageaudit.online/api/sites/:id/search-console/terms",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/sites/:id/analytics",
      "auth": "session",
      "grupo": "Google Analytics",
      "summary": "Read GA4 collection status, up to 90 daily rows and recent alert events.",
      "desc": "The public response contains normalized aggregates only. Complete bounded Google responses remain server-side for traceability.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "retorno": "GoogleAnalytics",
      "erros": {
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "503": "sites_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/analytics\").then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ available, ready, connected, stale, last_success_at, next_run_at, properties, series[{date,activeUsers,totalUsers,newUsers,sessions,engagedSessions,screenPageViews,eventCount,keyEvents,transactions,totalRevenue}], alerts[{source,signal_code,subject_key,period_end,state,payload,created_at,sent_at}] }",
      "url": "https://pageaudit.online/api/sites/:id/analytics",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/sites/:id/analytics/sync",
      "auth": "session",
      "grupo": "Google Analytics",
      "summary": "Collect the connected GA4 properties that are due for this site.",
      "desc": "The hourly schedule uses the same daily reservation, so repeated refreshes do not spend more Google requests. The first valid collection reads up to 90 days. Later collections overlap three days to include provider adjustments. Requests run in pairs and preserve the complete bounded provider response.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "retorno": "ColetaGoogleAnalytics",
      "erros": {
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "429": "ga4_data_quota",
        "503": "ga4_data_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/analytics/sync\",{method:\"POST\",headers:{\"x-csrf-token\":csrf}}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ available, ready, connected, stale, last_success_at, next_run_at, properties, series[{date,activeUsers,totalUsers,newUsers,sessions,engagedSessions,screenPageViews,eventCount,keyEvents,transactions,totalRevenue}], alerts[{source,signal_code,subject_key,period_end,state,payload,created_at,sent_at}], collections }",
      "url": "https://pageaudit.online/api/sites/:id/analytics/sync",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "DELETE",
      "path": "/api/sites/:id/search-console",
      "auth": "session",
      "grupo": "Search Console",
      "summary": "Stop collection and remove the local Google credential.",
      "desc": "The last good sample stays available. An in-flight callback or collection cannot reconnect the site.",
      "params": {
        "id": {
          "desc": "Site ID from Site.id.",
          "exemplo": "site-example"
        }
      },
      "retorno": "SearchConsole",
      "erros": {
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "503": "sites_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/search-console\", {method:\"DELETE\", headers:{\"X-CSRF-Token\":csrf}}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ available, connected, property, last_attempt_at, last_success_at, next_run_at, error, stale, snapshot{property,period,checked_at,queries,pages,coverage}, opportunities[{query,clicks,impressions,ctr,position}], terms[{query,last_alert_at,analysis,series}] }",
      "url": "https://pageaudit.online/api/sites/:id/search-console",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/oauth/search-console/callback",
      "auth": "session",
      "grupo": "Search Console",
      "summary": "Complete a PageAudit Google data authorization.",
      "desc": "The popup reports success to the existing workspace and closes. Account-level Search Console or Analytics authorization opens the import assistant. The legacy site-bound Search Console flow still reads its first sample. The response contains no Google code or credential.",
      "query": {
        "code": {
          "tipo": "string",
          "desc": "Google authorization code. Never log it."
        },
        "state": {
          "tipo": "string",
          "desc": "Single-use state from the consent URL.",
          "obrigatorio": true
        },
        "error": {
          "tipo": "string",
          "desc": "Google refusal code instead of code."
        },
        "scope": {
          "tipo": "string",
          "desc": "Google consent response; verified by the token response."
        }
      },
      "retorno": {
        "site_id": {
          "tipo": "string",
          "desc": "The connected site."
        },
        "search_console": {
          "tipo": "SearchConsole",
          "desc": "The first sample and connection state."
        }
      },
      "erros": {
        "400": "gsc_invalid_callback",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found",
        "409": "gsc_connection_changed",
        "503": "sites_unavailable"
      },
      "exemplo": "$ORIGIN/oauth/search-console/callback?code=GOOGLE_CODE&state=CONSENT_STATE",
      "returns": "{ site_id, search_console{available,connected,property,last_attempt_at,last_success_at,next_run_at,error,stale,snapshot,opportunities,terms} }",
      "url": "https://pageaudit.online/oauth/search-console/callback",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "auth": "session",
      "grupo": "Weekly site report",
      "params": {
        "id": {
          "desc": "Site owned by this account.",
          "exemplo": "SITE_ID"
        }
      },
      "erros": {
        "400": "invalid_body, invalid_weekly_settings or invalid_competitors",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found, weekly_report_not_found or weekly_audit_not_available",
        "409": "weekly_package_required, weekly_site_limit (with `limit` and `plans`) or weekly_settings_changed",
        "429": "rate_limited: more than 50 changes per site and day",
        "503": "weekly_audit_unavailable"
      },
      "method": "GET",
      "path": "/api/sites/:id/weekly-audit",
      "retorno": "SemanaDoSite",
      "query": {
        "lang": {
          "tipo": "string",
          "desc": "`pt` for the problem and the fix in Portuguese; English by default."
        }
      },
      "summary": "Read the weekly report of a site: the settings, the package, the last report and 12 weeks of history.",
      "desc": "Once a week the report checks the first 500 URLs of the site's sitemap, says what changed since the week before (new and fixed problems, new pages) and lists the new pages of up to 3 competitor sitemaps. It comes with the watch packages: one site for every 25 paid pages. Reading never runs the report.",
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/weekly-audit?lang=en\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ site_id, settings{version,competitors,paused,running,next_run_at,last_run_at,last_error}, plan{paid_pages,sites_allowed,sites_on,pages_per_site,plans}, report{id,created_at,origin,sitemap,urls_in_sitemap,urls_checked,urls_other_host,score,by_severity,changes,competitors,findings,truncated,ai_search}, history[{id,created_at,score,new_findings,fixed_findings,new_pages,new_competitor_urls}], _links }",
      "url": "https://pageaudit.online/api/sites/:id/weekly-audit",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "auth": "session",
      "grupo": "Weekly site report",
      "params": {
        "id": {
          "desc": "Site owned by this account.",
          "exemplo": "SITE_ID"
        }
      },
      "erros": {
        "400": "invalid_body, invalid_weekly_settings or invalid_competitors",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found, weekly_report_not_found or weekly_audit_not_available",
        "409": "weekly_package_required, weekly_site_limit (with `limit` and `plans`) or weekly_settings_changed",
        "429": "rate_limited: more than 50 changes per site and day",
        "503": "weekly_audit_unavailable"
      },
      "method": "PUT",
      "path": "/api/sites/:id/weekly-audit",
      "retorno": "SemanaDoSite",
      "query": {
        "lang": {
          "tipo": "string",
          "desc": "`pt` for the problem and the fix in Portuguese; English by default."
        }
      },
      "summary": "Turn the weekly report of a site on or off and set its competitors.",
      "desc": "Turning on needs a watch package in force and a free site in it (409 with `limit` and `plans` otherwise). The first report runs right away; later ones run a week after the last. A new competitor gets its base this week and its new pages from the next. Saves use the expected version.",
      "body": {
        "version": 0,
        "paused": false,
        "competitors": [
          "https://competitor.example/"
        ]
      },
      "corpo": {
        "version": {
          "tipo": "int",
          "desc": "0 the first time; then `settings.version`.",
          "obrigatorio": true
        },
        "paused": {
          "tipo": "bool",
          "desc": "`false` turns the weekly report on; `true` turns it off.",
          "obrigatorio": true
        },
        "competitors": {
          "tipo": "string[]",
          "desc": "Up to 3 competitor sites (any address; the report reads the origin's /sitemap.xml).",
          "obrigatorio": true
        }
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/weekly-audit\", {method:\"PUT\", headers:{\"Content-Type\":\"application/json\",\"X-CSRF-Token\":csrf}, body:JSON.stringify({version:0,paused:false,competitors:[\"https://competitor.example/\"]})}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ site_id, settings{version,competitors,paused,running,next_run_at,last_run_at,last_error}, plan{paid_pages,sites_allowed,sites_on,pages_per_site,plans}, report{id,created_at,origin,sitemap,urls_in_sitemap,urls_checked,urls_other_host,score,by_severity,changes,competitors,findings,truncated,ai_search}, history[{id,created_at,score,new_findings,fixed_findings,new_pages,new_competitor_urls}], _links }",
      "url": "https://pageaudit.online/api/sites/:id/weekly-audit",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "auth": "session",
      "grupo": "Weekly site report",
      "params": {
        "id": {
          "desc": "Site owned by this account.",
          "exemplo": "SITE_ID"
        },
        "report": {
          "desc": "A report id from `history`.",
          "exemplo": "REPORT_ID"
        }
      },
      "erros": {
        "400": "invalid_body, invalid_weekly_settings or invalid_competitors",
        "401": "account_required or session_ended",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found, weekly_report_not_found or weekly_audit_not_available",
        "409": "weekly_package_required, weekly_site_limit (with `limit` and `plans`) or weekly_settings_changed",
        "429": "rate_limited: more than 50 changes per site and day",
        "503": "weekly_audit_unavailable"
      },
      "method": "GET",
      "path": "/api/sites/:id/weekly-audit/reports/:report",
      "retorno": "DetalheSemana",
      "query": {
        "lang": {
          "tipo": "string",
          "desc": "`pt` for the problem and the fix in Portuguese; English by default."
        }
      },
      "summary": "Read one weekly report of the history.",
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/weekly-audit/reports/REPORT_ID\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ report{id,created_at,origin,sitemap,urls_in_sitemap,urls_checked,urls_other_host,score,by_severity,changes,competitors,findings,truncated,ai_search}, _links }",
      "url": "https://pageaudit.online/api/sites/:id/weekly-audit/reports/:report",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "auth": "session",
      "grupo": "AI search visibility",
      "params": {
        "id": {
          "desc": "Site owned by this account.",
          "exemplo": "SITE_ID"
        }
      },
      "query": {
        "lang": {
          "desc": "Language of the recommendations: en (default) or pt.",
          "exemplo": "pt"
        }
      },
      "erros": {
        "400": "invalid_body or invalid_ai_search_queries",
        "401": "account_required or session_ended",
        "402": "ai_search_queries_limit",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found or ai_search_not_available",
        "409": "ai_search_settings_changed",
        "429": "ai_search_free_limit or ai_search_daily_limit (with retry_after), ai_search_running or editorial_write_limit",
        "502": "ai_search_failed",
        "503": "ai_search_not_configured, ai_search_busy, ai_search_unavailable",
        "504": "ai_search_timeout"
      },
      "method": "GET",
      "path": "/api/sites/:id/ai-search",
      "retorno": "EstadoBuscaIa",
      "summary": "Read the plan, saved searches, the last twelve runs and the trend of each search.",
      "desc": "Reads never call the search engine. The trend compares each search of the newest run with the previous run that had it: up, down, same, new or lost.",
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/ai-search\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ available, plan{paid,queries_allowed}, settings{version,queries,updated_at}, runs[{id,created_at,origin,cost_micro,indexed,positions}], trend[{query,position,previous?,trend?}], latest{id,created_at,reused,url,index,queries,recommendations,suggested_queries,calls,paid_calls,cost_micro}, _links }",
      "url": "https://pageaudit.online/api/sites/:id/ai-search",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "auth": "session",
      "grupo": "AI search visibility",
      "params": {
        "id": {
          "desc": "Site owned by this account.",
          "exemplo": "SITE_ID"
        }
      },
      "query": {
        "lang": {
          "desc": "Language of the recommendations: en (default) or pt.",
          "exemplo": "pt"
        }
      },
      "erros": {
        "400": "invalid_body or invalid_ai_search_queries",
        "401": "account_required or session_ended",
        "402": "ai_search_queries_limit",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found or ai_search_not_available",
        "409": "ai_search_settings_changed",
        "429": "ai_search_free_limit or ai_search_daily_limit (with retry_after), ai_search_running or editorial_write_limit",
        "502": "ai_search_failed",
        "503": "ai_search_not_configured, ai_search_busy, ai_search_unavailable",
        "504": "ai_search_timeout"
      },
      "method": "PATCH",
      "path": "/api/sites/:id/ai-search",
      "retorno": "BuscasIaSalvas",
      "summary": "Save one to ten searches to track; the weekly report runs them.",
      "desc": "Saves use the expected version. Without a current watch package, an account tracks up to three searches. Uses the shared 240 writes per site and UTC day.",
      "body": {
        "version": 0,
        "queries": [
          "website audit tool"
        ]
      },
      "corpo": {
        "version": {
          "tipo": "int",
          "desc": "0 initially; then the current settings version.",
          "obrigatorio": true
        },
        "queries": {
          "tipo": "string[]",
          "desc": "One to ten distinct searches, 1 to 128 characters each.",
          "obrigatorio": true
        }
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/ai-search\", {method:\"PATCH\", headers:{\"Content-Type\":\"application/json\",\"X-CSRF-Token\":csrf}, body:JSON.stringify({version:0,queries:[\"website audit tool\"]})}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ settings{version,queries,updated_at}, plan{paid,queries_allowed}, _links }",
      "url": "https://pageaudit.online/api/sites/:id/ai-search",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "auth": "session",
      "grupo": "AI search visibility",
      "params": {
        "id": {
          "desc": "Site owned by this account.",
          "exemplo": "SITE_ID"
        }
      },
      "query": {
        "lang": {
          "desc": "Language of the recommendations: en (default) or pt.",
          "exemplo": "pt"
        }
      },
      "erros": {
        "400": "invalid_body or invalid_ai_search_queries",
        "401": "account_required or session_ended",
        "402": "ai_search_queries_limit",
        "403": "account_required or invalid_csrf",
        "404": "site_not_found or ai_search_not_available",
        "409": "ai_search_settings_changed",
        "429": "ai_search_free_limit or ai_search_daily_limit (with retry_after), ai_search_running or editorial_write_limit",
        "502": "ai_search_failed",
        "503": "ai_search_not_configured, ai_search_busy, ai_search_unavailable",
        "504": "ai_search_timeout"
      },
      "method": "POST",
      "path": "/api/sites/:id/ai-search",
      "retorno": "RodadaBuscaIa",
      "summary": "Check now whether the site's page appears in AI search, and who appears in its place.",
      "desc": "Send {} to use the saved searches, or the searches the page title and H1 suggest; send {queries} to choose. One index check plus one search per query, each USD 0.00075 when it returns results. A run with the same searches in the last 24 hours comes back with reused: true and no cost. Without a watch package: one run per week, three searches. With one: twenty runs per UTC day, ten searches. Result titles and snippets are third-party data.",
      "body": {},
      "corpo": {
        "queries": {
          "tipo": "string[]",
          "desc": "Optional. One to ten searches, 1 to 128 characters each."
        }
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/ai-search?lang=pt\", {method:\"POST\", headers:{\"Content-Type\":\"application/json\",\"X-CSRF-Token\":csrf}, body:\"{}\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ run{id,created_at,reused,url,index,queries,recommendations,suggested_queries,calls,paid_calls,cost_micro}, plan{paid,queries_allowed}, truncated, _links }",
      "url": "https://pageaudit.online/api/sites/:id/ai-search",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/sites/:id/journeys",
      "auth": "session",
      "grupo": "Journeys",
      "params": {
        "id": {
          "desc": "Owned Site.id.",
          "exemplo": "476690e0-7b92-40ca-9d73-9aa09b327287"
        }
      },
      "summary": "Read installation, action counts, paths and an ordered funnel for the last seven UTC days.",
      "desc": "Only this site's owner can read it. Data updates through the shared realtime transport. No user identity, typed text or screen recording.",
      "retorno": {
        "configured": {
          "tipo": "bool",
          "desc": "Whether the site has a journey configuration. When false, no other fields follow."
        },
        "active": {
          "tipo": "bool",
          "opcional": true,
          "desc": "Collection enabled with a valid site benefit."
        },
        "project": {
          "tipo": "string",
          "opcional": true,
          "desc": "Immutable event prefix."
        },
        "origin": {
          "tipo": "string",
          "opcional": true,
          "desc": "Exact HTTPS origin accepted by the collector."
        },
        "steps": {
          "tipo": "string[]",
          "opcional": true,
          "desc": "Up to eight ordered project_action IDs."
        },
        "expires_at": {
          "tipo": "int",
          "opcional": true,
          "desc": "Collection benefit cache expiry, Unix milliseconds. Internal benefits are checked daily."
        },
        "snippet": {
          "tipo": "string",
          "opcional": true,
          "desc": "Install this script and connect analytics consent."
        },
        "usage": {
          "tipo": "object",
          "opcional": true,
          "desc": "bytes, limite, dias and limited; UTC-day quota and seven-day retention."
        },
        "summary": {
          "tipo": "object",
          "opcional": true,
          "desc": "sessoes, ativas, eventos, ultima_coleta, acoes, caminhos and ordered funil. Sessions are not unique people."
        },
        "ga": {
          "tipo": "object",
          "opcional": true,
          "desc": "configured and delivery estado. No API secret. received means HTTP acceptance, not proof of a Google report."
        }
      },
      "erros": {
        "400": "journeys_invalid_config, journeys_invalid_project, journeys_invalid_steps or journeys_invalid_ga",
        "401": "account_required or session_ended",
        "402": "site_package_required",
        "403": "invalid_csrf",
        "404": "site_not_found",
        "503": "journeys_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/journeys\").then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ configured, active?, project?, origin?, steps?, expires_at?, snippet?, usage?, summary?, ga? }",
      "url": "https://pageaudit.online/api/sites/:id/journeys",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "PUT",
      "path": "/api/sites/:id/journeys",
      "auth": "session",
      "grupo": "Journeys",
      "params": {
        "id": {
          "desc": "Owned Site.id.",
          "exemplo": "476690e0-7b92-40ca-9d73-9aa09b327287"
        }
      },
      "summary": "Configure collection, funnel steps and optional server delivery to GA4.",
      "desc": "Activation requires the existing site package or an operator-authorised internal benefit. A 402 opens the package offer when available. It never buys automatically. Keep Google secrets out of URLs and browser scripts.",
      "corpo": {
        "active": {
          "tipo": "boolean",
          "obrigatorio": true,
          "desc": "Enable or pause collection."
        },
        "project": {
          "tipo": "string",
          "desc": "Required on creation: 2–20 lowercase letters, digits or underscores, starting with a letter. Cannot change."
        },
        "steps": {
          "tipo": "string[]",
          "desc": "Up to eight ordered project_action IDs."
        },
        "ga": {
          "tipo": "object",
          "desc": "measurement_id and api_secret; omit to keep, null to disconnect. The server encrypts the secret."
        }
      },
      "body": {
        "project": "example",
        "active": true,
        "steps": [
          "example_comprar",
          "example_pago"
        ]
      },
      "retorno": {
        "configured": {
          "tipo": "bool",
          "desc": "Whether the site has a journey configuration. When false, no other fields follow."
        },
        "active": {
          "tipo": "bool",
          "opcional": true,
          "desc": "Collection enabled with a valid site benefit."
        },
        "project": {
          "tipo": "string",
          "opcional": true,
          "desc": "Immutable event prefix."
        },
        "origin": {
          "tipo": "string",
          "opcional": true,
          "desc": "Exact HTTPS origin accepted by the collector."
        },
        "steps": {
          "tipo": "string[]",
          "opcional": true,
          "desc": "Up to eight ordered project_action IDs."
        },
        "expires_at": {
          "tipo": "int",
          "opcional": true,
          "desc": "Collection benefit cache expiry, Unix milliseconds. Internal benefits are checked daily."
        },
        "snippet": {
          "tipo": "string",
          "opcional": true,
          "desc": "Install this script and connect analytics consent."
        },
        "usage": {
          "tipo": "object",
          "opcional": true,
          "desc": "bytes, limite, dias and limited; UTC-day quota and seven-day retention."
        },
        "summary": {
          "tipo": "object",
          "opcional": true,
          "desc": "sessoes, ativas, eventos, ultima_coleta, acoes, caminhos and ordered funil. Sessions are not unique people."
        },
        "ga": {
          "tipo": "object",
          "opcional": true,
          "desc": "configured and delivery estado. No API secret. received means HTTP acceptance, not proof of a Google report."
        }
      },
      "erros": {
        "400": "journeys_invalid_config, journeys_invalid_project, journeys_invalid_steps or journeys_invalid_ga",
        "401": "account_required or session_ended",
        "402": "site_package_required",
        "403": "invalid_csrf",
        "404": "site_not_found",
        "503": "journeys_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/sites/SITE_ID/journeys\", {method:\"PUT\", headers:{\"Content-Type\":\"application/json\",\"X-CSRF-Token\":csrf}, body:JSON.stringify({project:\"example\",active:true,steps:[\"example_comprar\",\"example_pago\"]})}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ configured, active?, project?, origin?, steps?, expires_at?, snippet?, usage?, summary?, ga? }",
      "url": "https://pageaudit.online/api/sites/:id/journeys",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/journeys/collect",
      "auth": "none",
      "grupo": "Journeys",
      "summary": "Receive a bounded batch from the installed browser script.",
      "desc": "Public write key, exact registered Origin and active site package. No read authority. Requires consent:true. Do not submit test events to a customer's site. CORS accepts only its origin. Limit: 20 events and 12 KiB per batch; 1 MiB per site per UTC day, with a 32 MiB service ceiling.",
      "corpo": {
        "v": {
          "tipo": "number",
          "obrigatorio": true,
          "desc": "Protocol version 1."
        },
        "consent": {
          "tipo": "boolean",
          "obrigatorio": true,
          "desc": "Must be true after analytics consent."
        },
        "site": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Public site ID from the installation snippet."
        },
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Random batch ID, 16–80 URL-safe characters."
        },
        "sid": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Random consented tab session, 16–64 URL-safe characters."
        },
        "eventos": {
          "tipo": "object[]",
          "obrigatorio": true,
          "desc": "tipo (pagina, navegou, acao, evento), acao, pagina, em (Unix milliseconds); optional manual and structural seletor. No arbitrary properties."
        }
      },
      "retorno": {
        "_texto": "Empty response: 202 accepted, 204 privacy signal ignored."
      },
      "erros": {
        "400": "Invalid batch",
        "403": "Origin or entitlement refused",
        "413": "Body exceeds 12 KiB",
        "429": "Rate or volume limit",
        "503": "Collector unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/journeys/collect\", {method:\"POST\", credentials:\"omit\", referrerPolicy:\"no-referrer\", headers:{\"Content-Type\":\"text/plain\"}, body:JSON.stringify({v:1,consent:true,site:\"SITE_ID\",id:crypto.randomUUID(),sid:crypto.randomUUID(),eventos:[{tipo:\"evento\",acao:\"teste\",pagina:\"inicio\",em:Date.now()}]})});",
      "exemploLinguagem": "js",
      "returns": "Empty response: 202 accepted, 204 privacy signal ignored.",
      "url": "https://pageaudit.online/api/journeys/collect",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/pageaudit/organizations",
      "auth": "session",
      "summary": "Lists the account organizations that have PageAudit enabled.",
      "grupo": "Organizations",
      "desc": "Names, memberships and product access come from the shared account. Create, rename and configure organizations on `/conta/global`.",
      "retorno": {
        "organizations": {
          "tipo": "object[]",
          "desc": "Up to 100 organizations: `id`, `name` and the current account's `role`."
        },
        "truncated": {
          "tipo": "bool",
          "desc": "`true` when the account has more than 100 matching organizations."
        }
      },
      "erros": {
        "401": null,
        "503": "The shared account or the PageAudit database is unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/pageaudit/organizations\").then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ organizations, truncated }",
      "url": "https://pageaudit.online/api/pageaudit/organizations",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/pageaudit/organizations",
      "auth": "session",
      "summary": "Creates the account's first organization with PageAudit enabled.",
      "grupo": "Organizations",
      "desc": "The panel uses this only when the account has no organization. `requestId` makes a repeated request idempotent.",
      "corpo": {
        "name": {
          "tipo": "string",
          "desc": "Organization name.",
          "obrigatorio": true
        },
        "requestId": {
          "tipo": "string",
          "desc": "UUIDv4 idempotency key.",
          "obrigatorio": true
        }
      },
      "body": {
        "name": "My organization",
        "requestId": "00000000-0000-4000-8000-000000000000"
      },
      "retorno": {
        "organization": {
          "tipo": "object",
          "desc": "`id`, `name` and `role`."
        },
        "existing": {
          "tipo": "bool",
          "desc": "`true` when an enabled organization already existed."
        }
      },
      "erros": {
        "400": null,
        "401": null,
        "403": null,
        "409": null,
        "429": null,
        "503": "The shared account or the PageAudit database is unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/pageaudit/organizations\",{method:\"POST\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf},body:JSON.stringify({name:\"My organization\",requestId:crypto.randomUUID()})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ organization, existing }",
      "url": "https://pageaudit.online/api/pageaudit/organizations",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/pageaudit/organizations/adopt",
      "auth": "organization",
      "summary": "Places account projects created before organizations in the active organization.",
      "grupo": "Organizations",
      "desc": "Idempotent migration used by the workspace after an organization is selected.",
      "corpo": {},
      "body": {},
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "organization_id": {
          "tipo": "string",
          "desc": "The organization that received the legacy projects."
        }
      },
      "erros": {
        "400": null,
        "401": null,
        "403": null,
        "404": null,
        "503": "The shared account or the PageAudit database is unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/pageaudit/organizations/adopt\",{method:\"POST\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf,\"x-organization-id\":organizationId},body:\"{}\"}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ ok, organization_id }",
      "url": "https://pageaudit.online/api/pageaudit/organizations/adopt",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "PATCH",
      "path": "/api/pageaudit/projects/move",
      "auth": "organization",
      "summary": "Moves every saved page of one site to another PageAudit organization.",
      "grupo": "Organizations",
      "corpo": {
        "host": {
          "tipo": "string",
          "desc": "Project hostname without a protocol.",
          "obrigatorio": true
        },
        "organizationId": {
          "tipo": "string",
          "desc": "Destination organization ID.",
          "obrigatorio": true
        }
      },
      "body": {
        "host": "example.com",
        "organizationId": "org_example"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "host": {
          "tipo": "string",
          "desc": "Moved project hostname."
        },
        "moved": {
          "tipo": "int",
          "desc": "Number of saved pages moved."
        },
        "organization_id": {
          "tipo": "string",
          "desc": "Destination organization ID."
        }
      },
      "erros": {
        "400": "`host` or destination is invalid, or the destination is the active organization.",
        "401": null,
        "403": null,
        "404": "The source project or destination organization is unavailable.",
        "409": "The destination already contains one of the project's pages.",
        "503": "The shared account or the PageAudit database is unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/pageaudit/projects/move\",{method:\"PATCH\",headers:{\"content-type\":\"application/json\",\"x-csrf-token\":csrf,\"x-organization-id\":sourceOrganizationId},body:JSON.stringify({host:\"example.com\",organizationId:destinationOrganizationId})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ ok, host, moved, organization_id }",
      "url": "https://pageaudit.online/api/pageaudit/projects/move",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/guest",
      "auth": "none",
      "summary": "Creates a guest `pa_…` — it is what gives access to the tab workspace without an account.",
      "grupo": "Identity",
      "desc": "Keep the token: it is the identity of the tabs and of the audits tied to them.",
      "retorno": {
        "token": {
          "tipo": "string",
          "desc": "The guest, prefix `pa_`. Send it in `X-Guest-Token`, as Bearer or in `?guest_token=`."
        }
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/guest",
      "returns": "{ token }",
      "url": "https://pageaudit.online/api/guest",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/realtime/ticket",
      "auth": "guest",
      "summary": "Creates a short-lived browser ticket for the workspace's live status channel.",
      "grupo": "Realtime",
      "desc": "The PageAudit browser client calls this with the normal session and organization headers. Use the HTTP routes directly in server integrations. The opaque ticket never grants more access than the current owner.",
      "corpo": {},
      "body": {},
      "retorno": {
        "ticket": {
          "tipo": "string",
          "desc": "Opaque, short-lived credential for the PageAudit WebSocket."
        },
        "expires_in": {
          "tipo": "int",
          "desc": "Seconds until the unused ticket expires."
        },
        "namespace": {
          "tipo": "string",
          "desc": "Socket.IO namespace for PageAudit."
        },
        "path": {
          "tipo": "string",
          "desc": "Same-origin WebSocket transport path."
        },
        "routes": {
          "tipo": "object[]",
          "desc": "Live routes authorized for this owner."
        }
      },
      "erros": {
        "401": "The account session or guest has ended.",
        "403": "The request is not from the PageAudit browser origin.",
        "429": "Realtime admission is temporarily full.",
        "503": "Realtime transport is unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/realtime/ticket\",{method:\"POST\",headers:{\"content-type\":\"application/json\",\"x-mm-realtime\":\"1\",\"x-csrf-token\":csrf,\"x-organization-id\":organizationId},body:\"{}\"}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ ticket, expires_in, namespace, path, routes }",
      "url": "https://pageaudit.online/api/realtime/ticket",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/tabs/stream",
      "auth": "guest",
      "summary": "Returns the current lightweight tab list used as the live channel snapshot.",
      "grupo": "Realtime",
      "desc": "This is the HTTP twin of the `/api/tabs/:id/status` events. It omits billing and full reports, so one subscription can refresh the project list cheaply.",
      "retorno": {
        "tabs": {
          "tipo": "Aba[]",
          "desc": "Current tabs and audit status for this owner."
        }
      },
      "erros": [
        401
      ],
      "exemplo": "curl -s $ORIGIN/api/tabs/stream -H \"X-Guest-Token: $PA\"",
      "returns": "{ tabs[{id,url,alias,active,status,audit_id,score,counts,monitor,monitor_email,monitor_checked_at,monitor_alert_at,organization_id,created_at}] }",
      "url": "https://pageaudit.online/api/tabs/stream",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/tabs",
      "auth": "guest",
      "summary": "The owner's whole workspace, with the active tab's result already rehydrated.",
      "grupo": "Tabs",
      "desc": "One call loads the saved pages and the active page's last report. The UI groups pages by site in Projects and opens each site in a work tab. The introduction stays in Welcome. A signed-in account sends `X-Organization-Id`; a guest has one temporary workspace.",
      "retorno": "Workspace",
      "erros": [
        401
      ],
      "exemplo": "curl -s $ORIGIN/api/tabs -H \"X-Guest-Token: $PA\"",
      "returns": "{ tabs[{id,url,alias,active,status,audit_id,score,counts,monitor,monitor_email,monitor_checked_at,monitor_alert_at,organization_id,created_at}], active_id, active_result{audit_id,url,score,summary,counts,issues,jsonLd,headers,truncated,share_slug,created_at}, limit, owner, gate{ip,now,free_allowance,free_remaining,needs_payment}, billing }",
      "url": "https://pageaudit.online/api/tabs",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "POST",
      "path": "/api/tabs",
      "auth": "guest",
      "summary": "Opens a tab for the URL, or focuses the one that already exists for it.",
      "grupo": "Tabs",
      "desc": "Past the free tab allowance it answers 402 with `accepts[]`: pay and repeat.",
      "corpo": {
        "url": {
          "tipo": "string",
          "desc": "The URL the tab will follow.",
          "obrigatorio": true
        },
        "alias": {
          "tipo": "string",
          "desc": "Label to recognise the tab in the list."
        },
        "queue_audit": {
          "tipo": "bool",
          "desc": "Defaults to true. Use false to save an idle page for domain monitoring without queuing a page audit. Existing pages keep their current state."
        }
      },
      "body": {
        "url": "https://example.com/",
        "alias": "optional label"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "tab": {
          "tipo": "Aba",
          "desc": "The opened tab (or the one that already existed for that URL)."
        }
      },
      "erros": {
        "400": "`url` missing or not http.",
        "401": null,
        "402": null
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/tabs -H \"X-Guest-Token: $PA\" -H 'content-type: application/json' -d '{\"url\":\"https://example.com/\"}'",
      "returns": "{ ok, tab{id,url,alias,active,status,audit_id,score,counts,monitor,monitor_email,monitor_checked_at,monitor_alert_at,organization_id,created_at} }",
      "url": "https://pageaudit.online/api/tabs",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/tabs/:id",
      "auth": "guest",
      "summary": "One tab with the full report of its last run.",
      "grupo": "Tabs",
      "params": {
        "id": {
          "desc": "Tab ID, from `Aba.id`.",
          "exemplo": "tab_9f3c2b1d7a4e58b0c2d4"
        }
      },
      "retorno": {
        "tab": {
          "tipo": "Aba",
          "desc": "The requested tab."
        },
        "result": {
          "tipo": "AuditGravado",
          "desc": "The report of the last run; `null` if the tab never ran.",
          "nulo": true
        }
      },
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s $ORIGIN/api/tabs/TAB_ID -H \"X-Guest-Token: $PA\"",
      "returns": "{ tab{id,url,alias,active,status,audit_id,score,counts,monitor,monitor_email,monitor_checked_at,monitor_alert_at,organization_id,created_at}, result{audit_id,url,score,summary,counts,issues,jsonLd,headers,truncated,share_slug,created_at} }",
      "url": "https://pageaudit.online/api/tabs/:id",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "PATCH",
      "path": "/api/tabs/:id",
      "auth": "guest",
      "summary": "Renames the tab, puts it in focus or turns the daily watch on and off.",
      "grupo": "Tabs",
      "desc": "With `monitor: true` the page is re-audited once a day and an e-mail goes out when the score drops or a new error/warning appears — never on an improvement. Watching needs an account (the alert has to reach someone) and each account watches a limited number of pages. When a plan ends, the pages beyond the account's limit pause (the oldest stay) until the next purchase.",
      "params": {
        "id": {
          "desc": "ID of the tab to change.",
          "exemplo": "tab_9f3c2b1d7a4e58b0c2d4"
        }
      },
      "corpo": {
        "alias": {
          "tipo": "string",
          "desc": "New label of the tab."
        },
        "active": {
          "tipo": "bool",
          "desc": "`true` focuses this tab (and unfocuses the other)."
        },
        "monitor": {
          "tipo": "bool",
          "desc": "`true` watches the page daily; `false` stops watching it."
        },
        "monitor_email": {
          "tipo": "string",
          "desc": "Where the alert goes. Empty or `null` uses the account's e-mail."
        }
      },
      "body": {
        "alias": "new label",
        "active": true,
        "monitor": true
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "tab": {
          "tipo": "Aba",
          "desc": "The tab with the change applied."
        }
      },
      "erros": {
        "400": "No changeable field in the body, or `monitor_email` is not an e-mail.",
        "401": null,
        "403": "Watching needs an account: a guest token has no e-mail to be warned at (`account_required`).",
        "404": null,
        "409": "The account already watches all the pages it has (`monitor_limit`, with `limit`); a plan (`POST /api/plans/:plan`) adds more."
      },
      "exemplo": "curl -s -XPATCH $ORIGIN/api/tabs/TAB_ID -H \"X-Guest-Token: $PA\" -H 'content-type: application/json' -d '{\"active\":true}'",
      "returns": "{ ok, tab{id,url,alias,active,status,audit_id,score,counts,monitor,monitor_email,monitor_checked_at,monitor_alert_at,organization_id,created_at} }",
      "url": "https://pageaudit.online/api/tabs/:id",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "DELETE",
      "path": "/api/tabs/:id",
      "auth": "guest",
      "summary": "Closes the tab. Its audit history keeps existing.",
      "grupo": "Tabs",
      "params": {
        "id": {
          "desc": "ID of the tab to close.",
          "exemplo": "tab_9f3c2b1d7a4e58b0c2d4"
        }
      },
      "retorno": "Ok",
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s -XDELETE $ORIGIN/api/tabs/TAB_ID -H \"X-Guest-Token: $PA\"",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/tabs/:id",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "POST",
      "path": "/api/tabs/:id/run",
      "auth": "guest",
      "summary": "Re-audits the tab's URL and stores a new report.",
      "grupo": "Tabs",
      "desc": "Consumes the same daily allowance as `POST /api/audit` — past it, 402 with `accepts[]`.",
      "params": {
        "id": {
          "desc": "ID of the tab to re-audit.",
          "exemplo": "tab_9f3c2b1d7a4e58b0c2d4"
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "tab": {
          "tipo": "Aba",
          "desc": "The tab with the new run's score."
        },
        "gate": {
          "tipo": "Gate",
          "desc": "How much of the allowance is left after this run."
        },
        "result": {
          "tipo": "AuditGravado",
          "desc": "The report just stored."
        }
      },
      "erros": [
        401,
        402,
        404
      ],
      "exemplo": "curl -s -XPOST $ORIGIN/api/tabs/TAB_ID/run -H \"X-Guest-Token: $PA\"",
      "returns": "{ ok, tab{id,url,alias,active,status,audit_id,score,counts,monitor,monitor_email,monitor_checked_at,monitor_alert_at,organization_id,created_at}, gate{ip,now,free_allowance,free_remaining,needs_payment}, result{audit_id,url,score,summary,counts,issues,jsonLd,headers,truncated,share_slug,created_at} }",
      "url": "https://pageaudit.online/api/tabs/:id/run",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/tabs/:id/history",
      "auth": "guest",
      "summary": "Previous runs of that tab and what changed between each one and the one before it.",
      "grupo": "Tabs",
      "desc": "Every run has always been kept; this is where you read the series. Each entry carries the score, the counts and a `change` with the point delta and the findings that appeared (`broke`) or disappeared (`fixed`) since the previous run — compared by finding code, never by message. Runs older than the watch feature are matched by URL.",
      "params": {
        "id": {
          "desc": "ID of the tab whose runs you want.",
          "exemplo": "tab_9f3c2b1d7a4e58b0c2d4"
        }
      },
      "query": {
        "limit": {
          "desc": "How many runs to bring, newest first. Default 20, maximum 100."
        }
      },
      "retorno": {
        "tab": {
          "tipo": "Aba",
          "desc": "The tab the runs belong to."
        },
        "runs": {
          "tipo": "RunHistorico[]",
          "desc": "Runs from newest to oldest."
        },
        "timeline": {
          "tipo": "DomainAuditTimelineItem[]",
          "desc": "Runs and up to 50 private domain event groups, newest first. Domain events require an account and the current organization. No new collection occurs."
        },
        "limit": {
          "tipo": "int",
          "desc": "The limit actually applied."
        }
      },
      "erros": [
        401,
        404
      ],
      "exemplo": "curl -s \"$ORIGIN/api/tabs/TAB_ID/history?limit=10\" -H \"X-Guest-Token: $PA\"",
      "returns": "{ tab{id,url,alias,active,status,audit_id,score,counts,monitor,monitor_email,monitor_checked_at,monitor_alert_at,organization_id,created_at}, runs[{audit_id,score,counts,created_at,change,shared,api}], timeline[{kind,id,at,audit?,domain?,nearby_audits?}], limit }",
      "url": "https://pageaudit.online/api/tabs/:id/history",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "GET",
      "path": "/api/gate",
      "auth": "none",
      "summary": "How many free audits remain for this IP and whether the next one pays.",
      "grupo": "Tabs",
      "desc": "Check before spending a call: it is the number IN FORCE, not the allowance in the documentation.",
      "retorno": "Gate",
      "returns": "{ ip, now, free_allowance, free_remaining, needs_payment }",
      "url": "https://pageaudit.online/api/gate",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/tabs/:id/domain/tls",
      "auth": "organization",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        }
      },
      "stream": true,
      "summary": "Read today's private connection review.",
      "desc": "One review per account, organization, origin and UTC day. Starting requires current domain monitoring rights. The account has three starts per UTC day. Retries return the same review. The scanner samples one public IP, with three stages and a 170-second deadline. Partial results do not establish safety. GET never starts a scan. The shared realtime channel reports stage changes. Reports expire after 26 hours.",
      "retorno": {
        "review": {
          "tipo": "DomainTlsReview",
          "nulo": true,
          "desc": "Current review, or null if no review started today."
        },
        "limit_per_day": {
          "tipo": "int",
          "desc": "Maximum new reviews per account and UTC day."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "402": "domain_subscription_required. Checkout follows explicit use of the paid action.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "tls_review_https_required.",
        "429": "tls_review_daily_limit or tls_review_busy.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/TAB_ID/domain/tls\",{method:\"GET\",headers:{\"X-Organization-Id\":organization }}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ review{id,endpoint,status,stage,completed,started_at,finished_at,reason,checks,findings,scanner?,target?,truncated?}, limit_per_day }",
      "url": "https://pageaudit.online/api/tabs/:id/domain/tls",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/tabs/:id/domain/tls",
      "auth": "organization",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "stream": false,
      "summary": "Start a bounded TLS review for the saved HTTPS origin.",
      "desc": "One review per account, organization, origin and UTC day. Starting requires current domain monitoring rights. The account has three starts per UTC day. Retries return the same review. The scanner samples one public IP, with three stages and a 170-second deadline. Partial results do not establish safety. GET never starts a scan. The shared realtime channel reports stage changes. Reports expire after 26 hours.",
      "body": {},
      "retorno": {
        "review": {
          "tipo": "DomainTlsReview",
          "nulo": true,
          "desc": "Current review, or null if no review started today."
        },
        "limit_per_day": {
          "tipo": "int",
          "desc": "Maximum new reviews per account and UTC day."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "402": "domain_subscription_required. Checkout follows explicit use of the paid action.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "tls_review_https_required.",
        "429": "tls_review_daily_limit or tls_review_busy.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/TAB_ID/domain/tls\",{method:\"POST\",headers:{\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf },body:\"{}\"}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ review{id,endpoint,status,stage,completed,started_at,finished_at,reason,checks,findings,scanner?,target?,truncated?}, limit_per_day }",
      "url": "https://pageaudit.online/api/tabs/:id/domain/tls",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "DELETE",
      "path": "/api/tabs/:id/domain/tls",
      "auth": "organization",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "stream": false,
      "summary": "Cancel the active connection review.",
      "desc": "One review per account, organization, origin and UTC day. Starting requires current domain monitoring rights. The account has three starts per UTC day. Retries return the same review. The scanner samples one public IP, with three stages and a 170-second deadline. Partial results do not establish safety. GET never starts a scan. The shared realtime channel reports stage changes. Reports expire after 26 hours.",
      "retorno": {
        "review": {
          "tipo": "DomainTlsReview",
          "nulo": true,
          "desc": "Current review, or null if no review started today."
        },
        "limit_per_day": {
          "tipo": "int",
          "desc": "Maximum new reviews per account and UTC day."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "402": "domain_subscription_required. Checkout follows explicit use of the paid action.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "tls_review_https_required.",
        "429": "tls_review_daily_limit or tls_review_busy.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/TAB_ID/domain/tls\",{method:\"DELETE\",headers:{\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf }}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ review{id,endpoint,status,stage,completed,started_at,finished_at,reason,checks,findings,scanner?,target?,truncated?}, limit_per_day }",
      "url": "https://pageaudit.online/api/tabs/:id/domain/tls",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/tabs/domain-portfolio",
      "auth": "organization",
      "grupo": "Domains",
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "summary": "Read cached monitoring summaries for up to 100 selected saved pages.",
      "desc": "Every tab must belong to the current account and organization. Reads by primary key without collection, activation or billing. Repeat with the next group of IDs for a larger workspace.",
      "corpo": {
        "tabs": {
          "tipo": "string[]",
          "obrigatorio": true,
          "desc": "One to 100 owned tab IDs."
        }
      },
      "body": {
        "tabs": [
          "tab_example"
        ]
      },
      "retorno": {
        "items": {
          "tipo": "DomainPortfolioItem[]",
          "desc": "One summary per selected page with a registrable domain. IP targets are omitted."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/domain-portfolio\", {method:\"POST\",headers:{\"Content-Type\":\"application/json\",\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf},body:JSON.stringify({tabs:[\"TAB_ID\"]})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ items[{tab_id,domain,state,expires_at,expiring,checked_at,incidents,monitor,alerts}] }",
      "url": "https://pageaudit.online/api/tabs/domain-portfolio",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/tabs/domain-portfolio/alerts",
      "auth": "organization",
      "grupo": "Domains",
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "summary": "Preview renewal notice preferences for selected domains.",
      "desc": "Select up to 25 saved pages. Repeated registrable domains count once. The server checks the account, organization and current revisions again. A stale preview changes nothing. This does not activate monitoring or purchase access.",
      "corpo": {
        "tabs": {
          "tipo": "string[]",
          "obrigatorio": true,
          "desc": "One to 25 owned tab IDs."
        },
        "lead_days": {
          "tipo": "int",
          "obrigatorio": true,
          "valores": [
            7,
            14,
            30
          ],
          "desc": "Days before domain expiry for renewal notices."
        }
      },
      "body": {
        "tabs": [
          "tab_example"
        ],
        "lead_days": 14
      },
      "retorno": {
        "items": {
          "tipo": "DomainAlertPreviewItem[]",
          "desc": "Exact selected domains and their saved settings."
        },
        "lead_days": {
          "tipo": "int",
          "desc": "Requested renewal notice period."
        },
        "preview": {
          "tipo": "string",
          "desc": "Revision-bound preview for this authorized selection."
        },
        "applied": {
          "tipo": "bool",
          "opcional": true,
          "desc": "True after PATCH commits the selected preferences."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/domain-portfolio/alerts\", {method:\"POST\",headers:{\"Content-Type\":\"application/json\",\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf},body:JSON.stringify({tabs:[\"TAB_ID\"],lead_days:14})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ items[{domain,lead_days,revision}], lead_days, preview, applied? }",
      "url": "https://pageaudit.online/api/tabs/domain-portfolio/alerts",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "PATCH",
      "path": "/api/tabs/domain-portfolio/alerts",
      "auth": "organization",
      "grupo": "Domains",
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "summary": "Apply the reviewed renewal notice preferences.",
      "desc": "Select up to 25 saved pages. Repeated registrable domains count once. The server checks the account, organization and current revisions again. A stale preview changes nothing. This does not activate monitoring or purchase access.",
      "corpo": {
        "tabs": {
          "tipo": "string[]",
          "obrigatorio": true,
          "desc": "One to 25 owned tab IDs."
        },
        "lead_days": {
          "tipo": "int",
          "obrigatorio": true,
          "valores": [
            7,
            14,
            30
          ],
          "desc": "Days before domain expiry for renewal notices."
        },
        "preview": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Exact preview from the POST response for these tabs and lead_days."
        }
      },
      "body": {
        "tabs": [
          "tab_example"
        ],
        "lead_days": 14,
        "preview": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "retorno": {
        "items": {
          "tipo": "DomainAlertPreviewItem[]",
          "desc": "Exact selected domains and their saved settings."
        },
        "lead_days": {
          "tipo": "int",
          "desc": "Requested renewal notice period."
        },
        "preview": {
          "tipo": "string",
          "desc": "Revision-bound preview for this authorized selection."
        },
        "applied": {
          "tipo": "bool",
          "opcional": true,
          "desc": "True after PATCH commits the selected preferences."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/domain-portfolio/alerts\", {method:\"PATCH\",headers:{\"Content-Type\":\"application/json\",\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf},body:JSON.stringify({tabs:[\"TAB_ID\"],lead_days:14,preview})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ items[{domain,lead_days,revision}], lead_days, preview, applied? }",
      "url": "https://pageaudit.online/api/tabs/domain-portfolio/alerts",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "GET",
      "path": "/api/tabs/:id/domain",
      "auth": "guest",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        }
      },
      "summary": "Read registration, contacts, TLS, DNS, mail policies, certificate issuance and server comparison.",
      "desc": "Pages share public observations by registry domain and HTTPS origin. Private settings and history stay with the account and organization. Missing data is not a healthy result.",
      "retorno": {
        "target": {
          "tipo": "DomainTarget",
          "desc": "Registry domain and HTTPS origin derived from the owned tab."
        },
        "sources": {
          "tipo": "DomainSource[]",
          "desc": "Up to seven public sources, including three explicit DKIM selectors. No external call on GET."
        },
        "settings": {
          "tipo": "DomainSettings",
          "nulo": true,
          "desc": "Private manual expiry and DNS options. Null for a guest or an IP target."
        },
        "monitor": {
          "tipo": "PageAuditDomainMonitor",
          "nulo": true,
          "desc": "Private monitor. Null before activation or for a guest."
        },
        "contacts": {
          "tipo": "DomainContacts",
          "nulo": true,
          "desc": "Contact visibility for an authenticated organization. Null without a complete matching collection. No contact values or digests."
        },
        "transparency": {
          "tipo": "DomainTransparency",
          "nulo": true,
          "desc": "Bounded certificate issuance observations from the existing collection. Read coverage and dates. Discovered names are not automatically monitored."
        },
        "sample": {
          "tipo": "DomainSample",
          "nulo": true,
          "desc": "Limited DNS and IPv4/IPv6 comparison. Null without an available sample."
        },
        "alerts": {
          "tipo": "DomainAlertSettings",
          "nulo": true,
          "desc": "Private renewal notice preferences. Null for guests or IP targets."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "curl -s \"$ORIGIN/api/tabs/TAB_ID/domain\" -H \"X-Guest-Token: $TOKEN\"",
      "returns": "{ target{hostname,domain,suffix,tls_endpoint}, sources[{kind,target,reference,revision,next_check_at,status,stale,registration?}], settings{revision,manual_expires_on,updated_at,dns}, monitor{stream,enabled,suspension,endpoints,maintenance_until,next_check_at,checked_at,revision,sequence}, contacts{complete,fields}, transparency{domain,certificates,observed_at,through,scope,logs_have_gaps,truncated}, sample{data,comparison}, alerts{lead_days,revision} }",
      "url": "https://pageaudit.online/api/tabs/:id/domain",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "POST",
      "path": "/api/tabs/:id/domain",
      "auth": "guest",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "summary": "Refresh due domain sources, certificate issuance and a limited DNS/TLS sample.",
      "desc": "Reuse fresh observations. A failed or partial query preserves the last complete observation and its date. Up to 100 cold page reads per owner and UTC day. This call does not enable a monitor or buy access.",
      "body": {},
      "retorno": {
        "target": {
          "tipo": "DomainTarget",
          "desc": "Registry domain and HTTPS origin derived from the owned tab."
        },
        "sources": {
          "tipo": "DomainSource[]",
          "desc": "Up to seven public sources, including three explicit DKIM selectors. No external call on GET."
        },
        "settings": {
          "tipo": "DomainSettings",
          "nulo": true,
          "desc": "Private manual expiry and DNS options. Null for a guest or an IP target."
        },
        "monitor": {
          "tipo": "PageAuditDomainMonitor",
          "nulo": true,
          "desc": "Private monitor. Null before activation or for a guest."
        },
        "contacts": {
          "tipo": "DomainContacts",
          "nulo": true,
          "desc": "Contact visibility for an authenticated organization. Null without a complete matching collection. No contact values or digests."
        },
        "transparency": {
          "tipo": "DomainTransparency",
          "nulo": true,
          "desc": "Bounded certificate issuance observations from the existing collection. Read coverage and dates. Discovered names are not automatically monitored."
        },
        "sample": {
          "tipo": "DomainSample",
          "nulo": true,
          "desc": "Limited DNS and IPv4/IPv6 comparison. Null without an available sample."
        },
        "alerts": {
          "tipo": "DomainAlertSettings",
          "nulo": true,
          "desc": "Private renewal notice preferences. Null for guests or IP targets."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "curl -s -X POST \"$ORIGIN/api/tabs/TAB_ID/domain\" -H \"X-Guest-Token: $TOKEN\" -H \"Content-Type: application/json\" -d '{}'",
      "returns": "{ target{hostname,domain,suffix,tls_endpoint}, sources[{kind,target,reference,revision,next_check_at,status,stale,registration?}], settings{revision,manual_expires_on,updated_at,dns}, monitor{stream,enabled,suspension,endpoints,maintenance_until,next_check_at,checked_at,revision,sequence}, contacts{complete,fields}, transparency{domain,certificates,observed_at,through,scope,logs_have_gaps,truncated}, sample{data,comparison}, alerts{lead_days,revision} }",
      "url": "https://pageaudit.online/api/tabs/:id/domain",
      "auth_detail": "Guest token (`POST /api/guest`) in `X-Guest-Token: pa_…`, `Authorization: Bearer pa_…` or `?guest_token=pa_…`. Only a token this site issued is accepted. A signed-in account (cookie) also works and takes precedence; after the claim the token owns nothing. An agent without an account can send the wallet credential instead (`Authorization: Bearer mmw_…`, from `POST /api/auth/wallet/agent/start`): the wallet owns tabs and audits like a guest, as `x402:<address>`, and they move to the account it is linked to."
    },
    {
      "method": "PATCH",
      "path": "/api/tabs/:id/domain",
      "auth": "organization",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "summary": "Update the private manual expiry, known DKIM selectors or expected IP rotation.",
      "desc": "Send manual_expires_on, dns, or both. Omitted settings are preserved. DNS requires both selectors and rotating_addresses. A manual date does not change registry data or confirm renewal payment. An identical retry is idempotent. A conflicting revision returns 409.",
      "corpo": {
        "revision": {
          "tipo": "number",
          "obrigatorio": true,
          "desc": "Current revision from the corresponding settings or monitor. Use zero for the first change."
        },
        "manual_expires_on": {
          "tipo": "string",
          "nulo": true,
          "desc": "YYYY-MM-DD from 1970 through 2199; null removes the manual date."
        },
        "dns": {
          "tipo": "DomainDnsSettings",
          "desc": "Known selectors and expected IP rotation. Selector discovery is not performed."
        }
      },
      "body": {
        "revision": 0,
        "manual_expires_on": "2027-10-14"
      },
      "retorno": {
        "target": {
          "tipo": "DomainTarget",
          "desc": "Registry domain and HTTPS origin derived from the owned tab."
        },
        "sources": {
          "tipo": "DomainSource[]",
          "desc": "Up to seven public sources, including three explicit DKIM selectors. No external call on GET."
        },
        "settings": {
          "tipo": "DomainSettings",
          "nulo": true,
          "desc": "Private manual expiry and DNS options. Null for a guest or an IP target."
        },
        "monitor": {
          "tipo": "PageAuditDomainMonitor",
          "nulo": true,
          "desc": "Private monitor. Null before activation or for a guest."
        },
        "contacts": {
          "tipo": "DomainContacts",
          "nulo": true,
          "desc": "Contact visibility for an authenticated organization. Null without a complete matching collection. No contact values or digests."
        },
        "transparency": {
          "tipo": "DomainTransparency",
          "nulo": true,
          "desc": "Bounded certificate issuance observations from the existing collection. Read coverage and dates. Discovered names are not automatically monitored."
        },
        "sample": {
          "tipo": "DomainSample",
          "nulo": true,
          "desc": "Limited DNS and IPv4/IPv6 comparison. Null without an available sample."
        },
        "alerts": {
          "tipo": "DomainAlertSettings",
          "nulo": true,
          "desc": "Private renewal notice preferences. Null for guests or IP targets."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/TAB_ID/domain\", {method:\"PATCH\",headers:{\"Content-Type\":\"application/json\",\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf},body:JSON.stringify({revision:0,manual_expires_on:\"2027-10-14\"})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ target{hostname,domain,suffix,tls_endpoint}, sources[{kind,target,reference,revision,next_check_at,status,stale,registration?}], settings{revision,manual_expires_on,updated_at,dns}, monitor{stream,enabled,suspension,endpoints,maintenance_until,next_check_at,checked_at,revision,sequence}, contacts{complete,fields}, transparency{domain,certificates,observed_at,through,scope,logs_have_gaps,truncated}, sample{data,comparison}, alerts{lead_days,revision} }",
      "url": "https://pageaudit.online/api/tabs/:id/domain",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "PUT",
      "path": "/api/tabs/:id/domain/monitor",
      "auth": "organization",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "summary": "Enable monitoring, stop monitoring, or pause notices until a maintenance deadline.",
      "desc": "Activation requires a current domain benefit, a preserved prepaid watch benefit or the account trial. Count a registry domain once per account, including its organization copies. Add only the tab's origin. HTTP receives DNS checks only. Stopping monitoring keeps history and does not cancel a subscription. Maintenance pauses notices while collection continues. Confirm enabled, no suspension and next_check_at after saving.",
      "corpo": {
        "revision": {
          "tipo": "number",
          "obrigatorio": true,
          "desc": "Current revision from the corresponding settings or monitor. Use zero for the first change."
        },
        "enabled": {
          "tipo": "boolean",
          "obrigatorio": true,
          "desc": "True enables collection; false stops it."
        },
        "maintenance_until": {
          "tipo": "number",
          "obrigatorio": true,
          "nulo": true,
          "desc": "Future Unix milliseconds, at most 30 days ahead; null ends maintenance."
        }
      },
      "body": {
        "revision": 0,
        "enabled": true,
        "maintenance_until": null
      },
      "retorno": {
        "monitor": {
          "tipo": "PageAuditDomainMonitor",
          "nulo": true,
          "desc": "Private monitor. Null before activation or for a guest."
        },
        "access_sources": {
          "tipo": "string[]",
          "desc": "Current server-checked rights: subscription, prepaid or trial. Several may apply. Empty when stopping. A paid period does not prove automatic renewal remains enabled."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "402": "domain_subscription_required or domain_monitor_limit. subscription gives offer_id, resource and checkout_available; no payment is made.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/TAB_ID/domain/monitor\", {method:\"PUT\",headers:{\"Content-Type\":\"application/json\",\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf},body:JSON.stringify({revision:0,enabled:true,maintenance_until:null})}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ monitor{stream,enabled,suspension,endpoints,maintenance_until,next_check_at,checked_at,revision,sequence}, access_sources }",
      "url": "https://pageaudit.online/api/tabs/:id/domain/monitor",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "GET",
      "path": "/api/tabs/:id/domain/history",
      "auth": "organization",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        }
      },
      "summary": "Read private domain incidents, recovery, acknowledgement and notice delivery.",
      "desc": "A first observed risk is an initial incident, not evidence of an external change. The first DNS and certificate-issuance snapshots are silent baselines. DNS changes, incompatible server samples and connection failures need two distinct observations. Expected IP rotation is silent. Repeated observations do not duplicate a notice. Contact changes expose field states only. Issuance does not prove installation. DNS records do not prove authentication of sent mail.",
      "query": {
        "before": {
          "tipo": "number",
          "desc": "Return events with a number below this cursor."
        }
      },
      "colecao": {
        "porPagina": 50,
        "teto": 50,
        "anda": "Send the last number as before. An empty page ends the history."
      },
      "retorno": {
        "history": {
          "tipo": "DomainHistoryEntry[]",
          "desc": "Up to 50 private events, newest first."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "curl -s \"$ORIGIN/api/tabs/TAB_ID/domain/history\" -b sessao.cookies -H \"X-Organization-Id: $ORGANIZATION\"",
      "returns": "{ history[{number,occurred_at,events,acknowledged_at,delivery,attempts,sent_at}] }",
      "url": "https://pageaudit.online/api/tabs/:id/domain/history",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "POST",
      "path": "/api/tabs/:id/domain/history/:number/ack",
      "auth": "organization",
      "grupo": "Domains",
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Owned tab ID from GET /api/tabs.",
          "exemplo": "tab_example"
        },
        "number": {
          "tipo": "number",
          "desc": "Positive history event number.",
          "exemplo": 1
        }
      },
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        },
        "Origin": {
          "tipo": "string",
          "desc": "Exact product origin for an account write.",
          "obrigatorio": true
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "desc": "Current token from /api/auth/bootstrap for an account write.",
          "obrigatorio": true
        }
      },
      "summary": "Acknowledge an expected incident and suppress its notice if delivery has not started.",
      "desc": "Preserve the event and observation. Acknowledgement does not erase a confirmed risk or retract a sent notice. Repetition is idempotent.",
      "body": {},
      "retorno": {
        "history": {
          "tipo": "DomainHistoryEntry[]",
          "desc": "Up to 50 private events, newest first."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(\"$ORIGIN/api/tabs/TAB_ID/domain/history/1/ack\", {method:\"POST\",headers:{\"Content-Type\":\"application/json\",\"X-Organization-Id\":organization,\"X-CSRF-Token\":csrf},body:\"{}\"}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ history[{number,occurred_at,events,acknowledged_at,delivery,attempts,sent_at}] }",
      "url": "https://pageaudit.online/api/tabs/:id/domain/history/:number/ack",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "GET",
      "path": "/api/domains/:key",
      "auth": "organization",
      "grupo": "Domains",
      "stream": true,
      "headers": {
        "X-Organization-Id": {
          "tipo": "string",
          "desc": "Current organization for an account-owned tab."
        }
      },
      "params": {
        "key": {
          "tipo": "string",
          "desc": "Opaque 64-character key from monitor.stream.",
          "exemplo": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        }
      },
      "summary": "Read a private monitor snapshot or follow its changes through the shared realtime channel.",
      "desc": "Use monitor.stream from the owned tab. Every subscription and event checks the current account and organization. On a signal, re-read the tab's domain and history.",
      "retorno": {
        "monitor": {
          "tipo": "PageAuditDomainMonitor",
          "nulo": true,
          "desc": "Private monitor. Null before activation or for a guest."
        }
      },
      "erros": {
        "400": "Invalid input or organization_required.",
        "401": "owner_required or session_ended.",
        "403": "account_required, invalid_csrf or organization access denied.",
        "404": "tab_not_found or domain_monitor_not_found. No disclosure across accounts or organizations.",
        "409": "Stale revision or storage limit. Read the current state before editing again.",
        "422": "domain_target_invalid: unsupported URL or registry suffix.",
        "429": "rate_limited: new source collections reached the allowance.",
        "503": "Storage or account service unavailable."
      },
      "exemplo": "await fetch(new URL(monitor.stream,\"$ORIGIN\"), {headers:{\"X-Organization-Id\":organization}}).then(r=>r.json());",
      "exemploLinguagem": "js",
      "returns": "{ monitor{stream,enabled,suspension,endpoints,maintenance_until,next_check_at,checked_at,revision,sequence} }",
      "url": "https://pageaudit.online/api/domains/:key",
      "auth_detail": "Signed-in account session plus `X-Organization-Id`. The shared account checks current membership and that PageAudit is enabled for the organization. Writes also need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`."
    },
    {
      "method": "GET",
      "path": "/api/billing",
      "auth": "none",
      "summary": "Tab allowance, prices, full x402 configuration and the state of the trial.",
      "grupo": "Billing",
      "retorno": "Billing",
      "exemplo": "curl -s $ORIGIN/api/billing -H \"X-Guest-Token: $PA\"",
      "returns": "{ provider, mode, network, chain_id, pay_to, homolog, dev, dev_gate, alternavel, gratis?, cobrados?, facilitator, asset, asset_address, faucet, wallets, product, free_max_tabs, prices{extra_tab_usd}, usage{tabs,free_slots_left}, trial{days,active,days_left?,ends_at} }",
      "url": "https://pageaudit.online/api/billing",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/me",
      "auth": "session",
      "summary": "The session's account, its last 50 audits and the state of the trial.",
      "desc": "The account is the global account you sign in to at `/conta/global`, the same in every product; `user.id` is its id, which owns the tabs and audits here. There is no activation step.",
      "grupo": "Account",
      "retorno": {
        "user": {
          "tipo": "Conta",
          "desc": "The person who owns the session."
        },
        "profile": {
          "tipo": "object",
          "desc": "Global profile: `name`, `locale`, `timeZone`, `theme`, `revision`."
        },
        "audits": {
          "tipo": "AuditGravado[]",
          "desc": "The account's 50 most recent audits."
        },
        "trial": {
          "tipo": "Trial",
          "desc": "The account's trial: how many days, whether active and when it ends."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/me\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ user{id,email}, profile, audits[{audit_id,url,score,summary,counts,issues,jsonLd,headers,truncated,share_slug,created_at}], trial{days,active,days_left?,ends_at} }",
      "url": "https://pageaudit.online/api/me",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/auth/bootstrap",
      "auth": "none",
      "grupo": "Account",
      "summary": "Prepare the browser for global sign-in.",
      "desc": "Sets a host-only HttpOnly browser cookie. CSRF is bound to the current session. No CORS.",
      "retorno": {
        "csrf": {
          "tipo": "string",
          "desc": "X-CSRF-Token"
        },
        "context": {
          "tipo": "string",
          "desc": "Opaque view context, also in X-MM-Context; not a credential / contexto opaco da vista, não é credencial."
        }
      },
      "erros": {
        "400": "invalid_request",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable: a sessão anterior é preservada / the previous session is preserved"
      },
      "returns": "{ csrf, context }",
      "url": "https://pageaudit.online/api/auth/bootstrap",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/account/profile",
      "auth": "session",
      "grupo": "Account",
      "exemplo": "await fetch(\"$ORIGIN/api/account/profile\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "summary": "Read your global profile.",
      "desc": "Reads current preferences from the account. Edit them on your account page; products never own a separate profile.",
      "retorno": {
        "_texto": "{profile:{name,locale,timeZone,theme,revision}}"
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "returns": "{profile:{name,locale,timeZone,theme,revision}}",
      "url": "https://pageaudit.online/api/account/profile",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/account/avatar",
      "auth": "session",
      "grupo": "Account",
      "exemplo": "await fetch(\"$ORIGIN/api/account/avatar\", {credentials: \"same-origin\"}).then(r => {if (!r.ok) throw new Error(\"HTTP \" + r.status); return r.blob();});",
      "exemploLinguagem": "js",
      "summary": "Read your global profile photo.",
      "desc": "Private WebP, up to 64 KiB, no cache. Change it on your account. No user ID or object URL accepted.",
      "retorno": {
        "_texto": "image/webp; Cache-Control: no-store"
      },
      "erros": {
        "401": "invalid_session",
        "404": "not_found: no photo / sem foto",
        "503": "auth_unavailable"
      },
      "returns": "image/webp; Cache-Control: no-store",
      "url": "https://pageaudit.online/api/account/avatar",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/logout",
      "auth": "session",
      "grupo": "Account",
      "exemplo": "// Execute no console da página do produto / Run in the product page console.\n(async () => {\n  const origin = \"$ORIGIN\";\n  const {csrf} = await fetch(origin + \"/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(origin + \"/api/auth/logout\", {\n    method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({})\n  });\n  if (!r.ok) throw new Error(\"Auth HTTP \" + r.status);\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "summary": "Revoke this product session.",
      "desc": "Bootstrap/CSRF must belong to this browser and session. Other product sessions remain active.",
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_request",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable: a sessão anterior é preservada / the previous session is preserved"
      },
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/auth/logout",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/pwa/install",
      "auth": "session",
      "grupo": "Installed app",
      "summary": "Prepare a personal app installation.",
      "desc": "Requires recent sign-in, exact Origin and browser CSRF. Returns a private signed PIN and a no-store manifest address. At most 10 active installations per account and product.",
      "corpo": {
        "path": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Same-origin task path, up to 1600 characters."
        },
        "state": {
          "tipo": "object",
          "obrigatorio": true,
          "desc": "Up to 30 allowlisted preference keys and string values."
        },
        "manifest": {
          "tipo": "object",
          "obrigatorio": true,
          "desc": "Public app name and same-origin icons."
        }
      },
      "retorno": {
        "_texto": "{pin,path,manifest}"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const manifest = await fetch(document.querySelector('link[rel=\"manifest\"]').href).then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/auth/pwa/install\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({path: location.pathname + location.search, state: {}, manifest})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "erros": {
        "400": "invalid_installation_path / invalid_installation_state / invalid_installation_manifest",
        "401": "invalid_session / reauth_required",
        "409": "installation_not_created",
        "503": "auth_unavailable"
      },
      "returns": "{pin,path,manifest}",
      "url": "https://pageaudit.online/api/auth/pwa/install",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/pwa/restore",
      "auth": "none",
      "grupo": "Installed app",
      "summary": "Restore an installed app.",
      "desc": "Requires the signed PIN, exact Origin and browser CSRF. Verifies host, product, account status, security version and installation revocation. Keeps the original authentication time. Sensitive actions still require recent sign-in.",
      "corpo": {
        "pin": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Private signed installation credential."
        }
      },
      "retorno": {
        "_texto": "{ok:true,state,path,manifest}; HttpOnly session cookies"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/auth/pwa/restore\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({pin: \"…\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "erros": {
        "401": "invalid_installation",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable"
      },
      "returns": "{ok:true,state,path,manifest}; HttpOnly session cookies",
      "url": "https://pageaudit.online/api/auth/pwa/restore",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/auth/pwa/manifest",
      "auth": "none",
      "grupo": "Installed app",
      "summary": "Read the private installation manifest.",
      "desc": "Requires the installation cookie and its manifest key. Private, no-store. The start URL keeps the PIN in the fragment.",
      "query": {
        "key": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Key in the manifest address that the install or restore call returned."
        }
      },
      "retorno": {
        "_texto": "application/manifest+json"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/auth/pwa/manifest?key=…\", {credentials: \"same-origin\"}).then(r => r.json())",
      "exemploLinguagem": "js",
      "erros": {
        "401": "invalid_installation"
      },
      "returns": "application/manifest+json",
      "url": "https://pageaudit.online/api/auth/pwa/manifest",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/account/keys",
      "auth": "session",
      "grupo": "Account",
      "summary": "List your API keys in this product.",
      "desc": "Never returns the key itself: name, last 4 characters, organization, creation, last use (hourly) and whether it still works.",
      "retorno": {
        "keys": {
          "tipo": "object[]",
          "desc": "`id`, `name`, `organizationId`, `last4`, `createdAt`, `lastUsedAt`, `revokedAt`, `active` (false when revoked or stopped by a password change / ending all sessions)."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/account/keys\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ keys }",
      "url": "https://pageaudit.online/api/account/keys",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/keys/create",
      "auth": "session",
      "grupo": "Account",
      "summary": "Create an API key for agents and scripts.",
      "desc": "Needs a sign-in within the last 2 hours; an organization key also needs a second factor in the session and the owner/admin role with this product enabled. At most 10 live keys per account and product. The key (`secret`) is returned ONCE.",
      "corpo": {
        "name": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Up to 60 characters."
        },
        "organizationId": {
          "tipo": "string",
          "nulo": true,
          "obrigatorio": true,
          "desc": "`null` for an account key."
        }
      },
      "body": {
        "name": "agent",
        "organizationId": null
      },
      "retorno": {
        "key": {
          "tipo": "object",
          "desc": "`id`, `name`, `organizationId`, `last4`, `createdAt`."
        },
        "secret": {
          "tipo": "string",
          "desc": "`mmk_…`, shown once."
        }
      },
      "erros": {
        "400": "invalid_key_name / invalid_organization",
        "401": "invalid_session / reauth_required",
        "403": "invalid_origin / invalid_csrf / organization_forbidden / organization_mfa_required",
        "409": "key_limit_reached",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/keys/create\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({name: \"agent\", organizationId: null})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ key, secret }",
      "url": "https://pageaudit.online/api/account/keys/create",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/keys/revoke",
      "auth": "session",
      "grupo": "Account",
      "summary": "Revoke one of your API keys.",
      "desc": "Stops the key at once. Repeating is harmless.",
      "corpo": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The key `id`."
        }
      },
      "body": {
        "id": "…"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_key_id",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "404": "key_not_found",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/keys/revoke\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({id: \"…\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/account/keys/revoke",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/account/apps",
      "auth": "session",
      "grupo": "Account",
      "summary": "List the apps connected to your account in this product.",
      "desc": "The MCP clients you signed in with (OAuth), such as claude.ai or ChatGPT. Never returns a token.",
      "retorno": {
        "apps": {
          "tipo": "object[]",
          "desc": "Up to 50: `id`, `nome` (the app; its host when it identifies itself by a metadata document), `criadoEm`, `usadoEm` (hourly, or null)."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/account/apps\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ apps }",
      "url": "https://pageaudit.online/api/account/apps",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/apps/revoke",
      "auth": "session",
      "grupo": "Account",
      "summary": "Disconnect an app from your account.",
      "desc": "Stops the app's access at once; it asks you to sign in again. Repeating is harmless.",
      "corpo": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The app `id`."
        }
      },
      "body": {
        "id": "…"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_app_id",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "404": "app_not_found",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/apps/revoke\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({id: \"…\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/account/apps/revoke",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/account/callouts",
      "auth": "session",
      "grupo": "Account",
      "summary": "List the notices that you closed in this product.",
      "desc": "The keys of the notices that stay closed for your account, on every device, such as the explanation of an area.",
      "retorno": {
        "callouts": {
          "tipo": "string[]",
          "desc": "Up to 200 keys, in alphabetical order."
        }
      },
      "erros": {
        "401": "invalid_session",
        "403": "product_required",
        "503": "auth_unavailable"
      },
      "exemplo": "await fetch(\"$ORIGIN/api/account/callouts\", {credentials: \"same-origin\"}).then(r => r.json());",
      "exemploLinguagem": "js",
      "returns": "{ callouts }",
      "url": "https://pageaudit.online/api/account/callouts",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/callouts/dismiss",
      "auth": "session",
      "grupo": "Account",
      "summary": "Close a notice for good.",
      "desc": "The notice does not open again for your account in this product. Repeating is harmless.",
      "corpo": {
        "key": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The notice key: letters, digits and `_.:-`, up to 80."
        }
      },
      "body": {
        "key": "financeiro_expense"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_callout",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "409": "too_many_callouts",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/callouts/dismiss\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({key: \"financeiro_expense\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/account/callouts/dismiss",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/account/callouts/reopen",
      "auth": "session",
      "grupo": "Account",
      "summary": "Open a closed notice again.",
      "desc": "Undoes the close: the notice shows again for your account in this product. A product also keeps an on/off choice here, as the hidden values of Finance. Repeating is harmless.",
      "corpo": {
        "key": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "The notice key: letters, digits and `_.:-`, up to 80."
        }
      },
      "body": {
        "key": "financeiro_expense"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true"
        }
      },
      "erros": {
        "400": "invalid_callout",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "503": "auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/account/callouts/reopen\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({key: \"financeiro_expense\"})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/account/callouts/reopen",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/wallet/agent/start",
      "auth": "none",
      "grupo": "Account",
      "summary": "Start the wallet credential of an agent without an account.",
      "desc": "No account: the wallet proves it is hers by signing the message (EIP-4361). Signing does not pay or transfer anything. The message names this product and lasts 5 minutes. Before you sign, compare the message with the URL that you called. The domain on the first line must be the host of that URL. URI must be its origin. If one is different, do not sign. Sign it with personal_sign and send it to /api/auth/wallet/agent/token.",
      "corpo": {
        "address": {
          "tipo": "string",
          "desc": "0x…, a carteira (EOA ou contrato na Base)",
          "obrigatorio": true
        }
      },
      "body": {
        "address": "0x2c7536E3605D9C16a7a3D7b1898e529396a65c23"
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/auth/wallet/agent/start -H 'content-type: application/json' -d '{\"address\":\"0x2c7536E3605D9C16a7a3D7b1898e529396a65c23\"}'",
      "retorno": {
        "message": {
          "tipo": "string",
          "desc": "EIP-4361"
        },
        "issuedAt": {
          "tipo": "number",
          "desc": "segundos / seconds"
        },
        "expiresIn": {
          "tipo": "number",
          "desc": "300"
        }
      },
      "erros": {
        "400": "invalid_wallet_address",
        "503": "auth_unavailable"
      },
      "returns": "{ message, issuedAt, expiresIn }",
      "url": "https://pageaudit.online/api/auth/wallet/agent/start",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/auth/wallet/agent/token",
      "auth": "none",
      "grupo": "Account",
      "summary": "Get the wallet credential (mmw_…) of this product.",
      "desc": "The wallet's signature of the start message, checked here (own key) or on Base (contract, EIP-1271/6492). The credential lasts 30 days: send `Authorization: Bearer mmw_…` to the routes that accept the wallet as owner (owner `x402:<address>`). Once the wallet is linked to an account, what it did here moves to the account when the person signs in.",
      "corpo": {
        "address": {
          "tipo": "string",
          "desc": "0x…, a mesma carteira do start / the same wallet as in start",
          "obrigatorio": true
        },
        "issuedAt": {
          "tipo": "number",
          "desc": "o issuedAt do start / start's issuedAt",
          "obrigatorio": true
        },
        "signature": {
          "tipo": "string",
          "desc": "personal_sign (0x…)",
          "obrigatorio": true
        }
      },
      "body": {
        "address": "0x2c7536E3605D9C16a7a3D7b1898e529396a65c23",
        "issuedAt": 1791400000,
        "signature": "0x…"
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/auth/wallet/agent/token -H 'content-type: application/json' -d '{\"address\":\"0x2c7536E3605D9C16a7a3D7b1898e529396a65c23\",\"issuedAt\":1791400000,\"signature\":\"0x…\"}'",
      "retorno": {
        "token": {
          "tipo": "string",
          "desc": "mmw_…"
        },
        "owner": {
          "tipo": "string",
          "desc": "x402:0x…"
        },
        "expiresAt": {
          "tipo": "number",
          "desc": "segundos / seconds"
        }
      },
      "erros": {
        "400": "invalid_body",
        "401": "invalid_wallet",
        "429": "wallet_limited",
        "503": "wallet_unavailable / auth_unavailable"
      },
      "returns": "{ token, owner, expiresAt }",
      "url": "https://pageaudit.online/api/auth/wallet/agent/token",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/auth/claim/wallets",
      "auth": "session",
      "grupo": "Account",
      "summary": "Move to the account what its wallets did without an account.",
      "desc": "The sign-in window calls it after every sign-in. For each wallet linked to the account: the rights bought here by x402 (owner x402:<address>) and what this product keeps for that owner. Repeating moves nothing.",
      "exemplo": "await MMConta.fetch(new URL(\"$ORIGIN/api/auth/claim/wallets\").pathname, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: \"{}\" })",
      "retorno": {
        "carteiras": {
          "tipo": "number",
          "desc": "wallets"
        },
        "direitos": {
          "tipo": "number",
          "desc": "rights moved"
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "auth_unavailable"
      },
      "returns": "{ carteiras, direitos }",
      "url": "https://pageaudit.online/api/auth/claim/wallets",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/auth/claim",
      "auth": "session",
      "grupo": "Account",
      "summary": "Moves what the guest created to the signed-in account: tabs and audits.",
      "desc": "Bootstrap/CSRF of this browser required; the page does it right after sign-in. Only what the guest still owns moves, in one transaction, and anything that collides with what the account already has stays with the guest. What the guest bought moves too. An old unsigned token that owns nothing here is refused. Repeating it is harmless (moves zero).",
      "corpo": {
        "guest_token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Guest `pa_…` of this browser."
        }
      },
      "body": {
        "guest_token": "pa_…"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Whether the guest was recognized and moved."
        },
        "claimed": {
          "tipo": "object",
          "desc": "`product.movidos` (rows moved, per table), `product.apagados` (guest duplicates dropped) and `product.direitos` (purchases moved)."
        }
      },
      "erros": {
        "400": "invalid_product_claim / invalid_body",
        "401": "invalid_session",
        "403": "invalid_origin / invalid_csrf",
        "409": "unknown_guest (em `claimed.reason` / in `claimed.reason`)",
        "503": "product_claim_pending / auth_unavailable"
      },
      "exemplo": "(async () => {\n  const {csrf} = await fetch(\"$ORIGIN/api/auth/bootstrap\").then(r => r.json());\n  const r = await fetch(\"$ORIGIN/api/auth/claim\", {method: \"POST\", credentials: \"same-origin\",\n    headers: {\"Content-Type\": \"application/json\", \"X-CSRF-Token\": csrf},\n    body: JSON.stringify({guest_token: localStorage.getItem(\"pageaudit_guest\")})});\n  return r.json();\n})();",
      "exemploLinguagem": "js",
      "returns": "{ ok, claimed }",
      "url": "https://pageaudit.online/api/auth/claim",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/contact",
      "auth": "none",
      "summary": "Write to the people behind the product — free, for people and agents alike.",
      "grupo": "Contact",
      "desc": "One route for a question and for a sponsorship, partnership or ad proposal (`tipo`, with the placements of `GET /api/partners`). No captcha, no account, no payment. One message every 10 seconds per network: one that arrives sooner waits its turn and then goes out — no error. The message reaches the team by e-mail, with `email` as the reply address. For voice, send multipart/form-data: `dados` is this JSON (up to 32 KiB), and `audio` is one WebM, Ogg or M4A audio file (up to 2 MB). Audio can replace message. The browser records up to two minutes. Audio is attached to the owner's e-mail.",
      "corpo": {
        "name": {
          "tipo": "string",
          "desc": "What to call the person writing.",
          "obrigatorio": true
        },
        "email": {
          "tipo": "string",
          "desc": "Where to reply.",
          "obrigatorio": true
        },
        "message": {
          "tipo": "string",
          "desc": "What you want to say.",
          "obrigatorio": true
        },
        "whatsapp": {
          "tipo": "string",
          "desc": "Optional reply number with country code, such as +5561999999999."
        },
        "prefer_whatsapp": {
          "tipo": "bool",
          "desc": "Request a WhatsApp reply. Requires whatsapp."
        },
        "tipo": {
          "tipo": "string",
          "desc": "Proposal: `patrocinio`, `parceria`, `anuncio` or `consultoria`. Turns on the fields below."
        },
        "empresa": {
          "tipo": "string",
          "desc": "Who is proposing, when it is a company."
        },
        "site": {
          "tipo": "string",
          "desc": "Website of who is proposing."
        },
        "orcamento": {
          "tipo": "string",
          "desc": "Advertising: `ate_100`, `100_500`, `500_2000`, `2000_mais` or `a_combinar`. Consulting, in US dollars: `ate_2000`, `2000_5000`, `5000_10000`, `10000_mais` or `nao_sei`."
        },
        "espaco": {
          "tipo": "string[]",
          "desc": "Placement ids from `GET /api/partners`, up to 6."
        },
        "duracao": {
          "tipo": "string",
          "desc": "Exposure in days: `30`, `90` or `365`."
        },
        "pagamento": {
          "tipo": "string",
          "desc": "`usdc`, `deposito` or `a_combinar`."
        },
        "projeto": {
          "tipo": "string",
          "desc": "Consulting: the kind of project (`llm_privado`, `voz`, `dados_sensiveis`, `gpu`, `integracao` or `outro`)."
        },
        "prazo": {
          "tipo": "string",
          "desc": "Consulting: when to start (`agora`, `1_mes`, `1_3_meses` or `explorando`)."
        },
        "formato": {
          "tipo": "string",
          "desc": "Consulting: `projeto` (per scope), `mensal` (monthly) or `assessoria` (advisory)."
        },
        "pais": {
          "tipo": "string",
          "desc": "Consulting: the country of the person asking."
        },
        "fuso": {
          "tipo": "string",
          "desc": "Consulting: the IANA time zone, such as `America/New_York`."
        },
        "horarios": {
          "tipo": "string[]",
          "desc": "Consulting: up to 3 times for the call, `YYYY-MM-DDTHH:MM` in that time zone."
        }
      },
      "body": {
        "name": "Agent",
        "email": "agent@example.com",
        "message": "hello from an agent"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true` when the message was accepted."
        }
      },
      "erros": {
        "400": "Validation: `code` names the field.",
        "503": "Contact is not configured on this server."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/contact -H 'content-type: application/json' -d '{\"name\":\"Agent\",\"email\":\"agent@example.com\",\"message\":\"hello from an agent\"}'",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/contact",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/visit",
      "auth": "none",
      "summary": "Ping from the interface that increments the day's visits. Agents need not call it.",
      "grupo": "Billing",
      "desc": "Smoke does not count: `X-MM-Smoke`, User-Agent `mm-smoke` or `smoke: true` in the body come in as `counted: false`. Only the page's own ping counts: a call from outside a browser page of the same site (robot User-Agent, or no `Sec-Fetch-Site: same-origin`) comes in as `counted: false` with `reason: \"robo\"`.",
      "corpo": {
        "p": {
          "tipo": "string",
          "desc": "Path of the visited page."
        },
        "smoke": {
          "tipo": "bool",
          "desc": "`true` marks the call as a test and it does not enter the count."
        }
      },
      "body": {
        "p": "/"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "Always `true`."
        },
        "counted": {
          "tipo": "bool",
          "desc": "Whether the visit entered the day's count."
        },
        "reason": {
          "tipo": "string",
          "desc": "Why it did not count, when `counted` is `false`.",
          "opcional": true
        }
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/visit -H 'content-type: application/json' -d '{\"p\":\"/\",\"smoke\":true}'",
      "returns": "{ ok, counted, reason? }",
      "url": "https://pageaudit.online/api/visit",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/erro-cliente",
      "auth": "none",
      "summary": "Browser error report, sent by the interface itself. Agents need not call it.",
      "grupo": "Operations",
      "desc": "The interface reports on its own JS errors, unhandled rejections, scripts/CSS that failed to load and CSP blocks — once per session — and the app reports handled failures through `window.mmErro.relata`. The server validates the envelope, redacts credentials, e-mails and phone numbers, merges repeats of the same failure per minute and records an operational event; nothing is written to a database. It keeps no IP, cookie, query string or full User-Agent. Always answers 204, even for an invalid report.",
      "corpo": {
        "code": {
          "tipo": "string",
          "desc": "Failure code, `UI-` + letters/digits (`UI-JS-001` global error, `UI-PROMESSA-001`, `UI-RECURSO-001`, `UI-CSP-001`, `UI-APP-001` app report).",
          "obrigatorio": true
        },
        "phase": {
          "tipo": "string",
          "desc": "Where it broke, lowercase: `global`, `promessa`, `script`, `load_list`…",
          "obrigatorio": true
        },
        "path": {
          "tipo": "string",
          "desc": "Path of the open page, without query; numbers, hashes and tokens in it are kept as `:id`."
        },
        "message": {
          "tipo": "string",
          "desc": "Error message, up to 2000 characters."
        },
        "stack": {
          "tipo": "string",
          "desc": "Stack trace, up to 12000 characters."
        },
        "source": {
          "tipo": "string",
          "desc": "Originating script; only its path is kept."
        },
        "line": {
          "tipo": "int",
          "desc": "Line in the originating script."
        },
        "column": {
          "tipo": "int",
          "desc": "Column in the originating script."
        },
        "visivel": {
          "tipo": "bool",
          "desc": "Whether the tab was visible when it broke."
        },
        "build": {
          "tipo": "string",
          "desc": "Build of the reporting page (its `<meta name=\"build\">`), up to 64 letters, digits, `.`, `_` or `-`; it dates the failure."
        }
      },
      "body": {
        "code": "UI-APP-001",
        "phase": "carregar_lista",
        "path": "/",
        "message": "lista 500"
      },
      "retorno": {
        "_texto": "204 with no body, always — an invalid, repeated or over-cap report also gets 204."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/erro-cliente -H 'content-type: application/json' -d '{\"code\":\"UI-APP-001\",\"phase\":\"carregar_lista\",\"path\":\"/\",\"message\":\"lista 500\"}'",
      "returns": "204 with no body, always — an invalid, repeated or over-cap report also gets 204.",
      "url": "https://pageaudit.online/api/erro-cliente",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/pagamento/aberto",
      "auth": "none",
      "statusOk": 202,
      "summary": "The interface reports a visible payment prompt. Agents must not call this route.",
      "grupo": "Operations",
      "desc": "An empty same-origin report, sent automatically when a payment prompt becomes visible. It starts no payment, grants no access and receives no identity or credentials. It writes no database row per report. Counts events, not unique people. The private operator dashboard separates API payment requests and browser payment views per UTC day; the two counts may overlap.",
      "headers": {
        "Origin": {
          "tipo": "string",
          "desc": "The page origin, identical to this route's origin.",
          "obrigatorio": true
        },
        "Sec-Fetch-Site": {
          "tipo": "string",
          "desc": "`same-origin`, set by the browser.",
          "obrigatorio": true
        },
        "X-MM-Payment-View": {
          "tipo": "string",
          "desc": "`1`, set by the shared component.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "_texto": "202 with no body when accepted; 204 when ignored. Always no-store."
      },
      "returns": "202 with no body when accepted; 204 when ignored. Always no-store.",
      "url": "https://pageaudit.online/api/pagamento/aberto",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/funil",
      "auth": "none",
      "statusOk": 202,
      "summary": "The interface reports the steps of a visit (conversion funnel). Agents must not call this route.",
      "grupo": "Operations",
      "desc": "A same-origin batch sent by the page itself: page views, engagement, offer in view, buy click, payment window, payment sent or accepted. It keeps the browser's random id, the path without query, the referring host and the utm tags; never IP, e-mail or account. It writes no database row: one line per batch in the day's journal, with a cap. Declared robots and smoke tests are left out.",
      "headers": {
        "Origin": {
          "tipo": "string",
          "desc": "The page origin, identical to this route's origin.",
          "obrigatorio": false
        },
        "Content-Type": {
          "tipo": "string",
          "desc": "`application/json` or `text/plain`.",
          "obrigatorio": true
        }
      },
      "corpo": {
        "v": {
          "tipo": "number",
          "desc": "Batch version: `1`.",
          "obrigatorio": true
        },
        "vid": {
          "tipo": "string",
          "desc": "This browser's random id (UUID v4).",
          "obrigatorio": true
        },
        "sid": {
          "tipo": "string",
          "desc": "Session id (30 idle minutes end it).",
          "obrigatorio": true
        },
        "sn": {
          "tipo": "number",
          "desc": "This browser's session number.",
          "obrigatorio": true
        },
        "pv": {
          "tipo": "string",
          "desc": "Page view id.",
          "obrigatorio": true
        },
        "e": {
          "tipo": "object[]",
          "desc": "Up to 40 events `{ t, n, p? }` from the funnel vocabulary.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "_texto": "202 with no body when stored; 204 when ignored. Always no-store."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/funil -H 'content-type: text/plain' -H 'x-mm-smoke: 1' -d '{\"v\":1,\"vid\":\"0f8b9c1e-2a3b-4c5d-8e6f-7a8b9c0d1e2f\",\"sid\":\"s1abcdefgh\",\"sn\":1,\"pv\":\"p1abcdefgh\",\"e\":[{\"t\":0,\"n\":\"pagina\"}]}'",
      "returns": "202 with no body when stored; 204 when ignored. Always no-store.",
      "url": "https://pageaudit.online/api/funil",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/funil/arquivos",
      "auth": "none",
      "summary": "Operator: the funnel journal days kept by this app, with the size of each.",
      "grupo": "Operations",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>`.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "v": {
          "tipo": "number",
          "desc": "Stored batch version."
        },
        "produto": {
          "tipo": "string",
          "desc": "The product."
        },
        "teto": {
          "tipo": "object",
          "desc": "`bytesDia` and `dias` kept."
        },
        "dias": {
          "tipo": "object[]",
          "desc": "`{ dia, bytes }`, oldest to today."
        }
      },
      "erros": {
        "401": "Unauthorized",
        "503": "Not configured"
      },
      "exemplo": "curl -s $ORIGIN/api/funil/arquivos -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ v, produto, teto, dias }",
      "url": "https://pageaudit.online/api/funil/arquivos",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/funil/arquivo",
      "auth": "none",
      "summary": "Operator: a slice of one funnel journal day, as NDJSON, from a byte offset.",
      "grupo": "Operations",
      "desc": "Up to 4 MiB per response, cut at the last whole line. `X-MM-Funil-Proximo` says where to ask for the rest; `X-MM-Funil-Tamanho`, the day's size now.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>`.",
          "obrigatorio": true
        }
      },
      "query": {
        "dia": {
          "tipo": "string",
          "desc": "The day, `YYYY-MM-DD` (UTC).",
          "obrigatorio": true
        },
        "desde": {
          "tipo": "number",
          "desc": "The byte to read from; `0` at the start.",
          "obrigatorio": false
        }
      },
      "retorno": {
        "_texto": "JSON lines, one per stored batch."
      },
      "erros": {
        "400": "Invalid parameters",
        "401": "Unauthorized",
        "503": "Not configured"
      },
      "exemplo": "curl -s \"$ORIGIN/api/funil/arquivo?dia=2026-09-27&desde=0\" -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "JSON lines, one per stored batch.",
      "url": "https://pageaudit.online/api/funil/arquivo",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "The product's public numbers: traffic, agents, usage and reliability, no money.",
      "desc": "Projection published hourly by the house collector, rounded to two significant digits; `null` is a missing measurement, never zero. 15-minute cache with ETag (`If-None-Match` → 304). There is no way to send numbers through this route: publishing belongs to the collector, with its own token.",
      "retorno": {
        "v": {
          "tipo": "int",
          "desc": "Contract version (1)."
        },
        "produto": {
          "tipo": "string",
          "desc": "Product id."
        },
        "publicado": {
          "tipo": "bool",
          "desc": "`false` before the collector's first publication; then only these five keys come."
        },
        "atualizado_em": {
          "tipo": "string",
          "desc": "When the collector published (ISO 8601).",
          "nulo": true
        },
        "stale": {
          "tipo": "bool",
          "desc": "`true` when the projection is older than 26 h."
        },
        "nome": {
          "tipo": "string",
          "desc": "Product name.",
          "opcional": true
        },
        "desde": {
          "tipo": "string",
          "desc": "First day the series covers.",
          "nulo": true,
          "opcional": true
        },
        "fuso": {
          "tipo": "string",
          "desc": "Traffic time zone. Audience periods state their time zone in `pessoas.medicao`.",
          "opcional": true
        },
        "hoje": {
          "tipo": "object",
          "desc": "Today: pages by class (human, AI, bot), API calls by class, machine-surface reads and product usage.",
          "opcional": true
        },
        "dias": {
          "tipo": "object[]",
          "desc": "Up to 31 days, oldest first: `dia`, `paginas`, `api`, `api_ia`, `maquina`, `visitantes`, `uso`.",
          "opcional": true
        },
        "janelas": {
          "tipo": "object",
          "desc": "7- and 30-day sums (`d7`, `d30`).",
          "opcional": true
        },
        "visitantes": {
          "tipo": "object",
          "desc": "Unique browsers with recorded interaction over 7 days.",
          "opcional": true
        },
        "pessoas": {
          "tipo": "object",
          "desc": "First-party measurement of browsers confirmed by interaction: unique browsers, sessions and pages over 7 and 30 days, segments and coverage. `null` means no measurement. Browsers are not verified people.",
          "nulo": true,
          "opcional": true
        },
        "agentes": {
          "tipo": "object",
          "desc": "The AI agents and bots that read the most, 7 days.",
          "opcional": true
        },
        "superficies": {
          "tipo": "object",
          "desc": "Reads of OKF, llms, well-known, OpenAPI and MCP over 7 days.",
          "opcional": true
        },
        "mcp": {
          "tipo": "object",
          "desc": "MCP calls over 7 days.",
          "opcional": true
        },
        "uso": {
          "tipo": "object",
          "desc": "Real product usage per resource: label, today, 7 and 30 days.",
          "opcional": true
        },
        "contas": {
          "tipo": "object",
          "desc": "Users and guests.",
          "nulo": true,
          "opcional": true
        },
        "confiabilidade": {
          "tipo": "object",
          "desc": "Share of requests without 5xx over 7 days, and the live build.",
          "opcional": true
        },
        "catalogo": {
          "tipo": "object",
          "desc": "Size of the catalog, when the product has one.",
          "nulo": true,
          "opcional": true
        },
        "apoio": {
          "tipo": "object",
          "desc": "Measured sponsors: 1-second exposures and native clicks per page in confirmed sessions, 7-day CTR, observed days and coverage. Empty means no history, not zero CTR.",
          "opcional": true
        }
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine",
      "returns": "{ v, produto, publicado, atualizado_em, stale, nome?, desde?, fuso?, hoje?, dias?, janelas?, visitantes?, pessoas?, agentes?, superficies?, mcp?, uso?, contas?, confiabilidade?, catalogo?, apoio? }",
      "url": "https://pageaudit.online/api/vitrine",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine/operador",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "The product's full document on the operator panel — operator token only.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` — the operator class.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "produto": {
          "tipo": "string",
          "desc": "Product id."
        },
        "atualizado_em": {
          "tipo": "string",
          "desc": "When the collector published.",
          "nulo": true
        },
        "operador": {
          "tipo": "object",
          "desc": "The collector's full document, with what the public projection leaves out.",
          "nulo": true
        }
      },
      "erros": {
        "401": "No token, wrong token or a token of another class.",
        "503": "Worker without `METRICS_TOKEN` or without the control plane."
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine/operador -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ produto, atualizado_em, operador }",
      "url": "https://pageaudit.online/api/vitrine/operador",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine/painel",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "The whole house panel, in the shape the gm reads — operator token only.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` — the operator class.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "apps": {
          "tipo": "object[]",
          "desc": "One operator document per product, ordered by id."
        },
        "updated": {
          "tipo": "string",
          "desc": "When the collector closed the round.",
          "opcional": true
        },
        "totals": {
          "tipo": "object",
          "desc": "House totals.",
          "opcional": true
        }
      },
      "erros": {
        "401": "No token, wrong token or a token of another class.",
        "503": "Worker without `METRICS_TOKEN` or without the control plane."
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine/painel -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ apps, updated?, totals? }",
      "url": "https://pageaudit.online/api/vitrine/painel",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/vitrine/cursores",
      "auth": "none",
      "grupo": "Public stats",
      "summary": "Per-product cursors: `borda`, `borda5xx` (5xx only), and `cli`. Operator token only.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` — the operator class.",
          "obrigatorio": true
        }
      },
      "retorno": {
        "_texto": "JSON: `{ [product]: { borda?: ISO, borda5xx?: ISO, cli?: ISO } }`; empty is `{}`."
      },
      "erros": {
        "401": "No token, wrong token or a token of another class.",
        "503": "Worker without `METRICS_TOKEN` or without the control plane."
      },
      "exemplo": "curl -s $ORIGIN/api/vitrine/cursores -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "JSON: `{ [product]: { borda?: ISO, borda5xx?: ISO, cli?: ISO } }`; empty is `{}`.",
      "url": "https://pageaudit.online/api/vitrine/cursores",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/partners",
      "auth": "none",
      "grupo": "Partnership",
      "summary": "Fixed-price advertising: placements, duration, public numbers and checkout. Partnerships and custom campaigns use negotiation.",
      "desc": "Four packages priced in USD per week, as a one-off purchase or weekly card subscription. Lists real channels, current sponsors and public numbers. Purchase through POST /api/partners. Negotiation and partnerships use the contact form. Cached for up to 1 hour; with a linked order, 1 minute.",
      "retorno": {
        "status": {
          "tipo": "string",
          "desc": "`preco_fechado`: priced spaces can be purchased; other offers require negotiation."
        },
        "produto": {
          "tipo": "string",
          "desc": "Product name."
        },
        "idioma": {
          "tipo": "string",
          "desc": "Language of the texts (the product's)."
        },
        "titulo": {
          "tipo": "string",
          "desc": "Title of the offer."
        },
        "descricao": {
          "tipo": "string",
          "desc": "One sentence about the offer."
        },
        "publico": {
          "tipo": "string",
          "desc": "Who uses the product — the audience a sponsor reaches."
        },
        "modalidades": {
          "tipo": "object[]",
          "desc": "`{ id, nome }`: patrocinio, parceria, anuncio."
        },
        "placements": {
          "tipo": "object[]",
          "desc": "Package channels: id, nome, onde, formato, medicao and disponivel. Legacy monthly price is null in the weekly catalogue."
        },
        "packages": {
          "tipo": "object[]",
          "desc": "Four packages: id, nome, assinatura, preco_usd_semana, espacos, canais and allowed periods in exposicao[{dias,preco}]."
        },
        "house_bundle": {
          "tipo": "object",
          "desc": "Unavailable in the weekly catalogue: `{disponivel:false}`."
        },
        "parcerias": {
          "tipo": "string[]",
          "desc": "Partnership ideas the product is open to discuss."
        },
        "current_sponsors": {
          "tipo": "object[]",
          "desc": "Sponsors in effect: `id`, `nome`, `url`, `frase`, `espacos`, `ate`."
        },
        "stats": {
          "tipo": "object",
          "desc": "Excerpt of the public numbers (`hoje`, `janelas`, `agentes`, `confiabilidade`) and the `link` to `/api/vitrine`; `publicado: false` before the first publication."
        },
        "payment": {
          "tipo": "object",
          "desc": "Shared payment through the purchase POST. Wallet fields remain for compatibility; do not send a manual payment before the quote."
        },
        "contact": {
          "tipo": "object",
          "desc": "`email`, `form_url`, `api_url` (`POST /api/contact`, free: one message every 10 s per network), `campos` (required), `campos_proposta` (the optional proposal fields, each with its accepted values), `message_template`, `instructions`."
        },
        "politica": {
          "tipo": "object",
          "desc": "Placement label, refused sectors, prepayment, deadlines."
        },
        "_links": {
          "tipo": "object",
          "desc": "`self`, `stats`, `page` (`null` until the page exists), `contact`, `casa` (the same path on the ten products)."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/partners",
      "returns": "{ status, produto, idioma, titulo, descricao, publico, modalidades, placements, packages, house_bundle, parcerias, current_sponsors, stats, payment, contact, politica, _links }",
      "url": "https://pageaudit.online/api/partners",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/partners",
      "auth": "none",
      "grupo": "Partnership",
      "summary": "Buy a weekly ad package and submit the artwork for review.",
      "desc": "The server prices the selected package and number of weeks. One-off: shared 402 quote and payment, no renewal. modalidade=assinatura: signed-in account, same-origin CSRF, native card payment and explicit weekly renewal acceptance. Read the current BRL offer at /api/credito/assinaturas/ofertas. Cancel future charges in the account. modalidade=recibo returns only that account's paid campaign receipt. Payment precedes content approval; ad display starts on publication. Reuse the id, token and content for retries. The quote does not save artwork or reserve inventory. Custom campaigns and free partnerships use POST /api/contact.",
      "query": [
        {
          "nome": "modalidade",
          "tipo": "string",
          "desc": "Omit for one-off payment; assinatura uses native weekly card billing; recibo reads the account's campaign receipt."
        }
      ],
      "corpo": {
        "id": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Fresh random order id: 32 lowercase hexadecimal characters. Reuse for retries."
        },
        "token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Private random order capability: 64 lowercase hexadecimal characters. Keep it for recovery."
        },
        "espaco": {
          "tipo": "string[]",
          "desc": "Legacy paid-order retry only. New purchases select a package."
        },
        "pacote": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "One of the four package ids in GET /api/partners packages."
        },
        "duracao": {
          "tipo": "int",
          "obrigatorio": true,
          "desc": "Days from the package exposure list. Weekly subscriptions require 7."
        },
        "assinatura": {
          "tipo": "object",
          "desc": "Only with modalidade=assinatura: the native subscription body with recurso=publicidade:ORDER_ID, simulado, cartao and explicit renewal aceite. The authenticated account must own the campaign."
        },
        "pedido": {
          "tipo": "string",
          "desc": "Only with modalidade=recibo: the campaign id owned by the signed-in account."
        },
        "nome": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Brand or product, up to 80 characters."
        },
        "email": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Contact email."
        },
        "url": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "HTTPS destination URL."
        },
        "frase": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Ad copy, up to 100 characters."
        },
        "arte": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Base64 image bytes, without a data URL prefix. PNG, JPEG or WebP, up to 2 MB."
        },
        "direitos": {
          "tipo": "bool",
          "obrigatorio": true,
          "desc": "true confirms the right to use the image and content."
        }
      },
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Order id."
        },
        "estado": {
          "tipo": "string",
          "desc": "revisao: paid and awaiting content approval."
        },
        "recibo": {
          "tipo": "string",
          "desc": "Verified payment receipt."
        }
      },
      "erros": {
        "400": "Invalid order or artwork.",
        "402": "Payment required.",
        "409": "Space occupied or order changed.",
        "503": "Checkout unavailable."
      },
      "exemplo": "curl -X POST '$ORIGIN/api/partners' -H 'content-type: application/json' -H 'Idempotency-Key: ORDER_ID' --data-binary @campaign.json",
      "returns": "{ id, estado, recibo }",
      "url": "https://pageaudit.online/api/partners",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/newsletter",
      "auth": "session",
      "grupo": "Account",
      "summary": "Read email advertising consent.",
      "desc": "Uses the session account. Cannot select another account. Response is not cached.",
      "retorno": {
        "ativo": {
          "tipo": "bool",
          "desc": "Separate email advertising consent."
        },
        "areas": {
          "tipo": "string[]",
          "desc": "[\"publicidade\"]."
        },
        "idioma": {
          "tipo": "string",
          "desc": "pt | en."
        },
        "verificado": {
          "tipo": "bool",
          "desc": "The account has a verified email."
        }
      },
      "erros": {
        "401": "invalid_session",
        "503": "newsletter_indisponivel"
      },
      "exemplo": "curl -s \"$ORIGIN/api/newsletter\" -b \"$COOKIE\"",
      "returns": "{ ativo, areas, idioma, verificado }",
      "url": "https://pageaudit.online/api/newsletter",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "PUT",
      "path": "/api/newsletter",
      "auth": "session",
      "grupo": "Account",
      "summary": "Allow or stop advertising in requested notices.",
      "desc": "Requires a session, Origin and X-CSRF-Token. Enabling requires explicit consent and a verified email. Withdrawal is immediate and keeps essential notices. At most one ad per account in 24 hours.",
      "headers": {
        "X-CSRF-Token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "CSRF value supplied by the SDK."
        }
      },
      "corpo": {
        "ativo": {
          "tipo": "bool",
          "obrigatorio": true,
          "desc": "true allows; false withdraws."
        },
        "areas": {
          "tipo": "string[]",
          "obrigatorio": true,
          "desc": "[\"publicidade\"]."
        },
        "idioma": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Notice language: pt or en."
        },
        "consentimento": {
          "tipo": "bool",
          "desc": "true when allowing."
        }
      },
      "body": {
        "ativo": false,
        "areas": [
          "publicidade"
        ],
        "idioma": "en"
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true."
        },
        "ativo": {
          "tipo": "bool",
          "desc": "Separate email advertising consent."
        },
        "areas": {
          "tipo": "string[]",
          "desc": "[\"publicidade\"]."
        },
        "idioma": {
          "tipo": "string",
          "desc": "pt | en."
        }
      },
      "erros": {
        "400": "assinatura_invalida",
        "401": "invalid_session",
        "403": "invalid_csrf | email_verificado_exigido",
        "429": "aguarde_para_alterar | limite_diario",
        "503": "newsletter_indisponivel"
      },
      "exemplo": "curl -X PUT \"$ORIGIN/api/newsletter\" -b \"$COOKIE\" -H \"Origin: $ORIGIN\" -H \"X-CSRF-Token: $CSRF\" -H \"Content-Type: application/json\" -d '{\"ativo\":false,\"areas\":[\"publicidade\"],\"idioma\":\"en\"}'",
      "returns": "{ ok, ativo, areas, idioma }",
      "url": "https://pageaudit.online/api/newsletter",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/newsletter/cancelar",
      "auth": "none",
      "grupo": "Account",
      "summary": "Withdraw consent using the email link.",
      "desc": "The secret link token replaces the session. JSON requires the product Origin. Also accepts RFC 8058: List-Unsubscribe=One-Click form body and token in the query, without login. Essential notices continue.",
      "corpo": {
        "token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "43-character link token, without the prefix."
        }
      },
      "retorno": {
        "ok": {
          "tipo": "bool",
          "desc": "true."
        }
      },
      "erros": {
        "400": "cancelamento_invalido | confirmacao_exigida",
        "403": "invalid_origin",
        "404": "cancelamento_invalido_ou_vencido"
      },
      "exemplo": "curl \"$ORIGIN/api/newsletter/cancelar\" -H \"Origin: $ORIGIN\" -H \"Content-Type: application/json\" -d '{\"token\":\"<link-token>\"}'",
      "returns": "{ ok }",
      "url": "https://pageaudit.online/api/newsletter/cancelar",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/metrics",
      "auth": "none",
      "summary": "Metrics of the last 7 days and the most frequent findings across audits. With the operator token, includes payments.",
      "grupo": "Billing",
      "desc": "Without credentials it returns visits, usage and accounts. With `METRICS_TOKEN` as Bearer it adds production payments and `product_metrics`, a daily inventory of saved projects and prompts. Projects count domains per workspace. Prompt totals include free deliveries, not just purchases. No account, domain or prompt text leaves this report.",
      "headers": {
        "Authorization": {
          "tipo": "string",
          "desc": "`Bearer <METRICS_TOKEN>` to include the finance block.",
          "obrigatorio": false
        }
      },
      "retorno": "Metricas",
      "exemplo": "curl -s $ORIGIN/api/metrics -H \"Authorization: Bearer $METRICS_TOKEN\"",
      "returns": "{ app, today, today_visits, today_contacts?, days, usage, accounts, product_metrics?, financeiro?, payments?, achados }",
      "url": "https://pageaudit.online/api/metrics",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/plans",
      "auth": "none",
      "summary": "The plans for sale and the authenticated account or wallet's active plan.",
      "desc": "Each purchase lasts 30 days, with no automatic renewal. Without an account or wallet credential, `account` is `null`. Daily monitoring requires an account and its e-mail. Claim a wallet purchase after sign-in to use it there. Active Starter, Pro and Agency plans also include agent prompts for that account, within 60 requests/hour.",
      "grupo": "Billing",
      "query": {
        "resumo": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "Use `1` to read the active plan without cancellation, retention and refund details.",
          "exemplo": "1"
        }
      },
      "retorno": {
        "plans": {
          "tipo": "object[]",
          "desc": "Each plan: `id`, `name`, `watched_pages`, `days`, `price_usd`, `automatic_renewal`, `charged_now` and the `buy` URL."
        },
        "account": {
          "tipo": "object",
          "desc": "`active` (the plan that ends last, with `valid_until`) and `cancel` (what cancelling removes when the plan ends, the free-days offer, the refund, `keeps_until`, until when what you paid for stays active, `ends_now`, true when cancelling ends part of the plan now, and `renews`, true when a monthly subscription renews it). Without a refund, `ends_now` or `renews`, cancelling changes nothing. A human account also has watched_pages: total, free, paid, used and available. A wallet has only active.",
          "nulo": true
        }
      },
      "erros": {
        "401": "session_ended",
        "503": "auth_unavailable"
      },
      "exemplo": "curl -s $ORIGIN/api/plans",
      "returns": "{ plans, account }",
      "url": "https://pageaudit.online/api/plans",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/plans/:plan",
      "auth": "none",
      "summary": "Buys a plan for the account or paying wallet.",
      "desc": "Each purchase lasts 30 days, with no automatic renewal. Without payment, the answer is 402 with the price. Pay with prepaid credit (`X-Credito`) or x402 (`PAYMENT-SIGNATURE` v2 or `X-PAYMENT` v1). Without an account, the verified paying wallet owns the purchase. Use wallet agent authentication to read its plan, or claim it after account sign-in. The `compra` mark prevents repeat credit debits. An x402 nonce can settle only once; keep the receipt after a delivery error.",
      "grupo": "Billing",
      "params": {
        "plan": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "`starter` (25 more pages), `pro` (100 more pages), `agency` (300 more pages)",
          "exemplo": "starter"
        }
      },
      "query": {
        "compra": {
          "tipo": "string",
          "obrigatorio": false,
          "desc": "Mark of one credit purchase, 8 to 64 letters, digits, `-` or `_`. Repeating it with the same credit token does not debit again.",
          "exemplo": "purchase-2026-10-12"
        }
      },
      "retorno": {
        "plan": {
          "tipo": "string",
          "desc": "The plan bought."
        },
        "watched_pages_added": {
          "tipo": "number",
          "desc": "Units granted by this purchase."
        },
        "valid_until": {
          "tipo": "string",
          "desc": "When this purchase ends (ISO 8601)."
        },
        "automatic_renewal": {
          "tipo": "bool",
          "desc": "`false` for Pix, USDC and credit."
        },
        "account": {
          "tipo": "object",
          "desc": "The active plan for the owner; `null` if its summary is unavailable.",
          "nulo": true
        },
        "_links": {
          "tipo": "object",
          "desc": "Plans URL and, for a wallet purchase, wallet authentication and claim URLs."
        }
      },
      "erros": {
        "401": "An explicit account, wallet or credit credential was refused.",
        "402": "Payment required: the price and the accepted payments.",
        "403": "Writes with the account cookie need the same Origin and `X-CSRF-Token`.",
        "404": "Unknown plan (`unknown_plan`).",
        "409": "Plans are not on sale now (`sales_closed`) or billing is off (`billing_off`)."
      },
      "exemplo": "curl -s -X POST \"$ORIGIN/api/plans/starter?compra=purchase-2026-10-12\" -H \"X-CSRF-Token: $CSRF\" -H \"X-Credito: $CREDITO\" -b \"$COOKIE\"",
      "returns": "{ plan, watched_pages_added, valid_until, automatic_renewal, account, _links }",
      "url": "https://pageaudit.online/api/plans/:plan",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/plans/cancel",
      "auth": "session",
      "summary": "Cancels the active plan: nothing else is charged, and what you paid for stays active until the end of its period.",
      "desc": "Send `{ \"accept_data_removal\": true }`. The monthly card subscription stops. A purchase made less than 7 days ago and a paid period that has not started yet, such as a renewal, go back the same way they were paid (card or Pix) and end now. The rest of the paid period stays active until `keeps_until`; then what the plan added ends (`account.cancel.losses` in `GET /api/plans` says what). A purchase paid with USDC or with credit from another purchase stays active until its end; ask for its refund by e-mail, as the cancellation page says.",
      "grupo": "Billing",
      "corpo": {
        "accept_data_removal": {
          "tipo": "bool",
          "obrigatorio": true,
          "desc": "Must be `true`: you accept losing what the plan added."
        }
      },
      "body": {
        "accept_data_removal": true
      },
      "retorno": {
        "canceled": {
          "tipo": "bool",
          "desc": "`true` when a valid plan was canceled: it ended now, or it stays active until `keeps_until`."
        },
        "refund": {
          "tipo": "object",
          "desc": "`method` (`original`), `amount_cents` and its `currency` (`brl` when Asaas refunds, `usd` when Stripe refunds; `null` when both do).",
          "nulo": true
        },
        "keeps_until": {
          "tipo": "string",
          "desc": "When the paid period that stays active ends (ISO 8601); `null` when everything ended now.",
          "nulo": true
        },
        "account": {
          "tipo": "object",
          "desc": "The account state after cancelling, as in `GET /api/plans`."
        }
      },
      "erros": {
        "400": "Missing `accept_data_removal: true` (`confirmation_required`).",
        "401": "Sign in first (`account_required`), or the session ended (`session_ended`).",
        "403": "Writes with the account cookie need the same Origin and `X-CSRF-Token`."
      },
      "exemplo": "curl -s -X POST $ORIGIN/api/plans/cancel -H \"Content-Type: application/json\" -H \"X-CSRF-Token: $CSRF\" -b \"$COOKIE\" -d '{\"accept_data_removal\":true}'",
      "returns": "{ canceled, refund, keeps_until, account }",
      "url": "https://pageaudit.online/api/plans/cancel",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/plans/:plan/grace",
      "auth": "session",
      "summary": "Takes the free-days offer from the cancellation: the offered plan, free for 7 days, once per account.",
      "desc": "`account.cancel.offer` in `GET /api/plans` says which plan and for how long. Nothing renews by itself, and what you paid for stays active until its end; the free days start after it.",
      "grupo": "Billing",
      "params": {
        "plan": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "`starter` (25 more pages), `pro` (100 more pages), `agency` (300 more pages)",
          "exemplo": "starter"
        }
      },
      "retorno": {
        "plan": {
          "tipo": "string",
          "desc": "The plan granted."
        },
        "valid_until": {
          "tipo": "string",
          "desc": "When the free days end."
        },
        "price_usd": {
          "tipo": "number",
          "desc": "Always `0`."
        },
        "account": {
          "tipo": "object",
          "desc": "The account state after the offer, as in `GET /api/plans`."
        }
      },
      "erros": {
        "401": "Sign in first (`account_required`), or the session ended (`session_ended`).",
        "403": "Writes with the account cookie need the same Origin and `X-CSRF-Token`.",
        "404": "Unknown plan (`unknown_plan`).",
        "409": "No offer for this account or plan (`offer_unavailable`)."
      },
      "exemplo": "curl -s -X POST $ORIGIN/api/plans/starter/grace -H \"X-CSRF-Token: $CSRF\" -b \"$COOKIE\"",
      "returns": "{ plan, valid_until, price_usd, account }",
      "url": "https://pageaudit.online/api/plans/:plan/grace",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/credito/x402/saude",
      "auth": "none",
      "exemplo": "curl -s $ORIGIN/api/credito/x402/saude",
      "grupo": "Credit",
      "summary": "x402 verifier health: pending payments, missing hashes, failed or stalled reconciliation.",
      "retorno": {
        "verificado_em": {
          "tipo": "string",
          "desc": "Reading time in UTC."
        },
        "ativo": {
          "tipo": "boolean",
          "desc": "Verifier configured in this process."
        },
        "tarefas": {
          "tipo": "object[]",
          "desc": "Failures with id and reason, without payer data. Bounded counts show +."
        }
      },
      "erros": {
        "503": "Verifier unavailable in this process."
      },
      "returns": "{ verificado_em, ativo, tarefas }",
      "url": "https://pageaudit.online/api/credito/x402/saude",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/barrados/saude",
      "auth": "none",
      "exemplo": "curl -s $ORIGIN/api/credito/barrados/saude",
      "grupo": "Credit",
      "summary": "People who paid and were blocked on this site in the last 24 hours: the site asked for payment from someone with balance or a plan, or charged and did not deliver.",
      "retorno": {
        "verificado_em": {
          "tipo": "string",
          "desc": "Reading time in UTC."
        },
        "ativo": {
          "tipo": "boolean",
          "desc": "The reading worked."
        },
        "total": {
          "tipo": "int",
          "desc": "Number of different cases."
        },
        "tarefas": {
          "tipo": "object[]",
          "desc": "Up to 8 cases, with class, route, count and last time, without payer data."
        }
      },
      "returns": "{ verificado_em, ativo, total, tarefas }",
      "url": "https://pageaudit.online/api/credito/barrados/saude",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/conta",
      "auth": "credito",
      "exemplo": "curl -s $ORIGIN/api/credito/conta -H 'Cookie: <sessão>'",
      "grupo": "Credit",
      "summary": "The balance of the session account: the sum of its purchases in this environment, in every product, without the code. With movimentos=1, the statement of the account.",
      "query": {
        "movimentos": {
          "tipo": "string",
          "desc": "`1`: reads only the paged statement of the account, without the balance."
        },
        "de": {
          "tipo": "string",
          "desc": "Inclusive start in UTC ISO 8601 with milliseconds. Default: 1970-01-01T00:00:00.000Z."
        },
        "ate": {
          "tipo": "string",
          "desc": "Exclusive end in UTC ISO 8601 with milliseconds. Default: now. Keep the value between pages."
        },
        "antes": {
          "tipo": "string",
          "desc": "The `proximo` cursor of the previous page. Only with movimentos=1."
        },
        "limite": {
          "tipo": "int",
          "desc": "1 to 50 entries per page; default 20. Only with movimentos=1."
        }
      },
      "retorno": {
        "saldo_micros": {
          "tipo": "int",
          "desc": "The balance in millionths of a dollar."
        },
        "saldo_usd": {
          "tipo": "string",
          "desc": "The balance in dollars, formatted."
        },
        "referencia_usd": {
          "tipo": "string",
          "desc": "The balance right after the last top-up, added across the wallets with a balance: the base of what is left."
        },
        "cotacao_brl_por_usd": {
          "tipo": "number",
          "desc": "The BRL rate the screen uses to show the balance."
        },
        "movimentos": {
          "tipo": "object[]",
          "desc": "Only with movimentos=1: id, em, valor_usd, saldo_usd, motivo, produto and recurso, newest first."
        },
        "proximo": {
          "tipo": "string",
          "desc": "Only with movimentos=1: the cursor of the next page or null."
        }
      },
      "erros": {
        "400": "Invalid period, limit or cursor.",
        "401": "No session."
      },
      "returns": "{ saldo_micros, saldo_usd, referencia_usd, cotacao_brl_por_usd, movimentos, proximo }",
      "url": "https://pageaudit.online/api/credito/conta",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "POST",
      "path": "/api/credito/conta",
      "auth": "credito",
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/conta -H 'Authorization: Bearer cred_…' -H 'Cookie: <sessão>'",
      "grupo": "Credit",
      "summary": "Links credit bought without an account to the account of the session, USDC included. Then the balance works in the products of the shared credit with `X-Credito: conta`, added to the other purchases of the account.",
      "retorno": {
        "ok": {
          "tipo": "boolean",
          "desc": "The link was checked."
        },
        "ligadas": {
          "tipo": "int",
          "desc": "Purchases linked now. Zero when the purchase already has an account."
        },
        "da_conta": {
          "tipo": "boolean",
          "desc": "The balance of this code is already in the account balance."
        }
      },
      "erros": {
        "401": "No session or no credit code."
      },
      "returns": "{ ok, ligadas, da_conta }",
      "url": "https://pageaudit.online/api/credito/conta",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "POST",
      "path": "/api/credito",
      "auth": "none",
      "summary": "Top up prepaid credit: pay once with x402 and get the token that debits on any API of the house.",
      "grupo": "Credit",
      "preco_usd_faixa": [
        0.1,
        25
      ],
      "query": {
        "usd": {
          "tipo": "number",
          "desc": "Package: 0.1, 1, 5, 10 or 25 dollars.",
          "obrigatorio": true,
          "enum": [
            0.1,
            1,
            5,
            10,
            25
          ]
        }
      },
      "retorno": {
        "token": {
          "tipo": "string",
          "desc": "Bearer token for the balance (`cred_…`). Shown ONCE — it cannot be recovered."
        },
        "saldo_usd": {
          "tipo": "string",
          "desc": "Credited balance."
        },
        "guarde": {
          "tipo": "string",
          "desc": "Warning that the token is the bearer of the credit."
        },
        "usar": {
          "tipo": "string",
          "desc": "How to present the token on paid routes."
        },
        "usar_com_outra_credencial": {
          "tipo": "string",
          "desc": "The `X-Credito` header, for when `Authorization` already carries another credential."
        },
        "saldo_em": {
          "tipo": "string",
          "desc": "Where to check balance and statement."
        },
        "onde_gastar": {
          "tipo": "object",
          "desc": "`precos`: the paid actions and prices of this product (`/api/pricing`); `produtos`: the APIs.json with our other products."
        }
      },
      "erros": {
        "400": "Package outside the list (0.1, 1, 5, 10 or 25).",
        "402": "Unpaid — the body carries the x402 `accepts[]`."
      },
      "exemplo": "curl -s -XPOST '$ORIGIN/api/credito?usd=10'",
      "returns": "{ token, saldo_usd, guarde, usar, usar_com_outra_credencial, saldo_em, onde_gastar }",
      "url": "https://pageaudit.online/api/credito",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito",
      "auth": "credito",
      "summary": "Balance and statement with cred_…; wallet balance and up to 20 recovery records with mmw_….",
      "desc": "A request without a token receives 401. The Link header points to the x402 manifest. When billing is configured, accepts[] contains the same terms as the smallest POST top-up. GET cannot charge or create credit.",
      "grupo": "Credit",
      "retorno": {
        "saldo_micros": {
          "tipo": "int",
          "desc": "Balance in micro-dollars (1e-6 USD)."
        },
        "saldo_usd": {
          "tipo": "string",
          "desc": "Formatted balance."
        },
        "criado_em": {
          "tipo": "string",
          "desc": "When the credit was opened."
        },
        "movimentos": {
          "tipo": "object[]",
          "desc": "Recent credits and debits, with product and resource."
        },
        "wallet": {
          "tipo": "string",
          "desc": "Only with mmw_…: authenticated wallet address."
        },
        "payments": {
          "tipo": "object[]",
          "desc": "Only with mmw_…: up to 20 payments with id, produto, rede, tx_hash, micros, estado and criado_ms. creditado means recovered balance."
        },
        "onde_gastar": {
          "tipo": "object",
          "desc": "`precos`: the paid actions and prices of this product; `produtos`: the APIs.json with our other products."
        }
      },
      "erros": {
        "401": "No token, or unknown token."
      },
      "exemplo": "curl -s $ORIGIN/api/credito -H 'Authorization: Bearer cred_…'",
      "returns": "{ saldo_micros, saldo_usd, criado_em, movimentos, wallet, payments, onde_gastar }",
      "url": "https://pageaudit.online/api/credito",
      "auth_detail": "Credit token in `Authorization: Bearer cred_…` or `X-Credito`. For recovered x402 funds, use the wallet credential mmw_… in Bearer or X-Wallet-Token, issued through /api/auth/wallet/agent/start and /api/auth/wallet/agent/token."
    },
    {
      "method": "GET",
      "path": "/api/credito/pix",
      "auth": "none",
      "summary": "Credit by Pix (Brazil): the key, the fixed rate, the packs in reais and what the receipt accepts.",
      "grupo": "Credit",
      "retorno": {
        "chave": {
          "tipo": "string",
          "desc": "The Pix key that receives the payment."
        },
        "brl_por_usd": {
          "tipo": "number",
          "desc": "Fixed rate used for the packs."
        },
        "pacotes": {
          "tipo": "object[]",
          "desc": "The packs (3, 5, 10 or 25 dollars), each with `brl_centavos` and `copia_e_cola` (the Pix copy-and-paste code for the amount, the same text as the QR: the bank app comes with the amount filled in)."
        },
        "comprovante": {
          "tipo": "object",
          "desc": "Accepted types (photo or PDF) and the maximum size, in bytes."
        },
        "liberacao": {
          "tipo": "string",
          "desc": "`manual`: the owner checks the Pix and releases it."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/credito/pix",
      "returns": "{ chave, brl_por_usd, pacotes, comprovante, liberacao }",
      "url": "https://pageaudit.online/api/credito/pix",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/pix",
      "auth": "none",
      "summary": "Request credit paid by Pix: multipart with `usd`, `comprovante` (photo or PDF up to 2 MB) and `email`; the rest is optional.",
      "desc": "The credit code comes back in the response and works once the owner checks the Pix and releases it (by hand, usually the same day). `email` is required: it is how the owner reaches the person. Optional: `nome`, `pagina` (the page the request came from, up to 2,000 characters) and what the person was buying when the 402 came — `recurso` (up to 2,000), `descricao` (up to 300) and `preco_usd` (decimal, e.g. `0.50`); and `navegador_id` (a UUID that links the requests from the same browser). Everything that arrives is recorded with the request — the receipt included —, with the signed-in account (checked by the session cookie), the network and browser.",
      "grupo": "Credit",
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Request id, to follow it."
        },
        "estado": {
          "tipo": "string",
          "desc": "`pendente` until the decision."
        },
        "credito": {
          "tipo": "string",
          "desc": "The `cred_…` token, shown ONCE; it works after the release."
        },
        "estado_em": {
          "tipo": "string",
          "desc": "Where to follow the request."
        }
      },
      "erros": {
        "400": "Pack outside the list (3, 5, 10 or 25), no receipt or no valid e-mail.",
        "413": "Receipt above 2 MB.",
        "415": "Receipt that is neither a photo (JPEG, PNG, WebP) nor a PDF.",
        "429": "This network already sent today's requests.",
        "502": "The e-mail to the owner did not go out: the request is recorded as `falhou`; send it again.",
        "503": "Review queue full, or Pix not available in this app."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/pix -F usd=5 -F email=you@company.com -F comprovante=@pix.pdf",
      "returns": "{ id, estado, credito, estado_em }",
      "url": "https://pageaudit.online/api/credito/pix",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/pix/:id",
      "auth": "none",
      "summary": "Status of a Pix credit request: `pendente`, `liberado`, `recusado` or `falhou`.",
      "grupo": "Credit",
      "params": {
        "id": {
          "desc": "Request id (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "estado": {
          "tipo": "string",
          "desc": "`pendente`, `liberado`, `recusado` or `falhou` (the e-mail to the owner did not go out; send again)."
        },
        "usd": {
          "tipo": "int",
          "desc": "The requested pack."
        },
        "decidido_em": {
          "tipo": "string",
          "desc": "When the owner decided, or `null`."
        }
      },
      "erros": {
        "404": "Unknown request."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/pix/0123456789abcdef0123456789abcdef",
      "returns": "{ estado, usd, decidido_em }",
      "url": "https://pageaudit.online/api/credito/pix/:id",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/asaas/:id/cancelar",
      "auth": "none",
      "grupo": "Crédito",
      "summary": "Cancels a pending charge when the payment dialog closes.",
      "desc": "Requires the `cancelamento` capability returned at creation. An older credit charge accepts its token in `credito`. The product and environment must match. This does not refund payments or remove records. 202 means cancellation is pending; the scheduled worker retries provider failures.",
      "params": {
        "id": {
          "desc": "Charge id (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Charge id."
        },
        "estado": {
          "tipo": "string",
          "desc": "`cancelando`, `vencido`, or the confirmed final state."
        }
      },
      "erros": {
        "404": "Invalid charge or capability, or a different product or environment."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/asaas/0123456789abcdef0123456789abcdef/cancelar -H 'content-type: application/json' -d '{\"cancelamento\":\"<bilhete>\"}'",
      "returns": "{ id, estado }",
      "url": "https://pageaudit.online/api/credito/asaas/:id/cancelar",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/asaas",
      "auth": "none",
      "summary": "Automatic Pix: whether it is available in this app, the packs in reais and the balance the product sells.",
      "grupo": "Crédito",
      "retorno": {
        "disponivel": {
          "tipo": "boolean",
          "desc": "`false` when the app has no Asaas configured (use the Pix with receipt)."
        },
        "pacotes": {
          "tipo": "object[]",
          "desc": "The credit packs (3, 5, 10 or 25 dollars), with `brl_centavos`."
        },
        "exatos": {
          "tipo": "object[]",
          "desc": "Prices declared by the app, with `usd` and `brl_centavos`. Amounts start at R$ 5.00 and cannot exceed R$ 10,000."
        },
        "saldo": {
          "tipo": "object",
          "desc": "When the product sells a balance in reais: the range and whether the account can buy."
        },
        "precisa_documento": {
          "tipo": "boolean",
          "desc": "Whether the payer's CPF or CNPJ is still required."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/credito/asaas",
      "returns": "{ disponivel, pacotes, exatos, saldo, precisa_documento }",
      "url": "https://pageaudit.online/api/credito/asaas",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/asaas",
      "auth": "none",
      "summary": "Creates a dynamic Pix: JSON with `oferta` (`pacote` with `usd`, or `saldo` with `centavos`) and the payer.",
      "desc": "The payment releases what was bought by itself: the pack becomes the `cred_…` token already in the response; the balance goes to the logged-in account's organization. The first purchase needs `documento` (CPF or CNPJ, sent to the gateway and not stored here) and `nome`; `email`, `pagina` and `navegador_id` are optional. With `forma: \"cartao_boleto\"` the charge is open: the response brings `pagamento_em`, the gateway page where you pay by card, boleto or Pix (due in three days). With `forma: \"cartao\"` and `cartao` (`titular`, `numero`, `mes`, `ano`, `cvv`, `documento`, `email`, `cep`, `numero_endereco`, `telefone`), the charge is paid by card in the same request: 200 with `estado: \"pago\"`, 202 with `conferir` while the issuer reviews it, 422 when the card is declined; the card is not stored. Set `usd` to an amount from `pacotes` or `exatos` in the GET response. The server sets the amount in cents and rejects amounts that the app has not declared.",
      "grupo": "Crédito",
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Charge id, to follow up."
        },
        "credito": {
          "tipo": "string",
          "desc": "For a pack: the `cred_…` token, shown ONCE; valid when the payment arrives."
        },
        "forma": {
          "tipo": "string",
          "desc": "`pix` (the default), `cartao` or `cartao_boleto`."
        },
        "pix": {
          "tipo": "object",
          "desc": "`copia_e_cola`, `imagem` (PNG QR, data URI) and `expira_em`; `pix` form only."
        },
        "pagamento_em": {
          "tipo": "string",
          "desc": "In the `cartao_boleto` form: the gateway payment page."
        },
        "estado": {
          "tipo": "string",
          "desc": "In the `cartao` form: `pago`, or `pendente` with `conferir`."
        },
        "estado_em": {
          "tipo": "string",
          "desc": "Where to follow the payment."
        },
        "cancelamento": {
          "tipo": "string",
          "desc": "Capability to cancel a pending Pix or boleto charge."
        }
      },
      "erros": {
        "400": "Credit amount outside `pacotes` and `exatos`, amount out of range, invalid CPF/CNPJ, name or e-mail, invalid card data (`cartao_dados`, with the `campo`), or the gateway refused the payer (`pagador_recusado`, with the reason in `detalhe`).",
        "422": "Card declined (`cartao_recusado`, with the `campo` when the gateway says which); try another card with `POST /api/credito/asaas/:id/cartao`.",
        "429": "Limit of 3 attempts per minute or 10 per day, per account or network, or the card limit.",
        "502": "The gateway did not create the Pix; try again.",
        "503": "Automatic Pix unavailable in this app, the gateway is busy (`asaas_ocupado`, wait `retry_after_s`), or too many charges from people not signed in waiting for payment."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/asaas -H 'content-type: application/json' -d '{\"oferta\":\"pacote\",\"usd\":5,\"documento\":\"000.000.000-00\",\"nome\":\"Ana\"}'",
      "returns": "{ id, credito, forma, pix, pagamento_em, estado, estado_em, cancelamento }",
      "url": "https://pageaudit.online/api/credito/asaas",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/asaas/:id",
      "auth": "none",
      "summary": "State of an automatic Pix charge: `pendente`, `pago`, `vencido`, `estornado` or `falhou`.",
      "grupo": "Crédito",
      "params": {
        "id": {
          "desc": "Charge id (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "estado": {
          "tipo": "string",
          "desc": "`pago` releases the pack token or the balance; a Pix paid after the due date counts."
        },
        "pago_em": {
          "tipo": "string",
          "desc": "When the payment was confirmed, or `null`."
        }
      },
      "erros": {
        "404": "Unknown charge."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/asaas/0123456789abcdef0123456789abcdef",
      "returns": "{ estado, pago_em }",
      "url": "https://pageaudit.online/api/credito/asaas/:id",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/asaas/:id/cartao",
      "auth": "none",
      "summary": "Pays a card charge whose card was declined with another card: the same charge, never charged twice.",
      "desc": "JSON with the `cartao` (the same fields as `POST /api/credito/asaas`) and, without an account, the `navegador_id` that created the charge. Only a card charge waiting for payment, by whoever created it, and up to five attempts per charge.",
      "grupo": "Crédito",
      "params": {
        "id": {
          "desc": "Id da cobrança (32 hex).",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "estado": {
          "tipo": "string",
          "desc": "`pago`, or `pendente` with `conferir` while the issuer reviews it."
        },
        "estado_em": {
          "tipo": "string",
          "desc": "Where to follow the payment."
        }
      },
      "erros": {
        "400": "Invalid card data (`cartao_dados`, with the `campo`).",
        "404": "Unknown charge, someone else's charge, or not a card charge.",
        "409": "The charge expired or was closed; start the payment again.",
        "422": "Card declined (`cartao_recusado`).",
        "429": "Used up the card attempts for this charge or for today."
      },
      "exemplo": "curl -s -XPOST $ORIGIN/api/credito/asaas/0123456789abcdef0123456789abcdef/cartao -H 'content-type: application/json' -d '{\"cartao\":{…}}'",
      "returns": "{ estado, estado_em }",
      "url": "https://pageaudit.online/api/credito/asaas/:id/cartao",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/asaas/saude",
      "auth": "none",
      "summary": "Health of this app's Pix and enabled subscriptions: failures and reasons for the dashboard.",
      "grupo": "Crédito",
      "retorno": {
        "ativo": {
          "tipo": "boolean",
          "desc": "`false` when Asaas and subscriptions are disabled."
        },
        "tarefas": {
          "tipo": "object[]",
          "desc": "Each problem has `id`, `falhando`, and `motivo`. Includes Pix and, when enabled, subscription recovery, queue, and budget. Does not call the provider."
        }
      },
      "exemplo": "curl -s $ORIGIN/api/credito/asaas/saude",
      "returns": "{ ativo, tarefas }",
      "url": "https://pageaudit.online/api/credito/asaas/saude",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "GET",
      "path": "/api/credito/assinaturas",
      "auth": "session",
      "grupo": "Subscriptions",
      "summary": "List at most 20 subscriptions owned by this account in this product.",
      "desc": "Requires enabled monthly sales or a server-authorized payment test. Otherwise returns 404. Filters account, product, merchant and environment. Does not query Asaas or count the table. The accepted offer remains readable after sales close. Sandbox reads require a valid ticket. The complete query is limited to 640 characters, including the cursor. PageAudit domain monitoring uses recurso=domains. Site editorial offers remain unavailable.",
      "query": {
        "dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. At most 512 characters. It never authorizes another account."
        },
        "x402_dev": {
          "tipo": "string",
          "desc": "Alias for dev. Send only one of these parameters."
        },
        "cursor": {
          "tipo": "string",
          "desc": "proximo from the previous page: UTC ISO time with milliseconds, ~ and a 32-character hexadecimal ID."
        },
        "recurso": {
          "tipo": "string",
          "desc": "Product resource: at most 256 characters without controls. Filters this account's subscriptions."
        }
      },
      "headers": {
        "X-X402-Dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. The request body's sandbox flag does not authorize a test."
        }
      },
      "colecao": {
        "porPagina": 20,
        "teto": 20,
        "anda": "Use proximo as the cursor. Descending criado_em and id order. Null ends the list."
      },
      "retorno": {
        "itens": {
          "tipo": "RegistroDeAssinatura[]",
          "desc": "At most 20 records owned by this account."
        },
        "proximo": {
          "tipo": "string",
          "nulo": true,
          "desc": "Next-page cursor, or null."
        },
        "simulado": {
          "tipo": "bool",
          "desc": "Server-authorized environment for this request."
        }
      },
      "erros": {
        "400": "Invalid body, query, offer, acceptance, card or environment; card declined.",
        "401": "Missing or invalid session. An API key cannot authorize these requests.",
        "403": "Invalid origin or cookie; invalid CSRF protection on a write.",
        "404": "Sales and payment tests are unavailable, or the subscription is outside this account, product, merchant or environment.",
        "431": "Cookie header exceeds 8 KiB.",
        "503": "Account, database, configuration or provider unavailable; four concurrent operations already in progress."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/assinaturas -b sessao.cookies",
      "returns": "{ itens[{recurso,id,oferta_id,oferta,estado,simulado,pagamento_em,criado_em,atualizado_em,cancelamento_em,cancelado_em}], proximo, simulado }",
      "url": "https://pageaudit.online/api/credito/assinaturas",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/credito/assinaturas/ofertas",
      "auth": "none",
      "grupo": "Subscriptions",
      "summary": "Read the monthly offers and request environment when subscriptions are enabled.",
      "desc": "Requires enabled monthly sales or a server-authorized payment test. Otherwise returns 404. Creates no charge, reservation or benefit. Does not convert a trial into a purchase. PageAudit domain monitoring uses recurso=domains. Site editorial offers remain unavailable.",
      "query": {
        "dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. At most 512 characters. It never authorizes another account."
        },
        "x402_dev": {
          "tipo": "string",
          "desc": "Alias for dev. Send only one of these parameters."
        }
      },
      "headers": {
        "X-X402-Dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. The request body's sandbox flag does not authorize a test."
        }
      },
      "retorno": {
        "ofertas": {
          "tipo": "OfertaDeAssinatura[]",
          "desc": "1 to 8 versioned offers for this product."
        },
        "simulado": {
          "tipo": "bool",
          "desc": "Server-authorized environment for this request."
        }
      },
      "erros": {
        "400": "Invalid body, query, offer, acceptance, card or environment; card declined.",
        "404": "Sales and payment tests are unavailable, or the subscription is outside this account, product, merchant or environment.",
        "503": "Account, database, configuration or provider unavailable; four concurrent operations already in progress."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/assinaturas/ofertas",
      "returns": "{ ofertas[{id,produto,versao,preco_centavos,moeda,ciclo,renovacao_automatica,compromisso_meses,compromisso_semanas,cancelamento,acesso_pago,teste_dias,conversao_teste,beneficios,hash}], simulado }",
      "url": "https://pageaudit.online/api/credito/assinaturas/ofertas",
      "auth_detail": "Public, no credential."
    },
    {
      "method": "POST",
      "path": "/api/credito/assinaturas",
      "auth": "session",
      "grupo": "Subscriptions",
      "summary": "Subscribe and pay for the monthly offer by card through the API.",
      "desc": "Requires enabled monthly sales or a server-authorized payment test. Otherwise returns 404. Requires a human session authenticated within the last 2 hours. Rejects Authorization and X-Api-Key. Send only the declared fields, with HTTPS and a JSON body of at most 4 KiB. recurso is accepted only when the product verifies it. simulado must match the catalog and authorized environment. An expired test ticket cannot change the request to production. Price and benefits come from the server. Card data goes to Asaas for this request only; it is not stored or returned. The server supplies the buyer IP. Allow up to 120 seconds. A repeated offer returns the existing reservation. An uncertain outcome requires a reference lookup before another creation. Creation grants no benefit. Only an Asaas-confirmed payment grants the paid period. PageAudit domain monitoring uses recurso=domains. Site editorial offers remain unavailable.",
      "query": {
        "dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. At most 512 characters. It never authorizes another account."
        },
        "x402_dev": {
          "tipo": "string",
          "desc": "Alias for dev. Send only one of these parameters."
        }
      },
      "headers": {
        "X-X402-Dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. The request body's sandbox flag does not authorize a test."
        },
        "Origin": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Exact HTTPS origin of this product."
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "64-character token from this product's session bootstrap. Required for writes."
        }
      },
      "corpo": {
        "recurso": {
          "tipo": "string",
          "desc": "Use domains for domain monitoring.",
          "obrigatorio": true
        },
        "simulado": {
          "tipo": "bool",
          "obrigatorio": true,
          "desc": "Value from the catalog. States intent; grants no payment-test authority."
        },
        "aceite": {
          "tipo": "AceiteDeAssinatura",
          "obrigatorio": true,
          "desc": "Only oferta_id, versao, hash and renovacao. Match the current catalog."
        },
        "cartao": {
          "tipo": "CartaoDeAssinatura",
          "obrigatorio": true,
          "desc": "Cardholder's card and billing data. Do not send price, IP, environment or another account's identifier."
        }
      },
      "retorno": "RegistroDeAssinatura",
      "erros": {
        "400": "Invalid body, query, offer, acceptance, card or environment; card declined.",
        "401": "Missing or invalid session. An API key cannot authorize these requests.",
        "403": "Invalid origin or cookie; invalid CSRF protection on a write.",
        "404": "Sales and payment tests are unavailable, or the subscription is outside this account, product, merchant or environment.",
        "409": "Incompatible state or another offer already reserved for this account and product.",
        "413": "JSON body exceeds 4 KiB.",
        "415": "Body is not application/json or has content encoding.",
        "429": "This account reached its daily charge-creation limit.",
        "431": "Cookie header exceeds 8 KiB.",
        "503": "Account, database, configuration or provider unavailable; four concurrent operations already in progress."
      },
      "exemplo": "curl -s -X POST $ORIGIN/api/credito/assinaturas -b sessao.cookies -H \"Origin: $ORIGIN\" -H \"X-CSRF-Token: $CSRF\" -H \"content-type: application/json\" --data-binary @contratacao.json",
      "returns": "{ recurso, id, oferta_id, oferta{id,produto,versao,preco_centavos,moeda,ciclo,renovacao_automatica,compromisso_meses,compromisso_semanas,cancelamento,acesso_pago,teste_dias,conversao_teste,beneficios,hash}, estado, simulado, pagamento_em, criado_em, atualizado_em, cancelamento_em, cancelado_em }",
      "url": "https://pageaudit.online/api/credito/assinaturas",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/credito/assinaturas/:id",
      "auth": "session",
      "grupo": "Subscriptions",
      "summary": "Read an owned subscription and its accepted offer without creating a payment.",
      "desc": "Requires enabled monthly sales or a server-authorized payment test. Otherwise returns 404. Requires this product's valid human session. Does not query the provider. Sandbox reads also require a valid ticket. PageAudit domain monitoring uses recurso=domains. Site editorial offers remain unavailable.",
      "query": {
        "dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. At most 512 characters. It never authorizes another account."
        },
        "x402_dev": {
          "tipo": "string",
          "desc": "Alias for dev. Send only one of these parameters."
        },
        "recurso": {
          "tipo": "string",
          "desc": "Product resource: at most 256 characters without controls. Filters this account's subscriptions."
        }
      },
      "headers": {
        "X-X402-Dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. The request body's sandbox flag does not authorize a test."
        }
      },
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Local subscription ID: 32 lowercase hexadecimal characters.",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": "RegistroDeAssinatura",
      "erros": {
        "400": "Invalid body, query, offer, acceptance, card or environment; card declined.",
        "401": "Missing or invalid session. An API key cannot authorize these requests.",
        "403": "Invalid origin or cookie; invalid CSRF protection on a write.",
        "404": "Sales and payment tests are unavailable, or the subscription is outside this account, product, merchant or environment.",
        "431": "Cookie header exceeds 8 KiB.",
        "503": "Account, database, configuration or provider unavailable; four concurrent operations already in progress."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/assinaturas/0123456789abcdef0123456789abcdef -b sessao.cookies",
      "returns": "{ recurso, id, oferta_id, oferta{id,produto,versao,preco_centavos,moeda,ciclo,renovacao_automatica,compromisso_meses,compromisso_semanas,cancelamento,acesso_pago,teste_dias,conversao_teste,beneficios,hash}, estado, simulado, pagamento_em, criado_em, atualizado_em, cancelamento_em, cancelado_em }",
      "url": "https://pageaudit.online/api/credito/assinaturas/:id",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "POST",
      "path": "/api/credito/assinaturas/:id",
      "auth": "session",
      "grupo": "Subscriptions",
      "summary": "Stop future charges for an owned subscription and keep the paid period.",
      "desc": "Requires enabled monthly sales or a server-authorized payment test. Otherwise returns 404. Send only {} as JSON, with a valid session, origin and CSRF token. The server records cancellation intent before it calls Asaas. Pending is not confirmed cancellation. The request is repeatable. Refunds have a separate process; cancellation does not refund a payment. PageAudit domain monitoring uses recurso=domains. Site editorial offers remain unavailable.",
      "query": {
        "dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. At most 512 characters. It never authorizes another account."
        },
        "x402_dev": {
          "tipo": "string",
          "desc": "Alias for dev. Send only one of these parameters."
        },
        "recurso": {
          "tipo": "string",
          "desc": "Product resource: at most 256 characters without controls. Filters this account's subscriptions."
        }
      },
      "headers": {
        "X-X402-Dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. The request body's sandbox flag does not authorize a test."
        },
        "Origin": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "Exact HTTPS origin of this product."
        },
        "X-CSRF-Token": {
          "tipo": "string",
          "obrigatorio": true,
          "desc": "64-character token from this product's session bootstrap. Required for writes."
        }
      },
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Local subscription ID: 32 lowercase hexadecimal characters.",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "body": {},
      "retorno": "RegistroDeAssinatura",
      "erros": {
        "400": "Invalid body, query, offer, acceptance, card or environment; card declined.",
        "401": "Missing or invalid session. An API key cannot authorize these requests.",
        "403": "Invalid origin or cookie; invalid CSRF protection on a write.",
        "404": "Sales and payment tests are unavailable, or the subscription is outside this account, product, merchant or environment.",
        "409": "Incompatible state or another offer already reserved for this account and product.",
        "413": "JSON body exceeds 4 KiB.",
        "415": "Body is not application/json or has content encoding.",
        "431": "Cookie header exceeds 8 KiB.",
        "503": "Account, database, configuration or provider unavailable; four concurrent operations already in progress."
      },
      "exemplo": "curl -s -X POST $ORIGIN/api/credito/assinaturas/0123456789abcdef0123456789abcdef -b sessao.cookies -H \"Origin: $ORIGIN\" -H \"X-CSRF-Token: $CSRF\" -H \"content-type: application/json\" -d '{}'",
      "returns": "{ recurso, id, oferta_id, oferta{id,produto,versao,preco_centavos,moeda,ciclo,renovacao_automatica,compromisso_meses,compromisso_semanas,cancelamento,acesso_pago,teste_dias,conversao_teste,beneficios,hash}, estado, simulado, pagamento_em, criado_em, atualizado_em, cancelamento_em, cancelado_em }",
      "url": "https://pageaudit.online/api/credito/assinaturas/:id",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/credito/assinaturas/:id/estado",
      "auth": "session",
      "stream": true,
      "grupo": "Subscriptions",
      "summary": "Observe changes to an owned subscription through the realtime channel.",
      "desc": "Requires enabled monthly sales or a server-authorized payment test. Otherwise returns 404. Requires this product's human session. Returns only ID and cursor, including after reconnection. A signal grants no paid access and exposes no financial data. Read the authenticated subscription after a change. That read still verifies environment, merchant and resource. PageAudit domain monitoring uses recurso=domains. Site editorial offers remain unavailable.",
      "query": {
        "dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. At most 512 characters. It never authorizes another account."
        },
        "x402_dev": {
          "tipo": "string",
          "desc": "Alias for dev. Send only one of these parameters."
        }
      },
      "headers": {
        "X-X402-Dev": {
          "tipo": "string",
          "desc": "Valid signed payment-test ticket. The request body's sandbox flag does not authorize a test."
        }
      },
      "params": {
        "id": {
          "tipo": "string",
          "desc": "Local subscription ID: 32 lowercase hexadecimal characters.",
          "exemplo": "0123456789abcdef0123456789abcdef"
        }
      },
      "retorno": {
        "id": {
          "tipo": "string",
          "desc": "Local subscription ID: 32 lowercase hexadecimal characters.",
          "exemplo": "0123456789abcdef0123456789abcdef"
        },
        "cursor": {
          "tipo": "string",
          "desc": "State version. Compare for equality. It does not confirm a payment."
        }
      },
      "erros": {
        "400": "Invalid body, query, offer, acceptance, card or environment; card declined.",
        "401": "Missing or invalid session. An API key cannot authorize these requests.",
        "403": "Invalid origin or cookie; invalid CSRF protection on a write.",
        "404": "Sales and payment tests are unavailable, or the subscription is outside this account, product, merchant or environment.",
        "431": "Cookie header exceeds 8 KiB.",
        "503": "Account, database, configuration or provider unavailable; four concurrent operations already in progress."
      },
      "exemplo": "curl -s $ORIGIN/api/credito/assinaturas/0123456789abcdef0123456789abcdef/estado -b sessao.cookies",
      "returns": "{ id, cursor }",
      "url": "https://pageaudit.online/api/credito/assinaturas/:id/estado",
      "auth_detail": "Global account: HttpOnly cookie `__Host-mm-auth`, set when you sign in on this site's `/conta/global` page. An ended session answers 401 `session_ended` (it never falls back to the guest). Writes need the same Origin and `X-CSRF-Token` from `/api/auth/bootstrap`. No bearer token is issued to humans."
    },
    {
      "method": "GET",
      "path": "/api/pricing",
      "auth": "none",
      "grupo": "Discovery",
      "summary": "Current prices and free allowances.",
      "retorno": {
        "product": {
          "tipo": "string",
          "desc": "Product name."
        },
        "quota": {
          "tipo": "PaymentQuota",
          "desc": "Public allowances and current list prices; not personal usage."
        },
        "pricing": {
          "tipo": "string",
          "desc": "Absolute URL of the current price list."
        },
        "billing": {
          "tipo": "string",
          "desc": "Absolute URL of payment discovery or the existing billing summary."
        },
        "api_index": {
          "tipo": "string",
          "desc": "Absolute URL of the API catalog."
        }
      },
      "erros": {
        "405": "Use GET or HEAD."
      },
      "exemplo": "curl -s $ORIGIN/api/pricing",
      "returns": "{ product, quota{free,paid,how_to_pay,live,free_now?,charged_now?,trial?}, pricing, billing, api_index }",
      "url": "https://pageaudit.online/api/pricing",
      "auth_detail": "Public, no credential."
    }
  ],
  "data_retention": {
    "stored": "Each run writes one row in `audits` with the whole result: summary (including the redirect chain, the origin's robots.txt/sitemap, alt, links, word count, hreflang and legacy markup, discovery checks with evidence and references, per-crawler robots.txt access, Chrome UX Report metrics and, when requested, the lab test summary), issues, counts, the raw JSON-LD and every response header. Journeys keeps consented action batches for seven UTC days, up to 1 MiB per site per day. It stores random tab sessions, static action IDs and structural selectors, without typed text or screen recordings. Domain monitoring keeps current registration, certificate and DNS references, bounded certificate-issuance projections and private contact field states with keyed comparison digests. Contact values are discarded. Earlier versions and private incident history expire after 90 days. TLS review reports expire after 26 hours. Manual dates and settings remain until removal is requested through Contact.",
    "not_stored": [
      "The page's HTML — downloaded up to 2 MB, analysed and discarded. Re-reading requires a new run.",
      "The target's `Set-Cookie`, removed on purpose: it is a third party's credential and no check uses it.",
      "Discovery-file bodies: only bounded observations are stored; their text is never executed and their links are not crawled.",
      "The raw PageSpeed Insights answer (hundreds of KB with screenshots): only scores, metrics, the biggest savings and failed audits are kept.",
      "Raw registrant contacts from RDAP. Private comparison digests never enter public reports or notices. Domain alerts use the verified account destination."
    ],
    "truncation": "Nothing disappears silently. Size cuts show up in `truncated[]` (`jsonld_size`, `jsonld_nodes_over_50`, `result_size`) and `summary.jsonLdBlocks` / `summary.jsonLdDropped` say how many blocks existed and how many were left out."
  },
  "quota": {
    "free": [
      {
        "o_que": "agent prompts",
        "limite": "3 lifetime units per account or anonymous network; check=1, full=3",
        "janela": null
      },
      {
        "o_que": "audit (`POST /api/audit`)",
        "limite": "10 per account or anonymous network",
        "janela": "UTC day"
      },
      {
        "o_que": "audit — rate ceiling",
        "limite": "300 per IP",
        "janela": "hour"
      },
      {
        "o_que": "workspace tabs",
        "limite": "20 per owner",
        "janela": null
      }
    ],
    "paid": [
      {
        "o_que": "individual agent prompt after the free allowance",
        "price_usd": 0.1
      },
      {
        "o_que": "full audit agent prompt after the free allowance",
        "price_usd": 0.3
      },
      {
        "o_que": "audit beyond the daily allowance",
        "price_usd": 0.02
      },
      {
        "o_que": "tab beyond the allowance",
        "price_usd": 0.05
      },
      {
        "o_que": "GET /api/x402/audit — Audits one page for an agent, paid per call: no account, no allowance and no tab.",
        "price_usd": 0.02
      }
    ],
    "how_to_pay": "Read GET /api/billing before a paid operation. Each audit beyond the daily allowance costs $0.02, within the hourly limit. If a charge applies, the API returns HTTP 402 with accepts[]. Pay through x402 using USDC on Base, then repeat the same call with PAYMENT-SIGNATURE (x402 v2) or X-PAYMENT (v1); a standard x402 client such as @x402/fetch does it for you. A signed-in account can also use its 7-day trial.",
    "live": "https://pageaudit.online/api/billing",
    "trial": {
      "days": 7,
      "how": "Sign in at /conta/global to access 7 days without audit usage charges from the account's first use of PageAudit. This does not extend the three-unit prompt allowance. Hourly limits still apply."
    }
  },
  "mcp": {
    "endpoint": "https://pageaudit.online/mcp",
    "transport": "streamable-http",
    "tools": 62,
    "note": "Plugs straight into the MCP client; nothing to install. Tools = the operations below."
  },
  "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\""
  ]
}