Skip to main content

Overview

The /settle endpoint finalizes on-chain settlement for a transaction. Call this after successfully delivering a service to complete the payment flow.

Endpoint

POST https://open.x402.host/settle

When to Use

  • After service delivery - Once you’ve provided the paid service
  • Settlement finalization - To complete the payment cycle
  • Record keeping - For transaction finality
Settlement is typically called after /verify and after delivering the service.

Request

Headers

HeaderValueRequired
Content-Typeapplication/jsonYes

Body Parameters

ParameterTypeDescriptionRequired
transactionIdstringTransaction ID from /verifyYes
networkstringBlockchain networkYes
serviceIdstringYour internal service identifierNo

Example Request

async function settleTransaction(transactionId, network) {
  const response = await fetch('https://open.x402.host/settle', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      transactionId: transactionId,
      network: network,
      serviceId: 'my-service-123' // optional
    })
  });

  return await response.json();
}

// Usage
const result = await settleTransaction('tx_abc123', 'base');
console.log('Settlement status:', result.status);

Response

Success Response

{
  "success": true,
  "transactionId": "tx_abc123",
  "status": "settled",
  "blockHash": "0x...",
  "blockNumber": 12345678,
  "timestamp": 1699564800
}

Error Response

{
  "success": false,
  "error": "Transaction not found",
  "code": "TX_NOT_FOUND"
}

Response Fields

FieldTypeDescription
successbooleanWhether settlement succeeded
transactionIdstringTransaction identifier
statusstringSettlement status
blockHashstringBlockchain block hash
blockNumbernumberBlock number
timestampnumberUnix timestamp
errorstringError message (if failed)
codestringError code (if failed)

Settlement Status

StatusDescription
settledSuccessfully settled on-chain
pendingSettlement in progress
failedSettlement failed
already_settledAlready finalized

Complete Payment Flow

Here’s the full integration flow with verify and settle:
app.post('/api/premium-content', async (req, res) => {
  const paymentProof = req.headers['x-402-payment'];

  // Step 1: Verify payment
  const verification = await fetch('https://open.x402.host/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      proof: paymentProof,
      network: 'base'
    })
  }).then(r => r.json());

  if (!verification.valid) {
    return res.status(402).json({ error: 'Invalid payment' });
  }

  // Step 2: Deliver service
  const content = await generatePremiumContent();
  res.json({ content });

  // Step 3: Settle in background (don't await)
  settleInBackground(verification.transactionId);
});

async function settleInBackground(transactionId) {
  try {
    await fetch('https://open.x402.host/settle', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        transactionId,
        network: 'base'
      })
    });
    console.log(`Settled transaction: ${transactionId}`);
  } catch (error) {
    console.error(`Settlement failed for ${transactionId}:`, error);
    // Log for manual review
  }
}

Best Practices

  1. Settle after delivery - Only settle once service is provided
  2. Don’t block on settlement - Settle asynchronously in background
  3. Implement retries - Settlement can fail due to network issues
  4. Log all settlements - Keep records for accounting
  5. Monitor failures - Set up alerts for settlement errors

Retry Logic

async function settleWithRetry(transactionId, network, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('https://open.x402.host/settle', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ transactionId, network }),
        timeout: 10000
      });

      const result = await response.json();

      if (result.success || result.status === 'already_settled') {
        return result;
      }

      throw new Error(result.error || 'Settlement failed');

    } catch (error) {
      console.error(`Settlement attempt ${attempt} failed:`, error);

      if (attempt === maxRetries) {
        throw error;
      }

      // Exponential backoff
      await new Promise(resolve =>
        setTimeout(resolve, Math.pow(2, attempt) * 1000)
      );
    }
  }
}

Settlement Queue

For high-volume applications, use a queue:
const settlementQueue = [];

// Add to queue instead of immediate settlement
function queueSettlement(transactionId, network) {
  settlementQueue.push({ transactionId, network, retries: 0 });
}

// Process queue periodically
setInterval(async () => {
  while (settlementQueue.length > 0) {
    const item = settlementQueue.shift();

    try {
      await fetch('https://open.x402.host/settle', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          transactionId: item.transactionId,
          network: item.network
        })
      });

      console.log(`Settled: ${item.transactionId}`);

    } catch (error) {
      item.retries++;
      if (item.retries < 3) {
        settlementQueue.push(item); // Re-queue
      } else {
        console.error(`Failed to settle after 3 attempts: ${item.transactionId}`);
      }
    }
  }
}, 5000); // Process every 5 seconds

Rate Limiting

  • 200 requests per minute per IP
  • Use queuing for high-volume applications
  • Exceeding limits returns 429 Too Many Requests
  • /verify - Verify payment (call before settle)
  • /open - Production payment endpoint
  • /test - Development endpoint
  • /supported - Check supported networks