Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.x402.org/llms.txt

Use this file to discover all available pages before exploring further.

Hooks allow you to intercept and modify payment lifecycle events on clients, servers and facilitators.

Server Hooks

x402ResourceServer (Transport-agnostic)

Register hooks on the core resource server for verification and settlement lifecycle events. Use cases include logging payments, recording analytics, implementing custom access control or recovering from transient failures.
  • onBeforeVerify — Runs before payment verification. Return { abort: true, reason } to reject, or { skip: true, result } to use a locally produced verification result.
  • onAfterVerify — Runs after successful verification. Return { skipHandler: true, response? } to settle without invoking the resource handler.
  • onVerifyFailure — Runs on verification failure. Return { recovered: true, result } to override.
  • onBeforeSettle — Runs before settlement. Return { abort: true, reason } to reject, or { skip: true, result } to use a locally produced settlement result.
  • onAfterSettle — Runs after successful settlement.
  • onSettleFailure — Runs on settlement failure. Return { recovered: true, result } to override.
  • onVerifiedPaymentCanceled — Runs when a verified payment is not settled because the protected handler throws or returns an error response.
These server-level hooks run for every payment regardless of which extensions are active. Extensions and schemes can also contribute lifecycle hook adapters that run only when their extension key or scheme/network is used. See Extensions Overview for extension-level behavior.
import { x402ResourceServer } from "@x402/core";

const server = new x402ResourceServer(facilitatorClient);

server.onAfterSettle(async (context) => {
  await recordPayment({
    payer: context.result.payer,
    transaction: context.result.transaction,
    amount: context.requirements.amount,
    network: context.requirements.network,
  });
});

x402HTTPResourceServer (HTTP)

Register hooks for HTTP-specific request handling before payment processing. Use cases include bypassing payment for API key holders, granting access to subscribers or blocking specific clients.
  • onProtectedRequest — Runs on every request to a protected route.
    • Return { grantAccess: true } to bypass payment.
    • Return { abort: true, reason } to return 403.
    • Return void to continue to payment flow.
import { x402ResourceServer, x402HTTPResourceServer } from "@x402/core";

const server = new x402ResourceServer(facilitatorClient);
const httpServer = new x402HTTPResourceServer(server, routes);

httpServer.onProtectedRequest(async (context, routeConfig) => {
  const apiKey = context.adapter.getHeader("X-API-Key");

  if (apiKey && await isValidApiKey(apiKey)) {
    return { grantAccess: true };
  }

  // No valid API key — continue to payment flow
});

Client Hooks

x402Client (Transport-agnostic)

Register hooks on the core client for payment payload creation lifecycle events. Common use cases include enforcing spending limits, requiring approval for large payments or logging outgoing transactions.
  • onBeforePaymentCreation — Runs before creating a payment payload. Return { abort: true, reason } to cancel.
  • onAfterPaymentCreation — Runs after successful payload creation.
  • onPaymentCreationFailure — Runs on failure. Return { recovered: true, payload } to provide fallback.
  • onPaymentResponse — Runs after a paid request completes. Return { recovered: true } to tell the transport to retry with a fresh payload.
import { x402Client } from "@x402/core";

const client = new x402Client();

client.onBeforePaymentCreation(async (context) => {
  const maxAmount = BigInt("10000000"); // 10 USDC
  const requestedAmount = BigInt(context.selectedRequirements.amount);

  if (requestedAmount > maxAmount) {
    return { abort: true, reason: "Payment exceeds spending limit" };
  }
});

x402HTTPClient (HTTP)

Register hooks for HTTP-specific payment required handling. Use cases include trying API key authentication before paying or prompting users for payment confirmation.
  • onPaymentRequired — Runs when a 402 response is received.
    • Return { headers } to retry with alternate headers before paying.
    • Return void to proceed directly to payment.
import { x402Client, x402HTTPClient } from "@x402/core";

const client = new x402Client();
const httpClient = new x402HTTPClient(client);

