Stop paying for broken x402 endpoints - four plugins that fix the problem

RelAI Team
Mar 17, 2026 · 6 min read
Stop paying for broken x402 endpoints - four plugins that fix the problem

x402 is great until it isn't.

The protocol is simple: you call an API, it returns 402 Payment Required, you sign a payment, and you get access. Fast, trustless, no subscriptions. But there's a catch that nobody talks about enough: what happens when the endpoint is broken?

Your wallet signs. USDC leaves your account. The endpoint returns a 500. You paid for nothing.

This isn't a theoretical problem. Servers crash. Deployments go wrong. Databases run out of connections. External dependencies go offline. In the traditional API world, you get a refund or your subscription covers it. In x402, the payment is already settled on-chain. There is no chargeback button.

We decided to fix this at the SDK level.

The problem in detail

Here's the timeline of a bad x402 request:

  1. Buyer calls GET /api/data
  2. Server returns 402 Payment Required — price: $0.01
  3. Buyer's wallet signs the payment
  4. Facilitator settles USDC on-chain — money is gone
  5. Server processes the request… and crashes
  6. Buyer gets a 500 error
  7. Buyer has no recourse
The payment happened before the server tried to do its job. By the time it fails, the USDC is already in the seller's wallet. The buyer is left with an error and an empty receipt.

This is a fundamental trust problem. If buyers can't trust that they'll get what they pay for, x402 adoption stalls.

Four plugins, four layers of protection

We built four plugins for the @relai-fi/x402 SDK that address this at different stages of the request lifecycle. They can be used individually or stacked together.

1. Shield — global health gate

Shield checks whether your service is healthy before requesting payment. If your database is down or your Redis cache is unreachable, Shield returns 503 immediately — no payment required.

import { shield } from '@relai-fi/x402/plugins'

shield({
healthUrl: 'https://my-api.com/health',
cacheTtlMs: 10000,
})

How it works: On each request, Shield pings your health URL (or runs a custom function). If unhealthy, the beforePaymentCheck hook returns reject: true with a 503 status. The buyer never sees a 402, never signs, never pays.

Results are cached (default 10s) so the health check doesn't add latency to every request.

Best for: Global outages — DB down, external API down, service in maintenance mode.

2. Preflight — per-endpoint liveness probe

Preflight goes one level deeper. Instead of checking a global health endpoint, it probes the actual endpoint the buyer wants to call — before asking for payment.

import { preflight } from '@relai-fi/x402/plugins'

preflight({
baseUrl: 'https://my-api.com',
})

How it works: Before returning 402, Preflight sends a lightweight HEAD request with an X-Preflight: true header to the same path the buyer requested. If the endpoint doesn't respond (timeout, 5xx, connection refused), the buyer gets 503 instead of 402.

Per-path results are cached (default 5s), so a broken /api/translate won't affect a working /api/weather.

Best for: Per-endpoint failures — one route is broken while the rest of the service works fine.

3. Circuit Breaker — failure history tracking

Shield and Preflight make real-time HTTP probes. That adds a small amount of latency and network traffic. Circuit Breaker takes a different approach: zero extra network requests.

Instead of probing, it watches the outcomes of actual paid requests via the afterSettled hook. If an endpoint fails repeatedly, the circuit "opens" and blocks new payments until the endpoint recovers.

import { circuitBreaker } from '@relai-fi/x402/plugins'

circuitBreaker({
failureThreshold: 5,
resetTimeMs: 30000,
})

How it works:

The circuit has three states:

StateWhat happensTransition
ClosedNormal operation — requests flow throughOpens after N consecutive failures
OpenAll requests rejected with 503Half-opens after resetTimeMs
Half-openA few test requests allowed throughCloses on success, re-opens on failure
Every response includes an X-Circuit-State header so clients can observe the state. When open, a Retry-After header tells clients when to try again.

Circuits can be scoped globally or per-path:

circuitBreaker({
  failureThreshold: 5,
  scope: 'per-path',    // default — each endpoint has its own circuit
})
Best for: Endpoints with intermittent failures. Zero-latency protection without extra HTTP requests.

4. Refund — compensate after the fact

Sometimes failure happens after payment settles. The endpoint was up when the buyer paid, but the actual processing failed. Shield, Preflight, and Circuit Breaker can't catch this — the failure happens downstream.

Refund handles this case. It watches afterSettled outcomes and automatically compensates buyers when paid requests fail.

import { refund } from '@relai-fi/x402/plugins'

refund({
triggerCodes: [500, 502, 503],
mode: 'credit',
onRefund: (event) => {
console.log(Refund: $${event.amount} to ${event.payer})
},
})

How it works:

Two modes:

  • credit — records an in-memory credit per buyer. On their next request, beforePaymentCheck skips payment automatically. The buyer gets a free call as compensation. Response includes X-Refund-Credit: applied.
  • log — only calls the onRefund callback. You handle compensation yourself (database credit, Stripe refund, email notification, etc.).
The onRefund callback receives a RefundEvent with full context: payer, transactionId, amount, path, and reason. Best for: Post-payment failures. Compensating buyers when the endpoint was "up" but the actual logic failed.

Stacking all four together

The plugins run in array order. Here's the full protection stack:

import Relai from '@relai-fi/x402/server'
import { shield, preflight, circuitBreaker, refund } from '@relai-fi/x402/plugins'

const relai = new Relai({
network: 'base',
plugins: [
shield({ healthUrl: 'https://my-api.com/health' }),
preflight({ baseUrl: 'https://my-api.com' }),
circuitBreaker({ failureThreshold: 5 }),
refund({ triggerCodes: [500, 502, 503] }),
],
})

app.get('/api/data', relai.protect({
payTo: '0xYourWallet',
price: 0.01,
}), (req, res) => {
res.json({ data: 'premium content' })
})

What this does, in order:

  1. Shield — is the whole service up? If not → 503, no payment.
  2. Preflight — is this specific endpoint alive? If not → 503, no payment.
  3. Circuit Breaker — has this endpoint been failing repeatedly? If yes → 503, no payment.
  4. Refund — if everything looked fine but the response still fails → grant a credit for next time.
Four layers. The buyer either gets a working response or doesn't pay. If a rare edge case slips through, they get compensated automatically.

When to use what

ScenarioPlugin
Database is downShield
One endpoint is brokenPreflight
Endpoint fails intermittentlyCircuit Breaker
Processing fails after paymentRefund
Maximum protectionAll four
You don't need all four. Pick what fits your failure modes:
  • Simple API with one endpoint? — Shield + Refund is probably enough.
  • Multi-endpoint service? — Add Preflight for per-route protection.
  • Endpoints with flaky dependencies? — Circuit Breaker catches patterns Shield and Preflight miss.
  • Want belt-and-suspenders? — Stack all four. The overhead is minimal.

The bigger picture

x402 is a protocol for permissionless payments. That's powerful, but it also means there's no customer support to call when something goes wrong. The protocol doesn't have chargebacks. It doesn't have refund mechanisms. It settles and moves on.

That's by design — and it's what makes x402 fast and trustless. But it creates a gap: who protects the buyer?

These plugins are our answer. The SDK itself becomes the buyer protection layer. It's not a separate service, not a third-party escrow, not a governance vote. It's code that runs in the same middleware stack, at the same speed, with zero external dependencies.

If you're building paid APIs with x402, your buyers deserve to know they won't pay for broken endpoints. These four plugins make that guarantee.

Get started

All four plugins ship with @relai-fi/x402:

npm install @relai-fi/x402

Full documentation: relai.fi/documentation/sdk-plugins


x402 payments are instant and irreversible. Your endpoint protection should be too.

Understand x402 before you implement

This guide uses payment primitives from the x402 standard. Read the protocol overview for a complete flow, terminology, and integration FAQ.