{
  "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 and get a signed verdict on what came back. POST /v1/check/links (US$0.005): check up to 10 links in one call. POST /v1/check/packages (US$0.005): check whether up to 10 package names exist on npm, PyPI or crates.io. 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.08): verify up to 20 quotes on one web page in one call. 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), 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, 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://vehcdj664efetfrsolne5umanq.srv.us/terms"
  },
  "servers": [
    {
      "url": "https://vehcdj664efetfrsolne5umanq.srv.us"
    }
  ],
  "externalDocs": {
    "description": "Docs",
    "url": "https://vehcdj664efetfrsolne5umanq.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": "Use when an agent is about to quote or cite a web page: send the URL and the quote in your exact words, and get back whether those words, or a close fuzzy match, are on the live page (exact, fuzzy or none), with offsets, context, page hashes and a signed receipt, all in one call, so a misquoted or made-up quote is caught before a user sees it. For a paraphrased claim, match \"passages\" returns the 3 passages sharing most of its words, scored, with offsets: evidence to judge, not a verdict (no LLM, same answer every call). PDFs are read too, on the full service (an edge server answers 501 not_on_edge, uncharged): a match or passage on a PDF also gives pdf_page and pdf_page_end, its 1-based pages. For 9 or more quotes on one page, verify/quotes costs less: one call checks up to 20.",
        "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://vehcdj664efetfrsolne5umanq.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). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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",
          "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": "Use before giving a user an answer that cites web pages: send up to 10 quote and URL pairs, and get back for each one an exact, fuzzy or none match, score, offsets, context and the page's status, final URL and content_kind, under one signed receipt. Each page is fetched once. With match \"passages\", each paraphrased claim gets its 3 best-sharing passages, scored, as evidence to judge, not a verdict. A URL whose domain does not resolve is reported (http_error) and the batch is charged; a batch where no URL resolves gets 422 and is not charged. A cited PDF is read too, on the full service: each match or passage on it also gives pdf_page and pdf_page_end, its 1-based pages. A cited paper with a DOI also gets Crossref's title, authors, year and journal and a retracted flag (Retraction Watch data). For 1 to 9 pairs, one verify/quote call per pair costs less, without the Crossref data.",
        "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://vehcdj664efetfrsolne5umanq.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). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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",
          "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 and get a signed verdict on what came back",
        "description": "Use when an agent needs a web page as clean text and must know what actually came back: send a URL, and get back a content_kind verdict (real, bot_wall, js_shell, paywall, http_error and others), status, final URL, title, hashes, prompt-injection flags, the clean text when return_text is set, and a signed receipt, so a bot wall, JavaScript-only shell, paywall or error page is not summarised as the real page. PDFs are read too, on the full service (an edge server answers 501 not_on_edge, uncharged): the text layer, pages separated by a form feed, and page.pdf with page_count, pages_read and truncated.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Fetch a page and get a signed verdict on what came back",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://vehcdj664efetfrsolne5umanq.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)."
                  },
                  "return_text": {
                    "type": "boolean",
                    "description": "Also return up to 20,000 characters of clean text. Default false."
                  }
                },
                "required": [
                  "url"
                ],
                "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"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "page",
                    "receipt"
                  ]
                },
                "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). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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",
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/fetch"
        }
      }
    },
    "/v1/check/links": {
      "post": {
        "operationId": "attestpage_check_links",
        "summary": "Check up to 10 links in one call",
        "description": "Use when an agent is about to give a user links or citations: send up to 10 URLs, and get back for each one the status, redirect chain, final URL, content_kind, title and hash, so a dead, moved or blocked link is caught before it is shown. A URL whose domain does not resolve is reported as dead (http_error, signal outcome_dns_error) and the batch is charged; a batch where no URL resolves gets 422 and is not charged. A live PDF link is reported as content_kind pdf (not read, so no title or hash); a PDF or other file with an error status is http_error. A link redirected to another site that answers 4xx/5xx is off_site with off_site_error: true.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Check up to 10 links in one call",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://vehcdj664efetfrsolne5umanq.srv.us/docs/check-links"
        },
        "security": [
          {
            "x402": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "urls": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "1 to 10 http(s) URLs to check. 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 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."
                    },
                    "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",
                      "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",
                      "title": "IANA-managed Reserved Domains",
                      "content_sha256": "…"
                    }
                  ],
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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",
          "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",
        "description": "Use when an agent is about to install or recommend a package an LLM suggested: send up to 10 names, each with its registry (npm, pypi or crates), and get back whether the registry lists each name (exists true/false), latest version, description, licence, links, creation time and flags (deprecated, yanked, created_recently, name_differs), under one signed receipt, so a made-up package name is caught before install. It says whether the registry lists the name; it is not a security verdict. A batch where every lookup fails gets 502 and is not charged.",
        "tags": [
          "paid"
        ],
        "x-mcp-annotations": {
          "title": "Check whether up to 10 package names exist on npm, PyPI or crates.io",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "externalDocs": {
          "url": "https://vehcdj664efetfrsolne5umanq.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 }; 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)."
                        }
                      },
                      "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"
                      ],
                      "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.",
                  "receipt": "eyJ…",
                  "tier": "paid"
                }
              }
            }
          },
          "400": {
            "description": "Bad request (bad_request, invalid_request, invalid_url, blocked_target). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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",
          "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": "Use when an agent needs to show later that a file, answer or record existed unchanged at a point in time: send its sha256, and get back a signed, timestamped receipt over it; a later altered copy no longer matches. Optional note; with an idempotency_key (x402 payments only), a retry gets the same receipt and is not a new record.",
        "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://vehcdj664efetfrsolne5umanq.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 receipt; 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). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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",
          "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": "Use before sending a finished answer or report that quotes web pages: send the document as written, and get back every quote with its link, found by fixed rules and checked on the live page: exact, fuzzy or none, score, offsets in your document and on the page, and each page's status, final URL and content_kind, under one signed receipt. Up to 10 quote and link pairs per call; a longer document is checked in parts, and the free preview (POST /v1/verify/document/preview) lists the pairs and the price first. With match \"passages\", each sentence that carries a link is a claim and gets its best-matching passages as evidence. For a part of 1 to 9 pairs, one verify/quote call per pair costs less; the free preview lists each pair and its part.",
        "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://vehcdj664efetfrsolne5umanq.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"
                    ],
                    "description": "markdown (default) or text (only bare URLs and [n] references are links)."
                  },
                  "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 needs 3 or fewer."
                  },
                  "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."
                    },
                    "summary": {
                      "type": "object"
                    },
                    "receipt": {
                      "type": "string"
                    },
                    "tier": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "document_sha256",
                    "extractor",
                    "part",
                    "parts",
                    "results",
                    "pages",
                    "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": [],
                  "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). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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",
          "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": "Use when an agent is about to quote one web page several times: send the URL and up to 20 quotes in your exact words, and get back for each one whether it, or a close fuzzy match, is on the live page (exact, fuzzy or none), with offsets and context, plus the page's verdict and hashes, under one signed receipt. The page is fetched once and the call has one price for 1 to 20 quotes, so from 9 quotes up it costs less than one verify/quote call per quote. With match \"passages\", each paraphrased claim gets the 3 passages sharing most of its words, scored, as evidence to judge, not a verdict. Full service only (an edge server answers 501 not_on_edge, uncharged). A PDF is read too: each match or passage on it also gives pdf_page and pdf_page_end, its 1-based pages.",
        "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://vehcdj664efetfrsolne5umanq.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). 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 or check/links with more than 3 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"
                }
              }
            }
          },
          "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, 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 and /mcp (body_too_large). Never charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Cannot be done (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 (registry_unavailable: for check/packages, no registry lookup in the batch succeeded, 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.08"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "x-402": {
          "price_usd": "0.08",
          "price_sats": 80,
          "rail": "x402",
          "scheme": "exact",
          "network": "eip155:84532",
          "asset": "USDC",
          "testnet": true,
          "charged_when": "the route answers below 400",
          "sample": "/v1/sample/quotes"
        }
      }
    },
    "/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://vehcdj664efetfrsolne5umanq.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). 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 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://vehcdj664efetfrsolne5umanq.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"
                    ],
                    "description": "markdown (default) or text (only bare URLs and [n] references are links)."
                  },
                  "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 needs 3 or fewer."
                  },
                  "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). 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 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://vehcdj664efetfrsolne5umanq.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"
              ]
            }
          },
          {
            "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"
            }
          }
        ],
        "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). 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://vehcdj664efetfrsolne5umanq.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"
        ]
      }
    }
  }
}
