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.
Summary
Payment Verification
Verifies a payment using the default facilitator process name.
Verifies a payment using the given facilitator process.
Verifies a payment using the given facilitator process and hook module.
Payment Settlement
Settles a payment using the default facilitator process name.
Settles a payment using the given facilitator process.
Settles a payment using the given facilitator process and hook module.
Facilitator Discovery
Lists discoverable x402 resources from the facilitator's bazaar.
Searches discoverable x402 resources in the facilitator's bazaar.
Fetches the payment kinds, extensions, and signers a facilitator supports.
Types
Pagination metadata returned by GET /discovery/resources.
Validated response of list_resources/2.
Cursor pagination metadata returned by GET /discovery/search.
Validated response of search_resources/2.
Facilitator response payload, including values recovered or transformed by hooks.
Facilitator server identifier accepted by GenServer.call/3.
One supported payment kind advertised by GET /supported.
Validated response of supported/1.
Payment Verification
Verifies a payment using the default facilitator process name.
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.
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.
Payment Settlement
Settles a payment using the default facilitator process name.
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.
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.
Facilitator Discovery
@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(String.t/0) - Filter by resource type (for example"http"or"mcp").:pay_to(String.t/0) - Filter by payment recipient address (sent aspayTo).:scheme(String.t/0) - Filter by payment scheme (for example"exact").:network(String.t/0) - Filter by CAIP-2 payment network (for example"eip155:8453").:extensions(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(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
)
@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.
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(String.t/0) - Required. Natural-language search query.:type(String.t/0) - Filter by resource type (for example"http"or"mcp").:pay_to(String.t/0) - Filter by payment recipient address (sent aspayTo).:scheme(String.t/0) - Filter by payment scheme (for example"exact").:network(String.t/0) - Filter by CAIP-2 payment network (for example"eip155:8453").:extensions(String.t/0) - Filter by extension key present on each discovered resource.:limit(pos_integer/0) - Advisory maximum number of results; the facilitator may return fewer or ignore it.:cursor(String.t/0) - Advisory continuation cursor from a previous page'spagination.cursor.
Examples
{:ok, %{resources: resources, pagination: pagination}} =
X402.Facilitator.search_resources(MyFacilitator,
query: "weather forecast APIs",
network: "eip155:8453",
limit: 10
)
@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"))
Types
@type discovery_pagination() :: %{ limit: non_neg_integer(), offset: non_neg_integer(), total: non_neg_integer() }
Pagination metadata returned by GET /discovery/resources.
@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.
@type discovery_search_pagination() :: %{ limit: non_neg_integer(), cursor: String.t() | nil }
Cursor pagination metadata returned by GET /discovery/search.
@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.
@type operation_result() :: map()
Facilitator response payload, including values recovered or transformed by hooks.
For a response that reached the facilitator this is a
X402.Facilitator.HTTP.success/0 map extended with
extension_responses: X402.ExtensionResponses.t() | nil.
@type response() :: {:ok, operation_result()} | {:error, X402.Facilitator.Error.t() | X402.Hooks.hook_error() | term()}
@type server() :: GenServer.server()
Facilitator server identifier accepted by GenServer.call/3.
@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() }
@type supported_kind() :: %{ x402_version: integer(), scheme: String.t(), network: String.t(), extra: map() | nil }
One supported payment kind advertised by GET /supported.
@type supported_response() :: %{ kinds: [supported_kind()], extensions: [String.t()], signers: %{optional(String.t()) => [String.t()]} }
Validated response of supported/1.
Functions
@spec child_spec(keyword()) :: Supervisor.child_spec()
Returns a child specification for X402.Facilitator.
@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(term/0) - Registered name of the facilitator client process. The default value isX402.Facilitator.:otp_app(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 whereconfig/runtime.exsis the single source of truth. Available since v0.5.0.:url(String.t/0) - Facilitator base URL. The default value is"https://x402.org/facilitator".:finch(term/0) - Required. Finch process name used for HTTP requests.:hooks- Lifecycle hook module implementingX402.Hooks. The default value isX402.Hooks.Default.:max_retries(non_neg_integer/0) - Maximum retry count for transient errors. The default value is2.:retry_backoff_ms(non_neg_integer/0) - Initial retry backoff in milliseconds. The default value is100.:receive_timeout_ms(non_neg_integer/0) - HTTP receive timeout in milliseconds. The default value is5000.:auth- Request authentication. Eithernil(no authentication), anX402.Facilitator.Authmodule, or a{module, opts}tuple. SeeX402.Facilitator.Auth.CDPfor the Coinbase Developer Platform facilitator. Available since v0.5.0. The default value isnil.: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). SeeX402.Facilitator.Failoverfor 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[].