> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meterlane.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Meterlane x402 USDC Payments with the Vercel AI SDK

> Define a Vercel AI SDK tool that calls a Meterlane gateway route and pays x402 automatically with USDC whenever the server responds with 402.

The Vercel AI SDK's `tool()` helper lets you give a language model structured actions it can invoke during a conversation. By combining `tool()` with `createAgentClient` from `@meterlane/x402/client`, you can expose any Meterlane gateway route as a model-callable tool that handles x402 payment negotiation without any extra logic in your application code.

<Note>
  All examples on this page target **Base Sepolia** (`eip155:84532`) and the public sandbox demo route. Do not use mainnet keys in development or CI environments.
</Note>

<Steps>
  <Step title="Install dependencies">
    You need the `ai` package for the tool primitive and `generateText`, `zod` for parameter schemas, and `@meterlane/x402` for the payment client.

    <CodeGroup>
      ```bash npm theme={null}
      npm add ai zod @meterlane/x402
      ```

      ```bash yarn theme={null}
      yarn add ai zod @meterlane/x402
      ```

      ```bash pnpm theme={null}
      pnpm add ai zod @meterlane/x402
      ```
    </CodeGroup>

    Import the MIT-licensed client entry point — not the server middleware:

    ```typescript theme={null}
    import { createAgentClient } from "@meterlane/x402/client";
    ```
  </Step>

  <Step title="Set your environment variable">
    <Warning>
      Never hard-code your private key in source files or commit it to version control. Read it from `AGENT_PRIVATE_KEY` at runtime. Use a dedicated hot wallet funded with only the USDC you need for testing.
    </Warning>

    ```bash theme={null}
    AGENT_PRIVATE_KEY=0xYOUR_BASE_SEPOLIA_PRIVATE_KEY
    ```

    Get test USDC from the [Circle faucet](https://faucet.circle.com) — select **Base Sepolia** before requesting.
  </Step>

  <Step title="Define the tool">
    Save the following as `vercel-ai-meterlane-tool.ts`. The `execute` function uses `paidFetch` as a drop-in replacement for `fetch`; x402 payment is handled automatically on **402** responses.

    ```typescript theme={null}
    import { generateText, tool } from "ai";
    import { openai } from "@ai-sdk/openai";
    import { z } from "zod";
    import type { Hex } from "viem";
    import { createAgentClient } from "@meterlane/x402/client";

    const privateKey = process.env.AGENT_PRIVATE_KEY as Hex | undefined;
    if (!privateKey?.startsWith("0x")) {
      throw new Error("Set AGENT_PRIVATE_KEY to a 0x-prefixed Base Sepolia funded key");
    }

    // Drop-in fetch replacement — pays 402 automatically
    const paidFetch = createAgentClient(privateKey, "eip155:84532");

    const gatewayBase =
      process.env.GATEWAY_URL ?? "https://gateway.meterlane.app";
    const orgSlug = process.env.ORG_SLUG ?? "demo";

    export const meterlanePaidGet = tool({
      description:
        "Fetch a Meterlane gateway route. Pays x402 automatically on HTTP 402 using USDC on Base Sepolia.",
      parameters: z.object({
        path: z
          .string()
          .describe("Route path under /gateway/:orgSlug, e.g. /demo"),
      }),
      execute: async ({ path }) => {
        const normalized = path.startsWith("/") ? path : `/${path}`;
        const url = `${gatewayBase}/gateway/${orgSlug}${normalized}`;

        const res = await paidFetch(url, { method: "GET" });
        const text = await res.text();

        return {
          status: res.status,
          paymentReceipt:
            res.headers.get("PAYMENT-RESPONSE") ??
            res.headers.get("X-PAYMENT-RESPONSE") ??
            null,
          bodyPreview: text.slice(0, 500),
        };
      },
    });
    ```
  </Step>

  <Step title="Call the tool and optionally attach it to a model">
    You can invoke the tool directly for testing, or pass it to `generateText` so the model decides when to call it:

    ```typescript theme={null}
    async function main() {
      // Direct invocation — no model required, useful for smoke testing payment
      const result = await meterlanePaidGet.execute!({ path: "/demo" }, {
        messages: [],
        toolCallId: "test",
      });
      console.log("Direct result:", JSON.stringify(result, null, 2));

      // Model-driven invocation — requires OPENAI_API_KEY
      if (process.env.OPENAI_API_KEY) {
        const { text } = await generateText({
          model: openai("gpt-4o-mini"),
          tools: { meterlanePaidGet },
          maxSteps: 3,
          prompt:
            "Call meterlanePaidGet with path /demo and summarize the HTTP status and response.",
        });
        console.log("Model output:", text);
      }
    }

    main().catch((err) => {
      console.error(err);
      process.exit(1);
    });
    ```

    Run the file:

    ```bash theme={null}
    AGENT_PRIVATE_KEY=0xYOUR_KEY npx tsx vercel-ai-meterlane-tool.ts
    ```

    A successful paid call returns `"status": 200` and a `bodyPreview` with the upstream API response.

    <Tip>
      `tool.execute` signature requirements vary by `ai` package version. If your version only supports tools inside `generateText`, pass `tools: { meterlanePaidGet }` directly and let the model invoke it rather than calling `.execute` manually.
    </Tip>
  </Step>
</Steps>

## How it works

The `execute` handler calls `paidFetch` instead of native `fetch`. When the Meterlane gateway returns **402**, `@meterlane/x402/client` reads the `X-Payment-Requirements` header, uses `ExactEvmScheme` to produce an ERC-3009 USDC authorization signed by your private key on Base Sepolia, and retries the request with the authorization header attached. The second request receives **200** and the tool returns the response body to the model.

The tool also forwards the settlement receipt header (`PAYMENT-RESPONSE`) back to the model so it has an on-chain record of the payment if needed.
