{
  "openapi": "3.1.0",
  "info": {
    "title": "AttestPage",
    "version": "0.1.0",
    "summary": "Signed evidence about web pages for AI agents, paid per call.",
    "x-guidance": "Use these routes before an answer reaches a user: to fact-check quotes and citations against their sources, find dead links, catch hallucinated package names, read a page with a verdict on what came back, or timestamp a hash. POST /v1/verify/quote (US$0.01): verify that a quote appears on a web page. POST /v1/verify/citations (US$0.10): verify up to 10 quote and URL citations in one call. POST /v1/fetch (US$0.002): fetch a page, or up to 10 in one call, each with a verdict, injection flags and a signed receipt. POST /v1/recheck (US$0.003): re-check a page against an earlier receipt: unchanged, changed, gone or unreadable. POST /v1/check/links (US$0.005): check up to 10 links in one call: a link verdict (ok, broken, moved, unknown) per URL, optional expected_text. POST /v1/check/packages (US$0.005): check whether up to 10 package names exist on npm, PyPI or crates.io, with known OSV.dev advisories. POST /v1/attest (US$0.002): sign a timestamped receipt over a SHA-256 hash. POST /v1/verify/document (US$0.10): check the quotes and links in a whole document, 10 per part. POST /v1/verify/quotes (US$0.02): verify up to 20 quotes on one web page in one call. POST /v1/verify/text-quote (US$0.005): verify a quote against text you already hold, with no fetch. Each paid route takes a JSON body (see its requestBody schema and example). Call it without payment to get 402 with the x402 offer in the PAYMENT-REQUIRED header (USDC, network eip155:84532; pay on any one), sign it, and resend with PAYMENT-SIGNATURE; an empty body also gets the 402, so the price can be read. A bad request gets 400 before any payment and is never charged; payment settles only when the route answers below 400. Every answer carries an Ed25519-signed receipt: check one free with POST /v1/receipt/verify (a paid answer also names its receipt_id, and the receipt its payment.tx), or offline against /.well-known/jwks.json. GET /v1/sample/{name} runs any paid route free on a demo page.",
    "description": "Check whether a quote appears on a page or check up to 10 citations in one call, fetch a page with a verdict on what came back (real content, bot wall, JavaScript shell, paywall, error), check links, check that package names exist on npm, PyPI or crates.io, and timestamp a hash. Every answer carries an Ed25519-signed receipt you can verify offline. Paid per call with x402 (USDC on Base Sepolia (a test network)). A receipt shows what our fetcher saw at a time; it does not show that a statement is correct.",
    "termsOfService": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/terms"
  },
  "servers": [
    {
      "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us"
    }
  ],
  "externalDocs": {
    "description": "Docs",
    "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs"
  },
  "tags": [
    {
      "name": "paid",
      "description": "Paid per call with x402. Bad requests are refused before payment and never charged."
    },
    {
      "name": "free",
      "description": "No payment needed."
    }
  ],
  "paths": {
    "/v1/verify/quote": {
      "post": {
        "operationId": "attestpage_verify_quote",
        "summary": "Verify that a quote appears on a web page",
        "description": "Checks a quote on its live page: exact, fuzzy or none, offsets and a signed receipt. US$0.01 per call; POST url, quote.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Verify that a quote appears on a web page",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-quote"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "http(s) URL of the page to check (max 2,048 characters)."
                  },
                  "quote": {
                    "type": "string",
                    "description": "Text to look for on the page, or with match \"passages\" the claim to find support for (max 1,000 characters after normalisation)."
                  },
                  "match": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "fuzzy",
                      "passages"
                    ],
                    "description": "exact; fuzzy (default), which also accepts small differences; or passages, for a paraphrased claim: the 3 passages that share most of its words, scored, as evidence for you to judge (not a verdict)."
                  },
                  "case_sensitive": {
                    "type": "boolean",
                    "description": "Default false; must be false with match \"passages\"."
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 1,
                    "description": "Fuzzy similarity needed, default 0.9."
                  }
                },
                "required": [
                  "url",
                  "quote"
                ],
                "additionalProperties": false
              },
              "example": {
                "url": "https://www.iana.org/help/example-domains",
                "quote": "These domains may be used as illustrative examples in documents",
                "match": "fuzzy",
                "case_sensitive": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "description": "match, score, start, end, context, occurrences, normalised_sha256; with match \"passages\" also passages [{ start, end, score, text, missing, flags }], scorer and note; on a PDF the match and each passage also give pdf_page and pdf_page_end (1-based)."
                    },
                    "page": {
                      "type": "object",
                      "description": "Page verdict and evidence, including server_ip, tls and headers (the network evidence also signed into the receipt; null on an edge server)."
                    },
                    "injection_flags": {
                      "type": "array"
                    },
                    "advice": {
                      "type": "string"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "result",
                    "page",
                    "receipt"
                  ]
                },
                "example": {
                  "result": {
                    "match": "exact",
                    "score": 1,
                    "start": 171,
                    "end": 234,
                    "context": "...maintained for documentation purposes. These domains may be used as illustrative examples in documents without prior coordination with us...",
                    "occurrences": 1,
                    "normalised_sha256": "…"
                  },
                  "page": {
                    "content_kind": "real",
                    "http_status": 200
                  },
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.01"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.01",
          "price_sats": 10,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/quote"
        }
      }
    },
    "/v1/verify/citations": {
      "post": {
        "operationId": "attestpage_verify_citations",
        "summary": "Verify up to 10 quote and URL citations in one call",
        "description": "Checks up to 10 quote and URL pairs on live pages: exact, fuzzy or none, one receipt. US$0.10 per call; POST pairs.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Verify up to 10 quote and URL citations in one call",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-citations"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "citations": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "1 to 10 citations. Each URL is fetched once however many quotes cite it. Dead domains are reported as results; if none resolves, 422 and no charge.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "url": {
                          "type": "string",
                          "description": "http(s) URL of the cited page (max 2,048 characters)."
                        },
                        "quote": {
                          "type": "string",
                          "description": "Text the answer says is on that page, or with match \"passages\" the claim it says the page supports (max 1,000 characters after normalisation)."
                        }
                      },
                      "required": [
                        "url",
                        "quote"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "match": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "fuzzy",
                      "passages"
                    ],
                    "description": "For every citation: exact; fuzzy (default), which also accepts small differences; or passages, for a paraphrased claim: the 3 passages that share most of its words, scored, as evidence for you to judge (not a verdict)."
                  },
                  "case_sensitive": {
                    "type": "boolean",
                    "description": "Default false; must be false with match \"passages\"."
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 1,
                    "description": "Fuzzy similarity needed, default 0.9."
                  }
                },
                "required": [
                  "citations"
                ],
                "additionalProperties": false
              },
              "example": {
                "citations": [
                  {
                    "url": "https://www.iana.org/help/example-domains",
                    "quote": "These domains may be used as illustrative examples in documents"
                  },
                  {
                    "url": "https://www.iana.org/domains/reserved",
                    "quote": "IANA-managed Reserved Domains"
                  }
                ],
                "match": "fuzzy"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "description": "One entry per citation, in request order: the match verdict (with match \"passages\": passages [{ start, end, score, text, missing, flags }] and scorer), or error (match_too_costly) for that citation alone (on a PDF the match and each passage also give pdf_page and pdf_page_end, 1-based), plus its page's final_url, http_status and content_kind; page indexes pages."
                    },
                    "pages": {
                      "type": "array",
                      "description": "One entry per distinct URL: page verdict and evidence, including server_ip, tls and headers (also signed into the receipt). A page with a DOI also has crossref: { doi, doi_source, status (found, not_found or skipped), title, authors, year, container_title, type, retracted, updates, update_to } from Crossref."
                    },
                    "summary": {
                      "type": "object",
                      "description": "Counts of exact, fuzzy, none (and passages) verdicts, failed citations, retracted: citations whose page DOI Crossref marks retracted, and doi_unchecked: citations whose page DOI lookup was skipped (busy, rate limited, timed out), so not checked."
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "results",
                    "pages",
                    "receipt"
                  ]
                },
                "example": {
                  "results": [
                    {
                      "url": "https://www.iana.org/help/example-domains",
                      "page": 0,
                      "final_url": "https://www.iana.org/help/example-domains",
                      "http_status": 200,
                      "content_kind": "real",
                      "match": "exact",
                      "score": 1,
                      "start": 171,
                      "end": 234,
                      "context": "...These domains may be used as illustrative examples in documents without prior coordination with us...",
                      "occurrences": 1
                    },
                    {
                      "url": "https://www.iana.org/domains/reserved",
                      "page": 1,
                      "final_url": "https://www.iana.org/domains/reserved",
                      "http_status": 200,
                      "content_kind": "real",
                      "match": "exact",
                      "score": 1,
                      "start": 0,
                      "end": 29,
                      "context": "IANA-managed Reserved Domains...",
                      "occurrences": 1
                    }
                  ],
                  "pages": [
                    {
                      "requested_url": "https://www.iana.org/help/example-domains",
                      "final_url": "https://www.iana.org/help/example-domains",
                      "http_status": 200,
                      "content_kind": "real",
                      "title": "Example Domains",
                      "content_sha256": "…",
                      "normalised_sha256": "…",
                      "injection_flags": []
                    },
                    {
                      "requested_url": "https://www.iana.org/domains/reserved",
                      "final_url": "https://www.iana.org/domains/reserved",
                      "http_status": 200,
                      "content_kind": "real",
                      "title": "IANA-managed Reserved Domains",
                      "content_sha256": "…",
                      "normalised_sha256": "…",
                      "injection_flags": []
                    }
                  ],
                  "summary": {
                    "exact": 2,
                    "fuzzy": 0,
                    "none": 0,
                    "failed": 0
                  },
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.10"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.10",
          "price_sats": 100,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/citations"
        }
      }
    },
    "/v1/fetch": {
      "post": {
        "operationId": "attestpage_fetch",
        "summary": "Fetch a page, or up to 10 in one call, each with a verdict, injection flags and a signed receipt",
        "description": "Returns a web page as clean text, with title, hashes and a signed receipt. US$0.002 per URL; POST up to 10 URLs.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Fetch a page, or up to 10 in one call, each with a verdict, injection flags and a signed receipt",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/fetch"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "http(s) URL to fetch (max 2,048 characters). Send url or urls, not both."
                  },
                  "urls": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "Instead of url: 1 to 10 distinct http(s) URLs in one call, priced per URL (the route price × the count). The answer is results[], one per URL, each with its own receipt."
                  },
                  "return_text": {
                    "type": "boolean",
                    "description": "Also return up to 20,000 characters of clean text (per page). Default false."
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "url"
                    ]
                  },
                  {
                    "required": [
                      "urls"
                    ]
                  }
                ],
                "additionalProperties": false
              },
              "example": {
                "url": "https://www.iana.org/help/example-domains",
                "return_text": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "page": {
                      "type": "object",
                      "description": "Page verdict and evidence, including server_ip, tls and headers (the network evidence also signed into the receipt; null on an edge server); on a PDF also pdf { page_count, pages_read, truncated }."
                    },
                    "text": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "injection_flags": {
                      "type": "array"
                    },
                    "advice": {
                      "type": "string"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "description": "With urls only (then page, text and receipt are inside each entry): one entry per URL in order, { url, page, text, text_chars, text_truncated, injection_flags, advice, receipt }; each receipt signs result.batch { index, count }."
                    },
                    "tier": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "page": {
                    "final_url": "https://www.iana.org/help/example-domains",
                    "http_status": 200,
                    "content_kind": "real",
                    "title": "Example Domains",
                    "content_sha256": "…"
                  },
                  "text": "…Example Domains\n\nA number of domains … are maintained for documentation purposes…",
                  "injection_flags": [],
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.002"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.002",
          "price_sats": 2,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/fetch"
        }
      }
    },
    "/v1/recheck": {
      "post": {
        "operationId": "attestpage_recheck",
        "summary": "Re-check a page against an earlier receipt: unchanged, changed, gone or unreadable",
        "description": "Reports if a page changed since a receipt: unchanged, changed, gone, unreadable. US$0.003 per call; POST receipt, URL.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Re-check a page against an earlier receipt: unchanged, changed, gone or unreadable",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/recheck"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "receipt": {
                    "type": "string",
                    "description": "Compact JWS receipt from an earlier /v1/fetch, /v1/verify/quote, /v1/verify/quotes or /v1/recheck call of this service (paid or trial; not a sample). Max 160 KB."
                  },
                  "url": {
                    "type": "string",
                    "description": "Only without receipt: the http(s) URL to re-check (max 2,048 characters)."
                  },
                  "content_sha256": {
                    "type": "string",
                    "description": "Only with url: the page.content_sha256 from our own fetch (64 hex). Other tools hash other text."
                  },
                  "return_text": {
                    "type": "boolean",
                    "description": "Also return up to 20,000 characters of the new clean text. Default false."
                  },
                  "history": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "Optional: up to 10 earlier receipts of this service for the same url, oldest first (fetch, verify/quote or recheck; paid or trial). Adds a timeline. Not stored."
                  },
                  "quote": {
                    "type": "string",
                    "description": "Only with a verify/quote receipt in exact or fuzzy mode: the quote exactly as it was sent to verify/quote. Must match the one the receipt signed. We re-test it on the live page and answer quote.verdict: still_exact, now_exact, now_fuzzy, now_missing or unchecked."
                  },
                  "quotes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 20,
                    "description": "Only with a verify/quotes receipt in exact or fuzzy mode: all of its quotes, exactly as sent to verify/quotes, in the same order. Each must match the one the receipt signed. We re-test each on the live page and answer quotes[i].verdict (same values as quote.verdict). Not with quote."
                  }
                },
                "oneOf": [
                  {
                    "required": [
                      "receipt"
                    ]
                  },
                  {
                    "required": [
                      "url",
                      "content_sha256"
                    ]
                  }
                ],
                "additionalProperties": false
              },
              "example": {
                "url": "https://www.iana.org/help/example-domains",
                "content_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "return_text": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "change": {
                      "type": "string",
                      "enum": [
                        "unchanged",
                        "changed",
                        "gone",
                        "unreadable"
                      ]
                    },
                    "changed": {
                      "type": [
                        "boolean",
                        "null"
                      ]
                    },
                    "differences": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "baseline": {
                      "type": "object",
                      "description": "The baseline compared against; receipt_sha256 links a chain of recheck receipts."
                    },
                    "timeline": {
                      "type": [
                        "array",
                        "null"
                      ],
                      "description": "Only with history: each history receipt, then this check; changed compares clean-text hashes with the entry before."
                    },
                    "elapsed_seconds": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "page": {
                      "type": "object",
                      "description": "The new fetch, as /v1/fetch gives it."
                    },
                    "text": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "injection_flags": {
                      "type": "array"
                    },
                    "advice": {
                      "type": "string"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "change",
                    "changed",
                    "differences",
                    "baseline",
                    "page",
                    "receipt"
                  ]
                },
                "example": {
                  "change": "unchanged",
                  "changed": false,
                  "differences": [],
                  "baseline": {
                    "source": "caller",
                    "url": "https://www.iana.org/help/example-domains",
                    "content_sha256": "…"
                  },
                  "elapsed_seconds": null,
                  "page": {
                    "final_url": "https://www.iana.org/help/example-domains",
                    "http_status": 200,
                    "content_kind": "real",
                    "content_sha256": "…"
                  },
                  "text": null,
                  "injection_flags": [],
                  "advice": "…",
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.003"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.003",
          "price_sats": 3,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/recheck"
        }
      }
    },
    "/v1/check/links": {
      "post": {
        "operationId": "attestpage_check_links",
        "summary": "Check up to 10 links in one call: a link verdict (ok, broken, moved, unknown) per URL, optional expected_text",
        "description": "Checks up to 10 URLs: ok, broken, moved or unknown for each, with final URL and title. US$0.005 per call; POST URLs.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Check up to 10 links in one call: a link verdict (ok, broken, moved, unknown) per URL, optional expected_text",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/check-links"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "urls": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 10,
                    "items": {
                      "anyOf": [
                        {
                          "type": "string",
                          "minLength": 1
                        },
                        {
                          "type": "object",
                          "properties": {
                            "url": {
                              "type": "string",
                              "minLength": 1
                            },
                            "expected_text": {
                              "type": "string",
                              "description": "Optional text the page should contain (fuzzy match). Reported as expected_text.match; it does not change the link verdict."
                            }
                          },
                          "required": [
                            "url"
                          ],
                          "additionalProperties": false
                        }
                      ]
                    },
                    "description": "1 to 10 items, each an http(s) URL string or { \"url\", \"expected_text\" }. Dead domains are reported as results; if none resolves, 422 and no charge."
                  }
                },
                "required": [
                  "urls"
                ],
                "additionalProperties": false
              },
              "example": {
                "urls": [
                  "https://www.iana.org/help/example-domains",
                  "https://www.iana.org/domains/reserved"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "description": "One entry per URL, each with a link verdict (ok, broken, moved or unknown; per URL, signed into the receipt), server_ip, tls and headers (network evidence also signed into the receipt; null on an edge server). off_site_error: true when the URL redirected to another site that answers 4xx/5xx. expected_text: null unless the item sent expected_text and the page was read as real; then { match: exact, fuzzy or none, score, start, end, context, quote_sha256, evidence }, reported only (the verdict does not change)."
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "results",
                    "receipt"
                  ]
                },
                "example": {
                  "results": [
                    {
                      "url": "https://www.iana.org/help/example-domains",
                      "final_url": "https://www.iana.org/help/example-domains",
                      "http_status": 200,
                      "redirects": [],
                      "content_kind": "real",
                      "verdict": "ok",
                      "title": "Example Domains",
                      "content_sha256": "…"
                    },
                    {
                      "url": "https://www.iana.org/domains/reserved",
                      "final_url": "https://www.iana.org/domains/reserved",
                      "http_status": 200,
                      "redirects": [],
                      "content_kind": "real",
                      "verdict": "ok",
                      "title": "IANA-managed Reserved Domains",
                      "content_sha256": "…"
                    }
                  ],
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.005"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.005",
          "price_sats": 5,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/links"
        }
      }
    },
    "/v1/check/packages": {
      "post": {
        "operationId": "attestpage_check_packages",
        "summary": "Check whether up to 10 package names exist on npm, PyPI or crates.io, with known OSV.dev advisories",
        "description": "Checks up to 10 npm, PyPI or crates names: listed, version, licence, OSV.dev advisories. US$0.005 per call; POST names.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Check whether up to 10 package names exist on npm, PyPI or crates.io, with known OSV.dev advisories",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/check-packages"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "packages": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "1 to 10 packages to look up. Each is { ecosystem, name, version? }; ecosystem is npm, pypi or crates (crates.io).",
                    "items": {
                      "type": "object",
                      "properties": {
                        "ecosystem": {
                          "type": "string",
                          "enum": [
                            "npm",
                            "pypi",
                            "crates"
                          ]
                        },
                        "name": {
                          "type": "string",
                          "description": "Package name as you would install it (npm names may be scoped: @scope/name)."
                        },
                        "version": {
                          "type": "string",
                          "maxLength": 64,
                          "description": "Optional: the version you will install (e.g. a pinned 4.17.20). Advisories are then listed for this version; without it, for the latest."
                        }
                      },
                      "required": [
                        "ecosystem",
                        "name"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "packages"
                ],
                "additionalProperties": false
              },
              "example": {
                "packages": [
                  {
                    "ecosystem": "npm",
                    "name": "left-pad"
                  },
                  {
                    "ecosystem": "pypi",
                    "name": "requests"
                  },
                  {
                    "ecosystem": "crates",
                    "name": "serde"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array"
                    },
                    "note": {
                      "type": "string"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "results",
                    "note",
                    "receipt"
                  ]
                },
                "example": {
                  "results": [
                    {
                      "ecosystem": "npm",
                      "name": "left-pad",
                      "exists": true,
                      "status": "found",
                      "latest_version": "1.3.0",
                      "description": "String left pad",
                      "license": "WTFPL",
                      "homepage": "https://github.com/stevemao/left-pad#readme",
                      "repository": "git+ssh://git@github.com/stevemao/left-pad.git",
                      "created_at": "2014-03-14T09:09:20.762Z",
                      "deprecated": "use String.prototype.padStart()",
                      "yanked": null,
                      "signals": [
                        "deprecated"
                      ],
                      "vulnerabilities": {
                        "status": "checked",
                        "reason": null,
                        "version": "1.3.0",
                        "count": 0,
                        "truncated": false,
                        "items": []
                      },
                      "registry_url": "https://www.npmjs.com/package/left-pad",
                      "raw_sha256": "…"
                    }
                  ],
                  "note": "exists means the registry listed this name when we looked. It is not a security verdict: the package may still be malicious, unmaintained or not the one you meant; description and licence are the publisher's own text. vulnerabilities lists known OSV.dev advisories for the version shown (the one you sent, else the latest; name only when the registry does not list the name and no version was sent); none listed does not mean the package has no flaws, and advisory summaries are third-party text. Vulnerability data: OSV.dev (GitHub Advisory Database and PyPI Advisory Database, CC-BY 4.0; RustSec, CC0; OpenSSF Malicious Packages, Apache 2.0), see https://github.com/advisories.",
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.005"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.005",
          "price_sats": 5,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/packages"
        }
      }
    },
    "/v1/attest": {
      "post": {
        "operationId": "attestpage_attest",
        "summary": "Sign a timestamped receipt over a SHA-256 hash",
        "description": "Signs a timestamped receipt for a file's sha256, proving it existed unchanged then. US$0.002 per call; POST the hash.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Sign a timestamped receipt over a SHA-256 hash",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": false
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/attest"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "sha256": {
                    "type": "string",
                    "pattern": "^[0-9a-fA-F]{64}$",
                    "description": "SHA-256 (hex) of the thing being attested."
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 280,
                    "description": "Optional note; only its SHA-256 goes in the receipt."
                  },
                  "idempotency_key": {
                    "type": "string",
                    "maxLength": 128,
                    "description": "Optional, x402 payments only (keys are scoped to the paying address; trial and L402 calls get 400). Retries with the same key and sha256 get the same attestation (the receipt names that call own payment tx); a different sha256 gets 409."
                  }
                },
                "required": [
                  "sha256"
                ],
                "additionalProperties": false
              },
              "example": {
                "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
                "note": "delivery of report v3 to order 77"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attestation": {
                      "type": "object"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "attestation",
                    "receipt"
                  ]
                },
                "example": {
                  "attestation": {
                    "sha256": "e3b0…",
                    "note_sha256": "…",
                    "seq": 1042,
                    "issued_at": "2026-10-09T12:00:00Z",
                    "idempotency_key": null,
                    "idempotency_key_sha256": null,
                    "first_seen": true
                  },
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.002"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.002",
          "price_sats": 2,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/attest"
        }
      }
    },
    "/v1/verify/document": {
      "post": {
        "operationId": "attestpage_verify_document",
        "summary": "Check the quotes and links in a whole document, 10 per part",
        "description": "Checks every quote in a finished document against its live page, signed receipt. US$0.10 per call; POST the text.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Check the quotes and links in a whole document, 10 per part",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-document"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "The answer or report as written, 1 character to 60000 bytes (UTF-8). Its quotes and their links are found by fixed rules (extractor doc-v1)."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "markdown",
                      "text",
                      "html"
                    ],
                    "description": "markdown (default), text (only bare URLs and [n] references are links) or html (<a href> links, <blockquote> and <q> quotes; other tags are dropped)."
                  },
                  "match": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "fuzzy",
                      "passages"
                    ],
                    "description": "For every pair: exact; fuzzy (default), which also accepts small differences; or passages, for a paraphrased claim: the 3 passages that share most of its words, scored, as evidence for you to judge (not a verdict). With \"passages\", each sentence that carries a link is a claim."
                  },
                  "case_sensitive": {
                    "type": "boolean",
                    "description": "Default false; must be false with match \"passages\"."
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 1,
                    "description": "Fuzzy similarity needed, default 0.9."
                  },
                  "min_words": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20,
                    "description": "A quoted span with fewer words is skipped as too_short (default 4)."
                  },
                  "part_size": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10,
                    "description": "Pairs per part, default 10. A free trial call takes at most the size in extensions.trial.info.max_batch_items of the 402 offer."
                  },
                  "part": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "0-based part to check, default 0; call POST /v1/verify/document/preview first to see how many parts there are."
                  }
                },
                "required": [
                  "document"
                ],
                "additionalProperties": false
              },
              "example": {
                "document": "WHO says \"Air pollution kills an estimated seven million people every year\" ([WHO](https://www.who.int/health-topics/air-pollution)).\n\n> These domains may be used as illustrative examples in documents\n> — [IANA](https://www.iana.org/help/example-domains)",
                "format": "markdown",
                "match": "fuzzy",
                "part": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "document_sha256": {
                      "type": "string",
                      "description": "SHA-256 of the document as sent (UTF-8); the same on every part."
                    },
                    "extractor": {
                      "type": "string"
                    },
                    "part": {
                      "type": "integer"
                    },
                    "parts": {
                      "type": "integer"
                    },
                    "pairs_total": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "description": "One entry per pair of this part: pair, quote, url, doc_start/doc_end (code points in the document; doc_spans when the pair appears more than once, the first 20, with doc_spans_truncated counting the rest), then the verify/citations verdict: start/end are offsets on the page."
                    },
                    "pages": {
                      "type": "array",
                      "description": "One entry per distinct URL, as verify/citations."
                    },
                    "skipped": {
                      "type": "array",
                      "description": "Each skipped item with its reason: no_url, bad_url, too_short, too_long or beyond_limit. The first 100 only."
                    },
                    "skipped_truncated": {
                      "type": "integer",
                      "description": "Skipped items left out of skipped."
                    },
                    "skipped_counts": {
                      "type": "object",
                      "description": "Every skipped item counted by reason."
                    },
                    "summary": {
                      "type": "object"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "document_sha256",
                    "extractor",
                    "part",
                    "parts",
                    "results",
                    "pages",
                    "skipped",
                    "receipt"
                  ]
                },
                "example": {
                  "document_sha256": "…",
                  "extractor": "doc-v1",
                  "part": 0,
                  "parts": 1,
                  "pairs_total": 2,
                  "results": [
                    {
                      "pair": 0,
                      "quote": "Air pollution kills an estimated seven million people every year",
                      "url": "https://www.who.int/health-topics/air-pollution",
                      "doc_start": 10,
                      "doc_end": 74,
                      "page": 0,
                      "final_url": "https://www.who.int/health-topics/air-pollution",
                      "http_status": 200,
                      "content_kind": "real",
                      "match": "fuzzy",
                      "score": 0.96,
                      "start": 412,
                      "end": 478,
                      "context": "…",
                      "occurrences": 1
                    },
                    {
                      "pair": 1,
                      "quote": "These domains may be used as illustrative examples in documents",
                      "url": "https://www.iana.org/help/example-domains",
                      "doc_start": 137,
                      "doc_end": 200,
                      "page": 1,
                      "final_url": "https://www.iana.org/help/example-domains",
                      "http_status": 200,
                      "content_kind": "real",
                      "match": "exact",
                      "score": 1,
                      "start": 171,
                      "end": 234,
                      "context": "…",
                      "occurrences": 1
                    }
                  ],
                  "pages": [],
                  "skipped": [],
                  "summary": {
                    "exact": 1,
                    "fuzzy": 1,
                    "none": 0,
                    "failed": 0
                  },
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.10"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.10",
          "price_sats": 100,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/document"
        }
      }
    },
    "/v1/verify/quotes": {
      "post": {
        "operationId": "attestpage_verify_quotes",
        "summary": "Verify up to 20 quotes on one web page in one call",
        "description": "Checks up to 20 quotes on one page: exact, fuzzy or none, offsets, signed receipt. US$0.02 per call; POST url, quotes.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Verify up to 20 quotes on one web page in one call",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-quotes"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "http(s) URL of the page to check (max 2,048 characters). Fetched once for all the quotes."
                  },
                  "quotes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "description": "1 to 20 quotes to look for on that page, or with match \"passages\" claims to find support for (each max 1,000 characters after normalisation). One price for the call however many.",
                    "items": {
                      "type": "string"
                    }
                  },
                  "match": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "fuzzy",
                      "passages"
                    ],
                    "description": "For every quote: exact; fuzzy (default), which also accepts small differences; or passages, for a paraphrased claim: the 3 passages that share most of its words, scored, as evidence for you to judge (not a verdict)."
                  },
                  "case_sensitive": {
                    "type": "boolean",
                    "description": "Default false; must be false with match \"passages\"."
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 1,
                    "description": "Fuzzy similarity needed, default 0.9."
                  }
                },
                "required": [
                  "url",
                  "quotes"
                ],
                "additionalProperties": false
              },
              "example": {
                "url": "https://www.iana.org/help/example-domains",
                "quotes": [
                  "These domains may be used as illustrative examples in documents",
                  "without prior coordination with us"
                ],
                "match": "fuzzy"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "description": "One entry per quote, in request order, each the verify/quote result: match, score, start, end, context, occurrences, normalised_sha256; with match \"passages\" also passages [{ start, end, score, text, missing, flags }], scorer and note; on a PDF the match and each passage also give pdf_page and pdf_page_end (1-based). A quote too costly to match fuzzily has error (match_too_costly) and nulls instead, for that quote alone."
                    },
                    "page": {
                      "type": "object",
                      "description": "Page verdict and evidence, including server_ip, tls and headers (the network evidence also signed into the receipt)."
                    },
                    "injection_flags": {
                      "type": "array"
                    },
                    "advice": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "object",
                      "description": "Counts of exact, fuzzy, none (and passages) verdicts and failed quotes."
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "results",
                    "page",
                    "receipt"
                  ]
                },
                "example": {
                  "results": [
                    {
                      "match": "exact",
                      "score": 1,
                      "start": 171,
                      "end": 234,
                      "context": "...maintained for documentation purposes. These domains may be used as illustrative examples in documents without prior coordination with us...",
                      "occurrences": 1,
                      "normalised_sha256": "…"
                    },
                    {
                      "match": "exact",
                      "score": 1,
                      "start": 235,
                      "end": 269,
                      "context": "...illustrative examples in documents without prior coordination with us. They are not available for registration...",
                      "occurrences": 1,
                      "normalised_sha256": "…"
                    }
                  ],
                  "page": {
                    "content_kind": "real",
                    "http_status": 200
                  },
                  "summary": {
                    "exact": 2,
                    "fuzzy": 0,
                    "none": 0,
                    "failed": 0
                  },
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.02"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.02",
          "price_sats": 20,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/quotes"
        }
      }
    },
    "/v1/verify/text-quote": {
      "post": {
        "operationId": "attestpage_verify_text_quote",
        "summary": "Verify a quote against text you already hold, with no fetch",
        "description": "Checks a quote word for word against text you hold: exact, fuzzy or none, offsets. US$0.005 per call; POST text, quote.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Verify a quote against text you already hold, with no fetch",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": false
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-text-quote"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "The source text you already hold, as sent (max 50,000 characters; no fetch, no normalising before hashing). Offsets count characters (code points)."
                  },
                  "quote": {
                    "type": "string",
                    "description": "Text to look for in the source, or with match \"passages\" the claim to find support for (max 1,000 characters after normalisation)."
                  },
                  "start": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Optional, with end: the declared start offset of the quote in text, raw code points, 0-based. Only that slice is matched (max 1,000 code points), so the answer is whether the text at these offsets says the quote. Declared offsets are raw; result offsets are into the normalised text. If the slice misses but the quote is elsewhere in the text, result.located gives its position in the whole text."
                  },
                  "end": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Optional, with start: the declared end offset, raw code points, exclusive. Give both or neither; not with match \"passages\"."
                  },
                  "match": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "fuzzy",
                      "passages"
                    ],
                    "description": "exact; fuzzy (default), which also accepts small differences; or passages, for a paraphrased claim: the 3 passages of the text that share most of its words, scored, as evidence for you to judge (not a verdict). Not with start and end."
                  },
                  "case_sensitive": {
                    "type": "boolean",
                    "description": "Default false; must be false with match \"passages\"."
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 1,
                    "description": "Fuzzy similarity needed, default 0.9."
                  }
                },
                "required": [
                  "text",
                  "quote"
                ],
                "additionalProperties": false
              },
              "example": {
                "text": "Refunds are paid within 14 days of purchase, to the card that was used.",
                "quote": "refunds are paid within 14 days",
                "match": "fuzzy",
                "case_sensitive": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result with a signed evidence receipt. Charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "description": "match, score, occurrences; in search mode start, end (into the normalised text), context and normalised_sha256; with start and end, declared { start, end, slice_sha256, slice_matches } (raw offsets) and no start/end; when the slice misses but the quote is elsewhere in the text, located { match, score, start, end, occurrences, offsets: \"normalised\" } for the whole text; source { kind: \"supplied\", text_sha256, code_points }; with match \"passages\" also passages, scorer and note."
                    },
                    "injection_flags": {
                      "type": "array",
                      "description": "Prompt-injection signals found in the supplied text."
                    },
                    "advice": {
                      "type": "string"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "result",
                    "receipt"
                  ]
                },
                "example": {
                  "result": {
                    "match": "fuzzy",
                    "score": 0.972,
                    "start": 0,
                    "end": 34,
                    "context": "Refunds are paid within 14 days of purchase",
                    "occurrences": 1,
                    "normalised_sha256": "…",
                    "source": {
                      "kind": "supplied",
                      "text_sha256": "…",
                      "code_points": 79
                    }
                  },
                  "injection_flags": [],
                  "advice": "The supplied text contains a close match to this quote, with small differences. This says nothing about where the text came from.",
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required (payment_required) or the payment did not verify (payment_invalid). The PAYMENT-REQUIRED header carries the x402 offer; retry with a new PAYMENT-SIGNATURE header. An unpaid request with an empty body (or {}) gets this offer too, so the price can be read; the paid retry is checked first and a bad one gets 400, uncharged. A free-trial call to verify/citations, verify/document, verify/quotes, check/links or fetch with more than 5 items gets this offer instead of a trial, with details.reason trial_too_large. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Refused (robots_disallowed: the target site disallows our fetcher; payer_refused: the paying address is on a sanctions list). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "405": {
            "description": "Wrong method (method_not_allowed): this route takes POST only. A GET or HEAD gets 405 with Allow: POST, a Link header (rel=service-doc) to /docs/errors.md, and error.details.openapi pointing to /openapi.json and error.details.llms pointing to /llms.txt. Never charged.",
            "headers": {
              "Allow": {
                "description": "The method this route takes.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "POST"
                  ]
                }
              },
              "Link": {
                "description": "The error codes page: <origin/docs/errors.md>; rel=\"service-doc\"; type=\"text/markdown\".",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (payment_id_conflict: payment id reused for a different request; idempotency_conflict: attest key reused with a different hash or note; payment_already_used: this payment signature was already spent for another request (the same request with the same signature gets its stored result again), sign a new one, and this call did not charge again; settle_pending: an earlier call with this payment id ended in settle_failed and its payment could still settle, resend the same signed payment or a new one after Retry-After). Nothing is charged by the 409 itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (robots_too_large: for access-check, the site's robots.txt is over 2 MB, so its rules cannot be judged; host_not_found: the URL's host name does not resolve, or for check/links, verify/citations and verify/document none of the URLs resolves; match_too_costly: quote too costly to match fuzzily, use match \"exact\" or a shorter quote; for verify/citations, verify/document and verify/quotes only when every quote in the batch, part or call is; no_pairs: verify/document found no quote with a link in the document). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Upstream failed (robots_unreachable: for access-check, no robots.txt for the site could be read, so no verdict; never charged; registry_unavailable: for check/packages, no registry lookup in the batch succeeded, never charged; upstream_unreachable: /v1/fetch and /v1/recheck read no page (timeout, connect_error, decode_error, too_many_redirects, redirect_refused), never charged; facilitator_unavailable: the payment facilitator could not be reached, never charged; settle_failed: the work ran but the payment could not be settled, so the result is withheld; resend the same request with the same payment id: if the charge went through on chain the result is delivered without a new charge (payment-redelivered: true), otherwise the payment settles at most once; a charge that went through is recorded for reconciliation).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.005"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.005",
          "price_sats": 5,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "also_networks": [],
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/textquote"
        }
      }
    },
    "/v1/receipt/verify": {
      "post": {
        "operationId": "attestpage_verify_receipt",
        "summary": "Verify an evidence receipt",
        "description": "Use when you are handed an AttestPage evidence receipt and need to check it before relying on it: verifies the compact JWS against this service's published keys and returns its payload, so a forged or altered receipt is caught. Free.",
        "tags": [
          "free"
        ],
        "security": [],
        "x-mcp-annotations": {
          "title": "Verify an evidence receipt",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/receipt-verify"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "receipt": {
                    "type": "string",
                    "maxLength": 163840,
                    "description": "The compact JWS from the \"receipt\" field of a response."
                  }
                },
                "required": [
                  "receipt"
                ],
                "additionalProperties": false
              },
              "example": {
                "receipt": "eyJhbGciOiJFZERTQSIs…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification result. valid is false (with reason and message) when the signature, key or payload does not check out.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "valid": {
                      "type": "boolean",
                      "description": "True when the signature verifies with one of our keys and the payload matches schema v1."
                    },
                    "kid": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Key id from the JWS header."
                    },
                    "tier": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "enum": [
                        "paid",
                        "trial",
                        "sample",
                        null
                      ],
                      "description": "paid, trial or sample."
                    },
                    "payload": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "The decoded receipt payload."
                    },
                    "reason": {
                      "type": "string",
                      "description": "Machine-readable failure reason, only when valid is false."
                    },
                    "message": {
                      "type": "string",
                      "description": "Plain-language failure reason, only when valid is false."
                    }
                  },
                  "required": [
                    "valid"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/document/preview": {
      "post": {
        "operationId": "attestpage_preview_document",
        "summary": "Preview a document check",
        "description": "Use before paying for POST /v1/verify/document: send the same body, and get back the quote and link pairs it would check, what was skipped and why, the number of parts and the exact price. Nothing is fetched, signed or charged. Free.",
        "tags": [
          "free"
        ],
        "security": [],
        "x-mcp-annotations": {
          "title": "Preview a document check",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-document"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "document": {
                    "type": "string",
                    "description": "The answer or report as written, 1 character to 60000 bytes (UTF-8). Its quotes and their links are found by fixed rules (extractor doc-v1)."
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "markdown",
                      "text",
                      "html"
                    ],
                    "description": "markdown (default), text (only bare URLs and [n] references are links) or html (<a href> links, <blockquote> and <q> quotes; other tags are dropped)."
                  },
                  "match": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "fuzzy",
                      "passages"
                    ],
                    "description": "For every pair: exact; fuzzy (default), which also accepts small differences; or passages, for a paraphrased claim: the 3 passages that share most of its words, scored, as evidence for you to judge (not a verdict). With \"passages\", each sentence that carries a link is a claim."
                  },
                  "case_sensitive": {
                    "type": "boolean",
                    "description": "Default false; must be false with match \"passages\"."
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 1,
                    "description": "Fuzzy similarity needed, default 0.9."
                  },
                  "min_words": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20,
                    "description": "A quoted span with fewer words is skipped as too_short (default 4)."
                  },
                  "part_size": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10,
                    "description": "Pairs per part, default 10. A free trial call takes at most the size in extensions.trial.info.max_batch_items of the 402 offer."
                  },
                  "part": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "0-based part to check, default 0; call POST /v1/verify/document/preview first to see how many parts there are."
                  }
                },
                "required": [
                  "document"
                ],
                "additionalProperties": false
              },
              "example": {
                "document": "WHO says \"Air pollution kills an estimated seven million people every year\" ([WHO](https://www.who.int/health-topics/air-pollution)).\n\n> These domains may be used as illustrative examples in documents\n> — [IANA](https://www.iana.org/help/example-domains)",
                "format": "markdown",
                "match": "fuzzy",
                "part": 0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The pairs, what was skipped and the price. Not charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "document_sha256": {
                      "type": "string",
                      "description": "SHA-256 of the document as sent (UTF-8); the paid route signs the same value."
                    },
                    "extractor": {
                      "type": "string",
                      "description": "The fixed extraction rules used (doc-v1)."
                    },
                    "pairs_total": {
                      "type": "integer"
                    },
                    "part_size": {
                      "type": "integer"
                    },
                    "parts": {
                      "type": "integer",
                      "description": "Paid calls needed to check every pair (part 0 to parts - 1)."
                    },
                    "price": {
                      "type": "object",
                      "properties": {
                        "usd_per_part": {
                          "type": "string"
                        },
                        "sats_per_part": {
                          "type": "integer"
                        },
                        "usd_total": {
                          "type": "string"
                        },
                        "sats_total": {
                          "type": "integer"
                        }
                      }
                    },
                    "pairs": {
                      "type": "array",
                      "description": "Each pair: pair, part, quote, url, doc_start/doc_end (code points in the document); doc_spans when the pair appears more than once (the first 20), with doc_spans_truncated counting the rest."
                    },
                    "skipped": {
                      "type": "array",
                      "description": "Each skipped item with its reason: no_url, bad_url, too_short, too_long or beyond_limit. The first 100 only."
                    },
                    "skipped_truncated": {
                      "type": "integer",
                      "description": "Skipped items left out of skipped."
                    },
                    "skipped_counts": {
                      "type": "object",
                      "description": "Every skipped item counted by reason."
                    },
                    "limit_reached": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "document_sha256",
                    "extractor",
                    "pairs_total",
                    "parts",
                    "price",
                    "pairs",
                    "skipped"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/text-quote/preview": {
      "post": {
        "operationId": "attestpage_preview_text_quote",
        "summary": "Preview a text quote check",
        "description": "Send the same body as POST /v1/verify/text-quote (start and end are ignored) and get back exact, fuzzy or none, the score and the number of matches. Free, 30 a day per IP address. The answer is not signed and has no offsets or receipt; the paid route returns those.",
        "tags": [
          "free"
        ],
        "security": [],
        "x-mcp-annotations": {
          "title": "Preview a text quote check",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-text-quote"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "The source text you already hold, as sent (max 50,000 characters; no fetch, no normalising before hashing). Offsets count characters (code points)."
                  },
                  "quote": {
                    "type": "string",
                    "description": "Text to look for in the source, or with match \"passages\" the claim to find support for (max 1,000 characters after normalisation)."
                  },
                  "start": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Optional, with end: the declared start offset of the quote in text, raw code points, 0-based. Only that slice is matched (max 1,000 code points), so the answer is whether the text at these offsets says the quote. Declared offsets are raw; result offsets are into the normalised text. If the slice misses but the quote is elsewhere in the text, result.located gives its position in the whole text."
                  },
                  "end": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Optional, with start: the declared end offset, raw code points, exclusive. Give both or neither; not with match \"passages\"."
                  },
                  "match": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "fuzzy",
                      "passages"
                    ],
                    "description": "exact; fuzzy (default), which also accepts small differences; or passages, for a paraphrased claim: the 3 passages of the text that share most of its words, scored, as evidence for you to judge (not a verdict). Not with start and end."
                  },
                  "case_sensitive": {
                    "type": "boolean",
                    "description": "Default false; must be false with match \"passages\"."
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0.5,
                    "maximum": 1,
                    "description": "Fuzzy similarity needed, default 0.9."
                  }
                },
                "required": [
                  "text",
                  "quote"
                ],
                "additionalProperties": false
              },
              "example": {
                "text": "Refunds are paid within 14 days of purchase, to the card that was used.",
                "quote": "refunds are paid within 14 days",
                "match": "fuzzy",
                "case_sensitive": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The match, not signed. Not charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "result": {
                      "type": "object",
                      "properties": {
                        "match": {
                          "type": "string",
                          "enum": [
                            "exact",
                            "fuzzy",
                            "none"
                          ]
                        },
                        "score": {
                          "type": "number"
                        },
                        "occurrences": {
                          "type": "integer"
                        }
                      }
                    },
                    "signed": {
                      "type": "boolean",
                      "description": "Always false."
                    },
                    "advice": {
                      "type": "string"
                    },
                    "paid_route": {
                      "type": "object",
                      "description": "Path and price of the signed call."
                    }
                  },
                  "required": [
                    "result",
                    "signed"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Request body over 64 KB, or over 168 KB on receipt/verify, recheck and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sample/{name}": {
      "get": {
        "operationId": "attestpage_sample",
        "summary": "Try a route for free on a fixed demo page",
        "description": "Run a paid route's real code on a fixed demo input and return the full response, signed with the sample key (tier \"sample\"). Free; makes no outbound fetch.",
        "tags": [
          "free"
        ],
        "security": [],
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/samples"
        },
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "description": "Which route to sample: quote (verify/quote), citations (verify/citations), document (verify/document), fetch, links (check/links), packages (check/packages) or attest.",
            "schema": {
              "type": "string",
              "enum": [
                "quote",
                "citations",
                "document",
                "fetch",
                "links",
                "packages",
                "attest",
                "quotes",
                "textquote",
                "recheck"
              ]
            }
          },
          {
            "name": "match",
            "in": "query",
            "required": false,
            "description": "Only for the quote sample: fuzzy (default) checks a misformatted quote; passages shows match \"passages\" on a paraphrased claim.",
            "schema": {
              "type": "string",
              "enum": [
                "fuzzy",
                "passages"
              ],
              "default": "fuzzy"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Only for the document sample: markdown (default) or html, the same pairs written as HTML with format \"html\".",
            "schema": {
              "type": "string",
              "enum": [
                "markdown",
                "html"
              ],
              "default": "markdown"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The route's normal response plus sample: true, a note and the demo input used.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sample": {
                      "const": true
                    },
                    "note": {
                      "type": "string"
                    },
                    "input": {
                      "type": "object"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "const": "sample"
                    }
                  },
                  "required": [
                    "sample",
                    "input",
                    "receipt",
                    "tier"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target, baseline_invalid). Checked before payment; never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found (not_found).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited: to the target host, from your address or, for check/packages, the crates.io queue right now; trial_exhausted: free trial calls used up for the day); see Retry-After. Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (paused, signing_unavailable, payments_unavailable, payment_screening_unavailable; verify/citations, verify/document and verify/quotes also over_capacity with Retry-After). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sample/page": {
      "get": {
        "operationId": "attestpage_sample_page",
        "summary": "Get the demo page the samples read",
        "description": "Return the fixed demo HTML page that the free samples check. Its text is made up.",
        "tags": [
          "free"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "The demo page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "attestpage_health",
        "summary": "Check service status",
        "description": "Return service status (ok or paused), version, start time, which payment rails are enabled and uptime over the last 24 hours and 30 days from our own probe. Free.",
        "tags": [
          "free"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "paused"
                      ]
                    },
                    "service": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "time": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "started_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "rails": {
                      "type": "object"
                    },
                    "uptime": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Service availability from our probe: { h24, d30 }, each { checks, up, pct } (pct null with no checks). A check is up when /health answers 200 and every paid route answers (a 402 offer counts as up); probe test failures, facilitator errors and planned pauses are not downtime; a 404 on a route listed in /pricing.json is."
                    },
                    "payments_available": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "The same checks where a caller could pay: { h24, d30 }, each { checks, payable, pct }. A check is payable only when /health answers 200 and every paid route answers a valid 402 offer; facilitator errors, failed settlements and planned pauses are not payable. Checks before 2026-10-10T16:15:00Z that did not record enough to tell are left out of checks; later ones count as not payable."
                    },
                    "probe_checks": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "The same checks where every probe test passed (/health, each paid route's 402 offer matching pricing.json, a free sample): { h24, d30 }, each { checks, passed, pct }."
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/pricing.json": {
      "get": {
        "operationId": "attestpage_pricing",
        "summary": "Get prices",
        "description": "Return the price of every paid route, the payment rails and network, in machine-readable form. Free.",
        "tags": [
          "free"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Price list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "rails": {
                      "type": "object"
                    },
                    "routes": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/jwks.json": {
      "get": {
        "operationId": "attestpage_jwks",
        "summary": "Get the receipt signing keys",
        "description": "Return the Ed25519 public keys (JWKS) that sign evidence receipts, for offline verification. Free.",
        "tags": [
          "free"
        ],
        "security": [],
        "externalDocs": {
          "url": "https://s2h6qt73jtzlgbgnnfacln2poq.srv.us/docs/verify-offline"
        },
        "responses": {
          "200": {
            "description": "JSON Web Key Set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  },
                  "required": [
                    "keys"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "x402": {
        "type": "apiKey",
        "in": "header",
        "name": "PAYMENT-SIGNATURE",
        "description": "x402 v2 payment. Call without it to get 402 and the PAYMENT-REQUIRED offer, sign the offer, then retry with this header. See /docs/payments."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code."
              },
              "message": {
                "type": "string",
                "description": "Plain-language message."
              },
              "details": {
                "type": "object",
                "description": "Extra fields, such as field or retry_after_ms."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      }
    }
  }
}
