Set up the x402 facilitator

To build x402 endpoints for NFT metadata refresh, you first need a payment facilitator. The facilitator acts as the middleman that verifies on-chain payments before granting access to your API. For this guide, we will use the official x402 facilitator from the Coinbase Developer Platform (CDP), which simplifies handling USDC payments over HTTP.

1
Install the CDP SDK

Start by installing the necessary dependencies in your project directory. You will need the core CDP SDK to interact with the facilitator. Run the following command in your terminal:

Shell
Shell
npm install @coinbase/coinbase-sdk

This package provides the cryptographic primitives and API clients required to generate and verify payment proofs.

2
Initialize the Facilitator

Next, configure the facilitator in your backend environment. Create a new instance of the facilitator client using your API key and secret. This client will handle the communication with the x402 Bazaar discovery layer to locate valid payment endpoints.

JavaScript
JavaScript
import { Facilitator } from '@coinbase/coinbase-sdk';

const facilitator = new Facilitator({
  apiKeyId: process.env.CDP_API_KEY_ID,
  apiKeySecret: process.env.CDP_API_KEY_SECRET,
});

Keep these credentials secure and never expose them in client-side code.

3
Configure Payment Verification

Finally, set up the verification logic for your NFT metadata endpoint. When a request arrives, the facilitator checks if the payment proof in the header is valid. If the payment is confirmed, the facilitator returns a success object; otherwise, it rejects the request.

JavaScript
JavaScript
const verifyPayment = async (request) => {
  const proof = request.headers['x-payment-proof'];
  const isValid = await facilitator.verifyPayment(proof);
  return isValid;
};

This step ensures that only users who have paid the required fee can trigger the metadata refresh.

Build the metadata update logic

To create a reliable x402 endpoint for NFT metadata, you need a function that fetches the latest on-chain state and updates the token's metadata atomically. This logic must be idempotent—running it twice shouldn't break anything—and secure, ensuring only authorized requests trigger expensive on-chain writes.

to x402 Endpoints for NFT Metadata Refresh
1
Fetch on-chain token data

Start by connecting to the blockchain RPC. Use the appropriate SDK (like @solana/web3.js for Solana or ethers.js for Ethereum) to fetch the current token state. For ERC-721 or ERC-1155 tokens, you typically need the ownerOf or balanceOf calls to verify current ownership and traits. Ensure you handle network latency gracefully; if the RPC times out, return a 503 error rather than failing silently.

2
Validate the request payload

Before touching the blockchain, validate the incoming x402 payment and any associated parameters. Check that the payment proof is valid and that the requester has permission to update this specific NFT. If you are using a standard metadata schema (like Metaplex for Solana or OpenZeppelin for Ethereum), ensure the new metadata fields (name, symbol, URI) conform to the expected structure. Reject malformed payloads early to save gas and prevent state corruption.

3
Construct the metadata payload

Build the JSON object that represents the new metadata. This usually involves fetching off-chain data (like an IPFS hash or a centralized API response) and merging it with on-chain attributes. For example, if the NFT's traits change based on a game state, query your game server, then format the result into the standard metadata.json structure. Keep the payload lightweight; large JSON objects can exceed RPC size limits or blockchain storage constraints.

4
Execute the on-chain update

Submit the transaction to update the metadata. On Solana, this might mean calling updateMetadataAccount via the Metaplex Program. On Ethereum, you might be updating a tokenURI or calling a custom setMetadata function in your contract. Wrap this in a try-catch block. If the transaction fails (e.g., due to insufficient gas or a revert), log the error and return a clear failure message to the x402 client. Do not assume success until the transaction is confirmed.

5
Verify idempotency and return status

Finally, verify that the update was successful by re-fetching the metadata or checking the transaction receipt. If the metadata was already up-to-date (idempotent case), return a 200 OK with a message indicating no changes were made. This prevents unnecessary re-processing and confirms to the client that their payment resulted in a valid state change. Log the transaction hash for auditing purposes.

Wrap the endpoint with x402 payments

To secure your metadata refresh endpoint, you need to layer the x402 protocol over your existing logic. This ensures that the server only executes the update after a facilitator confirms the USDC payment. Without this gate, anyone could trigger a refresh for free.

The x402 facilitator acts as a neutral third party that handles the payment verification. Your API checks for a valid Authorization header containing the payment proof. If the proof is valid, the metadata updates. If not, the request is rejected.

to x402 Endpoints for NFT Metadata Refresh
1
Install the facilitator SDK

Start by adding the official x402 facilitator package to your project. This SDK provides the functions needed to verify payment proofs from the client side. It handles the complex cryptography of USDC transfers so you don't have to.

Shell
Shell
npm install @thirdweb-dev/extension-x402
2
Configure the payment verification middleware

Create a middleware function that intercepts incoming requests to your metadata endpoint. This function will extract the Authorization header and pass it to the facilitator's verification method. The facilitator will check if the USDC payment was successfully routed through the x402 network.

JavaScript
JavaScript
import { verifyPayment } from '@thirdweb-dev/extension-x402';

export async function verifyX402(req) {
  const authHeader = req.headers.get('Authorization');
  if (!authHeader) return false;
  
  const isValid = await verifyPayment(authHeader);
  return isValid;
}
3
Gate the metadata update logic

Wrap your existing metadata update function inside a conditional check. Only proceed with the on-chain or off-chain update if verifyX402 returns true. This step is critical; it ensures that the expensive operation of refreshing metadata is only performed for paying users.

JavaScript
JavaScript
app.post('/api/metadata/refresh', async (req, res) => {
  const isPaid = await verifyX402(req);
  
  if (!isPaid) {
    return res.status(402).json({ error: 'Payment required' });
  }

  // Proceed with metadata update
  await updateMetadata(req.body.tokenId);
  res.json({ success: true });
});
4
Test the payment flow

Use a testnet environment to verify the integration. Send a small USDC payment through the x402 facilitator and confirm that your API accepts the request and updates the metadata. Check the server logs to ensure the verification step completes without errors before the update executes.

This setup creates a robust payment gate. The facilitator handles the trust, while your API enforces the business logic. For more details on the verification process, refer to the official x402 documentation.

Test the payment-gated flow

Now that your endpoint is live, it’s time to prove it works. You need to verify that the x402 middleware correctly rejects unpaid requests and processes paid ones. This section walks you through the verification process, confirming that the endpoint rejects unpaid requests and processes paid ones correctly.

1. Verify unpaid requests are rejected

Start by sending a request without any payment. A properly configured x402 endpoint should return a 402 Payment Required status code. This is the core mechanism of the protocol: if the client hasn’t paid, the metadata stays locked.

Use curl or a tool like Postman to hit your endpoint. Check the response headers for the x-payment-accepted field. If it’s missing or set to false, the gate is holding.

2. Send a paid request

Next, simulate a payment. Depending on your implementation, this might involve signing a transaction, sending a micro-payment via Lightning, or using a testnet token. Once the payment is confirmed, send the request again.

This time, you should receive a 200 OK status. The x-payment-accepted header should now be true, and the response body should contain the updated NFT metadata.

3. Confirm metadata updates on-chain

Finally, verify that the metadata actually changed. Query the blockchain or your IPFS gateway to ensure the new metadata is reflected. This step confirms that the payment triggered the intended state change.

to x402 Endpoints for NFT Metadata Refresh
1
Send unpaid request

Send a GET request to your endpoint without attaching any payment credentials. Expect a 402 Payment Required response. Check the headers for x-payment-accepted: false.

2
Process payment and retry

Execute the payment flow (e.g., sign transaction, send LN invoice). Once confirmed, send the same GET request again. Expect a 200 OK response with the updated metadata in the body.

3
Verify on-chain state

Query the blockchain or IPFS gateway to confirm the metadata hash has updated. This proves the payment-gated logic successfully triggered the state change.

Deploy and monitor the endpoint

Once your x402 endpoint is built, the next step is getting it live. Serverless platforms like Vercel, Cloudflare Workers, or AWS Lambda are ideal for this because they handle the scaling automatically. Since x402 relies on HTTP headers to communicate payment status, your deployment environment must preserve these headers exactly as they arrive from the client.

Deployment checklist

Before you push to production, verify these basics:

  • Environment variables: Ensure your private keys and API tokens are stored securely in your platform’s secret manager, not in your code.
  • Header preservation: Confirm your hosting provider isn’t stripping or modifying x-payment-verification or x-payment-signature headers.
  • CORS settings: If your endpoint serves AI agents or web frontends, configure CORS to allow requests from known agent domains.

Monitoring payment failures

Even with perfect code, blockchain transactions can fail or time out. You need a way to detect when a payment doesn’t arrive within your expected window. Set up a simple health check that logs any requests that hit your endpoint without a valid payment signature.

Use your serverless platform’s built-in logging to track these events. Look for patterns where the x-payment-verification header is missing or invalid. If you see a spike in these failures, it might indicate that your pricing is too high or that your endpoint is being scraped by bots that don’t intend to pay.

For more details on how x402 endpoints are discovered and verified, refer to the x402 Bazaar documentation. This resource explains how agents find and interact with your service once it’s live.

Common x402 integration errors

Even with a solid setup, x402 endpoints can fail silently if the payment handshake isn't handled precisely. The most frequent issues stem from signature verification mismatches and timeout misconfigurations during the request cycle.

Signature verification failures

The x402 protocol relies on a specific signature structure attached to the request header. If the server rejects the signature, it’s usually because the verification logic doesn't match the client's signing method. Ensure your backend uses the same public key derivation and hash algorithm as the frontend facilitator. A mismatched signature format will cause the endpoint to return a 401 Unauthorized error, blocking the metadata refresh.

Timeout and handshake issues

Payment verification adds latency to the request. If your server times out before the x402 facilitator confirms the transaction, the client will see a generic network error. Set your server timeout to at least 15 seconds to accommodate L2 confirmation times. Additionally, verify that your CORS policies allow the facilitator's domain to make the initial payment request, as blocked preflight requests will halt the handshake before it begins.