> ## Documentation Index
> Fetch the complete documentation index at: https://developers.fireblocks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration

> Integrate the Fireblocks x402 Facilitator from your merchant server. Covers the payment processing API, transfer mechanisms, and pricing modes.

<Note>
  **Open-source or hosted? This page describes the open-source facilitator that you run yourself.** Fireblocks also offers a **fully managed, fully secured hosted x402 Facilitator** — production-grade security, operational support, monitoring, and a managed endpoint, with no infrastructure for you to run or upgrade. [Talk to us about early access](https://www.fireblocks.com/#request-demo).
</Note>

This page covers how to wire the facilitator into a merchant server: the middleware pattern, the four endpoints of the payment processing API, when to settle, the four transfer mechanisms, and how to price products. For the high-level concept and a quick-start bootstrap, see the [Overview](/docs/x402-facilitator-overview).

## Integrating from your merchant server

Your server keeps serving its own traffic. You add middleware (Express, Next.js, Hono, or any equivalent — the same pattern as the Coinbase x402 SDK packages) that does two things:

1. On every request, inspect the incoming path. If the path is payment-gated and lacks a `payment-signature` header, return **402** with a JSON body. Your middleware builds this from a product declared in your config.
2. On a request that carries a `payment-signature` header, call this facilitator's `POST /api/payments/verify` to check the signature, then `POST /api/payments/settle` to run the on-chain transaction. If both succeed, serve your resource.

The request your middleware sends to the facilitator:

```bash theme={"system"}
curl -X POST https://facilitator.example.com/api/payments/verify \
  -H "Authorization: Bearer $MERCHANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentPayload":      { /* the client-signed payload */ },
    "paymentRequirements": { /* what you advertised in the 402 */ }
  }'
```

`POST /api/payments/settle` takes the same body shape.

The facilitator never sees the end-user's HTTP request. It only handles the cryptographic verification and the Fireblocks `CONTRACT_CALL`.

## The payment processing API

These are the endpoints your merchant server (or any machine client) hits once you have given it an API key. All endpoints under `/api/payments/*` are authenticated with the merchant API key you minted via `x402 keys create` or `POST /api/admin/tokens` — except `/api/payments/supported`, which is public.

### `GET /api/payments/supported`

Public discovery endpoint. Returns the schemes, networks, and extensions this facilitator accepts.

### `POST /api/payments/create`

Returns a 402 `PaymentRequired` payload for a product without being gated behind a 402 flow. Useful for clients that want the quote before making the real request.

```bash theme={"system"}
curl -X POST https://facilitator.example.com/api/payments/create \
  -H "Authorization: Bearer $MACHINE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"product_id":"prod_7Lm3nOp"}'
```

### `POST /api/payments/verify`

Validates an EIP-712 signature off-chain without moving funds. Use this as a cheap pre-flight to confirm the signature is well-formed, the authorization covers the advertised price, and the payer signed the expected payload.

```bash theme={"system"}
curl -X POST https://facilitator.example.com/api/payments/verify \
  -H "Authorization: Bearer $MERCHANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "paymentPayload":      { /* signed payload from the client */ },
    "paymentRequirements": { /* what you advertised in the 402 */ }
  }'
```

Response shape:

```json theme={"system"}
{ "isValid": true, "payer": "0x...", "invalidReason": null }
```

### `POST /api/payments/settle`

Verifies the signature, then submits the on-chain transfer through Fireblocks. The request body matches `/verify`. The facilitator persists the Fireblocks transaction ID before polling for confirmation, so a process restart never loses track of an in-flight settlement.

```json theme={"system"}
{ "success": true, "txHash": "0x...", "networkId": "base-mainnet" }
```

### When to settle

Your middleware controls the order:

* **Optimistic settlement** — serve the response first, call `/settle` in the background. Simpler, preferred when the resource is idempotent or cheap.
* **Settle-first** — call `/settle` synchronously before serving. Safer for one-shot, expensive deliveries where a refund is not practical.

`/api/payments/settle` is a single atomic operation on the facilitator side. The choice of when to invoke it is a middleware concern, not a facilitator one.

## Transfer mechanisms

Every asset registered on the facilitator declares a `transfer_mechanism`. The mechanism controls how signatures are validated and how the on-chain transfer is executed. A product can offer the same asset under multiple mechanisms; the facilitator emits one `accepts[]` entry per pricing row in the 402 response.

| Mechanism      | Use it when                                                                                                                                                                                                            |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `eip-3009`     | The token natively supports `transferWithAuthorization()` (notably USDC). Single transaction, no helper contract. The default for USDC.                                                                                |
| `permit2`      | You need a universal ERC-20 path that does not depend on the token implementing 3009. Settled through the Uniswap-deployed [Permit2](https://github.com/Uniswap/permit2) contract via the `x402ExactPermit2Proxy`.     |
| `upto-permit2` | The merchant needs to charge a variable amount within a client-signed ceiling — metered API calls billed at the end, for example. The facilitator picks the actual charge up to the signed maximum.                    |
| `erc7710`      | The payer is a smart account (or an EOA upgraded via [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)) authorizing through the MetaMask Delegation Framework. Settled with `DelegationManager.redeemDelegations()`. |

<Warning>
  `upto-permit2` lets the facilitator select the actual charge amount up to the user's signed maximum. Your pricing logic is solely responsible for staying within that ceiling and for charging amounts the payer would expect. There is no protocol-level cap below the signed maximum.
</Warning>

### Picking a mechanism

* If you accept USDC and your customers hold standard USDC, choose `eip-3009`.
* If you accept other ERC-20s, or you want a single code path that works across tokens, choose `permit2`.
* If your billing model is metered or usage-based, choose `upto-permit2`.
* If your customers transact from smart accounts or want delegation-based authorization, choose `erc7710`.

The facilitator enforces the chosen mechanism on `/verify` and `/settle`. A client cannot upgrade from `eip-3009` to `permit2` by shipping a different payload shape — if the resolved mechanism is not in the product's `pricing[]`, the request returns `400 unsupported_scheme`. Requests missing `extra.productId` (that is, requests not originated through `/api/payments/create`) are rejected.

## Pricing modes

Each product has a `pricing[]` table listing one entry per accepted asset. Each entry is one of:

* **Native-denomination** — `amount` is set. The client is charged exactly `amount` base units of that asset, regardless of exchange rates. Useful when you bill in crypto directly.
* **USD-converted** — `amount` is null. The product's `usd_price` (fractional dollars) is converted to this asset at request time using a `PriceProvider`.

### Mixing both on one product

A product can mix native and USD-converted entries in the same `pricing[]`:

```jsonc theme={"system"}
{
  "usd_price": 0.10,
  "pricing": [
    { "asset_id": "USDC_BASE" },                       // convert $0.10 → USDC
    { "asset_id": "USDC_POLYGON", "amount": 90000 },   // override: pay 0.09 USDC on Polygon
    { "asset_id": "ETH_BASE" }                         // convert $0.10 → ETH at the current rate
  ]
}
```

### Multiple transfer mechanisms on the same asset

Each pricing row accepts an optional `transfer_mechanism` override. When present, it takes precedence over the asset's own `transfer_mechanism` — so a single asset can be offered under several mechanisms at once, and the 402 emits one `accepts[]` entry per row.

```jsonc theme={"system"}
{
  "usd_price": 0.10,
  "pricing": [
    { "asset_id": "USDC_BASE" },                                     // inherit → eip-3009
    { "asset_id": "USDC_BASE", "transfer_mechanism": "permit2" },    // same asset, permit2
    { "asset_id": "USDC_BASE", "transfer_mechanism": "erc7710" }     // same asset, MDF delegation
  ]
}
```

### PriceProvider

The bundled `PriceProvider` stack is a `CompositePriceProvider` that tries in order:

1. `StableOnlyPriceProvider` — any asset marked `stable: true` quotes at 1:1 USD. No network call.
2. `CoinGeckoPriceProvider` — queries CoinGecko `/simple/price` using the asset's `price_symbol` (for example, `ethereum`, `weth`, `matic-network`). 30-second in-memory cache; serves stale prices for up to 5 minutes if the live call fails. Pro-tier users can set `COINGECKO_API_KEY`.

Swap for Pyth, Chainlink, or a merchant-owned oracle by implementing the `PriceProvider` interface (`src/services/pricing/PriceProvider.ts`) and passing it into `PricingService` at startup.

Assets that cannot be priced at all (no `stable` flag, no `price_symbol`, live provider down) are **dropped from the 402 response**, not rejected — other accepted assets remain available to the client.

### Gas cost — placeholder today

Ethereum mainnet settlement can cost $1 or more in gas; the same transaction on Base is fractions of a cent. A merchant who quotes "$0.10" should not accept mainnet payments at \$0.10, or they lose money to settlement gas.

The `GasCostEstimator` interface and `NoopGasCostEstimator` are wired into the pricing pipeline today but return `0` on every chain. When you are ready to enable chain-aware pricing, implement `GasCostEstimator.estimate(chainId, mechanism)` (using your RPC, Chainlink gas oracles, or Fireblocks fee estimates) and pick a `GasCostPolicy`:

* `ignore` — the current default.
* `add-to-quote` — gross up the client's charge so merchant revenue is constant.
* `reject-if-above-pct` — drop uneconomic chains from `accepts[]`.

See `src/services/pricing/GasCostEstimator.ts` in the repo for the interfaces.

## What to read next

* [Operating and production](/docs/x402-facilitator-operations) — the config file, auth model, management API, CLI reference, Payment Instruction Integrity, production deployment, and operator responsibilities.
