Developer Guide

x402 Bridge Quickstart

Integrate xFunnel's x402 protocol bridge into your payment flow in three steps. Bridge endpoint: https://xfunnel.polsia.app/api/v1/bridge


Step 1

Send an x402 Payment Request

Forward your x402 (binary HTTP/S) payment request to the bridge endpoint. xFunnel reads the Authorization: Pay header and attached payment payload, routes to the target upstream, and captures the bridge fee before settlement.

Shell — curl POST
curl -X POST https://xfunnel.polsia.app/api/v1/bridge \\
  -H "Authorization: Pay recipient-address@chain" \\
  -H "Content-Type: application/octet-stream" \\
  -H "X-Request-Hash: sha256_abc123..." \\
  -H "X-Bridge-Fee-Percent: 5" \\
  --data-binary @payment_payload.bin

Required headers:

Header Value Notes
Authorization Pay 0x...@base Pay recipient in format address@chain. Chains: base, solana, polygon.
Content-Type application/octet-stream x402 binary payload format.
X-Request-Hash sha256_... SHA-256 hash of the binary payload for idempotency and deduplication.
X-Bridge-Fee-Percent 5 Optional. Default 5%. Set to override. Range: 020.
X-Settlement-Wallet 0x... Optional. Override the settlement wallet for fee routing. Defaults to bridge config.
X-Forward-To https://... Optional. Override the upstream relay target. Defaults to signallayer-4.polsia.app.

Step 2

Handle Bridge Responses

The bridge returns status codes that tell you how to proceed. Always inspect X-Funnel-Fee and X-Funnel-Tx-Hash on successful responses.

Status Meaning Action
200 OK Payment verified and routed. Fee captured. Read X-Funnel-Fee (fee collected, ETH on Base). Read X-Funnel-Tx-Hash for settlement reference. Proceed with your workflow.
402 Payment Required Payment payload present but not verified by CDP. Fee not captured. Inspect WWW-Authenticate header for challenge details. Retry after resolving the payment condition.
503 Service Unavailable Upstream unreachable or circuit-breaking. Payment not routed. Read Retry-After header. Implement exponential backoff in your retry logic. Check GET /api/v1/bridge/metrics for upstream health.
400 Bad Request Malformed request — missing headers, invalid chain, bad payload. Fix request format. Response body contains specific field error.
Fee Capture Always Happens on 200
When the bridge returns 200, the bridge fee is captured atomically with the transaction. The fee is settled to the configured CDP_SETTLEMENT_WALLET on ETH/Base. No fee is captured on 402, 503, or error responses — the payment is not confirmed.
Shell — 200 response HTTP/1.1 200 OK
X-Funnel-Fee: 0.005 ETH
X-Funnel-Tx-Hash: 0xabc123def456...
X-Funnel-Bridge-Version: 1.0.1

{
  "status": "verified",
  "upstream_tx_hash": "0xabc123def456...",
  "fee_captured": "0.005",
  "fee_asset": "ETH",
  "fee_chain": "base",
  "latency_ms": 340
}

Step 3

Read TA Metadata for Smart Provider Selection

The bridge exposes a TA (Technical Analysis) metadata endpoint that surfaces real-time provider health signals. Use this to implement intelligent routing: pick the upstream with the lowest latency, highest uptime, and best handshake reliability.

Shell — curl GET
curl https://xfunnel.polsia.app/api/v1/metrics
Response JSON application/json
{
  "latency_ms": { "p50": 330, "p95": 1096, "p99": 2100, "avg": 414 },
  "uptime_pct": 99.8,
  "handshake_reliability": 0.98,
  "assets_supported": ["ETH", "USDC", "DAI"],
  "total_requests": 1874,
  "total_fees_collected": "12.4500",
  "volume_routed": "249.00",
  "chain_breakdown": [
    { "chain": "base",    "volume": "142.00", "tx_count": 891, "fees": "7.10" },
    { "chain": "solana",  "volume": "67.00",  "tx_count": 445, "fees": "3.35" },
    { "chain": "polygon", "volume": "40.00",  "tx_count": 538, "fees": "2.00" }
  ],
  "last_successful_at": "2026-06-16T03:10:44.000Z"
}

Key TA fields for routing:

Field Type Routing Use
latency_ms.avg number (ms) Average round-trip. Lower = faster upstream.
uptime_pct number (0–100) Historical uptime. Drop below 95%? Failover.
handshake_reliability number (0–1) CDP verification success rate. Below 0.9? Circuit-break.
assets_supported string[] Accepted fee assets. ETH on Base is always supported.
chain_breakdown object[] Per-chain volume and fee splits. Use for load-aware routing.

Fees

Bridge Fee Structure

xFunnel charges a flat 5% bridge fee on all proxied transactions by default. Fees are captured in ETH on Base and settled to the configured CDP_SETTLEMENT_WALLET.

Parameter Default Override via Header Range
Bridge fee percent 5% X-Bridge-Fee-Percent 0 – 20
Fee asset ETH Fixed: ETH on Base
Fee capture timing On 200 Atomic with payment verification
Settlement wallet CDP_SETTLEMENT_WALLET env X-Settlement-Wallet Any ETH address
Fee Calculation Example
Transaction amount: 1.00 ETH · Bridge fee: 5%0.05 ETH captured at settlement. Settlement to CDP_SETTLEMENT_WALLET on Base.

Full Example

Node.js Integration

End-to-end integration with xFunnel. Handles request signing, response parsing, TA-metadata-driven retry logic, and error handling.

// xfunnel-client.js Node.js
const https = require('https');

const BASE = 'xfunnel.polsia.app';
const BRIDGE = `/api/v1/bridge`;
const METRICS = `/api/v1/metrics`;

// ── Fetch TA metadata ────────────────────────────────────────
async function getBridgeHealth() {
  return new Promise((resolve, reject) => {
    const opts = { hostname: BASE, path: METRICS, method: 'GET' };
    let data = '';
    const req = https.request(opts, res => {
      res.on('data', chunk => data += chunk);
      res.on('end', () => resolve(JSON.parse(data)));
    });
    req.on('error', reject);
    req.end();
  });
}

// ── Send x402 payment through xFunnel bridge ──────────────────
async function sendPayment({ recipient, chain, payload, feePercent = 5 }) {
  return new Promise((resolve, reject) => {
    const body = Buffer.from(payload); // binary payload
    const headers = {
      'Authorization': `Pay ${recipient}@${chain}`,
      'Content-Type': 'application/octet-stream',
      'Content-Length': body.length,
      'X-Request-Hash': `sha256_${Buffer.from(body.slice(0, 32)).toString('hex').slice(0, 16)}`,
      'X-Bridge-Fee-Percent': String(feePercent),
    };

    const req = https.request(
      { hostname: BASE, path: BRIDGE, method: 'POST', headers },
      res => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body: data }));
      }
    );
    req.on('error', reject);
    req.write(body);
    req.end();
  });
}

// ── Main: check health, then route ───────────────────────────
async function routePayment(opts) {
  const health = await getBridgeHealth();

  // Circuit-break if upstream is unhealthy
  if (health.handshake_reliability < 0.9) {
    throw new Error(`Upstream unhealthy (reliability=${health.handshake_reliability}). Retry later.`);
  }

  // Route with current avg latency logged
  console.log(`[xFunnel] Routing to upstream (latency_avg=${health.latency_ms.avg}ms)`);

  const result = await sendPayment(opts);

  if (result.status === 200) {
    console.log(`[xFunnel] Verified — fee ${result.headers['x-funnel-fee']} captured`);
    return { ok: true, txHash: result.headers['x-funnel-tx-hash'], latencyMs: parseInt(result.body?.latency_ms || '0') };
  }

  if (result.status === 402) {
    console.warn(`[xFunnel] Payment required — challenge: ${result.headers['www-authenticate']}`);
    throw new Error('Payment not verified. Check WWW-Authenticate header.');
  }

  if (result.status === 503) {
    const retryAfter = result.headers['retry-after'] || 5;
    console.warn(`[xFunnel] Upstream unavailable. Retry after ${retryAfter}s.`);
    throw new Error(`Upstream down. Retry after ${retryAfter}s.`);
  }

  throw new Error(`Unexpected response: ${result.status} — ${result.body}`);
}

// ── Usage ─────────────────────────────────────────────────────
routePayment({
  recipient: '0xYourRecipientAddress',
  chain: 'base',
  payload: Buffer.from('your-binary-payment-data'),
  feePercent: 5,
}).then(r => console.log('Success:', r))
  .catch(err => console.error('Failed:', err.message));
Next Steps
For the full onboarding guide — architecture overview, live demo, API playground, and FAQ — see /docs/onboarding. Monitor your bridge via the dashboard or fetch live metrics at GET /api/v1/metrics.