Skip to main content
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.
  • onAfterVerify — Runs after successful verification.
  • onVerifyFailure — Runs on verification failure. Return { recovered: true, result } to override.
  • onBeforeSettle — Runs before settlement. Return { abort: true, reason } to reject.
  • onAfterSettle — Runs after successful settlement.
  • onSettleFailure — Runs on settlement failure. Return { recovered: true, result } to override.
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.
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(),
    });
  }
});

Hook Chaining

All SDKs support method chaining for registering multiple hooks:
server
  .onBeforeVerify(validatePayment)
  .onAfterVerify(logVerification)
  .onBeforeSettle(checkBalance)
  .onAfterSettle(recordTransaction);

Next, explore: