Pay from coding tools

Last updated Jun 3, 2026 The following examples show how to add x402 payment handling to AI coding tools. When the tool encounters a 402 response, it pays automatically and retries.

Both examples require:

  • A wallet private key (set as X402_PRIVATE_KEY environment variable)
  • The x402 packages: @x402/fetch, @x402/evm, and viem

OpenCode plugin

OpenCode plugins expose tools to the agent. To create an x402-fetch tool that handles 402 responses, create .opencode/plugins/x402-payment.ts:

// Use base-sepolia for testing. Get test USDC from https://faucet.circle.com/
import type { Plugin } from "@opencode-ai/plugin";
import { tool } from "@opencode-ai/plugin";
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

export const X402PaymentPlugin: Plugin = async () => ({
    tool: {
        "x402-fetch": tool({
            description:
                "Fetch a URL with x402 payment. Use when webfetch returns 402.",
            args: {
                url: tool.schema.string().describe("The URL to fetch"),
                timeout: tool.schema.number().optional().describe("Timeout in seconds"),
            },
            async execute(args) {
                const privateKey = process.env.X402_PRIVATE_KEY;
                if (!privateKey) {
                    throw new Error("X402_PRIVATE_KEY environment variable is not set.");
                }

                // Your human-in-the-loop confirmation flow...
                // const approved = await confirmPayment(args.url, estimatedCost);
                // if (!approved) throw new Error("Payment declined by user");

                const account = privateKeyToAccount(privateKey as `0x${string}`);
                const client = new x402Client();
                registerExactEvmScheme(client, { signer: account });
                const paidFetch = wrapFetchWithPayment(fetch, client);

                const response = await paidFetch(args.url, {
                    method: "GET",
                    signal: args.timeout
                        ? AbortSignal.timeout(args.timeout * 1000)
                        : undefined,
                });

                if (!response.ok) {
                    throw new Error(`${response.status} ${response.statusText}`);
                }

                return await response.text();
            },
        }),
    },
});

When the built-in webfetch returns a 402, the agent calls x402-fetch to retry with payment.

Claude Code hook

Claude Code hooks intercept tool results. To handle 402s transparently, create a script at .claude/scripts/handle-x402.mjs:

// Use base-sepolia for testing. Get test USDC from https://faucet.circle.com/
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const input = JSON.parse(await readStdin());

const haystack = JSON.stringify(input.tool_response ?? input.error ?? "");
if (!haystack.includes("402")) process.exit(0);

const url = input.tool_input?.url;
if (!url) process.exit(0);

const privateKey = process.env.X402_PRIVATE_KEY;
if (!privateKey) {
    console.error("X402_PRIVATE_KEY not set.");
    process.exit(2);
}

try {
    // Your human-in-the-loop confirmation flow...
    // const approved = await confirmPayment(url);
    // if (!approved) process.exit(0);

    const account = privateKeyToAccount(privateKey);
    const client = new x402Client();
    registerExactEvmScheme(client, { signer: account });
    const paidFetch = wrapFetchWithPayment(fetch, client);

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

    if (!res.ok) {
        console.error(`Paid fetch failed: ${res.status}`);
        process.exit(2);
    }

    console.log(
        JSON.stringify({
            hookSpecificOutput: {
                hookEventName: "PostToolUse",
                additionalContext: `Paid for "${url}" via x402:\n${text}`,
            },
        }),
    );
} catch (err) {
    console.error(`x402 payment failed: ${err.message}`);
    process.exit(2);
}

function readStdin() {
    return new Promise((resolve) => {
        let data = "";
        process.stdin.on("data", (chunk) => (data += chunk));
        process.stdin.on("end", () => resolve(data));
    });
}

Register the hook in .claude/settings.json:

{
    "hooks": {
        "PostToolUse": [
            {
                "matcher": "WebFetch",
                "hooks": [
                    {
                        "type": "command",
                        "command": "node .claude/scripts/handle-x402.mjs",
                        "timeout": 30
                    }
                ]
            }
        ]
    }
}

Was this helpful?

YesNo

On this page

Pay from coding tools - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/tools/payments/x402/pay-with-tool-plugins/#page","headline":"Pay from coding tools","description":"Add x402 payment handling to OpenCode and Claude Code.","url":"https://developers.cloudflare.com/agents/tools/payments/x402/pay-with-tool-plugins/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/tools/payments/x402/pay-with-tool-plugins/og.png?v=00afef328aa9b66e","dateModified":"2026-06-03","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: Give an agent a Linux sandbox to run shell commands, scripts, and tests. title: Sandbox image: https://developers.cloudflare.com/agents/tools/sandbox/og.png?v=f5d0095ed58b60e2


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agents/llms.txt
Use this file to discover all available pages before exploring further.