Skip to content

x402 Payment Protocol

The x402 protocol enables micropayments for accessing paid content. This guide explains how payments work in Grapevine.

What is x402?

x402 is a payment protocol that allows:

  • Micropayments - Pay only for what you access
  • On-chain settlements - Payments on Base blockchain
  • USDC payments - Stablecoin for predictable costs
  • Automatic processing - Seamless integration

When Payments Are Required

Payments are required for:

  • Accessing paid entries - Content marked with is_free: false
Free operations (no payment required):
  • Creating feeds
  • Creating entries
  • Reading free entries
  • All GET/list operations

Creating Paid Content

To monetize your content, create entries with is_free: false and a price:

import { GrapevineClient } from '@pinata/grapevine-sdk';
 
const grapevine = new GrapevineClient({
  network: 'mainnet',
  privateKey: process.env.PRIVATE_KEY
});
 
// Create a paid entry
const paidEntry = await grapevine.entries.create(feedId, {
  title: 'Premium Market Report',
  content: 'Exclusive analysis and insights...',
  description: 'In-depth quarterly market analysis',
  is_free: false,
  price: {
    amount: '1000000',  // 1.00 USDC (6 decimals)
    currency: 'USDC'
  }
});
 
console.log('Paid entry created:', paidEntry.id);
Price amounts are in smallest units (6 decimals for USDC):
  • '1000000' = 1.00 USDC
  • '500000' = 0.50 USDC
  • '100000' = 0.10 USDC
  • '10000' = 0.01 USDC

Accessing Paid Content

When a user tries to access paid content, the API returns a 402 Payment Required response. The SDK handles this automatically when using the appropriate methods.

Payment Flow

  1. Request paid content → API returns 402 with payment requirements
  2. Parse requirements → Extract payment details (amount, asset, network)
  3. Create payment → Sign transaction with x402 protocol
  4. Retry with payment → Include X-PAYMENT header
  5. Access granted → Content returned successfully

Manual Payment Handling (Advanced)

For direct API integration without the SDK:

import { createPaymentHeader, selectPaymentRequirements } from 'x402/client';
import { PaymentRequirementsSchema } from 'x402/types';
import { facilitator } from '@coinbase/x402';
 
// 1. Request returns 402
const response = await fetch(`${API_URL}/v1/feeds/${feedId}/entries/${entryId}/access-link`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    ...authHeaders
  }
});
 
if (response.status === 402) {
  const paymentData = await response.json();
  
  // 2. Parse requirements
  const requirements = paymentData.accepts.map(x => 
    PaymentRequirementsSchema.parse(x)
  );
  
  // 3. Select matching network
  const selected = selectPaymentRequirements(
    requirements,
    ['base'], // or ['base-sepolia'] for testnet
    'exact'
  );
  
  // 4. Create payment header
  const paymentHeader = await createPaymentHeader(
    walletClient,
    parseInt(paymentData.x402Version),
    selected,
    facilitator
  );
  
  // 5. Retry with payment
  const paidResponse = await fetch(`${API_URL}/v1/feeds/${feedId}/entries/${entryId}/access-link`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      ...authHeaders,
      'X-PAYMENT': paymentHeader
    }
  });
  
  // Check for payment confirmation
  if (paidResponse.headers.get('X-PAYMENT-RESPONSE')) {
    console.log('Payment processed successfully');
  }
  
  const accessLink = await paidResponse.json();
  console.log('Access URL:', accessLink.url);
}

Payment Requirements Response

When a 402 is returned, the response includes payment requirements:

{
  "error": "payment_required",
  "x402Version": "0",
  "accepts": [{
    "scheme": "exact",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "network": "base",
    "maxAmountRequired": "1000000"
  }]
}

Supported Networks

Base Sepolia (Testnet)

  • Network: base-sepolia
  • Chain ID: 84532
  • Currency: Test USDC
  • API: https://api.grapevine.markets

Base (Mainnet)

  • Network: base
  • Chain ID: 8453
  • Currency: USDC
  • API: https://api.grapevine.fyi

Getting USDC

Testnet (Base Sepolia)

  1. Get ETH from Alchemy Faucet
  2. Bridge USDC from Ethereum Sepolia
  3. Or contact Grapevine support for test tokens

Mainnet (Base)

  1. Buy USDC on an exchange
  2. Bridge to Base network
  3. Send to your wallet address

Error Handling

No Matching Network

const selected = selectPaymentRequirements(
  requirements,
  [network],
  'exact'
);
 
if (!selected) {
  throw new Error(
    `Payment requires ${requirements[0].network} but wallet is on ${network}`
  );
}

Insufficient Balance

try {
  const paymentHeader = await createPaymentHeader(...);
} catch (error) {
  if (error.message.includes('insufficient')) {
    console.error('Insufficient USDC balance');
    // Prompt user to add funds
  }
}

Payment Failed

const response = await fetch(url, {
  headers: { 'X-PAYMENT': paymentHeader }
});
 
if (!response.ok) {
  const error = await response.json();
  console.error('Payment failed:', error);
}

Next Steps