X402.Facilitator (X402 v0.9.0)

Copy Markdown View Source

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.

Cursor pagination metadata returned by GET /discovery/search.

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.

Functions

Returns a child specification for X402.Facilitator.

Starts the facilitator client.

Payment Verification

verify(payment_payload, requirements)

(since 0.1.0)
@spec verify(map(), map()) :: response()

Verifies a payment using the default facilitator process name.

verify(server, payment_payload, requirements)

(since 0.1.0)
@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(server, payment_payload, requirements, hooks_module)

(since 0.1.0)
@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.

Payment Settlement

settle(payment_payload, requirements)

(since 0.1.0)
@spec settle(map(), map()) :: response()

Settles a payment using the default facilitator process name.

settle(server, payment_payload, requirements)

(since 0.1.0)
@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(server, payment_payload, requirements, hooks_module)

(since 0.1.0)
@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.

Facilitator Discovery

list_resources(server_or_params \\ X402.Facilitator, params \\ [])

(since 0.6.0)
@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 as payTo).

  • :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
  )

search_resources(server_or_params \\ X402.Facilitator, params \\ [])

(since 0.9.0)
@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 as payTo).

  • :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's pagination.cursor.

Examples

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

supported(server \\ X402.Facilitator)

(since 0.6.0)
@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

discovery_pagination()

@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()

@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()

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

Cursor pagination metadata returned by GET /discovery/search.

discovery_search_response()

@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()

@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.

response()

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

server()

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

Facilitator server identifier accepted by GenServer.call/3.

state()

@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()

@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()

@type supported_response() :: %{
  kinds: [supported_kind()],
  extensions: [String.t()],
  signers: %{optional(String.t()) => [String.t()]}
}

Validated response of supported/1.

Functions

child_spec(init_arg)

(since 0.1.0)
@spec child_spec(keyword()) :: Supervisor.child_spec()

Returns a child specification for X402.Facilitator.

start_link(opts)

(since 0.1.0)
@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 is X402.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 where config/runtime.exs is 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 implementing X402.Hooks. The default value is X402.Hooks.Default.

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

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

  • :receive_timeout_ms (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 [].