xrpl-x402-facilitator¶
Install:
pip install xrpl-x402-facilitator
Use xrpl-x402-facilitator when you need a self-hosted verifier/settler for presigned XRPL payments.
Public Entry Points¶
create_app(...)xrpl_x402_facilitator.main:appxrpl-x402-facilitator
Stable HTTP Contract¶
GET /healthGET /supportedPOST /verifyPOST /settle
Run It¶
export MY_DESTINATION_ADDRESS=rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe
export FACILITATOR_BEARER_TOKEN=replace-with-your-token
export REDIS_URL=redis://127.0.0.1:6379/0
export NETWORK_ID=xrpl:1
export XRPL_RPC_URL=https://your-testnet-rpc.example/
xrpl-x402-facilitator --host 127.0.0.1 --port 8000
Redis is required in every mode because the facilitator uses it for replay protection and rate limiting.
For local demos, python -m devtools.quickstart writes a resolved Testnet XRPL_RPC_URL into
.env.quickstart for you.
Operating Modes¶
Gateway Authentication¶
GATEWAY_AUTH_MODE controls how sellers authenticate to POST /verify and POST /settle.
single_token: every seller sharesFACILITATOR_BEARER_TOKENredis_gateways: bearer tokens are looked up in Redis and tied to a specificgateway_id
Use single_token for local development and single-merchant deployments. Use redis_gateways when you want per-gateway isolation, Redis-backed token status, and stricter freshness checks.
Settlement¶
SETTLEMENT_MODE controls what counts as success after POST /settle.
validated: submit the transaction, poll XRPL until it is validated, and returnstatus="validated"optimistic: submit the transaction and return immediately withstatus="submitted"
validated is the safer default for public deployments. optimistic is useful when downstream systems are already tracking validation and you want lower request latency.
Key Configuration¶
XRPL_RPC_URL: XRPL JSON-RPC endpoint used forSubmitOnly,Tx, and validated-ledger checksMY_DESTINATION_ADDRESS: the only destination address the facilitator will acceptNETWORK_ID: CAIP-2 XRPL network identifier such asxrpl:0orxrpl:1VALIDATION_TIMEOUT: how longvalidatedsettlement mode waits for XRPL validationMIN_XRP_DROPS: minimum acceptable XRP exact paymentALLOWED_ISSUED_ASSETS: comma-separated extra assets inCODE:ISSUERformMAX_REQUEST_BODY_BYTES: hard request-size limit forPOST /verifyandPOST /settleREPLAY_PROCESSED_TTL_SECONDS: how long processed invoice/blob replay markers stay in RedisMAX_PAYMENT_LEDGER_WINDOW: maximum futureLastLedgerSequencewindow inredis_gatewaysmodeENABLE_API_DOCS: exposes/docs,/redoc, and/openapi.jsonwhentrue
The full operator baseline is also shown in the repo-root .env.example.
Supported Assets¶
GET /supported is the discovery endpoint for what a facilitator instance will accept.
The facilitator always supports XRP. It also includes built-in RLUSD and USDC issuers for xrpl:0 and xrpl:1, and appends any extra ALLOWED_ISSUED_ASSETS entries after normalizing duplicates.
Example response:
{
"network": "xrpl:1",
"assets": [
{"code": "XRP"},
{"code": "RLUSD", "issuer": "rnEVYfAWYP5HpPaWQiPSJMyDeUiEJ6zhy2"},
{"code": "USDC", "issuer": "rHuGNhqTG32mfmAvWA8hUyWRLV3tCSwKQt"}
],
"settlement_mode": "validated"
}
The seller middleware caches this response, and the optional x402 server adapter surfaces the same information through SupportedKind.extra.xrpl.
HTTP Contract¶
GET /health¶
Health is intentionally small and stable:
{
"status": "healthy",
"network": "xrpl:1"
}
GET /supported¶
This endpoint requires no authentication and returns the network, supported assets, and settlement mode.
POST /verify¶
Request body:
{
"signed_tx_blob": "120000228000000024...",
"invoice_id": "A7F9C76B2EAC41A9B2D500AA76B8FA18"
}
Successful response:
{
"valid": true,
"invoice_id": "A7F9C76B2EAC41A9B2D500AA76B8FA18",
"amount": "0.001 XRP",
"asset": {"code": "XRP"},
"amount_details": {
"value": "1000",
"unit": "drops",
"asset": {"code": "XRP"},
"drops": 1000
},
"payer": "rBuyerAddress...",
"destination": "rMerchantAddress...",
"message": "Payment valid"
}
/verify confirms that the blob is a signed XRPL Payment, the asset and amount satisfy policy, the destination matches MY_DESTINATION_ADDRESS, and the payment is not already reserved or processed.
POST /settle¶
Request body is identical to /verify.
Successful validated response:
{
"settled": true,
"tx_hash": "A1B2C3...",
"status": "validated"
}
Successful optimistic response:
{
"settled": true,
"tx_hash": "A1B2C3...",
"status": "submitted"
}
/settle reserves the payment against replay, submits the transaction to XRPL, and returns either a validated or submitted settlement state depending on SETTLEMENT_MODE.
Status Codes And Errors¶
200: successfulGET /health,GET /supported,POST /verify, orPOST /settle400: malformed request, including missingsigned_tx_blob401: missing or invalid bearer token on payment endpoints402: the payment was rejected by facilitator policy or settlement failed413: request body exceededMAX_REQUEST_BODY_BYTES429: Redis-backed rate limiter rejected the request
The most useful 402 categories are:
- invalid signature or malformed XRPL transaction
- wrong destination address
- partial payment flag set
- XRP below
MIN_XRP_DROPS - unsupported issued asset
- invoice/blob replay detected
- freshness failures in
redis_gatewaysmode - XRPL submission or validation failure during settlement
Auth, Replay, And Freshness Notes¶
POST /verifyis limited to30/minutePOST /settleis limited to20/minute- rate limits are keyed by authenticated
gateway_idwhen available, otherwise by client IP - replay protection is tracked by both
invoice_idand signed-blob hash redis_gatewaysmode additionally requiresLastLedgerSequenceand rejects stale or overly-far-future transactions
See Header Contract for the wire format that middleware and client exchange, and Replay And Freshness for the Redis and XRPL timing rules behind facilitator decisions.