AttestPage · Docs · Pricing · OpenAPI · llms.txt

Vercel AI SDK: check a quote before citing it

Last updated: 2026-10-12

A Vercel AI SDK (ai) agent with two AttestPage tools: verify_quote (US$0.01 a call) and check_links (US$0.005 per batch of up to 10 URLs). It uses the attestpage.mjs client from openai-agents-sdk.md: with no key it calls the free trial, which allows 5 calls per IP address per UTC day and up to 3 URLs per check_links call. With ATTESTPAGE_X402_KEY set, it pays each call with x402 on Base, in USDC. When a call needs payment and no key is set, the tool returns the price to the agent instead of raising, so the agent can say what the call would cost.

With no ANTHROPIC_API_KEY, the file skips the model and calls verify_quote once itself, printing the JSON the agent would get.

For the same check in other frameworks, see LangChain, LlamaIndex.TS, OpenAI Agents SDK and Claude Agent SDK.

Node.js 20 or later. Save the client as attestpage.mjs next to vercel-ai-sdk.mjs.

Install

npm init -y && npm pkg set type=module
npm install ai@7 @ai-sdk/anthropic zod
npm install @x402/fetch @x402/evm viem   # only needed to pay
export ANTHROPIC_API_KEY=...             # leave unset to call the tool without a model

Any model the AI SDK supports works: install its provider package and pass its model as model.

vercel-ai-sdk.mjs

// Vercel AI SDK agent that checks a quote with AttestPage before it cites it.
// With no ANTHROPIC_API_KEY it skips the model and calls verify_quote itself, so you can see what the tool returns.
import { z } from 'zod';
import { createClient } from './attestpage.mjs';

const attestpage = await createClient();

// tool() from 'ai' only adds types, so plain objects work. Tools return the route's JSON.
const tools = {
  verify_quote: {
    description: 'Use before quoting a web page: is this exact or near-exact quote on the page at url? '
      + 'Returns result.match (exact, fuzzy or none), page.content_kind and a signed receipt. US$0.01.',
    inputSchema: z.object({ url: z.string(), quote: z.string() }),
    execute: ({ url, quote }) => attestpage.verifyQuote({ url, quote }),
  },
  check_links: {
    description: 'Use before putting links in an answer: status, final URL and content_kind for each URL. '
      + 'Up to 10 URLs (3 on the free trial). US$0.005 per batch.',
    inputSchema: z.object({ urls: z.array(z.string()).min(1).max(10) }),
    execute: ({ urls }) => attestpage.checkLinks({ urls }),
  },
};

const args = process.argv.slice(2);
if (!process.env.ANTHROPIC_API_KEY) {
  // No model: node vercel-ai-sdk.mjs [url] [quote]
  const [url = 'https://www.iana.org/help/example-domains', quote = 'These domains may be used as illustrative examples in documents'] = args;
  console.log(JSON.stringify(await tools.verify_quote.execute(tools.verify_quote.inputSchema.parse({ url, quote })), null, 2));
} else {
  const [{ generateText, isStepCount }, { anthropic }] = await Promise.all([import('ai'), import('@ai-sdk/anthropic')]);
  const question = args.join(' ')
    || 'Does https://www.iana.org/help/example-domains say "These domains may be used as illustrative examples in documents"?';
  const { text } = await generateText({
    model: anthropic(process.env.MODEL || 'claude-sonnet-5-5'),
    tools,
    stopWhen: isStepCount(5),
    instructions: 'Before you quote a web page, call verify_quote. Cite a quote only when result.match is exact or fuzzy '
      + 'and page.content_kind is real. If a tool returns payment_required, give the price and stop.',
    prompt: question,
  });
  console.log(text);
}
node vercel-ai-sdk.mjs                              # free trial
ATTESTPAGE_X402_KEY=0x... node vercel-ai-sdk.mjs    # pays US$0.01 per verify_quote in USDC

With no ANTHROPIC_API_KEY, pass a URL and a quote instead of a question: node vercel-ai-sdk.mjs <url> "<quote>".

What comes back

verify_quote returns the route's JSON. The parts an agent needs: