# `X402.Facilitator`
[🔗](https://github.com/cardotrejos/x402/blob/v0.9.0/lib/x402/facilitator.ex#L1)

Client for the x402 facilitator API: verify, settle, supported, discovery.

The facilitator process is a supervised configuration holder: it resolves
and stores connection settings (URL, Finch pool, hooks, retry policy, auth
state) once at startup. The HTTP work for `verify/2`, `settle/2`,
`supported/1`, and `list_resources/2` — including retries with backoff,
lifecycle hooks, telemetry spans, and per-request auth header minting —
executes in the **calling process**. Concurrent payment operations
therefore never serialize behind the facilitator process; throughput is
bounded only by the Finch pool.

Because operations run in the caller, `X402.Hooks` callbacks are invoked in
the calling process (not in the facilitator process), and the duration of an
operation is bounded by the configured `:receive_timeout_ms` and retry
policy rather than by a `GenServer.call/3` timeout.

## Failover

With `:fallbacks` configured, read operations and `verify/2` retry the
next endpoint on transport errors, timeouts, and 5xx responses; a
per-endpoint circuit breaker (`:failover` `cooldown_ms`) skips endpoints
that recently failed. `settle/2` fails over **only** when the request
provably never reached the endpoint (connection refused, DNS, TLS
handshake) — never on timeouts or 5xx, since the settlement may already
have executed. See `X402.Facilitator.Failover`. Each failover emits
`[:x402, :facilitator, :failover]`.

## Operation results

`verify/2` and `settle/2` return the facilitator's HTTP response as a map
with `:status`, the decoded JSON `:body` (the `VerifyResponse` /
`SettleResponse` object), the response `:headers`, and
`:extension_responses` — the decoded `EXTENSION-RESPONSES` sidechannel
(§7.2.1), or `nil` when the facilitator sent none or sent a malformed
value (see `X402.ExtensionResponses`). The sidechannel is meant for the
resource server only and must not be forwarded to the buyer.

# `verify`
*since 0.1.0* 

```elixir
@spec verify(map(), map()) :: response()
```

Verifies a payment using the default facilitator process name.

# `verify`
*since 0.1.0* 

```elixir
@spec verify(server(), map(), map()) :: response()
```

Verifies a payment using the given facilitator process.

The HTTP request (including retries, hooks, and telemetry) executes in the
calling process; the facilitator process is only consulted for its
configuration.

# `verify`
*since 0.1.0* 

```elixir
@spec verify(server(), map(), map(), module()) :: response()
```

Verifies a payment using the given facilitator process and hook module.

This overrides the hook module configured when the facilitator process
started. Hook callbacks run in the calling process.

# `settle`
*since 0.1.0* 

```elixir
@spec settle(map(), map()) :: response()
```

Settles a payment using the default facilitator process name.

# `settle`
*since 0.1.0* 

```elixir
@spec settle(server(), map(), map()) :: response()
```

Settles a payment using the given facilitator process.

The HTTP request (including retries, hooks, and telemetry) executes in the
calling process; the facilitator process is only consulted for its
configuration.

# `settle`
*since 0.1.0* 

```elixir
@spec settle(server(), map(), map(), module()) :: response()
```

Settles a payment using the given facilitator process and hook module.

This overrides the hook module configured when the facilitator process
started. Hook callbacks run in the calling process.

# `list_resources`
*since 0.6.0* 

```elixir
@spec list_resources(
  server() | keyword(),
  keyword()
) ::
  {:ok, discovery_resources_response()}
  | {:error, X402.Facilitator.Error.t() | NimbleOptions.ValidationError.t()}
```

Lists discoverable x402 resources from the facilitator's bazaar.

Performs `GET /discovery/resources` in the calling process. Filter and
pagination parameters are validated with `NimbleOptions` and encoded as
query string parameters. The response is validated fail-closed: a malformed
body returns
`{:error, %X402.Facilitator.Error{type: :malformed_facilitator_response}}`.

Items are returned as raw, string-keyed maps exactly as sent by the
facilitator. `X402.Hooks` callbacks do not apply to this read-only
operation.

When called with just a keyword list — `list_resources(limit: 20)` — the
parameters apply to the default facilitator process name.

## Parameters

* `:type` (`t:String.t/0`) - Filter by resource type (for example `"http"` or `"mcp"`).

* `:pay_to` (`t:String.t/0`) - Filter by payment recipient address (sent as `payTo`).

* `:scheme` (`t:String.t/0`) - Filter by payment scheme (for example `"exact"`).

* `:network` (`t:String.t/0`) - Filter by CAIP-2 payment network (for example `"eip155:8453"`).

* `:extensions` (`t:String.t/0`) - Filter by extension key present on each discovered resource.

* `:limit` - Maximum number of results to return (1–100; server default 20).

* `:offset` (`t:non_neg_integer/0`) - Number of results to skip for pagination.

## Examples

    {:ok, %{items: items, pagination: pagination}} =
      X402.Facilitator.list_resources(MyFacilitator,
        network: "eip155:8453",
        limit: 20
      )

# `search_resources`
*since 0.9.0* 

```elixir
@spec search_resources(
  server() | keyword(),
  keyword()
) ::
  {:ok, discovery_search_response()}
  | {:error, X402.Facilitator.Error.t() | NimbleOptions.ValidationError.t()}
```

Searches discoverable x402 resources in the facilitator's bazaar.

Performs `GET /discovery/search` in the calling process — the
natural-language search endpoint of the
[bazaar extension](https://github.com/x402-foundation/x402/blob/main/specs/extensions/bazaar.md).
Parameters are validated with `NimbleOptions` and encoded as query string
parameters; `:query` is required. The response is validated fail-closed:
a malformed body returns
`{:error, %X402.Facilitator.Error{type: :malformed_facilitator_response}}`.

Unlike `list_resources/2`, the search endpoint pages with an opaque
cursor: pass `pagination.cursor` from one response as `:cursor` to fetch
the next page, until it is `nil`. Both `:limit` and `:cursor` are
advisory — the facilitator may ignore them.

Resources are returned as raw, string-keyed maps exactly as sent by the
facilitator (`X402.Extensions.Bazaar.search/2` parses them). `X402.Hooks`
callbacks do not apply to this read-only operation.

When called with just a keyword list — `search_resources(query: "weather")`
— the parameters apply to the default facilitator process name.

## Parameters

* `:query` (`t:String.t/0`) - Required. Natural-language search query.

* `:type` (`t:String.t/0`) - Filter by resource type (for example `"http"` or `"mcp"`).

* `:pay_to` (`t:String.t/0`) - Filter by payment recipient address (sent as `payTo`).

* `:scheme` (`t:String.t/0`) - Filter by payment scheme (for example `"exact"`).

* `:network` (`t:String.t/0`) - Filter by CAIP-2 payment network (for example `"eip155:8453"`).

* `:extensions` (`t:String.t/0`) - Filter by extension key present on each discovered resource.

* `:limit` (`t:pos_integer/0`) - Advisory maximum number of results; the facilitator may return fewer or ignore it.

* `:cursor` (`t:String.t/0`) - Advisory continuation cursor from a previous page's `pagination.cursor`.

## Examples

    {:ok, %{resources: resources, pagination: pagination}} =
      X402.Facilitator.search_resources(MyFacilitator,
        query: "weather forecast APIs",
        network: "eip155:8453",
        limit: 10
      )

# `supported`
*since 0.6.0* 

```elixir
@spec supported(server()) ::
  {:ok, supported_response()} | {:error, X402.Facilitator.Error.t()}
```

Fetches the payment kinds, extensions, and signers a facilitator supports.

Performs `GET /supported` in the calling process and validates the response
fail-closed: a malformed body returns
`{:error, %X402.Facilitator.Error{type: :malformed_facilitator_response}}`
rather than partial data. Missing `extensions` and `signers` fields default
to `[]` and `%{}` (matching the reference TypeScript client); a missing or
malformed `kinds` field is an error.

`X402.Hooks` callbacks do not apply to this read-only operation.

## Examples

    {:ok, %{kinds: kinds, extensions: _, signers: signers}} =
      X402.Facilitator.supported(MyFacilitator)

    Enum.any?(kinds, &(&1.scheme == "exact" and &1.network == "eip155:8453"))

# `discovery_pagination`

```elixir
@type discovery_pagination() :: %{
  limit: non_neg_integer(),
  offset: non_neg_integer(),
  total: non_neg_integer()
}
```

Pagination metadata returned by `GET /discovery/resources`.

# `discovery_resources_response`

```elixir
@type discovery_resources_response() :: %{
  x402_version: integer() | nil,
  items: [map()],
  pagination: discovery_pagination() | nil
}
```

Validated response of `list_resources/2`.

Each item is the raw, string-keyed discovered-resource map from the wire.

# `discovery_search_pagination`

```elixir
@type discovery_search_pagination() :: %{
  limit: non_neg_integer(),
  cursor: String.t() | nil
}
```

Cursor pagination metadata returned by `GET /discovery/search`.

# `discovery_search_response`

```elixir
@type discovery_search_response() :: %{
  x402_version: integer() | nil,
  resources: [map()],
  partial_results: boolean() | nil,
  pagination: discovery_search_pagination() | nil
}
```

Validated response of `search_resources/2`.

Each resource is the raw, string-keyed discovered-resource map from the
wire. `:partial_results` is `true` when the facilitator truncated the
match list, `nil` when it did not say.

# `operation_result`

```elixir
@type operation_result() :: map()
```

Facilitator response payload, including values recovered or transformed
by hooks.

For a response that reached the facilitator this is a
`t:X402.Facilitator.HTTP.success/0` map extended with
`extension_responses: X402.ExtensionResponses.t() | nil`.

# `response`

```elixir
@type response() ::
  {:ok, operation_result()}
  | {:error, X402.Facilitator.Error.t() | X402.Hooks.hook_error() | term()}
```

# `server`

```elixir
@type server() :: GenServer.server()
```

Facilitator server identifier accepted by `GenServer.call/3`.

# `state`

```elixir
@type state() :: %{
  url: String.t(),
  finch: term(),
  hooks: module(),
  auth: nil | X402.Facilitator.Auth.t(),
  max_retries: non_neg_integer(),
  retry_backoff_ms: non_neg_integer(),
  receive_timeout_ms: non_neg_integer(),
  fallbacks: [X402.Facilitator.Failover.endpoint()],
  failover: X402.Facilitator.Failover.policy(),
  breaker: X402.Facilitator.Failover.breaker()
}
```

# `supported_kind`

```elixir
@type supported_kind() :: %{
  x402_version: integer(),
  scheme: String.t(),
  network: String.t(),
  extra: map() | nil
}
```

One supported payment kind advertised by `GET /supported`.

# `supported_response`

```elixir
@type supported_response() :: %{
  kinds: [supported_kind()],
  extensions: [String.t()],
  signers: %{optional(String.t()) =&gt; [String.t()]}
}
```

Validated response of `supported/1`.

# `child_spec`
*since 0.1.0* 

```elixir
@spec child_spec(keyword()) :: Supervisor.child_spec()
```

Returns a child specification for `X402.Facilitator`.

# `start_link`
*since 0.1.0* 

```elixir
@spec start_link(keyword()) ::
  GenServer.on_start() | {:error, NimbleOptions.ValidationError.t()}
```

Starts the facilitator client.

When an `otp_app` is given, options are merged over the `config :app, <name>`
configuration entry, with the explicit options taking precedence. This
enables configuring the facilitator at runtime without hardcoding secrets:

    # config/runtime.exs
    config :my_app, MyX402,
      url: X402.Facilitator.Auth.CDP.facilitator_url(),
      finch: MyFinch,
      auth: {X402.Facilitator.Auth.CDP,
             api_key_id: System.fetch_env!("CDP_API_KEY_ID"),
             api_key_secret: System.fetch_env!("CDP_API_KEY_SECRET")}

    # application.ex
    children = [{X402.Facilitator, otp_app: :my_app, name: MyX402}]

## Options

* `:name` (`t:term/0`) - Registered name of the facilitator client process. The default value is `X402.Facilitator`.

* `:otp_app` (`t:atom/0`) - Application that holds this facilitator's configuration. When set, `config :app, <name>` is merged under the given options, with the options taking precedence. Enables the Ecto-style pattern where `config/runtime.exs` is the single source of truth. Available since v0.5.0.

* `:url` (`t:String.t/0`) - Facilitator base URL. The default value is `"https://x402.org/facilitator"`.

* `:finch` (`t:term/0`) - Required. Finch process name used for HTTP requests.

* `:hooks` - Lifecycle hook module implementing `X402.Hooks`. The default value is `X402.Hooks.Default`.

* `:max_retries` (`t:non_neg_integer/0`) - Maximum retry count for transient errors. The default value is `2`.

* `:retry_backoff_ms` (`t:non_neg_integer/0`) - Initial retry backoff in milliseconds. The default value is `100`.

* `:receive_timeout_ms` (`t:non_neg_integer/0`) - HTTP receive timeout in milliseconds. The default value is `5000`.

* `:auth` - Request authentication. Either `nil` (no authentication), an `X402.Facilitator.Auth` module, or a `{module, opts}` tuple. See `X402.Facilitator.Auth.CDP` for the Coinbase Developer Platform facilitator. Available since v0.5.0. The default value is `nil`.

* `:fallbacks` - Fallback endpoints tried in order when the primary fails — a list of keyword lists with `:url` (required), `:auth`, `:finch`, `:max_retries`, `:retry_backoff_ms`, and `:receive_timeout_ms` (unset transport settings inherit the primary's). See `X402.Facilitator.Failover` for which errors fail over — settlement only on provably undelivered requests. Available since v0.9.0. The default value is `[]`.

* `:failover` - Failover policy: `:max_attempts` (endpoints tried per operation, default all) and `:cooldown_ms` (how long an endpoint that failed over is skipped, default 30000). Available since v0.9.0. The default value is `[]`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