httpClient.onPaymentRequired(async ({ paymentRequired }) => {
  // Try API key authentication first
  const apiKey = process.env.API_KEY;
  if (apiKey) {
    return { headers: { "Authorization": `Bearer ${apiKey}` } };
  }
  // Return void to proceed with payment
});

Facilitator Hooks

x402Facilitator

Register hooks on the facilitator for verification and settlement lifecycle events. Use cases include populating a bazaar discovery catalog, compliance checks or collecting metrics across all processed payments.
  • onBeforeVerify / onAfterVerify / onVerifyFailure — Same pattern as server hooks.
  • onBeforeSettle / onAfterSettle / onSettleFailure — Same pattern as server hooks.
import { x402Facilitator } from "@x402/core";
import { extractDiscoveryInfo } from "@x402/extensions/bazaar";

const facilitator = new x402Facilitator();

facilitator.onAfterVerify(async (context) => {
  // Extract resource info from payment for bazaar catalog
  const discovered = extractDiscoveryInfo(
    context.paymentPayload,
    context.requirements,
    true, // validate
  );

  if (discovered) {
    bazaarCatalog.add({
      resource: discovered.resourceUrl,
      description: discovered.description,
      mimeType: discovered.mimeType,
      x402Version: discovered.x402Version,
      accepts: [context.requirements],
      lastUpdated: new Date().toISOString(),
    });
  }
});

MCP Hooks

x402MCPClient (MCP client)

Register hooks on the MCP client for payment lifecycle events specific to tool calls. Use cases include logging payments, enforcing per-tool spending limits or auditing payment receipts.
  • onPaymentRequired — Runs when a 402 payment required response is received from a tool call. First hook to return a result wins. Return { abort: true } to cancel, { payment } to supply a pre-built payload, or void to proceed with normal payment flow.
  • onBeforePayment — Runs after payment approval but before the payment payload is created.
  • onAfterPayment — Runs after the payment payload is submitted and the tool result is received.
import { x402MCPClient } from "@x402/mcp";

const client = new x402MCPClient(mcpClient, paymentClient);

client
  .onPaymentRequired(async ({ toolName, paymentRequired }) => {
    if (blocklist.has(toolName)) {
      return { abort: true };
    }
    // Return undefined to proceed with normal payment flow
  })
  .onBeforePayment(async ({ toolName, paymentRequired }) => {
    console.log(`Creating payment for tool: ${toolName}`);
  })
  .onAfterPayment(async ({ toolName, paymentPayload, result, settleResponse }) => {
    await auditLog.record({
      tool: toolName,
      transaction: settleResponse?.transaction,
    });
  });

x402MCPServer payment wrapper (MCP server)

Register hooks in the PaymentWrapperConfig.hooks object when creating a payment wrapper. Use cases include rate limiting, logging tool executions or sending payment receipts.
  • onBeforeExecution — Runs after payment verification, before the tool handler executes. Return false to abort execution.
  • onAfterExecution — Runs after the tool handler returns, before settlement.
  • onAfterSettlement — Runs after successful payment settlement.
import { createPaymentWrapper } from "@x402/mcp";

const paid = createPaymentWrapper(resourceServer, {
  accepts,
  hooks: {
    onBeforeExecution: async ({ toolName, paymentPayload }) => {
      if (await isRateLimited(paymentPayload.payer)) {
        return false; // Abort execution
      }
    },
    onAfterExecution: async ({ toolName, result }) => {
      console.log(`Tool ${toolName} executed successfully`);
    },
    onAfterSettlement: async ({ settlement }) => {
      await sendReceipt({ transaction: settlement.transaction });
    },
  },
});

Hook Chaining

Hooks can be chained when registering multiple handlers:
server
  .onBeforeVerify(validatePayment)
  .onAfterVerify(logVerification)
  .onBeforeSettle(checkBalance)
  .onAfterSettle(recordTransaction);

Next, explore: