What x402 facilitators do and how to choose one
By Requestway · · 8 min read
An x402 facilitator is the service your API calls to check a payment and put it on chain. It exposes three endpoints, never holds your money, and is the one piece of x402 infrastructure you have to choose deliberately before you charge real users. This guide covers what a facilitator does at each step of a paid request, what it cannot do, and how to pick one.
If you are new to the protocol itself, read What is x402 first. This article assumes you know that a paid route answers 402 Payment Required and that the client retries with a signed payment.
What a facilitator is
A facilitator is an HTTP service with three endpoints. Your API (the "resource server" in the spec) talks to it. The agent paying you never does.
| Endpoint | Method | Request | Response |
|---|---|---|---|
/verify |
POST | { x402Version, paymentPayload, paymentRequirements } |
isValid, plus invalidReason and payer when relevant |
/settle |
POST | { x402Version, paymentPayload, paymentRequirements } |
success, transaction, network, plus errorReason and payer |
/supported |
GET | none | the scheme and network combinations it handles |
It exists because checking an EIP-3009 signature against a token contract and broadcasting the transfer both need access to the chain. Without a facilitator your server would need an RPC connection, a funded account to pay gas, and code to build and submit transactions. With one, your server needs a wallet address and an HTTP client. No node, no web3 extension, no private key.
For the "exact" scheme on EVM chains, the facilitator also pays the gas. It submits the payer's signed transferWithAuthorization and covers the transaction fee, so neither you nor the agent needs ETH. The agent needs USDC; you need an address to receive it.
Where the facilitator sits in a paid request
Here is a single paid request with the facilitator's part marked:
- The agent requests your route. Your server answers
402with aPAYMENT-REQUIREDheader listing what itaccepts. No facilitator call yet. - The agent signs an EIP-3009 authorization and retries with a
PAYMENT-SIGNATUREheader. - Your server checks the payload locally: it decodes, matches the client's
acceptedecho against what was advertised, checks the amount, the validity window and the replay cache. - Facilitator: your server posts the payload and requirements to
/verify. A valid signature from a wallet with enough funds returnsisValid: true. - Your handler runs.
- Facilitator: your server posts to
/settle. The facilitator submits the transfer and waits for the result. - Your server answers
200with aPAYMENT-RESPONSEheader carrying the receipt.
The settle response is what ends up, re-encoded, in that receipt:
{
"success": true,
"transaction": "0x…",
"network": "eip155:8453",
"payer": "0x…"
}
Step 3 matters more than it looks. A well-built server rejects underpayments, wrong recipients, expired authorizations and replays before it spends a network round trip on them. The Laravel package, for example, claims each payload in the cache before verification and rejects an underpayment locally with invalid_exact_evm_payload_authorization_value_mismatch. The facilitator is the second line of checks, not the only one.
What a facilitator cannot do
The trust model is narrower than people expect, because the payer's signature fixes the important terms.
- It cannot change the amount or the recipient. Both are inside what the payer signed. If a facilitator altered either, the signature would no longer match and the token contract would refuse the transfer.
- It never holds your funds. The transfer moves USDC directly from the payer's wallet to your
payToaddress. There is no intermediate balance to withdraw from. - It cannot settle the same payment twice. Each authorization carries a unique nonce and a validity window (
validAfter,validBefore), and the token contract refuses to execute the same authorization twice. - It does not decide prices. Your server builds the requirements. The facilitator only checks a payment against the requirements your server sends it.
What you do rely on it for is availability and an honest answer. If /settle says success: true, your server serves the response. You can always check the transaction hash on a block explorer (basescan.org for Base, sepolia.basescan.org for Base Sepolia), but in the request path you are trusting its reply.
It also sees the signed payment and the requirements, which means it sees the payer's address, the amount and your address for every payment that passes through it.
The options
There are three realistic choices today.
| Facilitator | Networks | Account | Best for |
|---|---|---|---|
https://x402.org/facilitator |
testnets only | none, free | local development, staging, test suites against a real chain |
Coinbase Developer Platform (CDP), https://api.cdp.coinbase.com/platform/v2/x402 |
mainnet | required, per-request signed headers | production on mainnet without running infrastructure |
| Self-hosted | whatever you configure | yours | full control over chains, latency and operations |
x402.org is the default in the Laravel package. It is the right choice until the day you switch to mainnet, and it will not settle mainnet payments, so it cannot be the facilitator for a production route charging real USDC.
CDP runs a managed mainnet facilitator. It does not take a long-lived token: each call carries authentication headers signed for that specific request. The Laravel package README also lists KYT/OFAC screening for it. Read CDP's own documentation for account setup and terms.
Self-hosting means running any service that exposes /verify, /settle and /supported. You take on what a managed facilitator did for you: chain access, a funded account to pay gas, and keeping it up. That is a reasonable trade if you already operate chain infrastructure, and a lot of work if you do not.
How to choose
Ask these questions in order. The first two rule options out; the rest decide between what is left.
Does it support your scheme and network?
Call /supported. The response lists every combination the facilitator handles:
{
"kinds": [
{ "x402Version": 2, "scheme": "exact", "network": "eip155:8453" }
],
"extensions": [],
"signers": {}
}
That is the shape, not a real facilitator's output. If your route advertises eip155:8453 and that network is missing from kinds, payments will fail at verification no matter what else you get right. In Laravel, php artisan x402:doctor calls /supported and checks it against your configured networks.
Is it testnet or mainnet?
Networks are CAIP-2 ids: Base is eip155:8453, Base Sepolia is eip155:84532. A testnet-only facilitator is fine for staging and wrong for production. You can run both: x402.org on staging, a mainnet facilitator in production, chosen per environment.
How does it authenticate?
A facilitator either accepts a bearer token or wants headers built per request. This decides how you configure it and how you rotate credentials. In Laravel, a static token goes in X402_FACILITATOR_TOKEN; per-request signing goes in a class you name in X402_FACILITATOR_AUTH.
What happens when it is slow or down?
Every paid request makes at least two facilitator calls. Its latency adds to yours, and its outage becomes your outage unless you decide otherwise. Look at where it runs relative to your servers, and decide your timeout and failure policy before launch (see below).
Configuring a facilitator
Laravel
The requestway/laravel-x402 package reads the facilitator from the environment:
X402_FACILITATOR_URL=https://x402.org/facilitator
X402_FACILITATOR_TOKEN=
X402_FACILITATOR_TIMEOUT=30
For CDP, point the URL at its endpoint and give the package a class that builds headers for each operation:
X402_FACILITATOR_URL=https://api.cdp.coinbase.com/platform/v2/x402
X402_FACILITATOR_AUTH=App\Services\CdpAuth
use Requestway\X402\Core\Facilitator\FacilitatorAuth;
final class CdpAuth implements FacilitatorAuth
{
public function headers(string $operation): array
{
// $operation is 'verify', 'settle' or 'supported'.
// Build and return the signed headers CDP expects for this call.
}
}
The default timeout is 30 seconds. The reference TypeScript client defaults to 90; the package chose 30 because a PHP worker held for 90 seconds is rarely what you want.
Node
With the official v2 packages, the facilitator is the HTTPFacilitatorClient you pass to the resource server:
import { HTTPFacilitatorClient, x402ResourceServer } from '@x402/core/server'
import { ExactEvmScheme } from '@x402/evm/exact/server'
const server = new x402ResourceServer(new HTTPFacilitatorClient({ url: 'https://x402.org/facilitator' }))
.register('eip155:84532', new ExactEvmScheme())
Switching facilitator is a change to that URL plus whatever authentication it needs; see the SDK in the x402 repository for how the reference client attaches auth headers. The Express, Next.js and Hono guide shows the full middleware setup.
When the facilitator fails
Facilitator failures are where most of the subtle behaviour lives. This is how the Laravel package handles them, and it is a good model to compare any implementation against.
| Situation | What happens |
|---|---|
Connection failure on /verify or /supported |
retried, X402_FACILITATOR_RETRIES times (default 2) |
| Facilitator answers with an HTTP error | not retried: it answered, and asking again would get the same answer |
/settle times out |
not retried unless X402_FACILITATOR_SETTLE_RETRIES is set |
| Facilitator unreachable | client gets 502, resource not served, failure reason facilitator_unavailable |
/settle returns settlement_pending |
retried once, then recorded as pending rather than failed |
The settle timeout rule is the one to understand. A timed-out settle is indeterminate: the transaction may have been broadcast and may confirm. Retrying blindly is safe in the sense that the token contract will not execute the same authorization twice, but the second attempt can report an error for a payment that actually went through. That is why settle retries are off by default.
On an outage you have a policy choice. The default is fail closed: a 502, nothing served, nothing charged. Setting X402_ON_FACILITATOR_ERROR=fail_open serves the resource free instead, which keeps your API up at the cost of giving it away. Choose based on what the endpoint costs you to serve.
Settlement mode interacts with all of this. In the default before_response mode, a failed settle discards the response and the client gets a 402. In after_response mode the client already has its response when settlement runs, so a failure there means you served it unpaid; the package fires PaymentFailed and servedWithoutPayment() returns true. If you pick after_response, alert on that. The failure guide goes through each error code.
Watching your facilitator in production
You will not notice a facilitator degrading from a single request. You notice it as a pattern: failures at the settle stage climbing, or unexpected_verify_error appearing where there was none. The Laravel package stores the facilitator on every payment record, so you can query it, and Requestway groups failures by stage and reason on its dashboard so a facilitator problem stands out from a client problem. Before switching facilitators, the endpoint tester runs a real paid request on testnet and shows the receipt and transaction.
FAQ
Do I need an account to use an x402 facilitator?
Not for testnets: https://x402.org/facilitator is free and needs no account. For mainnet you need a production facilitator, such as Coinbase Developer Platform's, which requires an account and signed requests, or one you host yourself.
Can the x402.org facilitator settle mainnet payments?
No. It supports testnets only. If you switch X402_NETWORK to eip155:8453 and leave the facilitator on x402.org, payments will not settle. The mainnet checklist covers the switch.
Does the x402 facilitator hold my USDC?
No. The payer signs a transfer from their wallet directly to your payTo address, and the facilitator only submits it. It cannot change the amount or the recipient, because both are inside the signature.
Who pays the gas fees for x402 payments?
For EIP-3009 transfers under the "exact" scheme, the facilitator submits the transaction and pays the gas. Neither the agent nor your server needs ETH.
Can I change facilitators without breaking clients?
Yes. The facilitator is server-side configuration and does not appear in the requirements a client signs against, so clients keep working. Check that the new facilitator's /supported lists your networks before you switch.
Related articles
-
Production
A checklist for moving an x402 API from testnet to mainnet
7 min read
-
Concepts
How AI agents pay for APIs: wallets, signatures and facilitators
7 min read
-
Concepts
What HTTP 402 Payment Required means and how x402 uses it
8 min read